VirtualBox

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

最後變更 在這個檔案從92227是 92211,由 vboxsync 提交於 3 年 前

Audio/Validation Kit: More code for audio data beacon handling. Now has dedicated beacons for recording / playback tests. bugref:10008

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

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