VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioValidationKit.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 62.4 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * Host audio driver - ValidationKit - For dumping and injecting audio data from/to the device emulation.
4 */
5
6/*
7 * Copyright (C) 2016-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Defined Constants And Macros *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
33#include <iprt/dir.h>
34#include <iprt/env.h>
35#include <iprt/mem.h>
36#include <iprt/path.h>
37#include <iprt/semaphore.h>
38#include <iprt/stream.h>
39#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
40
41#include <VBox/log.h>
42#include <VBox/vmm/pdmaudioifs.h>
43#include <VBox/vmm/pdmaudioinline.h>
44
45#include "VBoxDD.h"
46#include "AudioHlp.h"
47#include "AudioTest.h"
48#include "AudioTestService.h"
49
50
51#ifdef DEBUG_andy
52/** Enables dumping audio streams to the temporary directory for debugging. */
53# define VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
54#endif
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * Structure for keeping a Validation Kit input/output stream.
62 */
63typedef struct VALKITAUDIOSTREAM
64{
65 /** Common part. */
66 PDMAUDIOBACKENDSTREAM Core;
67 /** The stream's acquired configuration. */
68 PDMAUDIOSTREAMCFG Cfg;
69#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
70 /** Audio file to dump output to. */
71 PAUDIOHLPFILE pFile;
72#endif
73} VALKITAUDIOSTREAM;
74/** Pointer to a Validation Kit stream. */
75typedef VALKITAUDIOSTREAM *PVALKITAUDIOSTREAM;
76
77/**
78 * Test tone-specific instance data.
79 */
80typedef struct VALKITTESTTONEDATA
81{
82 /* Test tone beacon to use.
83 * Will be re-used for pre/post beacons. */
84 AUDIOTESTTONEBEACON Beacon;
85 union
86 {
87 struct
88 {
89 /** How many bytes to write. */
90 uint64_t cbToWrite;
91 /** How many bytes already written. */
92 uint64_t cbWritten;
93 } Rec;
94 struct
95 {
96 /** How many bytes to read. */
97 uint64_t cbToRead;
98 /** How many bytes already read. */
99 uint64_t cbRead;
100 } Play;
101 } u;
102 /** The test tone instance to use. */
103 AUDIOTESTTONE Tone;
104 /** The test tone parameters to use. */
105 AUDIOTESTTONEPARMS Parms;
106} VALKITTESTTONEDATA;
107
108/**
109 * Structure keeping a single Validation Kit test.
110 */
111typedef struct VALKITTESTDATA
112{
113 /** The list node. */
114 RTLISTNODE Node;
115 /** Index in test sequence (0-based). */
116 uint32_t idxTest;
117 /** Current test set entry to process. */
118 PAUDIOTESTENTRY pEntry;
119 /** Current test state. */
120 AUDIOTESTSTATE enmState;
121 /** Current test object to process. */
122 AUDIOTESTOBJ Obj;
123 /** Stream configuration to use for this test. */
124 PDMAUDIOSTREAMCFG StreamCfg;
125 union
126 {
127 /** Test tone-specific data. */
128 VALKITTESTTONEDATA TestTone;
129 } t;
130 /** Time stamp (real, in ms) when test got registered. */
131 uint64_t msRegisteredTS;
132 /** Time stamp (real, in ms) when test started. */
133 uint64_t msStartedTS;
134} VALKITTESTDATA;
135/** Pointer to Validation Kit test data. */
136typedef VALKITTESTDATA *PVALKITTESTDATA;
137
138/**
139 * Validation Kit audio driver instance data.
140 * @implements PDMIAUDIOCONNECTOR
141 */
142typedef struct DRVHOSTVALKITAUDIO
143{
144 /** Pointer to the driver instance structure. */
145 PPDMDRVINS pDrvIns;
146 /** Pointer to host audio interface. */
147 PDMIHOSTAUDIO IHostAudio;
148 /** Total number of bytes played since driver construction. */
149 uint64_t cbPlayedTotal;
150 /** Total number of bytes recorded since driver construction. */
151 uint64_t cbRecordedTotal;
152 /** Total number of bytes silence was played in a consequtive block so far.
153 * Will be reset once audible data is being played (again). */
154 uint64_t cbPlayedSilence;
155 /** Total number of bytes audio (audible or not) was played while no active
156 * audio test was registered / available. */
157 uint64_t cbPlayedNoTest;
158 /** Temporary path to use. */
159 char szPathTemp[RTPATH_MAX];
160 /** Output path to use. */
161 char szPathOut[RTPATH_MAX];
162 /** Current test set being handled.
163 * At the moment only one test set can be around at a time. */
164 AUDIOTESTSET Set;
165 /** Number of total tests in \a lstTestsRec and \a lstTestsPlay. */
166 uint32_t cTestsTotal;
167 /** Number of tests in \a lstTestsRec. */
168 uint32_t cTestsRec;
169 /** List keeping the recording tests (FIFO). */
170 RTLISTANCHOR lstTestsRec;
171 /** Pointer to current recording test being processed.
172 * NULL if no current test active. */
173 PVALKITTESTDATA pTestCurRec;
174 /** Number of tests in \a lstTestsPlay. */
175 uint32_t cTestsPlay;
176 /** List keeping the recording tests (FIFO). */
177 RTLISTANCHOR lstTestsPlay;
178 /** Pointer to current playback test being processed.
179 * NULL if no current test active. */
180 PVALKITTESTDATA pTestCurPlay;
181 /** Critical section for serializing access across threads. */
182 RTCRITSECT CritSect;
183 /** Whether the test set needs to end.
184 * Needed for packing up (to archive) and termination, as capturing and playback
185 * can run in asynchronous threads. */
186 bool fTestSetEnd;
187 /** Event semaphore for waiting on the current test set to end. */
188 RTSEMEVENT EventSemEnded;
189 /** The Audio Test Service (ATS) instance. */
190 ATSSERVER Srv;
191 /** Absolute path to the packed up test set archive.
192 * Keep it simple for now and only support one (open) archive at a time. */
193 char szTestSetArchive[RTPATH_MAX];
194 /** File handle to the (opened) test set archive for reading. */
195 RTFILE hTestSetArchive;
196
197} DRVHOSTVALKITAUDIO;
198/** Pointer to a Validation Kit host audio driver instance. */
199typedef DRVHOSTVALKITAUDIO *PDRVHOSTVALKITAUDIO;
200
201
202/*********************************************************************************************************************************
203* Internal test handling code *
204*********************************************************************************************************************************/
205
206/**
207 * Unregisters a ValKit test, common code.
208 *
209 * @param pThis ValKit audio driver instance.
210 * @param pTst Test to unregister.
211 * The pointer will be invalid afterwards.
212 */
213static void drvHostValKiUnregisterTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
214{
215 AssertPtrReturnVoid(pTst);
216
217 RTListNodeRemove(&pTst->Node);
218
219 AudioTestObjClose(pTst->Obj);
220 pTst->Obj = NULL;
221
222 if (pTst->pEntry) /* Set set entry assign? Mark as done. */
223 {
224 AssertPtrReturnVoid(pTst->pEntry);
225 pTst->pEntry = NULL;
226 }
227
228 RTMemFree(pTst);
229 pTst = NULL;
230
231 Assert(pThis->cTestsTotal);
232 pThis->cTestsTotal--;
233 if (pThis->cTestsTotal == 0)
234 {
235 if (ASMAtomicReadBool(&pThis->fTestSetEnd))
236 {
237 int rc2 = RTSemEventSignal(pThis->EventSemEnded);
238 AssertRC(rc2);
239 }
240 }
241}
242
243/**
244 * Unregisters a ValKit recording test.
245 *
246 * @param pThis ValKit audio driver instance.
247 * @param pTst Test to unregister.
248 * The pointer will be invalid afterwards.
249 */
250static void drvHostValKiUnregisterRecTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
251{
252 Assert(pThis->cTestsRec);
253 pThis->cTestsRec--;
254
255 drvHostValKiUnregisterTest(pThis, pTst);
256}
257
258/**
259 * Unregisters a ValKit playback test.
260 *
261 * @param pThis ValKit audio driver instance.
262 * @param pTst Test to unregister.
263 * The pointer will be invalid afterwards.
264 */
265static void drvHostValKiUnregisterPlayTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
266{
267 Assert(pThis->cTestsPlay);
268 pThis->cTestsPlay--;
269
270 drvHostValKiUnregisterTest(pThis, pTst);
271}
272
273/**
274 * Performs some internal cleanup / housekeeping of all registered tests.
275 *
276 * @param pThis ValKit audio driver instance.
277 */
278static void drvHostValKitCleanup(PDRVHOSTVALKITAUDIO pThis)
279{
280 LogRel(("ValKit: Cleaning up ...\n"));
281
282 if ( pThis->cTestsTotal
283 && ( !pThis->cbPlayedTotal
284 && !pThis->cbRecordedTotal)
285 )
286 {
287 LogRel(("ValKit: Warning: Did not get any audio data to play or record altough tests were configured\n\n"));
288 LogRel(("ValKit: Hints:\n"
289 "ValKit: - Audio device emulation configured and enabled for the VM?\n"
290 "ValKit: - Audio input and/or output enabled for the VM?\n"
291 "ValKit: - Is the guest able to play / record sound at all?\n"
292 "ValKit: - Is the guest's audio mixer or input / output sinks muted?\n"
293 "ValKit: - Audio stack misconfiguration / bug?\n\n"));
294 }
295
296 if (pThis->cTestsRec)
297 LogRel(("ValKit: Warning: %RU32 guest recording tests still outstanding:\n", pThis->cTestsRec));
298
299 PVALKITTESTDATA pTst, pTstNext;
300 RTListForEachSafe(&pThis->lstTestsRec, pTst, pTstNext, VALKITTESTDATA, Node)
301 {
302 if (pTst->enmState != AUDIOTESTSTATE_DONE)
303 LogRel(("ValKit: \tWarning: Test #%RU32 (recording) not done yet (state is '%s')\n",
304 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
305
306 if (pTst->t.TestTone.u.Rec.cbToWrite > pTst->t.TestTone.u.Rec.cbWritten)
307 {
308 size_t const cbOutstanding = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
309 if (cbOutstanding)
310 LogRel(("ValKit: \tWarning: Recording test #%RU32 has %RU64 bytes (%RU64ms) outstanding (%RU8%% left)\n",
311 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
312 100 - (pTst->t.TestTone.u.Rec.cbWritten * 100) / RT_MAX(pTst->t.TestTone.u.Rec.cbToWrite, 1)));
313 }
314 drvHostValKiUnregisterRecTest(pThis, pTst);
315 }
316
317 if (pThis->cTestsPlay)
318 LogRel(("ValKit: Warning: %RU32 guest playback tests still outstanding:\n", pThis->cTestsPlay));
319
320 RTListForEachSafe(&pThis->lstTestsPlay, pTst, pTstNext, VALKITTESTDATA, Node)
321 {
322 if (pTst->enmState != AUDIOTESTSTATE_DONE)
323 LogRel(("ValKit: \tWarning: Test #%RU32 (playback) not done yet (state is '%s')\n",
324 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
325
326 if (pTst->t.TestTone.u.Play.cbToRead > pTst->t.TestTone.u.Play.cbRead)
327 {
328 size_t const cbOutstanding = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
329 if (cbOutstanding)
330 LogRel(("ValKit: \tWarning: Playback test #%RU32 has %RU64 bytes (%RU64ms) outstanding (%RU8%% left)\n",
331 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
332 100 - (pTst->t.TestTone.u.Play.cbRead * 100) / RT_MAX(pTst->t.TestTone.u.Play.cbToRead, 1)));
333 }
334 drvHostValKiUnregisterPlayTest(pThis, pTst);
335 }
336
337 Assert(pThis->cTestsRec == 0);
338 Assert(pThis->cTestsPlay == 0);
339
340 if (pThis->cbPlayedNoTest)
341 {
342 LogRel2(("ValKit: Warning: Guest was playing back audio when no playback test is active (%RU64 bytes total)\n",
343 pThis->cbPlayedNoTest));
344 pThis->cbPlayedNoTest = 0;
345 }
346}
347
348
349/*********************************************************************************************************************************
350* ATS callback implementations *
351*********************************************************************************************************************************/
352
353/** @copydoc ATSCALLBACKS::pfnHowdy */
354static DECLCALLBACK(int) drvHostValKitHowdy(void const *pvUser)
355{
356 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
357 RT_NOREF(pThis);
358
359 LogRel(("ValKit: Client connected\n"));
360
361 return VINF_SUCCESS;
362}
363
364/** @copydoc ATSCALLBACKS::pfnBye */
365static DECLCALLBACK(int) drvHostValKitBye(void const *pvUser)
366{
367 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
368 RT_NOREF(pThis);
369
370 LogRel(("ValKit: Client disconnected\n"));
371
372 return VINF_SUCCESS;
373}
374
375/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
376static DECLCALLBACK(int) drvHostValKitTestSetBegin(void const *pvUser, const char *pszTag)
377{
378 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
379
380 LogRel(("ValKit: Beginning test set '%s'\n", pszTag));
381
382 int rc = RTCritSectEnter(&pThis->CritSect);
383 if (RT_SUCCESS(rc))
384 {
385 rc = AudioTestSetCreate(&pThis->Set, pThis->szPathTemp, pszTag);
386
387 int rc2 = RTCritSectLeave(&pThis->CritSect);
388 if (RT_SUCCESS(rc))
389 rc = rc2;
390 }
391
392 if (RT_FAILURE(rc))
393 LogRel(("ValKit: Beginning test set failed with %Rrc\n", rc));
394
395 return rc;
396}
397
398/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
399static DECLCALLBACK(int) drvHostValKitTestSetEnd(void const *pvUser, const char *pszTag)
400{
401 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
402
403 LogRel(("ValKit: Ending test set '%s'\n", pszTag));
404
405 int rc = RTCritSectEnter(&pThis->CritSect);
406 if (RT_SUCCESS(rc))
407 {
408 const PAUDIOTESTSET pSet = &pThis->Set;
409
410 const char *pszTagSet = AudioTestSetGetTag(pSet);
411 if (RTStrCmp(pszTagSet, pszTag) != 0)
412 {
413 LogRel(("ValKit: Error: Current test does not match test set to end ('%s' vs '%s')\n", pszTagSet, pszTag));
414
415 int rc2 = RTCritSectLeave(&pThis->CritSect);
416 AssertRC(rc2);
417
418 return VERR_NOT_FOUND; /* Return to the caller. */
419 }
420
421 LogRel(("ValKit: Test set has %RU32 tests total, %RU32 (still) running, %RU32 failures total so far\n",
422 AudioTestSetGetTestsTotal(pSet), AudioTestSetGetTestsRunning(pSet), AudioTestSetGetTotalFailures(pSet)));
423 LogRel(("ValKit: %RU32 tests still registered total (%RU32 play, %RU32 record)\n",
424 pThis->cTestsTotal, pThis->cTestsPlay, pThis->cTestsRec));
425
426 if ( AudioTestSetIsRunning(pSet)
427 || pThis->cTestsTotal)
428 {
429 ASMAtomicWriteBool(&pThis->fTestSetEnd, true);
430
431 rc = RTCritSectLeave(&pThis->CritSect);
432 if (RT_SUCCESS(rc))
433 {
434 LogRel(("ValKit: Waiting for all tests of set '%s' to end ...\n", pszTag));
435 rc = RTSemEventWait(pThis->EventSemEnded, RT_MS_5SEC);
436 if (RT_FAILURE(rc))
437 {
438 LogRel(("ValKit: Waiting for tests of set '%s' to end failed with %Rrc\n", pszTag, rc));
439
440 /* The verification on the host will tell us later which tests did run and which didn't (anymore).
441 * So continue and pack (plus transfer) the test set to the host. */
442 if (rc == VERR_TIMEOUT)
443 rc = VINF_SUCCESS;
444 }
445
446 int rc2 = RTCritSectEnter(&pThis->CritSect);
447 if (RT_SUCCESS(rc))
448 rc = rc2;
449 }
450 }
451
452 if (RT_SUCCESS(rc))
453 {
454 LogRel(("ValKit: Closing test set '%s' ...\n", pszTag));
455
456 /* Close the test set first. */
457 rc = AudioTestSetClose(pSet);
458 if (RT_SUCCESS(rc))
459 {
460 /* Before destroying the test environment, pack up the test set so
461 * that it's ready for transmission. */
462 rc = AudioTestSetPack(pSet, pThis->szPathOut, pThis->szTestSetArchive, sizeof(pThis->szTestSetArchive));
463 if (RT_SUCCESS(rc))
464 {
465 LogRel(("ValKit: Packed up to '%s'\n", pThis->szTestSetArchive));
466 }
467 else
468 LogRel(("ValKit: Packing up test set failed with %Rrc\n", rc));
469
470 /* Do some internal housekeeping. */
471 drvHostValKitCleanup(pThis);
472
473#ifndef DEBUG_andy
474 int rc2 = AudioTestSetWipe(pSet);
475 if (RT_SUCCESS(rc))
476 rc = rc2;
477#endif
478 }
479 else
480 LogRel(("ValKit: Closing test set failed with %Rrc\n", rc));
481
482 int rc2 = AudioTestSetDestroy(pSet);
483 if (RT_FAILURE(rc2))
484 {
485 LogRel(("ValKit: Destroying test set failed with %Rrc\n", rc));
486 if (RT_SUCCESS(rc))
487 rc = rc2;
488 }
489 }
490
491 int rc2 = RTCritSectLeave(&pThis->CritSect);
492 if (RT_SUCCESS(rc))
493 rc = rc2;
494 }
495
496 if (RT_FAILURE(rc))
497 LogRel(("ValKit: Ending test set failed with %Rrc\n", rc));
498
499 return rc;
500}
501
502/** @copydoc ATSCALLBACKS::pfnTonePlay
503 *
504 * Creates and registers a new test tone guest recording test.
505 * This backend will play (inject) input data to the guest.
506 */
507static DECLCALLBACK(int) drvHostValKitRegisterGuestRecTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
508{
509 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
510
511 PVALKITTESTDATA pTst = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
512 AssertPtrReturn(pTst, VERR_NO_MEMORY);
513
514 pTst->enmState = AUDIOTESTSTATE_INIT;
515
516 memcpy(&pTst->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
517
518 PPDMAUDIOPCMPROPS const pProps = &pTst->t.TestTone.Parms.Props;
519
520 AssertReturn(pTst->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
521 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
522
523 AudioTestToneInit(&pTst->t.TestTone.Tone, pProps, pTst->t.TestTone.Parms.dbFreqHz);
524
525 pTst->t.TestTone.u.Rec.cbToWrite = PDMAudioPropsMilliToBytes(pProps,
526 pTst->t.TestTone.Parms.msDuration);
527
528 /* We inject a pre + post beacon before + after the actual test tone.
529 * We always start with the pre beacon. */
530 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pToneParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, pProps);
531
532 int rc = RTCritSectEnter(&pThis->CritSect);
533 if (RT_SUCCESS(rc))
534 {
535 LogRel(("ValKit: Registering guest recording test #%RU32 (%RU32ms, %RU64 bytes) as test #%RU32\n",
536 pThis->cTestsRec, pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
537 pToneParms->Hdr.idxTest));
538
539 const uint32_t cbBeacon = AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon);
540 if (cbBeacon)
541 LogRel2(("ValKit: Test #%RU32: Uses 2 x %RU32 bytes of pre/post beacons\n",
542 pToneParms->Hdr.idxTest, cbBeacon));
543
544 RTListAppend(&pThis->lstTestsRec, &pTst->Node);
545
546 pTst->msRegisteredTS = RTTimeMilliTS();
547 pTst->idxTest = pToneParms->Hdr.idxTest; /* Use the test ID from the host (so that the beacon IDs match). */
548
549 pThis->cTestsRec++;
550 pThis->cTestsTotal++;
551
552 int rc2 = RTCritSectLeave(&pThis->CritSect);
553 AssertRC(rc2);
554 }
555
556 return VINF_SUCCESS;
557}
558
559/** @copydoc ATSCALLBACKS::pfnToneRecord
560 *
561 * Creates and registers a new test tone guest playback test.
562 * This backend will record the guest output data.
563 */
564static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
565{
566 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
567
568 PVALKITTESTDATA pTst = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
569 AssertPtrReturn(pTst, VERR_NO_MEMORY);
570
571 pTst->enmState = AUDIOTESTSTATE_INIT;
572
573 memcpy(&pTst->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
574
575 PPDMAUDIOPCMPROPS const pProps = &pTst->t.TestTone.Parms.Props;
576
577 AssertReturn(pTst->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
578 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
579
580 pTst->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(pProps,
581 pTst->t.TestTone.Parms.msDuration);
582
583 /* We play a pre + post beacon before + after the actual test tone.
584 * We always start with the pre beacon. */
585 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pToneParms->Hdr.idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, pProps);
586
587 int rc = RTCritSectEnter(&pThis->CritSect);
588 if (RT_SUCCESS(rc))
589 {
590 LogRel(("ValKit: Registering guest playback test #%RU32 (%RU32ms, %RU64 bytes) as test #%RU32\n",
591 pThis->cTestsPlay, pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Play.cbToRead,
592 pToneParms->Hdr.idxTest));
593
594 const uint32_t cbBeacon = AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon);
595 if (cbBeacon)
596 LogRel2(("ValKit: Test #%RU32: Uses x %RU32 bytes of pre/post beacons\n",
597 pToneParms->Hdr.idxTest, cbBeacon));
598
599 RTListAppend(&pThis->lstTestsPlay, &pTst->Node);
600
601 pTst->msRegisteredTS = RTTimeMilliTS();
602 pTst->idxTest = pToneParms->Hdr.idxTest; /* Use the test ID from the host (so that the beacon IDs match). */
603
604 pThis->cTestsTotal++;
605 pThis->cTestsPlay++;
606
607 int rc2 = RTCritSectLeave(&pThis->CritSect);
608 AssertRC(rc2);
609 }
610
611 return VINF_SUCCESS;
612}
613
614/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
615static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
616{
617 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
618
619 int rc = RTCritSectEnter(&pThis->CritSect);
620 if (RT_SUCCESS(rc))
621 {
622 if (RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
623 {
624 rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
625 if (RT_SUCCESS(rc))
626 {
627 uint64_t uSize;
628 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
629 if (RT_SUCCESS(rc))
630 LogRel(("ValKit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
631 }
632 }
633 else
634 rc = VERR_FILE_NOT_FOUND;
635
636 int rc2 = RTCritSectLeave(&pThis->CritSect);
637 if (RT_SUCCESS(rc))
638 rc = rc2;
639 }
640
641 if (RT_FAILURE(rc))
642 LogRel(("ValKit: Beginning to send test set '%s' failed with %Rrc\n", pszTag, rc));
643
644 return rc;
645}
646
647/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
648static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
649 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
650{
651 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
652
653 int rc = RTCritSectEnter(&pThis->CritSect);
654 if (RT_SUCCESS(rc))
655 {
656 if (RTFileIsValid(pThis->hTestSetArchive))
657 {
658 rc = RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
659 }
660 else
661 rc = VERR_WRONG_ORDER;
662
663 int rc2 = RTCritSectLeave(&pThis->CritSect);
664 if (RT_SUCCESS(rc))
665 rc = rc2;
666 }
667
668 if (RT_FAILURE(rc))
669 LogRel(("ValKit: Reading from test set '%s' failed with %Rrc\n", pszTag, rc));
670
671 return rc;
672}
673
674/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
675static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
676{
677 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
678
679 int rc = RTCritSectEnter(&pThis->CritSect);
680 if (RT_SUCCESS(rc))
681 {
682 if (RTFileIsValid(pThis->hTestSetArchive))
683 {
684 rc = RTFileClose(pThis->hTestSetArchive);
685 if (RT_SUCCESS(rc))
686 pThis->hTestSetArchive = NIL_RTFILE;
687 }
688
689 int rc2 = RTCritSectLeave(&pThis->CritSect);
690 if (RT_SUCCESS(rc))
691 rc = rc2;
692 }
693
694 if (RT_FAILURE(rc))
695 LogRel(("ValKit: Ending to send test set '%s' failed with %Rrc\n", pszTag, rc));
696
697 return rc;
698}
699
700
701/*********************************************************************************************************************************
702* PDMIHOSTAUDIO interface implementation *
703*********************************************************************************************************************************/
704
705/**
706 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
707 */
708static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
709{
710 RT_NOREF(pInterface);
711 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
712
713 /*
714 * Fill in the config structure.
715 */
716 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
717 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
718 pBackendCfg->fFlags = 0;
719 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
720 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
721
722 return VINF_SUCCESS;
723}
724
725
726/**
727 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
728 */
729static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
730{
731 RT_NOREF(enmDir);
732 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
733
734 return PDMAUDIOBACKENDSTS_RUNNING;
735}
736
737
738/**
739 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
740 */
741static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
742 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
743{
744 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
745 PVALKITAUDIOSTREAM pStreamValKit = (PVALKITAUDIOSTREAM)pStream;
746 AssertPtrReturn(pStreamValKit, VERR_INVALID_POINTER);
747 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
748 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
749 RT_NOREF(pThis);
750
751 PDMAudioStrmCfgCopy(&pStreamValKit->Cfg, pCfgAcq);
752
753 int rc2;
754#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
755 rc2 = AudioHlpFileCreateAndOpenEx(&pStreamValKit->pFile, AUDIOHLPFILETYPE_WAV, NULL /*use temp dir*/,
756 pThis->pDrvIns->iInstance, AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
757 &pCfgReq->Props, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
758 pCfgReq->enmDir == PDMAUDIODIR_IN ? "ValKitAudioIn" : "ValKitAudioOut");
759 if (RT_FAILURE(rc2))
760 LogRel(("ValKit: Failed to creating debug file for %s stream '%s' in the temp directory: %Rrc\n",
761 pCfgReq->enmDir == PDMAUDIODIR_IN ? "input" : "output", pCfgReq->szName, rc2));
762#endif
763
764 int rc = RTCritSectEnter(&pThis->CritSect);
765 if (RT_SUCCESS(rc))
766 {
767 if (pThis->pTestCurRec == NULL)
768 {
769 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
770 if (pThis->pTestCurRec)
771 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
772 }
773
774 PVALKITTESTDATA pTst = pThis->pTestCurRec;
775
776 /* If we have a test registered and in the queue coming up next, use
777 * the beacon size (if any, could be 0) as pre-buffering requirement. */
778 if (pTst)
779 {
780 const uint32_t cFramesBeacon = PDMAudioPropsBytesToFrames(&pCfgAcq->Props,
781 AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon));
782 if (cFramesBeacon) /* Only assign if not 0, otherwise stay with the default. */
783 pCfgAcq->Backend.cFramesPreBuffering = cFramesBeacon;
784 }
785
786 rc2 = RTCritSectLeave(&pThis->CritSect);
787 AssertRC(rc2);
788 }
789
790 return rc;
791}
792
793/**
794 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
795 */
796static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
797 bool fImmediate)
798{
799 RT_NOREF(pInterface, fImmediate);
800 PVALKITAUDIOSTREAM pStreamValKit = (PVALKITAUDIOSTREAM)pStream;
801 AssertPtrReturn(pStreamValKit, VERR_INVALID_POINTER);
802
803#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
804 if (pStreamValKit->pFile)
805 {
806 AudioHlpFileDestroy(pStreamValKit->pFile);
807 pStreamValKit->pFile = NULL;
808 }
809#endif
810
811 return VINF_SUCCESS;
812}
813
814
815/**
816 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
817 */
818static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
819{
820 RT_NOREF(pInterface, pStream);
821 return VINF_SUCCESS;
822}
823
824
825/**
826 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
827 */
828static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
829{
830 RT_NOREF(pInterface, pStream);
831 return VINF_SUCCESS;
832}
833
834
835/**
836 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
837 */
838static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
839{
840 RT_NOREF(pInterface, pStream);
841 return VINF_SUCCESS;
842}
843
844
845/**
846 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
847 */
848static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
849{
850 RT_NOREF(pInterface, pStream);
851 return VINF_SUCCESS;
852}
853
854
855/**
856 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
857 */
858static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
859{
860 RT_NOREF(pInterface, pStream);
861 return VINF_SUCCESS;
862}
863
864
865/**
866 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
867 */
868static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
869{
870 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
871 PVALKITAUDIOSTREAM pStreamValKit = (PVALKITAUDIOSTREAM)pStream;
872
873 if (pStreamValKit->Cfg.enmDir == PDMAUDIODIR_OUT)
874 {
875 LogRel(("ValKit: Warning: Trying to read from non-input stream '%s' -- report this bug!\n",
876 pStreamValKit->Cfg.szName));
877 return 0;
878 }
879
880 /* We return UINT32_MAX by default (when no tests are running [anymore] for not being marked
881 * as "unreliable stream" in the audio mixer. See audioMixerSinkUpdateInput(). */
882 uint32_t cbReadable = UINT32_MAX;
883
884 int rc = RTCritSectEnter(&pThis->CritSect);
885 if (RT_SUCCESS(rc))
886 {
887 if (pThis->pTestCurRec == NULL)
888 {
889 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
890 if (pThis->pTestCurRec)
891 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
892 }
893
894 PVALKITTESTDATA pTst = pThis->pTestCurRec;
895 if (pTst)
896 {
897 switch (pTst->enmState)
898 {
899 case AUDIOTESTSTATE_INIT:
900 RT_FALL_THROUGH();
901 case AUDIOTESTSTATE_PRE:
902 RT_FALL_THROUGH();
903 case AUDIOTESTSTATE_POST:
904 {
905 cbReadable = AudioTestBeaconGetRemaining(&pTst->t.TestTone.Beacon);
906 break;
907 }
908
909 case AUDIOTESTSTATE_RUN:
910 {
911 AssertBreakStmt(pTst->t.TestTone.u.Rec.cbToWrite >= pTst->t.TestTone.u.Rec.cbWritten,
912 rc = VERR_INVALID_STATE);
913 cbReadable = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
914 break;
915 }
916
917 case AUDIOTESTSTATE_DONE:
918 RT_FALL_THROUGH();
919 default:
920 break;
921 }
922
923 LogRel2(("ValKit: Test #%RU32: Reporting %RU32 readable (%zu/%zu, state '%s')\n",
924 pThis->pTestCurRec->idxTest, cbReadable, pTst->t.TestTone.u.Rec.cbWritten, pTst->t.TestTone.u.Rec.cbToWrite,
925 AudioTestStateToStr(pTst->enmState)));
926
927 if (cbReadable == 0)
928 LogRel2(("ValKit: Test #%RU32: Warning: Not readable anymore (state is '%s'), returning 0\n",
929 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
930 }
931
932 int rc2 = RTCritSectLeave(&pThis->CritSect);
933 AssertRC(rc2);
934 }
935
936 if (RT_FAILURE(rc))
937 LogRel(("ValKit: Reporting readable bytes failed with %Rrc\n", rc));
938
939 Log3Func(("returns %#x (%RU32)\n", cbReadable, cbReadable));
940 return cbReadable;
941}
942
943
944/**
945 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
946 */
947static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
948{
949 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
950 RT_NOREF(pStream);
951
952 uint32_t cbWritable = UINT32_MAX;
953 PVALKITTESTDATA pTst = NULL;
954
955 int rc = RTCritSectEnter(&pThis->CritSect);
956 if (RT_SUCCESS(rc))
957 {
958 pTst = pThis->pTestCurPlay;
959
960 if (pTst)
961 {
962 switch (pTst->enmState)
963 {
964 case AUDIOTESTSTATE_PRE:
965 RT_FALL_THROUGH();
966 case AUDIOTESTSTATE_POST:
967 {
968 cbWritable = AudioTestBeaconGetRemaining(&pTst->t.TestTone.Beacon);
969 break;
970 }
971
972 case AUDIOTESTSTATE_RUN:
973 {
974 AssertReturn(pTst->t.TestTone.u.Play.cbToRead >= pTst->t.TestTone.u.Play.cbRead, 0);
975 cbWritable = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
976 break;
977 }
978
979 default:
980 break;
981 }
982
983 LogRel2(("ValKit: Test #%RU32: Reporting %RU32 bytes writable (state is '%s')\n",
984 pTst->idxTest, cbWritable, AudioTestStateToStr(pTst->enmState)));
985
986 if (cbWritable == 0)
987 {
988 LogRel2(("ValKit: Test #%RU32: Warning: Not writable anymore (state is '%s'), returning UINT32_MAX\n",
989 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
990 cbWritable = UINT32_MAX;
991 }
992 }
993 else
994 LogRel2(("ValKit: Reporting UINT32_MAX bytes writable (no playback test running)\n"));
995
996 int rc2 = RTCritSectLeave(&pThis->CritSect);
997 AssertRC(rc2);
998 }
999
1000 return cbWritable;
1001}
1002
1003
1004/**
1005 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
1006 */
1007static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
1008 PPDMAUDIOBACKENDSTREAM pStream)
1009{
1010 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
1011
1012#if 0
1013 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1014 PDMHOSTAUDIOSTREAMSTATE enmState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
1015
1016 if (pStream->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1017 {
1018 int rc2 = RTCritSectEnter(&pThis->CritSect);
1019 if (RT_SUCCESS(rc2))
1020 {
1021 enmState = pThis->cTestsRec == 0
1022 ? PDMHOSTAUDIOSTREAMSTATE_INACTIVE : PDMHOSTAUDIOSTREAMSTATE_OKAY;
1023
1024 rc2 = RTCritSectLeave(&pThis->CritSect);
1025 AssertRC(rc2);
1026 }
1027 }
1028 else
1029 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
1030
1031 return enmState;
1032#else
1033 RT_NOREF(pInterface, pStream);
1034 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
1035#endif
1036}
1037
1038
1039/**
1040 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1041 */
1042static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1043 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1044{
1045 if (cbBuf == 0)
1046 {
1047 /* Fend off draining calls. */
1048 *pcbWritten = 0;
1049 return VINF_SUCCESS;
1050 }
1051
1052 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1053 PVALKITTESTDATA pTst = NULL;
1054
1055 int rc2;
1056#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
1057 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1058 rc2 = AudioHlpFileWrite(pStrmValKit->pFile, pvBuf, cbBuf);
1059 AssertRC(rc2);
1060#endif
1061
1062 /* Flag indicating whether the whole block we're going to play is silence or not. */
1063 bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBuf, cbBuf);
1064
1065 int rc = RTCritSectEnter(&pThis->CritSect);
1066 if (RT_SUCCESS(rc))
1067 {
1068 pThis->cbPlayedTotal += cbBuf; /* Do a bit of accounting. */
1069
1070 if (pThis->pTestCurPlay == NULL)
1071 {
1072 pThis->pTestCurPlay = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
1073 if (pThis->pTestCurPlay)
1074 LogRel(("ValKit: Next guest playback test in queue is test #%RU32\n", pThis->pTestCurPlay->idxTest));
1075 }
1076
1077 pTst = pThis->pTestCurPlay;
1078
1079 rc2 = RTCritSectLeave(&pThis->CritSect);
1080 AssertRC(rc2);
1081 }
1082
1083 if (pTst == NULL) /* Empty list? */
1084 {
1085 pThis->cbPlayedNoTest += cbBuf;
1086
1087 *pcbWritten = cbBuf;
1088 return VINF_SUCCESS;
1089 }
1090
1091 if (pThis->cbPlayedNoTest)
1092 {
1093 LogRel(("ValKit: Warning: Guest was playing back audio (%RU64 bytes, %RU64ms) when no playback test is active\n",
1094 pThis->cbPlayedNoTest, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedNoTest)));
1095 pThis->cbPlayedNoTest = 0;
1096 }
1097
1098 if (fIsAllSilence)
1099 {
1100 pThis->cbPlayedSilence += cbBuf;
1101 }
1102 else /* Audible data */
1103 {
1104 if (pThis->cbPlayedSilence)
1105 LogRel(("ValKit: Guest was playing back %RU64 bytes (%RU64ms) of silence\n",
1106 pThis->cbPlayedSilence, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedSilence)));
1107 pThis->cbPlayedSilence = 0;
1108 }
1109
1110 LogRel3(("ValKit: Test #%RU32: Playing stream '%s' (%RU32 bytes / %RU64ms) -- state is '%s' ...\n",
1111 pTst->idxTest, pStream->pStream->Cfg.szName,
1112 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1113 AudioTestStateToStr(pTst->enmState)));
1114
1115 LogRel4(("ValKit: Playback audio data (%RU32 bytes):\n"
1116 "%.*Rhxd\n", cbBuf, cbBuf, pvBuf));
1117
1118 if (pTst->enmState == AUDIOTESTSTATE_INIT) /* Test not started yet? */
1119 {
1120 AUDIOTESTPARMS Parms;
1121 RT_ZERO(Parms);
1122 Parms.enmDir = PDMAUDIODIR_IN;
1123 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
1124 Parms.TestTone = pTst->t.TestTone.Parms;
1125
1126 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
1127 &Parms, &pTst->pEntry);
1128 if (RT_SUCCESS(rc))
1129 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->Obj);
1130
1131 if (RT_SUCCESS(rc))
1132 {
1133 pTst->msStartedTS = RTTimeMilliTS();
1134 LogRel(("ValKit: Test #%RU32: Recording audio data (%RU16Hz, %RU32ms) for host test #%RU32 started (delay is %RU32ms)\n",
1135 pTst->idxTest, (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration,
1136 Parms.TestTone.Hdr.idxTest, RTTimeMilliTS() - pTst->msRegisteredTS));
1137
1138 char szTimeCreated[RTTIME_STR_LEN];
1139 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
1140 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
1141
1142 pTst->enmState = AUDIOTESTSTATE_PRE;
1143 }
1144 }
1145
1146 uint32_t cbWritten = 0;
1147 uint8_t *auBuf = (uint8_t *)pvBuf;
1148
1149 uint64_t const msStartedTS = RTTimeMilliTS();
1150
1151 while (cbWritten < cbBuf)
1152 {
1153 switch (pTst->enmState)
1154 {
1155 case AUDIOTESTSTATE_PRE:
1156 RT_FALL_THROUGH();
1157 case AUDIOTESTSTATE_POST:
1158 {
1159 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1160
1161 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) beacon data remaining\n",
1162 pTst->idxTest,
1163 AudioTestBeaconGetRemaining(pBeacon),
1164 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, AudioTestBeaconGetRemaining(pBeacon))));
1165
1166 bool fGoToNextStage = false;
1167
1168 if ( AudioTestBeaconGetSize(pBeacon)
1169 && !AudioTestBeaconIsComplete(pBeacon))
1170 {
1171 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1172
1173 size_t off = 0; /* Points at the data right *after* the found beacon data on return. */
1174 rc2 = AudioTestBeaconAddConsecutive(pBeacon, auBuf, cbBuf - cbWritten, &off);
1175 if (RT_SUCCESS(rc2))
1176 {
1177 cbWritten += (uint32_t)off;
1178 auBuf += off;
1179 }
1180 else /* No beacon data found. */
1181 {
1182 LogRel2(("ValKit: Test #%RU32: Warning: Beacon data for '%s' not found (%Rrc) - Skipping ...\n",
1183 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType), rc2));
1184 cbWritten = cbBuf; /* Skip all. */
1185 break;
1186 }
1187
1188 if (fStarted)
1189 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon started (%RU64ms played so far)\n",
1190 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType),
1191 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedTotal)));
1192 if (AudioTestBeaconIsComplete(pBeacon))
1193 {
1194 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon ended\n",
1195 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1196
1197 fGoToNextStage = true;
1198 }
1199 }
1200 else
1201 fGoToNextStage = true;
1202
1203 if (fGoToNextStage)
1204 {
1205 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1206 pTst->enmState = AUDIOTESTSTATE_RUN;
1207 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1208 pTst->enmState = AUDIOTESTSTATE_DONE;
1209 }
1210 break;
1211 }
1212
1213 case AUDIOTESTSTATE_RUN:
1214 {
1215 uint32_t const cbRemaining = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
1216
1217 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) audio data remaining\n",
1218 pTst->idxTest, cbRemaining, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRemaining)));
1219
1220 /* Don't read more than we're told to.
1221 * After the actual test tone data there might come a post beacon which also
1222 * needs to be handled in the AUDIOTESTSTATE_POST state then. */
1223 const uint32_t cbData = RT_MIN(cbBuf - cbWritten, cbRemaining);
1224
1225 pTst->t.TestTone.u.Play.cbRead += cbData;
1226
1227 cbWritten += cbData;
1228 auBuf += cbData;
1229
1230 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
1231 if (fComplete)
1232 {
1233 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
1234 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1235
1236 pTst->enmState = AUDIOTESTSTATE_POST;
1237
1238 /* Re-use the beacon object, but this time it's the post beacon. */
1239 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pTst->idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
1240 &pTst->t.TestTone.Parms.Props);
1241 }
1242 break;
1243 }
1244
1245 case AUDIOTESTSTATE_DONE:
1246 {
1247 /* Handled below. */
1248 break;
1249 }
1250
1251 default:
1252 AssertFailed();
1253 break;
1254 }
1255
1256 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1257 break;
1258
1259 if (RTTimeMilliTS() - msStartedTS > RT_MS_30SEC)
1260 {
1261 LogRel(("ValKit: Test #%RU32: Error: Playback processing timed out -- please report this bug!\n", pTst->idxTest));
1262 break;
1263 }
1264 }
1265
1266 LogRel3(("ValKit: Test #%RU32: Played %RU32/%RU32 bytes\n", pTst->idxTest, cbWritten, cbBuf));
1267
1268 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1269 AssertRC(rc);
1270
1271 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1272 {
1273 AudioTestSetTestDone(pTst->pEntry);
1274
1275 rc = RTCritSectEnter(&pThis->CritSect);
1276 if (RT_SUCCESS(rc))
1277 {
1278 drvHostValKiUnregisterPlayTest(pThis, pTst);
1279
1280 pThis->pTestCurPlay = NULL;
1281 pTst = NULL;
1282
1283 rc2 = RTCritSectLeave(&pThis->CritSect);
1284 if (RT_SUCCESS(rc))
1285 rc = rc2;
1286 }
1287 }
1288
1289 if (RT_FAILURE(rc))
1290 {
1291 if ( pTst
1292 && pTst->pEntry)
1293 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
1294 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
1295 }
1296
1297 *pcbWritten = cbWritten;
1298
1299 return VINF_SUCCESS; /** @todo Return rc here? */
1300}
1301
1302
1303/**
1304 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1305 */
1306static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1307 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1308{
1309 RT_NOREF(pStream);
1310
1311 if (cbBuf == 0)
1312 {
1313 /* Fend off draining calls. */
1314 *pcbRead = 0;
1315 return VINF_SUCCESS;
1316 }
1317
1318 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1319 PVALKITTESTDATA pTst = NULL;
1320
1321 LogRel3(("ValKit: Capturing stream '%s' (%RU32 bytes / %RU64ms -- %RU64 bytes / %RU64ms total so far) ...\n",
1322 pStream->pStream->Cfg.szName,
1323 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1324 pThis->cbRecordedTotal, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbRecordedTotal)));
1325
1326 int rc = RTCritSectEnter(&pThis->CritSect);
1327 if (RT_SUCCESS(rc))
1328 {
1329 if (pThis->pTestCurRec == NULL)
1330 {
1331 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1332 if (pThis->pTestCurRec)
1333 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1334 }
1335
1336 pTst = pThis->pTestCurRec;
1337
1338 int rc2 = RTCritSectLeave(&pThis->CritSect);
1339 AssertRC(rc2);
1340 }
1341
1342 LogRel4(("ValKit: Capture audio data (%RU32 bytes):\n"
1343 "%.*Rhxd\n", cbBuf, cbBuf, pvBuf));
1344
1345 if (pTst == NULL) /* Empty list? */
1346 {
1347 LogRel(("ValKit: Warning: Guest is trying to record audio data when no recording test is active\n"));
1348
1349 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1350 * but the guest side just will record silence and the audio test verification
1351 * will have to deal with (and/or report) it then. */
1352 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1353 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1354
1355 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1356 return VINF_SUCCESS;
1357 }
1358
1359 uint32_t cbWritten = 0;
1360
1361 switch (pTst->enmState)
1362 {
1363 case AUDIOTESTSTATE_INIT: /* Test not started yet? */
1364 {
1365 AUDIOTESTPARMS Parms;
1366 RT_ZERO(Parms);
1367 Parms.enmDir = PDMAUDIODIR_OUT;
1368 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
1369 Parms.TestTone = pTst->t.TestTone.Parms;
1370
1371 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
1372 &Parms, &pTst->pEntry);
1373 if (RT_SUCCESS(rc))
1374 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->Obj);
1375
1376 if (RT_SUCCESS(rc))
1377 {
1378 pTst->msStartedTS = RTTimeMilliTS();
1379 LogRel(("ValKit: Test #%RU32: Injecting audio input data (%RU16Hz, %RU32ms, %RU32 bytes) for host test #%RU32 started (delay is %RU32ms)\n",
1380 pTst->idxTest, (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
1381 pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
1382 Parms.TestTone.Hdr.idxTest, RTTimeMilliTS() - pTst->msRegisteredTS));
1383
1384 char szTimeCreated[RTTIME_STR_LEN];
1385 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
1386 LogRel2(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
1387
1388 pTst->enmState = AUDIOTESTSTATE_PRE;
1389 }
1390 else
1391 break;
1392
1393 RT_FALL_THROUGH();
1394 }
1395
1396 case AUDIOTESTSTATE_PRE:
1397 RT_FALL_THROUGH();
1398 case AUDIOTESTSTATE_POST:
1399 {
1400 bool fGoToNextStage = false;
1401
1402 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1403 if ( AudioTestBeaconGetSize(pBeacon)
1404 && !AudioTestBeaconIsComplete(pBeacon))
1405 {
1406 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1407
1408 uint32_t const cbBeaconRemaining = AudioTestBeaconGetRemaining(pBeacon);
1409 AssertBreakStmt(cbBeaconRemaining, rc = VERR_WRONG_ORDER);
1410
1411 /* Limit to exactly one beacon (pre or post). */
1412 uint32_t const cbToWrite = RT_MIN(cbBuf, cbBeaconRemaining);
1413
1414 rc = AudioTestBeaconWrite(pBeacon, pvBuf, cbToWrite);
1415 if (RT_SUCCESS(rc))
1416 cbWritten = cbToWrite;
1417
1418 if (fStarted)
1419 LogRel2(("ValKit: Test #%RU32: Writing %s beacon begin\n",
1420 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1421 if (AudioTestBeaconIsComplete(pBeacon))
1422 {
1423 LogRel2(("ValKit: Test #%RU32: Writing %s beacon end\n",
1424 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1425
1426 fGoToNextStage = true;
1427 }
1428 }
1429 else
1430 fGoToNextStage = true;
1431
1432 if (fGoToNextStage)
1433 {
1434 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1435 pTst->enmState = AUDIOTESTSTATE_RUN;
1436 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1437 pTst->enmState = AUDIOTESTSTATE_DONE;
1438 }
1439 break;
1440 }
1441
1442 case AUDIOTESTSTATE_RUN:
1443 {
1444 uint32_t const cbToWrite = RT_MIN(cbBuf, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1445 if (cbToWrite)
1446 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbWritten);
1447 if ( RT_SUCCESS(rc)
1448 && cbWritten)
1449 {
1450 Assert(cbWritten == cbToWrite);
1451 pTst->t.TestTone.u.Rec.cbWritten += cbWritten;
1452 }
1453
1454 LogRel3(("ValKit: Test #%RU32: Supplied %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1455 pTst->idxTest, cbWritten, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten));
1456
1457 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1458 if (fComplete)
1459 {
1460 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1461 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1462
1463 pTst->enmState = AUDIOTESTSTATE_POST;
1464
1465 /* Re-use the beacon object, but this time it's the post beacon. */
1466 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pTst->idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
1467 &pTst->t.TestTone.Parms.Props);
1468 }
1469 break;
1470 }
1471
1472 case AUDIOTESTSTATE_DONE:
1473 {
1474 /* Handled below. */
1475 break;
1476 }
1477
1478 default:
1479 AssertFailed();
1480 break;
1481 }
1482
1483 if (RT_SUCCESS(rc))
1484 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1485
1486 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1487 {
1488 AudioTestSetTestDone(pTst->pEntry);
1489
1490 rc = RTCritSectEnter(&pThis->CritSect);
1491 if (RT_SUCCESS(rc))
1492 {
1493 drvHostValKiUnregisterRecTest(pThis, pTst);
1494
1495 pThis->pTestCurRec = NULL;
1496 pTst = NULL;
1497
1498 int rc2 = RTCritSectLeave(&pThis->CritSect);
1499 AssertRC(rc2);
1500 }
1501 }
1502
1503 if (RT_FAILURE(rc))
1504 {
1505 if (pTst->pEntry)
1506 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1507 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1508 }
1509
1510 pThis->cbRecordedTotal += cbWritten; /* Do a bit of accounting. */
1511
1512 *pcbRead = cbWritten;
1513
1514 Log3Func(("returns %Rrc *pcbRead=%#x (%#x/%#x), %#x total\n",
1515 rc, cbWritten, pTst ? pTst->t.TestTone.u.Rec.cbWritten : 0, pTst ? pTst->t.TestTone.u.Rec.cbToWrite : 0,
1516 pThis->cbRecordedTotal));
1517 return VINF_SUCCESS; /** @todo Return rc here? */
1518}
1519
1520
1521/**
1522 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1523 */
1524static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1525{
1526 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1527 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1528
1529 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1530 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1531 return NULL;
1532}
1533
1534
1535/**
1536 * Constructs a VaKit audio driver instance.
1537 *
1538 * @copydoc FNPDMDRVCONSTRUCT
1539 */
1540static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1541{
1542 RT_NOREF(pCfg, fFlags);
1543 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1544 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1545 LogRel(("Audio: Initializing VALKIT driver\n"));
1546
1547 /*
1548 * Init the static parts.
1549 */
1550 pThis->pDrvIns = pDrvIns;
1551 /* IBase */
1552 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1553 /* IHostAudio */
1554 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1555 pThis->IHostAudio.pfnGetDevices = NULL;
1556 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1557 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1558 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1559 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1560 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1561 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1562 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1563 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1564 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1565 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1566 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1567 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1568 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1569 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1570 pThis->IHostAudio.pfnStreamGetPending = NULL;
1571 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1572 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1573 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1574
1575 int rc = RTCritSectInit(&pThis->CritSect);
1576 AssertRCReturn(rc, rc);
1577 rc = RTSemEventCreate(&pThis->EventSemEnded);
1578 AssertRCReturn(rc, rc);
1579
1580 pThis->cbPlayedTotal = 0;
1581 pThis->cbRecordedTotal = 0;
1582 pThis->cbPlayedSilence = 0;
1583 pThis->cbPlayedNoTest = 0;
1584
1585 pThis->cTestsTotal = 0;
1586 pThis->fTestSetEnd = false;
1587
1588 RTListInit(&pThis->lstTestsRec);
1589 pThis->cTestsRec = 0;
1590 RTListInit(&pThis->lstTestsPlay);
1591 pThis->cTestsPlay = 0;
1592
1593 ATSCALLBACKS Callbacks;
1594 RT_ZERO(Callbacks);
1595 Callbacks.pfnHowdy = drvHostValKitHowdy;
1596 Callbacks.pfnBye = drvHostValKitBye;
1597 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1598 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1599 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1600 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1601 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1602 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1603 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1604 Callbacks.pvUser = pThis;
1605
1606 /** @todo Make this configurable via CFGM. */
1607 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1608 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1609
1610 LogRel2(("ValKit: Debug logging enabled\n"));
1611
1612 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1613 pszBindAddr, uBindPort));
1614
1615 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1616 * from starting -- not critical but warn the user though. */
1617 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1618 if (RT_SUCCESS(rc2))
1619 {
1620 RTGETOPTUNION Val;
1621 RT_ZERO(Val);
1622
1623 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1624 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1625 AssertRC(rc2);
1626
1627 Val.psz = pszBindAddr;
1628 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1629 AssertRC(rc2);
1630
1631 Val.u16 = uBindPort;
1632 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1633 AssertRC(rc2);
1634
1635 rc2 = AudioTestSvcStart(&pThis->Srv);
1636 }
1637
1638 if (RT_SUCCESS(rc2))
1639 {
1640 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1641
1642 /** @todo Let the following be customizable by CFGM later. */
1643 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1644 if (RT_SUCCESS(rc2))
1645 {
1646 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1647 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1648 if (RT_SUCCESS(rc2))
1649 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1650 }
1651 }
1652
1653 if (RT_FAILURE(rc2))
1654 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1655
1656 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1657 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1658
1659 return rc;
1660}
1661
1662static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1663{
1664 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1665 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1666
1667 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1668
1669 int rc = AudioTestSvcStop(&pThis->Srv);
1670 if (RT_SUCCESS(rc))
1671 rc = AudioTestSvcDestroy(&pThis->Srv);
1672
1673 if (RT_SUCCESS(rc))
1674 {
1675 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1676 drvHostValKitCleanup(pThis);
1677 }
1678 else
1679 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1680
1681 /* Try cleaning up a bit. */
1682 RTDirRemove(pThis->szPathTemp);
1683 RTDirRemove(pThis->szPathOut);
1684
1685 RTSemEventDestroy(pThis->EventSemEnded);
1686
1687 if (RTCritSectIsInitialized(&pThis->CritSect))
1688 {
1689 int rc2 = RTCritSectDelete(&pThis->CritSect);
1690 if (RT_SUCCESS(rc))
1691 rc = rc2;
1692 }
1693
1694 if (RT_FAILURE(rc))
1695 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1696}
1697
1698/**
1699 * Char driver registration record.
1700 */
1701const PDMDRVREG g_DrvHostValidationKitAudio =
1702{
1703 /* u32Version */
1704 PDM_DRVREG_VERSION,
1705 /* szName */
1706 "ValidationKitAudio",
1707 /* szRCMod */
1708 "",
1709 /* szR0Mod */
1710 "",
1711 /* pszDescription */
1712 "ValidationKitAudio audio host driver",
1713 /* fFlags */
1714 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1715 /* fClass. */
1716 PDM_DRVREG_CLASS_AUDIO,
1717 /* cMaxInstances */
1718 ~0U,
1719 /* cbInstance */
1720 sizeof(DRVHOSTVALKITAUDIO),
1721 /* pfnConstruct */
1722 drvHostValKitAudioConstruct,
1723 /* pfnDestruct */
1724 drvHostValKitAudioDestruct,
1725 /* pfnRelocate */
1726 NULL,
1727 /* pfnIOCtl */
1728 NULL,
1729 /* pfnPowerOn */
1730 NULL,
1731 /* pfnReset */
1732 NULL,
1733 /* pfnSuspend */
1734 NULL,
1735 /* pfnResume */
1736 NULL,
1737 /* pfnAttach */
1738 NULL,
1739 /* pfnDetach */
1740 NULL,
1741 /* pfnPowerOff */
1742 NULL,
1743 /* pfnSoftReset */
1744 NULL,
1745 /* u32EndVersion */
1746 PDM_DRVREG_VERSION
1747};
1748
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette