VirtualBox

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

Last change on this file since 98169 was 98103, checked in by vboxsync, 23 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.3 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 98103 2023-01-17 14:15:46Z 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-2023 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 bytes readable (state is '%s')\n",
924 pTst->idxTest, cbReadable, AudioTestStateToStr(pTst->enmState)));
925
926 if (cbReadable == 0)
927 LogRel2(("ValKit: Test #%RU32: Warning: Not readable anymore (state is '%s'), returning 0\n",
928 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
929 }
930
931 int rc2 = RTCritSectLeave(&pThis->CritSect);
932 AssertRC(rc2);
933 }
934
935 if (RT_FAILURE(rc))
936 LogRel(("ValKit: Reporting readable bytes failed with %Rrc\n", rc));
937
938 Log3Func(("returns %#x (%RU32)\n", cbReadable, cbReadable));
939 return cbReadable;
940}
941
942
943/**
944 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
945 */
946static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
947{
948 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
949 RT_NOREF(pStream);
950
951 uint32_t cbWritable = UINT32_MAX;
952 PVALKITTESTDATA pTst = NULL;
953
954 int rc = RTCritSectEnter(&pThis->CritSect);
955 if (RT_SUCCESS(rc))
956 {
957 pTst = pThis->pTestCurPlay;
958
959 if (pTst)
960 {
961 switch (pTst->enmState)
962 {
963 case AUDIOTESTSTATE_PRE:
964 RT_FALL_THROUGH();
965 case AUDIOTESTSTATE_POST:
966 {
967 cbWritable = AudioTestBeaconGetRemaining(&pTst->t.TestTone.Beacon);
968 break;
969 }
970
971 case AUDIOTESTSTATE_RUN:
972 {
973 AssertReturn(pTst->t.TestTone.u.Play.cbToRead >= pTst->t.TestTone.u.Play.cbRead, 0);
974 cbWritable = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
975 break;
976 }
977
978 default:
979 break;
980 }
981
982 LogRel2(("ValKit: Test #%RU32: Reporting %RU32 bytes writable (state is '%s')\n",
983 pTst->idxTest, cbWritable, AudioTestStateToStr(pTst->enmState)));
984
985 if (cbWritable == 0)
986 {
987 LogRel2(("ValKit: Test #%RU32: Warning: Not writable anymore (state is '%s'), returning UINT32_MAX\n",
988 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
989 cbWritable = UINT32_MAX;
990 }
991 }
992 else
993 LogRel2(("ValKit: Reporting UINT32_MAX bytes writable (no playback test running)\n"));
994
995 int rc2 = RTCritSectLeave(&pThis->CritSect);
996 AssertRC(rc2);
997 }
998
999 return cbWritable;
1000}
1001
1002
1003/**
1004 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
1005 */
1006static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
1007 PPDMAUDIOBACKENDSTREAM pStream)
1008{
1009 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
1010
1011#if 0
1012 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1013 PDMHOSTAUDIOSTREAMSTATE enmState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
1014
1015 if (pStream->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1016 {
1017 int rc2 = RTCritSectEnter(&pThis->CritSect);
1018 if (RT_SUCCESS(rc2))
1019 {
1020 enmState = pThis->cTestsRec == 0
1021 ? PDMHOSTAUDIOSTREAMSTATE_INACTIVE : PDMHOSTAUDIOSTREAMSTATE_OKAY;
1022
1023 rc2 = RTCritSectLeave(&pThis->CritSect);
1024 AssertRC(rc2);
1025 }
1026 }
1027 else
1028 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
1029
1030 return enmState;
1031#else
1032 RT_NOREF(pInterface, pStream);
1033 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
1034#endif
1035}
1036
1037
1038/**
1039 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1040 */
1041static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1042 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1043{
1044 if (cbBuf == 0)
1045 {
1046 /* Fend off draining calls. */
1047 *pcbWritten = 0;
1048 return VINF_SUCCESS;
1049 }
1050
1051 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1052 PVALKITTESTDATA pTst = NULL;
1053
1054 int rc2;
1055#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
1056 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1057 rc2 = AudioHlpFileWrite(pStrmValKit->pFile, pvBuf, cbBuf);
1058 AssertRC(rc2);
1059#endif
1060
1061 /* Flag indicating whether the whole block we're going to play is silence or not. */
1062 bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBuf, cbBuf);
1063
1064 int rc = RTCritSectEnter(&pThis->CritSect);
1065 if (RT_SUCCESS(rc))
1066 {
1067 pThis->cbPlayedTotal += cbBuf; /* Do a bit of accounting. */
1068
1069 if (pThis->pTestCurPlay == NULL)
1070 {
1071 pThis->pTestCurPlay = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
1072 if (pThis->pTestCurPlay)
1073 LogRel(("ValKit: Next guest playback test in queue is test #%RU32\n", pThis->pTestCurPlay->idxTest));
1074 }
1075
1076 pTst = pThis->pTestCurPlay;
1077
1078 rc2 = RTCritSectLeave(&pThis->CritSect);
1079 AssertRC(rc2);
1080 }
1081
1082 if (pTst == NULL) /* Empty list? */
1083 {
1084 pThis->cbPlayedNoTest += cbBuf;
1085
1086 *pcbWritten = cbBuf;
1087 return VINF_SUCCESS;
1088 }
1089
1090 if (pThis->cbPlayedNoTest)
1091 {
1092 LogRel(("ValKit: Warning: Guest was playing back audio (%RU64 bytes, %RU64ms) when no playback test is active\n",
1093 pThis->cbPlayedNoTest, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedNoTest)));
1094 pThis->cbPlayedNoTest = 0;
1095 }
1096
1097 if (fIsAllSilence)
1098 {
1099 pThis->cbPlayedSilence += cbBuf;
1100 }
1101 else /* Audible data */
1102 {
1103 if (pThis->cbPlayedSilence)
1104 LogRel(("ValKit: Guest was playing back %RU64 bytes (%RU64ms) of silence\n",
1105 pThis->cbPlayedSilence, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedSilence)));
1106 pThis->cbPlayedSilence = 0;
1107 }
1108
1109 LogRel3(("ValKit: Test #%RU32: Playing stream '%s' (%RU32 bytes / %RU64ms) -- state is '%s' ...\n",
1110 pTst->idxTest, pStream->pStream->Cfg.szName,
1111 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1112 AudioTestStateToStr(pTst->enmState)));
1113
1114 LogRel4(("ValKit: Playback audio data (%RU32 bytes):\n"
1115 "%.*Rhxd\n", cbBuf, cbBuf, pvBuf));
1116
1117 if (pTst->enmState == AUDIOTESTSTATE_INIT) /* Test not started yet? */
1118 {
1119 AUDIOTESTPARMS Parms;
1120 RT_ZERO(Parms);
1121 Parms.enmDir = PDMAUDIODIR_IN;
1122 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
1123 Parms.TestTone = pTst->t.TestTone.Parms;
1124
1125 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
1126 &Parms, &pTst->pEntry);
1127 if (RT_SUCCESS(rc))
1128 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->Obj);
1129
1130 if (RT_SUCCESS(rc))
1131 {
1132 pTst->msStartedTS = RTTimeMilliTS();
1133 LogRel(("ValKit: Test #%RU32: Recording audio data (%RU16Hz, %RU32ms) for host test #%RU32 started (delay is %RU32ms)\n",
1134 pTst->idxTest, (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration,
1135 Parms.TestTone.Hdr.idxTest, RTTimeMilliTS() - pTst->msRegisteredTS));
1136
1137 char szTimeCreated[RTTIME_STR_LEN];
1138 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
1139 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
1140
1141 pTst->enmState = AUDIOTESTSTATE_PRE;
1142 }
1143 }
1144
1145 uint32_t cbWritten = 0;
1146 uint8_t *auBuf = (uint8_t *)pvBuf;
1147
1148 uint64_t const msStartedTS = RTTimeMilliTS();
1149
1150 while (cbWritten < cbBuf)
1151 {
1152 switch (pTst->enmState)
1153 {
1154 case AUDIOTESTSTATE_PRE:
1155 RT_FALL_THROUGH();
1156 case AUDIOTESTSTATE_POST:
1157 {
1158 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1159
1160 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) beacon data remaining\n",
1161 pTst->idxTest,
1162 AudioTestBeaconGetRemaining(pBeacon),
1163 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, AudioTestBeaconGetRemaining(pBeacon))));
1164
1165 bool fGoToNextStage = false;
1166
1167 if ( AudioTestBeaconGetSize(pBeacon)
1168 && !AudioTestBeaconIsComplete(pBeacon))
1169 {
1170 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1171
1172 size_t off = 0; /* Points at the data right *after* the found beacon data on return. */
1173 rc2 = AudioTestBeaconAddConsecutive(pBeacon, auBuf, cbBuf - cbWritten, &off);
1174 if (RT_SUCCESS(rc2))
1175 {
1176 cbWritten += (uint32_t)off;
1177 auBuf += off;
1178 }
1179 else /* No beacon data found. */
1180 {
1181 LogRel2(("ValKit: Test #%RU32: Warning: Beacon data for '%s' not found (%Rrc) - Skipping ...\n",
1182 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType), rc2));
1183 cbWritten = cbBuf; /* Skip all. */
1184 break;
1185 }
1186
1187 if (fStarted)
1188 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon started (%RU64ms played so far)\n",
1189 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType),
1190 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedTotal)));
1191 if (AudioTestBeaconIsComplete(pBeacon))
1192 {
1193 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon ended\n",
1194 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1195
1196 fGoToNextStage = true;
1197 }
1198 }
1199 else
1200 fGoToNextStage = true;
1201
1202 if (fGoToNextStage)
1203 {
1204 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1205 pTst->enmState = AUDIOTESTSTATE_RUN;
1206 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1207 pTst->enmState = AUDIOTESTSTATE_DONE;
1208 }
1209 break;
1210 }
1211
1212 case AUDIOTESTSTATE_RUN:
1213 {
1214 uint32_t const cbRemaining = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
1215
1216 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) audio data remaining\n",
1217 pTst->idxTest, cbRemaining, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRemaining)));
1218
1219 /* Don't read more than we're told to.
1220 * After the actual test tone data there might come a post beacon which also
1221 * needs to be handled in the AUDIOTESTSTATE_POST state then. */
1222 const uint32_t cbData = RT_MIN(cbBuf - cbWritten, cbRemaining);
1223
1224 pTst->t.TestTone.u.Play.cbRead += cbData;
1225
1226 cbWritten += cbData;
1227 auBuf += cbData;
1228
1229 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
1230 if (fComplete)
1231 {
1232 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
1233 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1234
1235 pTst->enmState = AUDIOTESTSTATE_POST;
1236
1237 /* Re-use the beacon object, but this time it's the post beacon. */
1238 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pTst->idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
1239 &pTst->t.TestTone.Parms.Props);
1240 }
1241 break;
1242 }
1243
1244 case AUDIOTESTSTATE_DONE:
1245 {
1246 /* Handled below. */
1247 break;
1248 }
1249
1250 default:
1251 AssertFailed();
1252 break;
1253 }
1254
1255 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1256 break;
1257
1258 if (RTTimeMilliTS() - msStartedTS > RT_MS_30SEC)
1259 {
1260 LogRel(("ValKit: Test #%RU32: Error: Playback processing timed out -- please report this bug!\n", pTst->idxTest));
1261 break;
1262 }
1263 }
1264
1265 LogRel3(("ValKit: Test #%RU32: Played %RU32/%RU32 bytes\n", pTst->idxTest, cbWritten, cbBuf));
1266
1267 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1268 AssertRC(rc);
1269
1270 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1271 {
1272 AudioTestSetTestDone(pTst->pEntry);
1273
1274 rc = RTCritSectEnter(&pThis->CritSect);
1275 if (RT_SUCCESS(rc))
1276 {
1277 drvHostValKiUnregisterPlayTest(pThis, pTst);
1278
1279 pThis->pTestCurPlay = NULL;
1280 pTst = NULL;
1281
1282 rc2 = RTCritSectLeave(&pThis->CritSect);
1283 if (RT_SUCCESS(rc))
1284 rc = rc2;
1285 }
1286 }
1287
1288 if (RT_FAILURE(rc))
1289 {
1290 if ( pTst
1291 && pTst->pEntry)
1292 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
1293 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
1294 }
1295
1296 *pcbWritten = cbWritten;
1297
1298 return VINF_SUCCESS; /** @todo Return rc here? */
1299}
1300
1301
1302/**
1303 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1304 */
1305static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1306 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1307{
1308 RT_NOREF(pStream);
1309
1310 if (cbBuf == 0)
1311 {
1312 /* Fend off draining calls. */
1313 *pcbRead = 0;
1314 return VINF_SUCCESS;
1315 }
1316
1317 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1318 PVALKITTESTDATA pTst = NULL;
1319
1320 LogRel3(("ValKit: Capturing stream '%s' (%RU32 bytes / %RU64ms -- %RU64 bytes / %RU64ms total so far) ...\n",
1321 pStream->pStream->Cfg.szName,
1322 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1323 pThis->cbRecordedTotal, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbRecordedTotal)));
1324
1325 int rc = RTCritSectEnter(&pThis->CritSect);
1326 if (RT_SUCCESS(rc))
1327 {
1328 if (pThis->pTestCurRec == NULL)
1329 {
1330 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1331 if (pThis->pTestCurRec)
1332 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1333 }
1334
1335 pTst = pThis->pTestCurRec;
1336
1337 int rc2 = RTCritSectLeave(&pThis->CritSect);
1338 AssertRC(rc2);
1339 }
1340
1341 LogRel4(("ValKit: Capture audio data (%RU32 bytes):\n"
1342 "%.*Rhxd\n", cbBuf, cbBuf, pvBuf));
1343
1344 if (pTst == NULL) /* Empty list? */
1345 {
1346 LogRel(("ValKit: Warning: Guest is trying to record audio data when no recording test is active\n"));
1347
1348 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1349 * but the guest side just will record silence and the audio test verification
1350 * will have to deal with (and/or report) it then. */
1351 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1352 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1353
1354 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1355 return VINF_SUCCESS;
1356 }
1357
1358 uint32_t cbWritten = 0;
1359
1360 switch (pTst->enmState)
1361 {
1362 case AUDIOTESTSTATE_INIT: /* Test not started yet? */
1363 {
1364 AUDIOTESTPARMS Parms;
1365 RT_ZERO(Parms);
1366 Parms.enmDir = PDMAUDIODIR_OUT;
1367 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
1368 Parms.TestTone = pTst->t.TestTone.Parms;
1369
1370 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
1371 &Parms, &pTst->pEntry);
1372 if (RT_SUCCESS(rc))
1373 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->Obj);
1374
1375 if (RT_SUCCESS(rc))
1376 {
1377 pTst->msStartedTS = RTTimeMilliTS();
1378 LogRel(("ValKit: Test #%RU32: Injecting audio input data (%RU16Hz, %RU32ms, %RU32 bytes) for host test #%RU32 started (delay is %RU32ms)\n",
1379 pTst->idxTest, (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
1380 pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
1381 Parms.TestTone.Hdr.idxTest, RTTimeMilliTS() - pTst->msRegisteredTS));
1382
1383 char szTimeCreated[RTTIME_STR_LEN];
1384 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
1385 LogRel2(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
1386
1387 pTst->enmState = AUDIOTESTSTATE_PRE;
1388 }
1389 else
1390 break;
1391
1392 RT_FALL_THROUGH();
1393 }
1394
1395 case AUDIOTESTSTATE_PRE:
1396 RT_FALL_THROUGH();
1397 case AUDIOTESTSTATE_POST:
1398 {
1399 bool fGoToNextStage = false;
1400
1401 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1402 if ( AudioTestBeaconGetSize(pBeacon)
1403 && !AudioTestBeaconIsComplete(pBeacon))
1404 {
1405 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1406
1407 uint32_t const cbBeaconRemaining = AudioTestBeaconGetRemaining(pBeacon);
1408 AssertBreakStmt(cbBeaconRemaining, VERR_WRONG_ORDER);
1409
1410 /* Limit to exactly one beacon (pre or post). */
1411 uint32_t const cbToWrite = RT_MIN(cbBuf, cbBeaconRemaining);
1412
1413 rc = AudioTestBeaconWrite(pBeacon, pvBuf, cbToWrite);
1414 if (RT_SUCCESS(rc))
1415 cbWritten = cbToWrite;
1416
1417 if (fStarted)
1418 LogRel2(("ValKit: Test #%RU32: Writing %s beacon begin\n",
1419 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1420 if (AudioTestBeaconIsComplete(pBeacon))
1421 {
1422 LogRel2(("ValKit: Test #%RU32: Writing %s beacon end\n",
1423 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1424
1425 fGoToNextStage = true;
1426 }
1427 }
1428 else
1429 fGoToNextStage = true;
1430
1431 if (fGoToNextStage)
1432 {
1433 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1434 pTst->enmState = AUDIOTESTSTATE_RUN;
1435 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1436 pTst->enmState = AUDIOTESTSTATE_DONE;
1437 }
1438 break;
1439 }
1440
1441 case AUDIOTESTSTATE_RUN:
1442 {
1443 uint32_t const cbToWrite = RT_MIN(cbBuf, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1444 if (cbToWrite)
1445 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbWritten);
1446 if ( RT_SUCCESS(rc)
1447 && cbWritten)
1448 {
1449 Assert(cbWritten == cbToWrite);
1450 pTst->t.TestTone.u.Rec.cbWritten += cbWritten;
1451 }
1452
1453 LogRel3(("ValKit: Test #%RU32: Supplied %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1454 pTst->idxTest, cbWritten, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten));
1455
1456 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1457 if (fComplete)
1458 {
1459 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1460 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1461
1462 pTst->enmState = AUDIOTESTSTATE_POST;
1463
1464 /* Re-use the beacon object, but this time it's the post beacon. */
1465 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, pTst->idxTest, AUDIOTESTTONEBEACONTYPE_PLAY_POST,
1466 &pTst->t.TestTone.Parms.Props);
1467 }
1468 break;
1469 }
1470
1471 case AUDIOTESTSTATE_DONE:
1472 {
1473 /* Handled below. */
1474 break;
1475 }
1476
1477 default:
1478 AssertFailed();
1479 break;
1480 }
1481
1482 if (RT_SUCCESS(rc))
1483 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1484
1485 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1486 {
1487 AudioTestSetTestDone(pTst->pEntry);
1488
1489 rc = RTCritSectEnter(&pThis->CritSect);
1490 if (RT_SUCCESS(rc))
1491 {
1492 drvHostValKiUnregisterRecTest(pThis, pTst);
1493
1494 pThis->pTestCurRec = NULL;
1495 pTst = NULL;
1496
1497 int rc2 = RTCritSectLeave(&pThis->CritSect);
1498 AssertRC(rc2);
1499 }
1500 }
1501
1502 if (RT_FAILURE(rc))
1503 {
1504 if (pTst->pEntry)
1505 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1506 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1507 }
1508
1509 pThis->cbRecordedTotal += cbWritten; /* Do a bit of accounting. */
1510
1511 *pcbRead = cbWritten;
1512
1513 Log3Func(("returns %Rrc *pcbRead=%#x (%#x/%#x), %#x total\n",
1514 rc, cbWritten, pTst ? pTst->t.TestTone.u.Rec.cbWritten : 0, pTst ? pTst->t.TestTone.u.Rec.cbToWrite : 0,
1515 pThis->cbRecordedTotal));
1516 return VINF_SUCCESS; /** @todo Return rc here? */
1517}
1518
1519
1520/**
1521 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1522 */
1523static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1524{
1525 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1526 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1527
1528 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1529 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1530 return NULL;
1531}
1532
1533
1534/**
1535 * Constructs a VaKit audio driver instance.
1536 *
1537 * @copydoc FNPDMDRVCONSTRUCT
1538 */
1539static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1540{
1541 RT_NOREF(pCfg, fFlags);
1542 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1543 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1544 LogRel(("Audio: Initializing VALKIT driver\n"));
1545
1546 /*
1547 * Init the static parts.
1548 */
1549 pThis->pDrvIns = pDrvIns;
1550 /* IBase */
1551 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1552 /* IHostAudio */
1553 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1554 pThis->IHostAudio.pfnGetDevices = NULL;
1555 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1556 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1557 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1558 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1559 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1560 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1561 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1562 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1563 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1564 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1565 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1566 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1567 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1568 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1569 pThis->IHostAudio.pfnStreamGetPending = NULL;
1570 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1571 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1572 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1573
1574 int rc = RTCritSectInit(&pThis->CritSect);
1575 AssertRCReturn(rc, rc);
1576 rc = RTSemEventCreate(&pThis->EventSemEnded);
1577 AssertRCReturn(rc, rc);
1578
1579 pThis->cbPlayedTotal = 0;
1580 pThis->cbRecordedTotal = 0;
1581 pThis->cbPlayedSilence = 0;
1582 pThis->cbPlayedNoTest = 0;
1583
1584 pThis->cTestsTotal = 0;
1585 pThis->fTestSetEnd = false;
1586
1587 RTListInit(&pThis->lstTestsRec);
1588 pThis->cTestsRec = 0;
1589 RTListInit(&pThis->lstTestsPlay);
1590 pThis->cTestsPlay = 0;
1591
1592 ATSCALLBACKS Callbacks;
1593 RT_ZERO(Callbacks);
1594 Callbacks.pfnHowdy = drvHostValKitHowdy;
1595 Callbacks.pfnBye = drvHostValKitBye;
1596 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1597 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1598 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1599 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1600 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1601 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1602 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1603 Callbacks.pvUser = pThis;
1604
1605 /** @todo Make this configurable via CFGM. */
1606 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1607 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1608
1609 LogRel2(("ValKit: Debug logging enabled\n"));
1610
1611 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1612 pszBindAddr, uBindPort));
1613
1614 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1615 * from starting -- not critical but warn the user though. */
1616 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1617 if (RT_SUCCESS(rc2))
1618 {
1619 RTGETOPTUNION Val;
1620 RT_ZERO(Val);
1621
1622 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1623 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1624 AssertRC(rc2);
1625
1626 Val.psz = pszBindAddr;
1627 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1628 AssertRC(rc2);
1629
1630 Val.u16 = uBindPort;
1631 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1632 AssertRC(rc2);
1633
1634 rc2 = AudioTestSvcStart(&pThis->Srv);
1635 }
1636
1637 if (RT_SUCCESS(rc2))
1638 {
1639 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1640
1641 /** @todo Let the following be customizable by CFGM later. */
1642 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1643 if (RT_SUCCESS(rc2))
1644 {
1645 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1646 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1647 if (RT_SUCCESS(rc2))
1648 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1649 }
1650 }
1651
1652 if (RT_FAILURE(rc2))
1653 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1654
1655 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1656 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1657
1658 return rc;
1659}
1660
1661static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1662{
1663 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1664 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1665
1666 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1667
1668 int rc = AudioTestSvcStop(&pThis->Srv);
1669 if (RT_SUCCESS(rc))
1670 rc = AudioTestSvcDestroy(&pThis->Srv);
1671
1672 if (RT_SUCCESS(rc))
1673 {
1674 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1675 drvHostValKitCleanup(pThis);
1676 }
1677 else
1678 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1679
1680 /* Try cleaning up a bit. */
1681 RTDirRemove(pThis->szPathTemp);
1682 RTDirRemove(pThis->szPathOut);
1683
1684 RTSemEventDestroy(pThis->EventSemEnded);
1685
1686 if (RTCritSectIsInitialized(&pThis->CritSect))
1687 {
1688 int rc2 = RTCritSectDelete(&pThis->CritSect);
1689 if (RT_SUCCESS(rc))
1690 rc = rc2;
1691 }
1692
1693 if (RT_FAILURE(rc))
1694 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1695}
1696
1697/**
1698 * Char driver registration record.
1699 */
1700const PDMDRVREG g_DrvHostValidationKitAudio =
1701{
1702 /* u32Version */
1703 PDM_DRVREG_VERSION,
1704 /* szName */
1705 "ValidationKitAudio",
1706 /* szRCMod */
1707 "",
1708 /* szR0Mod */
1709 "",
1710 /* pszDescription */
1711 "ValidationKitAudio audio host driver",
1712 /* fFlags */
1713 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1714 /* fClass. */
1715 PDM_DRVREG_CLASS_AUDIO,
1716 /* cMaxInstances */
1717 ~0U,
1718 /* cbInstance */
1719 sizeof(DRVHOSTVALKITAUDIO),
1720 /* pfnConstruct */
1721 drvHostValKitAudioConstruct,
1722 /* pfnDestruct */
1723 drvHostValKitAudioDestruct,
1724 /* pfnRelocate */
1725 NULL,
1726 /* pfnIOCtl */
1727 NULL,
1728 /* pfnPowerOn */
1729 NULL,
1730 /* pfnReset */
1731 NULL,
1732 /* pfnSuspend */
1733 NULL,
1734 /* pfnResume */
1735 NULL,
1736 /* pfnAttach */
1737 NULL,
1738 /* pfnDetach */
1739 NULL,
1740 /* pfnPowerOff */
1741 NULL,
1742 /* pfnSoftReset */
1743 NULL,
1744 /* u32EndVersion */
1745 PDM_DRVREG_VERSION
1746};
1747
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