VirtualBox

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

Last change on this file since 92030 was 91908, checked in by vboxsync, 3 years ago

Audio/Validation Kit: Implemented support for injecting pre/post audio beacons for the Validation Kit audio driver. ​bugref:10008

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