VirtualBox

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

Last change on this file since 82255 was 82255, checked in by vboxsync, 5 years ago

vmm/pdmaudioifs.h: More of the same. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 82255 2019-11-27 23:20:26Z 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-2019 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) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
81{
82 RT_NOREF(pInterface);
83 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
84
85 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit audio driver");
86
87 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
88 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
89
90 pBackendCfg->cMaxStreamsOut = 1; /* Output */
91 pBackendCfg->cMaxStreamsIn = 0; /* No input supported yet. */
92
93 return VINF_SUCCESS;
94}
95
96
97/**
98 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
99 */
100static DECLCALLBACK(int) drvHostValKitAudioHA_Init(PPDMIHOSTAUDIO pInterface)
101{
102 RT_NOREF(pInterface);
103
104 LogFlowFuncLeaveRC(VINF_SUCCESS);
105 return VINF_SUCCESS;
106}
107
108
109/**
110 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
111 */
112static DECLCALLBACK(void) drvHostValKitAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
113{
114 RT_NOREF(pInterface);
115}
116
117
118/**
119 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
120 */
121static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
122{
123 RT_NOREF(enmDir);
124 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
125
126 return PDMAUDIOBACKENDSTS_RUNNING;
127}
128
129
130static int drvHostValKitAudioCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
131 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
132{
133 RT_NOREF(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
134
135 return VINF_SUCCESS;
136}
137
138
139static int drvHostValKitAudioCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
140 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
141{
142 RT_NOREF(pDrv, pCfgAcq);
143
144 int rc = VINF_SUCCESS;
145
146 pStreamDbg->tsStarted = 0;
147 pStreamDbg->uSamplesSinceStarted = 0;
148 pStreamDbg->Out.tsLastPlayed = 0;
149 pStreamDbg->Out.cbPlayBuffer = DrvAudioHlpFramesToBytes(pCfgReq->Backend.cFramesBufferSize, &pCfgReq->Props);
150 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
151 if (!pStreamDbg->Out.pu8PlayBuffer)
152 rc = VERR_NO_MEMORY;
153
154 if (RT_SUCCESS(rc))
155 {
156 char szTemp[RTPATH_MAX];
157 rc = RTPathTemp(szTemp, sizeof(szTemp));
158
159 RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
160
161 if (RT_SUCCESS(rc))
162 {
163 char szFile[RTPATH_MAX + 1];
164
165 rc = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, "VaKit",
166 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
167 if (RT_SUCCESS(rc))
168 {
169 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pStreamDbg->pFile);
170 if (RT_SUCCESS(rc))
171 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
172 }
173
174 if (RT_FAILURE(rc))
175 {
176 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
177 }
178 else
179 {
180 size_t cch;
181 char szTimingInfo[128];
182 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %uHz %uch %ubit\n",
183 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cbSample * 8);
184
185 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
186 }
187 }
188 else
189 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
190 }
191
192 return rc;
193}
194
195
196/**
197 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
198 */
199static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
200 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
201{
202 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
203 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
204 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
205 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
206
207 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
208 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
209
210 int rc;
211 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
212 rc = drvHostValKitAudioCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
213 else
214 rc = drvHostValKitAudioCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
215
216 if (RT_SUCCESS(rc))
217 {
218 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
219 if (!pStreamDbg->pCfg)
220 rc = VERR_NO_MEMORY;
221 }
222
223 return rc;
224}
225
226
227/**
228 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
229 */
230static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
231 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
232{
233 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
234 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
235 RT_NOREF(pDrv);
236
237 uint64_t tsSinceStart;
238 size_t cch;
239 char szTimingInfo[128];
240
241 if (pStreamDbg->tsStarted == 0)
242 {
243 pStreamDbg->tsStarted = RTTimeNanoTS();
244 tsSinceStart = 0;
245 }
246 else
247 {
248 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
249 }
250
251 // Microseconds are used everythere below
252 uint32_t sBuf = uBufSize >> pStreamDbg->pCfg->Props.cShift;
253 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
254 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
255 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
256 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
257 sBuf);
258 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
259 pStreamDbg->uSamplesSinceStarted += sBuf;
260
261 /* Remember when samples were consumed. */
262 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
263
264 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, uBufSize, 0 /* fFlags */);
265 if (RT_FAILURE(rc2))
266 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
267
268 *puWritten = uBufSize;
269
270 return VINF_SUCCESS;
271}
272
273
274/**
275 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
276 */
277static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
278 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
279{
280 RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
281
282 /* Never capture anything. */
283 if (puRead)
284 *puRead = 0;
285
286 return VINF_SUCCESS;
287}
288
289
290static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
291{
292 RT_NOREF(pDrv, pStreamDbg);
293 return VINF_SUCCESS;
294}
295
296
297static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
298{
299 RT_NOREF(pDrv);
300
301 if (pStreamDbg->Out.pu8PlayBuffer)
302 {
303 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
304 pStreamDbg->Out.pu8PlayBuffer = NULL;
305 }
306
307 if (pStreamDbg->pFile)
308 {
309 size_t cbDataSize = DrvAudioHlpFileGetDataSize(pStreamDbg->pFile);
310 if (cbDataSize)
311 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
312
313 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
314 pStreamDbg->pFile = NULL;
315 }
316
317 return VINF_SUCCESS;
318}
319
320
321static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
322{
323 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
324
325 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
326 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
327
328 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
329 return VINF_SUCCESS;
330
331 int rc;
332 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
333 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
334 else
335 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
336
337 if (RT_SUCCESS(rc))
338 {
339 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
340 pStreamDbg->pCfg = NULL;
341 }
342
343 return rc;
344}
345
346static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
347 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
348{
349 RT_NOREF(enmStreamCmd);
350 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
351 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
352
353 return VINF_SUCCESS;
354}
355
356/**
357 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
358 */
359static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
360{
361 RT_NOREF(pInterface, pStream);
362
363 return UINT32_MAX;
364}
365
366
367/**
368 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
369 */
370static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
371{
372 RT_NOREF(pInterface, pStream);
373
374 return UINT32_MAX;
375}
376
377static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostValKitAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
378 PPDMAUDIOBACKENDSTREAM pStream)
379{
380 RT_NOREF(pInterface, pStream);
381
382 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
383}
384
385static DECLCALLBACK(int) drvHostValKitAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
386{
387 RT_NOREF(pInterface, pStream);
388 return VINF_SUCCESS;
389}
390
391
392/**
393 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
394 */
395static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
396{
397 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
398 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
399
400 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
401 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
402 return NULL;
403}
404
405
406/**
407 * Constructs a VaKit audio driver instance.
408 *
409 * @copydoc FNPDMDRVCONSTRUCT
410 */
411static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
412{
413 RT_NOREF(pCfg, fFlags);
414 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
415 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
416 LogRel(("Audio: Initializing VAKIT driver\n"));
417
418 /*
419 * Init the static parts.
420 */
421 pThis->pDrvIns = pDrvIns;
422 /* IBase */
423 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
424 /* IHostAudio */
425 pThis->IHostAudio.pfnInit = drvHostValKitAudioHA_Init;
426 pThis->IHostAudio.pfnShutdown = drvHostValKitAudioHA_Shutdown;
427 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
428 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
429 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
430 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
431 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
432 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
433 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
434 pThis->IHostAudio.pfnStreamGetStatus = drvHostValKitAudioHA_StreamGetStatus;
435 pThis->IHostAudio.pfnStreamIterate = drvHostValKitAudioHA_StreamIterate;
436 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
437 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
438 pThis->IHostAudio.pfnSetCallback = NULL;
439 pThis->IHostAudio.pfnGetDevices = NULL;
440 pThis->IHostAudio.pfnStreamGetPending = NULL;
441 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
442 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
443 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
444 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
445
446 return VINF_SUCCESS;
447}
448
449/**
450 * Char driver registration record.
451 */
452const PDMDRVREG g_DrvHostValidationKitAudio =
453{
454 /* u32Version */
455 PDM_DRVREG_VERSION,
456 /* szName */
457 "ValidationKitAudio",
458 /* szRCMod */
459 "",
460 /* szR0Mod */
461 "",
462 /* pszDescription */
463 "ValidationKitAudio audio host driver",
464 /* fFlags */
465 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
466 /* fClass. */
467 PDM_DRVREG_CLASS_AUDIO,
468 /* cMaxInstances */
469 ~0U,
470 /* cbInstance */
471 sizeof(DRVHOSTVAKITAUDIO),
472 /* pfnConstruct */
473 drvHostValKitAudioConstruct,
474 /* pfnDestruct */
475 NULL,
476 /* pfnRelocate */
477 NULL,
478 /* pfnIOCtl */
479 NULL,
480 /* pfnPowerOn */
481 NULL,
482 /* pfnReset */
483 NULL,
484 /* pfnSuspend */
485 NULL,
486 /* pfnResume */
487 NULL,
488 /* pfnAttach */
489 NULL,
490 /* pfnDetach */
491 NULL,
492 /* pfnPowerOff */
493 NULL,
494 /* pfnSoftReset */
495 NULL,
496 /* u32EndVersion */
497 PDM_DRVREG_VERSION
498};
499
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