VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostValidationKit.cpp@ 73370

Last change on this file since 73370 was 73370, checked in by vboxsync, 6 years ago

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 73370 2018-07-26 13:52:12Z vboxsync $ */
2/** @file
3 * ValidationKit audio driver - host backend for dumping and injecting audio data
4 * from/to the device emulation.
5 */
6
7/*
8 * Copyright (C) 2016-2017 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#include <iprt/alloc.h>
20#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
21
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25
26#include "DrvAudio.h"
27#include "VBoxDD.h"
28
29
30/**
31 * Structure for keeping a VAKIT input/output stream.
32 */
33typedef struct VAKITAUDIOSTREAM
34{
35 /** The stream's acquired configuration. */
36 PPDMAUDIOSTREAMCFG pCfg;
37 /** Audio file to dump output to or read input from. */
38 PPDMAUDIOFILE pFile;
39 /** Text file to store timing of audio buffers submittions**/
40 RTFILE hFileTiming;
41 /** Timestamp of the first play or record request**/
42 uint64_t tsStarted;
43 /** Total number of samples played or recorded so far**/
44 uint32_t uSamplesSinceStarted;
45 union
46 {
47 struct
48 {
49 /** Timestamp of last captured samples. */
50 uint64_t tsLastCaptured;
51 } In;
52 struct
53 {
54 /** Timestamp of last played samples. */
55 uint64_t tsLastPlayed;
56 uint8_t *pu8PlayBuffer;
57 uint32_t cbPlayBuffer;
58 } Out;
59 };
60} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
61
62/**
63 * VAKIT audio driver instance data.
64 * @implements PDMIAUDIOCONNECTOR
65 */
66typedef struct DRVHOSTVAKITAUDIO
67{
68 /** Pointer to the driver instance structure. */
69 PPDMDRVINS pDrvIns;
70 /** Pointer to host audio interface. */
71 PDMIHOSTAUDIO IHostAudio;
72} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
73
74/*******************************************PDM_AUDIO_DRIVER******************************/
75
76
77/**
78 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
79 */
80static DECLCALLBACK(int) drvHostVaKitAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
81{
82 RT_NOREF(pInterface);
83 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
84
85 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
86 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
87
88 pBackendCfg->cMaxStreamsOut = 1; /* Output */
89 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
90
91 return VINF_SUCCESS;
92}
93
94
95/**
96 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
97 */
98static DECLCALLBACK(int) drvHostVaKitAudioInit(PPDMIHOSTAUDIO pInterface)
99{
100 RT_NOREF(pInterface);
101
102 LogFlowFuncLeaveRC(VINF_SUCCESS);
103 return VINF_SUCCESS;
104}
105
106
107/**
108 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
109 */
110static DECLCALLBACK(void) drvHostVaKitAudioShutdown(PPDMIHOSTAUDIO pInterface)
111{
112 RT_NOREF(pInterface);
113}
114
115
116/**
117 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
118 */
119static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostVaKitAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
120{
121 RT_NOREF(enmDir);
122 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
123
124 return PDMAUDIOBACKENDSTS_RUNNING;
125}
126
127
128static int debugCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
129 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
130{
131 RT_NOREF(pDrv, pStreamDbg, pCfgReq);
132
133 if (pCfgAcq)
134 pCfgAcq->cfPeriod = _1K;
135
136 return VINF_SUCCESS;
137}
138
139
140static int debugCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
141 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
142{
143 RT_NOREF(pDrv);
144
145 int rc = VINF_SUCCESS;
146
147 pStreamDbg->tsStarted = 0;
148 pStreamDbg->uSamplesSinceStarted = 0;
149 pStreamDbg->Out.tsLastPlayed = 0;
150 pStreamDbg->Out.cbPlayBuffer = 16 * _1K * PDMAUDIOSTREAMCFG_F2B(pCfgReq, 1); /** @todo Make this configurable? */
151 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
152 if (!pStreamDbg->Out.pu8PlayBuffer)
153 rc = VERR_NO_MEMORY;
154
155 if (RT_SUCCESS(rc))
156 {
157 char szTemp[RTPATH_MAX];
158 rc = RTPathTemp(szTemp, sizeof(szTemp));
159
160 RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
161
162 if (RT_SUCCESS(rc))
163 {
164 char szFile[RTPATH_MAX + 1];
165
166 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, "VaKit",
167 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
168 if (RT_SUCCESS(rc))
169 {
170 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE, &pStreamDbg->pFile);
171 if (RT_SUCCESS(rc))
172 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
173 }
174
175 if (RT_FAILURE(rc))
176 {
177 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
178 }
179 else
180 {
181 size_t cch;
182 char szTimingInfo[128];
183 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %dHz %dch %dbps\n",
184 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cBits);
185
186 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
187 }
188 }
189 else
190 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
191 }
192
193 if (RT_SUCCESS(rc))
194 {
195 if (pCfgAcq)
196 pCfgAcq->cfPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
197 }
198
199 return rc;
200}
201
202
203/**
204 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
205 */
206static DECLCALLBACK(int) drvHostVaKitAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
207 PPDMAUDIOBACKENDSTREAM pStream,
208 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
209{
210 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
211 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
212 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
213 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
214
215 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
216 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
217
218 int rc;
219 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
220 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
221 else
222 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
223
224 if (RT_SUCCESS(rc))
225 {
226 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
227 if (!pStreamDbg->pCfg)
228 rc = VERR_NO_MEMORY;
229 }
230
231 return rc;
232}
233
234
235/**
236 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
237 */
238static DECLCALLBACK(int) drvHostVaKitAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
239 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
240 uint32_t *pcxWritten)
241{
242 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
243 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
244 RT_NOREF(pDrv);
245
246 uint64_t tsSinceStart;
247 size_t cch;
248 char szTimingInfo[128];
249
250 if (pStreamDbg->tsStarted == 0)
251 {
252 pStreamDbg->tsStarted = RTTimeNanoTS();
253 tsSinceStart = 0;
254 }
255 else
256 {
257 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
258 }
259
260 // Microseconds are used everythere below
261 uint32_t sBuf = cxBuf >> pStreamDbg->pCfg->Props.cShift;
262 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
263 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
264 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
265 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
266 sBuf);
267 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
268 pStreamDbg->uSamplesSinceStarted += sBuf;
269
270 /* Remember when samples were consumed. */
271 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
272
273 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cxBuf, 0 /* fFlags */);
274 if (RT_FAILURE(rc2))
275 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
276
277 *pcxWritten = cxBuf;
278
279 return VINF_SUCCESS;
280}
281
282
283/**
284 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
285 */
286static DECLCALLBACK(int) drvHostVaKitAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
287 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cxBuf,
288 uint32_t *pcxRead)
289{
290 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
291
292 /* Never capture anything. */
293 if (pcxRead)
294 *pcxRead = 0;
295
296 return VINF_SUCCESS;
297}
298
299
300static int debugDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
301{
302 RT_NOREF(pDrv, pStreamDbg);
303 return VINF_SUCCESS;
304}
305
306
307static int debugDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
308{
309 RT_NOREF(pDrv);
310
311 if (pStreamDbg->Out.pu8PlayBuffer)
312 {
313 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
314 pStreamDbg->Out.pu8PlayBuffer = NULL;
315 }
316
317 if (pStreamDbg->pFile)
318 {
319 size_t cbDataSize = DrvAudioHlpFileGetDataSize(pStreamDbg->pFile);
320 if (cbDataSize)
321 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
322
323 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
324 pStreamDbg->pFile = NULL;
325 }
326
327 return VINF_SUCCESS;
328}
329
330
331static DECLCALLBACK(int) drvHostVaKitAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
332{
333 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
334
335 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
336 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
337
338 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
339 return VINF_SUCCESS;
340
341 int rc;
342 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
343 rc = debugDestroyStreamIn (pDrv, pStreamDbg);
344 else
345 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
346
347 if (RT_SUCCESS(rc))
348 {
349 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
350 pStreamDbg->pCfg = NULL;
351 }
352
353 return rc;
354}
355
356static DECLCALLBACK(int) drvHostVaKitAudioStreamControl(PPDMIHOSTAUDIO pInterface,
357 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
358{
359 RT_NOREF(enmStreamCmd);
360 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
361 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
362
363 return VINF_SUCCESS;
364}
365
366/**
367 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
368 */
369static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
370{
371 RT_NOREF(pInterface, pStream);
372
373 return UINT32_MAX;
374}
375
376
377/**
378 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
379 */
380static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
381{
382 RT_NOREF(pInterface, pStream);
383
384 return UINT32_MAX;
385}
386
387static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostVaKitAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
388{
389 RT_NOREF(pInterface, pStream);
390
391 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
392}
393
394static DECLCALLBACK(int) drvHostVaKitAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
395{
396 RT_NOREF(pInterface, pStream);
397 return VINF_SUCCESS;
398}
399
400
401/**
402 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
403 */
404static DECLCALLBACK(void *) drvHostVaKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
405{
406 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
407 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
408
409 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
410 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
411 return NULL;
412}
413
414
415/**
416 * Constructs a VaKit audio driver instance.
417 *
418 * @copydoc FNPDMDRVCONSTRUCT
419 */
420static DECLCALLBACK(int) drvHostVaKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
421{
422 RT_NOREF(pCfg, fFlags);
423 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
424 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
425 LogRel(("Audio: Initializing VAKIT driver\n"));
426
427 /*
428 * Init the static parts.
429 */
430 pThis->pDrvIns = pDrvIns;
431 /* IBase */
432 pDrvIns->IBase.pfnQueryInterface = drvHostVaKitAudioQueryInterface;
433 /* IHostAudio */
434 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostVaKitAudio);
435
436 return VINF_SUCCESS;
437}
438
439/**
440 * Char driver registration record.
441 */
442const PDMDRVREG g_DrvHostValidationKitAudio =
443{
444 /* u32Version */
445 PDM_DRVREG_VERSION,
446 /* szName */
447 "ValidationKitAudio",
448 /* szRCMod */
449 "",
450 /* szR0Mod */
451 "",
452 /* pszDescription */
453 "ValidationKitAudio audio host driver",
454 /* fFlags */
455 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
456 /* fClass. */
457 PDM_DRVREG_CLASS_AUDIO,
458 /* cMaxInstances */
459 ~0U,
460 /* cbInstance */
461 sizeof(DRVHOSTVAKITAUDIO),
462 /* pfnConstruct */
463 drvHostVaKitAudioConstruct,
464 /* pfnDestruct */
465 NULL,
466 /* pfnRelocate */
467 NULL,
468 /* pfnIOCtl */
469 NULL,
470 /* pfnPowerOn */
471 NULL,
472 /* pfnReset */
473 NULL,
474 /* pfnSuspend */
475 NULL,
476 /* pfnResume */
477 NULL,
478 /* pfnAttach */
479 NULL,
480 /* pfnDetach */
481 NULL,
482 /* pfnPowerOff */
483 NULL,
484 /* pfnSoftReset */
485 NULL,
486 /* u32EndVersion */
487 PDM_DRVREG_VERSION
488};
489
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