VirtualBox

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

Last change on this file since 89186 was 89174, checked in by vboxsync, 4 years ago

Audio/ValKit: Started working on the audio test execution service (ATS). bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 89174 2021-05-19 15:05:29Z 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/env.h>
24#include <iprt/mem.h>
25#include <iprt/path.h>
26#include <iprt/stream.h>
27#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
28
29#include <VBox/log.h>
30#include <VBox/vmm/pdmaudioifs.h>
31#include <VBox/vmm/pdmaudioinline.h>
32
33#include "VBoxDD.h"
34#include "AudioHlp.h"
35#include "AudioTest.h"
36#include "AudioTestService.h"
37
38
39/*********************************************************************************************************************************
40* Structures and Typedefs *
41*********************************************************************************************************************************/
42/**
43 * Structure for keeping a validation kit input/output stream.
44 */
45typedef struct VAKITAUDIOSTREAM
46{
47 /** Common part. */
48 PDMAUDIOBACKENDSTREAM Core;
49 /** The stream's acquired configuration. */
50 PDMAUDIOSTREAMCFG Cfg;
51 /** Audio file to dump output to or read input from. */
52 PAUDIOHLPFILE pFile;
53 /** Text file to store timing of audio buffers submittions. */
54 PRTSTREAM pFileTiming;
55 /** Timestamp of the first play or record request. */
56 uint64_t tsStarted;
57 /** Total number of frames played or recorded so far. */
58 uint32_t cFramesSinceStarted;
59 union
60 {
61 struct
62 {
63 /** Timestamp of last captured samples. */
64 uint64_t tsLastCaptured;
65 } In;
66 struct
67 {
68 /** Timestamp of last played samples. */
69 uint64_t tsLastPlayed;
70 uint8_t *pbPlayBuffer;
71 uint32_t cbPlayBuffer;
72 } Out;
73 };
74} VAKITAUDIOSTREAM;
75/** Pointer to a validation kit stream. */
76typedef VAKITAUDIOSTREAM *PVAKITAUDIOSTREAM;
77
78/**
79 * Validation kit audio driver instance data.
80 * @implements PDMIAUDIOCONNECTOR
81 */
82typedef struct DRVHOSTVAKITAUDIO
83{
84 /** Pointer to the driver instance structure. */
85 PPDMDRVINS pDrvIns;
86 /** Pointer to host audio interface. */
87 PDMIHOSTAUDIO IHostAudio;
88} DRVHOSTVAKITAUDIO;
89/** Pointer to a validation kit host audio driver instance. */
90typedef DRVHOSTVAKITAUDIO *PDRVHOSTVAKITAUDIO;
91
92
93
94/**
95 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
96 */
97static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
98{
99 RT_NOREF(pInterface);
100 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
101
102 /*
103 * Fill in the config structure.
104 */
105 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
106 pBackendCfg->cbStream = sizeof(VAKITAUDIOSTREAM);
107 pBackendCfg->fFlags = 0;
108 pBackendCfg->cMaxStreamsOut = 1; /* Output */
109 pBackendCfg->cMaxStreamsIn = 0; /* No input supported yet. */
110
111 return VINF_SUCCESS;
112}
113
114
115/**
116 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
117 */
118static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
119{
120 RT_NOREF(enmDir);
121 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
122
123 return PDMAUDIOBACKENDSTS_RUNNING;
124}
125
126
127static int drvHostValKitAudioCreateStreamIn(PDRVHOSTVAKITAUDIO pThis, PVAKITAUDIOSTREAM pStreamDbg,
128 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
129{
130 RT_NOREF(pThis, pStreamDbg, pCfgReq, pCfgAcq);
131
132 return VINF_SUCCESS;
133}
134
135
136static int drvHostValKitAudioCreateStreamOut(PDRVHOSTVAKITAUDIO pThis, PVAKITAUDIOSTREAM pStreamDbg,
137 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
138{
139 RT_NOREF(pThis, pCfgAcq);
140
141 /* Use the test box scratch dir if we're running in such an
142 environment, otherwise just dump the output in the temp
143 directory. */
144 char szTemp[RTPATH_MAX];
145 int rc = RTEnvGetEx(RTENV_DEFAULT, "TESTBOX_PATH_SCRATCH", szTemp, sizeof(szTemp), NULL);
146 if (RT_FAILURE(rc))
147 {
148 rc = RTPathTemp(szTemp, sizeof(szTemp));
149 if (RT_SUCCESS(rc))
150 rc = RTPathAppend(szTemp, sizeof(szTemp), "VBoxAudioValKit");
151 AssertRCReturn(rc, rc);
152 }
153
154 /* Get down to things that may fail and need cleanup. */
155 pStreamDbg->tsStarted = 0;
156 pStreamDbg->cFramesSinceStarted = 0;
157 pStreamDbg->Out.tsLastPlayed = 0;
158 pStreamDbg->Out.cbPlayBuffer = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
159 pStreamDbg->Out.pbPlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
160 AssertReturn(pStreamDbg->Out.pbPlayBuffer, VERR_NO_MEMORY);
161
162 rc = AudioHlpFileCreateAndOpenEx(&pStreamDbg->pFile, AUDIOHLPFILETYPE_WAV, szTemp, "ValKit",
163 pThis->pDrvIns->iInstance, AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
164 &pCfgReq->Props, AUDIOHLPFILE_DEFAULT_OPEN_FLAGS);
165 if (RT_SUCCESS(rc))
166 {
167 rc = RTPathAppend(szTemp, sizeof(szTemp), "ValKitTimings.txt");
168 if (RT_SUCCESS(rc))
169 {
170 rc = RTStrmOpen(szTemp, "w", &pStreamDbg->pFileTiming);
171 if (RT_SUCCESS(rc))
172 {
173 RTStrmPrintf(pStreamDbg->pFileTiming, "# %uHz %uch %ubit\n",
174 PDMAudioPropsHz(&pCfgReq->Props),
175 PDMAudioPropsChannels(&pCfgReq->Props),
176 PDMAudioPropsSampleBits(&pCfgReq->Props));
177 return VINF_SUCCESS;
178 }
179
180 LogRel(("ValKitAudio: Opening output file '%s' failed: %Rrc\n", szTemp, rc));
181 }
182 else
183 LogRel(("ValKitAudio: Constructing timing file path: %Rrc\n", rc));
184
185 AudioHlpFileDestroy(pStreamDbg->pFile);
186 pStreamDbg->pFile = NULL;
187 }
188 else
189 LogRel(("ValKitAudio: Creating output file 'ValKit' in '%s' failed: %Rrc\n", szTemp, rc));
190
191 RTMemFree(pStreamDbg->Out.pbPlayBuffer);
192 pStreamDbg->Out.pbPlayBuffer = NULL;
193 return rc;
194}
195
196
197/**
198 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
199 */
200static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
201 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
202{
203 PDRVHOSTVAKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
204 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
205 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
206 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
207 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
208
209 int rc;
210 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
211 rc = drvHostValKitAudioCreateStreamIn( pThis, pStreamDbg, pCfgReq, pCfgAcq);
212 else
213 rc = drvHostValKitAudioCreateStreamOut(pThis, pStreamDbg, pCfgReq, pCfgAcq);
214 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
215 return rc;
216}
217
218
219/**
220 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
221 */
222static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
223{
224 RT_NOREF(pInterface); //PDRVHOSTVAKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
225 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
226 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
227
228 if ( pStreamDbg->Cfg.enmDir == PDMAUDIODIR_OUT
229 && pStreamDbg->Out.pbPlayBuffer)
230 {
231 RTMemFree(pStreamDbg->Out.pbPlayBuffer);
232 pStreamDbg->Out.pbPlayBuffer = NULL;
233 }
234
235 if (pStreamDbg->pFile)
236 {
237 size_t cbDataSize = AudioHlpFileGetDataSize(pStreamDbg->pFile);
238 if (cbDataSize)
239 LogRel(("ValKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
240
241 AudioHlpFileDestroy(pStreamDbg->pFile);
242 pStreamDbg->pFile = NULL;
243 }
244
245 if (pStreamDbg->pFileTiming)
246 {
247 RTStrmClose(pStreamDbg->pFileTiming);
248 pStreamDbg->pFileTiming = NULL;
249 }
250
251 return VINF_SUCCESS;
252}
253
254
255/**
256 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
257 */
258static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControlStub(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
259{
260 RT_NOREF(pInterface, pStream);
261 return VINF_SUCCESS;
262}
263
264
265/**
266 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
267 */
268static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(PPDMIHOSTAUDIO pInterface,
269 PPDMAUDIOBACKENDSTREAM pStream)
270{
271 RT_NOREF(pInterface);
272 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
273 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
274
275 if (pStreamDbg->pFileTiming)
276 RTStrmFlush(pStreamDbg->pFileTiming);
277
278 return VINF_SUCCESS;
279}
280
281
282/**
283 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
284 */
285static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
286 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
287{
288 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
289 * replacing it with individual StreamXxxx methods. That would save us
290 * potentally huge switches and more easily see which drivers implement
291 * which operations (grep for pfnStreamXxxx). */
292 switch (enmStreamCmd)
293 {
294 case PDMAUDIOSTREAMCMD_ENABLE:
295 return drvHostValKitAudioHA_StreamControlStub(pInterface, pStream);
296 case PDMAUDIOSTREAMCMD_DISABLE:
297 return drvHostValKitAudioHA_StreamControlStub(pInterface, pStream);
298 case PDMAUDIOSTREAMCMD_PAUSE:
299 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
300 case PDMAUDIOSTREAMCMD_RESUME:
301 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
302 case PDMAUDIOSTREAMCMD_DRAIN:
303 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
304
305 case PDMAUDIOSTREAMCMD_END:
306 case PDMAUDIOSTREAMCMD_32BIT_HACK:
307 case PDMAUDIOSTREAMCMD_INVALID:
308 /* no default*/
309 break;
310 }
311 return VERR_NOT_SUPPORTED;
312}
313
314
315/**
316 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
317 */
318static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
319{
320 RT_NOREF(pInterface, pStream);
321 return UINT32_MAX;
322}
323
324
325/**
326 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
327 */
328static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
329{
330 RT_NOREF(pInterface, pStream);
331 return UINT32_MAX;
332}
333
334
335/**
336 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
337 */
338static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
339 PPDMAUDIOBACKENDSTREAM pStream)
340{
341 RT_NOREF(pInterface);
342 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
343 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
344}
345
346
347/**
348 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
349 */
350static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
351 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
352{
353 PDRVHOSTVAKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
354 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
355 RT_NOREF(pThis);
356
357 uint64_t cNsSinceStart;
358 if (pStreamDbg->tsStarted != 0)
359 cNsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
360 else
361 {
362 pStreamDbg->tsStarted = RTTimeNanoTS();
363 cNsSinceStart = 0;
364 }
365
366 // Microseconds are used everythere below
367 uint32_t const cFrames = PDMAudioPropsBytesToFrames(&pStreamDbg->Cfg.Props, cbBuf);
368 RTStrmPrintf(pStreamDbg->pFileTiming, "%d %d %d %d\n",
369 // Host time elapsed since Guest submitted the first buffer for playback:
370 (uint32_t)(cNsSinceStart / 1000),
371 // how long all the samples submitted previously were played:
372 (uint32_t)(pStreamDbg->cFramesSinceStarted * 1.0E6 / pStreamDbg->Cfg.Props.uHz),
373 // how long a new uSamplesReady samples should/will be played:
374 (uint32_t)(cFrames * 1.0E6 / pStreamDbg->Cfg.Props.uHz),
375 cFrames);
376
377 pStreamDbg->cFramesSinceStarted += cFrames;
378
379 /* Remember when samples were consumed. */
380 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
381
382 int rc2 = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
383 if (RT_FAILURE(rc2))
384 LogRel(("ValKitAudio: Writing output failed with %Rrc\n", rc2));
385
386 *pcbWritten = cbBuf;
387 return VINF_SUCCESS;
388}
389
390
391/**
392 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
393 */
394static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
395 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
396{
397 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
398
399 /* Never capture anything. */
400 *pcbRead = 0;
401 return VINF_SUCCESS;
402}
403
404
405/**
406 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
407 */
408static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
409{
410 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
411 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
412
413 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
414 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
415 return NULL;
416}
417
418
419/**
420 * Constructs a VaKit audio driver instance.
421 *
422 * @copydoc FNPDMDRVCONSTRUCT
423 */
424static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
425{
426 RT_NOREF(pCfg, fFlags);
427 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
428 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
429 LogRel(("Audio: Initializing VALKIT driver\n"));
430
431 /*
432 * Init the static parts.
433 */
434 pThis->pDrvIns = pDrvIns;
435 /* IBase */
436 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
437 /* IHostAudio */
438 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
439 pThis->IHostAudio.pfnGetDevices = NULL;
440 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
441 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
442 pThis->IHostAudio.pfnStreamConfigHint = NULL;
443 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
444 pThis->IHostAudio.pfnStreamInitAsync = NULL;
445 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
446 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
447 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
448 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
449 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
450 pThis->IHostAudio.pfnStreamGetPending = NULL;
451 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
452 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
453 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
454
455 return VINF_SUCCESS;
456}
457
458/**
459 * Char driver registration record.
460 */
461const PDMDRVREG g_DrvHostValidationKitAudio =
462{
463 /* u32Version */
464 PDM_DRVREG_VERSION,
465 /* szName */
466 "ValidationKitAudio",
467 /* szRCMod */
468 "",
469 /* szR0Mod */
470 "",
471 /* pszDescription */
472 "ValidationKitAudio audio host driver",
473 /* fFlags */
474 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
475 /* fClass. */
476 PDM_DRVREG_CLASS_AUDIO,
477 /* cMaxInstances */
478 ~0U,
479 /* cbInstance */
480 sizeof(DRVHOSTVAKITAUDIO),
481 /* pfnConstruct */
482 drvHostValKitAudioConstruct,
483 /* pfnDestruct */
484 NULL,
485 /* pfnRelocate */
486 NULL,
487 /* pfnIOCtl */
488 NULL,
489 /* pfnPowerOn */
490 NULL,
491 /* pfnReset */
492 NULL,
493 /* pfnSuspend */
494 NULL,
495 /* pfnResume */
496 NULL,
497 /* pfnAttach */
498 NULL,
499 /* pfnDetach */
500 NULL,
501 /* pfnPowerOff */
502 NULL,
503 /* pfnSoftReset */
504 NULL,
505 /* u32EndVersion */
506 PDM_DRVREG_VERSION
507};
508
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