VirtualBox

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

Last change on this file since 88477 was 88462, checked in by vboxsync, 4 years ago

DrvHostAudioDebug,DrvHostAudioNull,DrvHostAudioValidationKit: Prepare splitting up pfnStreamControl. bugref:9890

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

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