VirtualBox

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

Last change on this file since 88994 was 88887, checked in by vboxsync, 4 years ago

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