VirtualBox

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

Last change on this file since 91861 was 91655, checked in by vboxsync, 3 years ago

Audio/Validation Kit: A bit more code for the audio data beacons. ​bugref:10008

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

© 2023 Oracle
ContactPrivacy policyTerms of Use