VirtualBox

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

Last change on this file since 89234 was 89213, checked in by vboxsync, 4 years ago

Audio: Added an fImmediate indicator to the pfnStreamDestroy methods so the backend knows whether it's okay to continue draining the stream or if it must be destroyed without delay. The latter is typically only for shutdown and driver plumbing. This helps quite a bit for HDA/CoreAudio/knoppix. bugref:9890

  • 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 89213 2021-05-21 10:00:12Z 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 bool fImmediate)
224{
225 RT_NOREF(pInterface, fImmediate);
226 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
227 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
228
229 if ( pStreamDbg->Cfg.enmDir == PDMAUDIODIR_OUT
230 && pStreamDbg->Out.pbPlayBuffer)
231 {
232 RTMemFree(pStreamDbg->Out.pbPlayBuffer);
233 pStreamDbg->Out.pbPlayBuffer = NULL;
234 }
235
236 if (pStreamDbg->pFile)
237 {
238 size_t cbDataSize = AudioHlpFileGetDataSize(pStreamDbg->pFile);
239 if (cbDataSize)
240 LogRel(("ValKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
241
242 AudioHlpFileDestroy(pStreamDbg->pFile);
243 pStreamDbg->pFile = NULL;
244 }
245
246 if (pStreamDbg->pFileTiming)
247 {
248 RTStrmClose(pStreamDbg->pFileTiming);
249 pStreamDbg->pFileTiming = NULL;
250 }
251
252 return VINF_SUCCESS;
253}
254
255
256/**
257 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
258 */
259static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControlStub(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
260{
261 RT_NOREF(pInterface, pStream);
262 return VINF_SUCCESS;
263}
264
265
266/**
267 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
268 */
269static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(PPDMIHOSTAUDIO pInterface,
270 PPDMAUDIOBACKENDSTREAM pStream)
271{
272 RT_NOREF(pInterface);
273 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
274 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
275
276 if (pStreamDbg->pFileTiming)
277 RTStrmFlush(pStreamDbg->pFileTiming);
278
279 return VINF_SUCCESS;
280}
281
282
283/**
284 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
285 */
286static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
287 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
288{
289 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
290 * replacing it with individual StreamXxxx methods. That would save us
291 * potentally huge switches and more easily see which drivers implement
292 * which operations (grep for pfnStreamXxxx). */
293 switch (enmStreamCmd)
294 {
295 case PDMAUDIOSTREAMCMD_ENABLE:
296 return drvHostValKitAudioHA_StreamControlStub(pInterface, pStream);
297 case PDMAUDIOSTREAMCMD_DISABLE:
298 return drvHostValKitAudioHA_StreamControlStub(pInterface, pStream);
299 case PDMAUDIOSTREAMCMD_PAUSE:
300 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
301 case PDMAUDIOSTREAMCMD_RESUME:
302 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
303 case PDMAUDIOSTREAMCMD_DRAIN:
304 return drvHostValKitAudioHA_StreamDisableOrPauseOrDrain(pInterface, pStream);
305
306 case PDMAUDIOSTREAMCMD_END:
307 case PDMAUDIOSTREAMCMD_32BIT_HACK:
308 case PDMAUDIOSTREAMCMD_INVALID:
309 /* no default*/
310 break;
311 }
312 return VERR_NOT_SUPPORTED;
313}
314
315
316/**
317 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
318 */
319static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
320{
321 RT_NOREF(pInterface, pStream);
322 return UINT32_MAX;
323}
324
325
326/**
327 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
328 */
329static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
330{
331 RT_NOREF(pInterface, pStream);
332 return UINT32_MAX;
333}
334
335
336/**
337 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
338 */
339static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
340 PPDMAUDIOBACKENDSTREAM pStream)
341{
342 RT_NOREF(pInterface);
343 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
344 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
345}
346
347
348/**
349 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
350 */
351static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
352 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
353{
354 PDRVHOSTVAKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
355 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
356 RT_NOREF(pThis);
357
358 uint64_t cNsSinceStart;
359 if (pStreamDbg->tsStarted != 0)
360 cNsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
361 else
362 {
363 pStreamDbg->tsStarted = RTTimeNanoTS();
364 cNsSinceStart = 0;
365 }
366
367 // Microseconds are used everythere below
368 uint32_t const cFrames = PDMAudioPropsBytesToFrames(&pStreamDbg->Cfg.Props, cbBuf);
369 RTStrmPrintf(pStreamDbg->pFileTiming, "%d %d %d %d\n",
370 // Host time elapsed since Guest submitted the first buffer for playback:
371 (uint32_t)(cNsSinceStart / 1000),
372 // how long all the samples submitted previously were played:
373 (uint32_t)(pStreamDbg->cFramesSinceStarted * 1.0E6 / pStreamDbg->Cfg.Props.uHz),
374 // how long a new uSamplesReady samples should/will be played:
375 (uint32_t)(cFrames * 1.0E6 / pStreamDbg->Cfg.Props.uHz),
376 cFrames);
377
378 pStreamDbg->cFramesSinceStarted += cFrames;
379
380 /* Remember when samples were consumed. */
381 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
382
383 int rc2 = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
384 if (RT_FAILURE(rc2))
385 LogRel(("ValKitAudio: Writing output failed with %Rrc\n", rc2));
386
387 *pcbWritten = cbBuf;
388 return VINF_SUCCESS;
389}
390
391
392/**
393 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
394 */
395static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
396 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
397{
398 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
399
400 /* Never capture anything. */
401 *pcbRead = 0;
402 return VINF_SUCCESS;
403}
404
405
406/**
407 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
408 */
409static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
410{
411 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
412 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
413
414 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
415 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
416 return NULL;
417}
418
419
420/**
421 * Constructs a VaKit audio driver instance.
422 *
423 * @copydoc FNPDMDRVCONSTRUCT
424 */
425static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
426{
427 RT_NOREF(pCfg, fFlags);
428 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
429 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
430 LogRel(("Audio: Initializing VALKIT driver\n"));
431
432 /*
433 * Init the static parts.
434 */
435 pThis->pDrvIns = pDrvIns;
436 /* IBase */
437 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
438 /* IHostAudio */
439 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
440 pThis->IHostAudio.pfnGetDevices = NULL;
441 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
442 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
443 pThis->IHostAudio.pfnStreamConfigHint = NULL;
444 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
445 pThis->IHostAudio.pfnStreamInitAsync = NULL;
446 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
447 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
448 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
449 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
450 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
451 pThis->IHostAudio.pfnStreamGetPending = NULL;
452 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
453 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
454 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
455
456 return VINF_SUCCESS;
457}
458
459/**
460 * Char driver registration record.
461 */
462const PDMDRVREG g_DrvHostValidationKitAudio =
463{
464 /* u32Version */
465 PDM_DRVREG_VERSION,
466 /* szName */
467 "ValidationKitAudio",
468 /* szRCMod */
469 "",
470 /* szR0Mod */
471 "",
472 /* pszDescription */
473 "ValidationKitAudio audio host driver",
474 /* fFlags */
475 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
476 /* fClass. */
477 PDM_DRVREG_CLASS_AUDIO,
478 /* cMaxInstances */
479 ~0U,
480 /* cbInstance */
481 sizeof(DRVHOSTVAKITAUDIO),
482 /* pfnConstruct */
483 drvHostValKitAudioConstruct,
484 /* pfnDestruct */
485 NULL,
486 /* pfnRelocate */
487 NULL,
488 /* pfnIOCtl */
489 NULL,
490 /* pfnPowerOn */
491 NULL,
492 /* pfnReset */
493 NULL,
494 /* pfnSuspend */
495 NULL,
496 /* pfnResume */
497 NULL,
498 /* pfnAttach */
499 NULL,
500 /* pfnDetach */
501 NULL,
502 /* pfnPowerOff */
503 NULL,
504 /* pfnSoftReset */
505 NULL,
506 /* u32EndVersion */
507 PDM_DRVREG_VERSION
508};
509
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