VirtualBox

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

Last change on this file since 92208 was 92206, checked in by vboxsync, 3 years ago

Audio/Validation Kit: Do the test state checking / warning in drvHostValKitCleanup() to have it all in one place. bugref:10008

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