VirtualBox

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

Last change on this file since 88137 was 88028, checked in by vboxsync, 4 years ago

Audio: Moving some of the DrvAudio.h stuff into PDM - VBox/vmm/pdmaudioinline.h. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 88028 2021-03-08 19:31:22Z 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-2020 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#include <VBox/vmm/pdmaudioinline.h>
26
27#include "DrvAudio.h"
28#include "VBoxDD.h"
29
30
31/**
32 * Structure for keeping a VAKIT input/output stream.
33 */
34typedef struct VAKITAUDIOSTREAM
35{
36 /** The stream's acquired configuration. */
37 PPDMAUDIOSTREAMCFG pCfg;
38 /** Audio file to dump output to or read input from. */
39 PPDMAUDIOFILE pFile;
40 /** Text file to store timing of audio buffers submittions**/
41 RTFILE hFileTiming;
42 /** Timestamp of the first play or record request**/
43 uint64_t tsStarted;
44 /** Total number of samples played or recorded so far**/
45 uint32_t uSamplesSinceStarted;
46 union
47 {
48 struct
49 {
50 /** Timestamp of last captured samples. */
51 uint64_t tsLastCaptured;
52 } In;
53 struct
54 {
55 /** Timestamp of last played samples. */
56 uint64_t tsLastPlayed;
57 uint8_t *pu8PlayBuffer;
58 uint32_t cbPlayBuffer;
59 } Out;
60 };
61} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
62
63/**
64 * VAKIT audio driver instance data.
65 * @implements PDMIAUDIOCONNECTOR
66 */
67typedef struct DRVHOSTVAKITAUDIO
68{
69 /** Pointer to the driver instance structure. */
70 PPDMDRVINS pDrvIns;
71 /** Pointer to host audio interface. */
72 PDMIHOSTAUDIO IHostAudio;
73} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
74
75/*******************************************PDM_AUDIO_DRIVER******************************/
76
77
78/**
79 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
80 */
81static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
82{
83 RT_NOREF(pInterface);
84 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
85
86 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
87
88 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
89 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
90
91 pBackendCfg->cMaxStreamsOut = 1; /* Output */
92 pBackendCfg->cMaxStreamsIn = 0; /* No input supported yet. */
93
94 return VINF_SUCCESS;
95}
96
97
98/**
99 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
100 */
101static DECLCALLBACK(int) drvHostValKitAudioHA_Init(PPDMIHOSTAUDIO pInterface)
102{
103 RT_NOREF(pInterface);
104
105 LogFlowFuncLeaveRC(VINF_SUCCESS);
106 return VINF_SUCCESS;
107}
108
109
110/**
111 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
112 */
113static DECLCALLBACK(void) drvHostValKitAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
114{
115 RT_NOREF(pInterface);
116}
117
118
119/**
120 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
121 */
122static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
123{
124 RT_NOREF(enmDir);
125 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
126
127 return PDMAUDIOBACKENDSTS_RUNNING;
128}
129
130
131static int drvHostValKitAudioCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
132 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
133{
134 RT_NOREF(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
135
136 return VINF_SUCCESS;
137}
138
139
140static int drvHostValKitAudioCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
141 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
142{
143 RT_NOREF(pDrv, pCfgAcq);
144
145 pStreamDbg->tsStarted = 0;
146 pStreamDbg->uSamplesSinceStarted = 0;
147 pStreamDbg->Out.tsLastPlayed = 0;
148 pStreamDbg->Out.cbPlayBuffer = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
149 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
150 AssertReturn(pStreamDbg->Out.pu8PlayBuffer, VERR_NO_MEMORY);
151
152 char szTemp[RTPATH_MAX];
153 int rc = RTPathTemp(szTemp, sizeof(szTemp));
154 if (RT_SUCCESS(rc))
155 rc = RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
156 if (RT_SUCCESS(rc))
157 {
158 char szFile[RTPATH_MAX];
159 rc = DrvAudioHlpFileNameGet(szFile, sizeof(szFile), szTemp, "VaKit",
160 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
161 if (RT_SUCCESS(rc))
162 {
163 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pStreamDbg->pFile);
164 if (RT_SUCCESS(rc))
165 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
166 }
167
168 if (RT_FAILURE(rc))
169 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
170 else
171 {
172 size_t cch;
173 char szTimingInfo[128];
174 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %uHz %uch %ubit\n",
175 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cbSample * 8);
176
177 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
178 }
179 }
180 else
181 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
182 return rc;
183}
184
185
186/**
187 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
188 */
189static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
190 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
191{
192 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
193 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
194 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
195 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
196
197 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
198 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
199
200 int rc;
201 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
202 rc = drvHostValKitAudioCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
203 else
204 rc = drvHostValKitAudioCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
205
206 if (RT_SUCCESS(rc))
207 {
208 pStreamDbg->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
209 if (!pStreamDbg->pCfg)
210 rc = VERR_NO_MEMORY;
211 }
212
213 return rc;
214}
215
216
217/**
218 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
219 */
220static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
221 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
222{
223 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
224 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
225 RT_NOREF(pDrv);
226
227 uint64_t tsSinceStart;
228 size_t cch;
229 char szTimingInfo[128];
230
231 if (pStreamDbg->tsStarted == 0)
232 {
233 pStreamDbg->tsStarted = RTTimeNanoTS();
234 tsSinceStart = 0;
235 }
236 else
237 {
238 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
239 }
240
241 // Microseconds are used everythere below
242 uint32_t sBuf = uBufSize >> pStreamDbg->pCfg->Props.cShift;
243 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
244 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
245 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
246 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
247 sBuf);
248 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
249 pStreamDbg->uSamplesSinceStarted += sBuf;
250
251 /* Remember when samples were consumed. */
252 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
253
254 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, uBufSize, 0 /* fFlags */);
255 if (RT_FAILURE(rc2))
256 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
257
258 *puWritten = uBufSize;
259
260 return VINF_SUCCESS;
261}
262
263
264/**
265 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
266 */
267static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
268 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
269{
270 RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
271
272 /* Never capture anything. */
273 if (puRead)
274 *puRead = 0;
275
276 return VINF_SUCCESS;
277}
278
279
280static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
281{
282 RT_NOREF(pDrv, pStreamDbg);
283 return VINF_SUCCESS;
284}
285
286
287static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
288{
289 RT_NOREF(pDrv);
290
291 if (pStreamDbg->Out.pu8PlayBuffer)
292 {
293 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
294 pStreamDbg->Out.pu8PlayBuffer = NULL;
295 }
296
297 if (pStreamDbg->pFile)
298 {
299 size_t cbDataSize = DrvAudioHlpFileGetDataSize(pStreamDbg->pFile);
300 if (cbDataSize)
301 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
302
303 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
304 pStreamDbg->pFile = NULL;
305 }
306
307 return VINF_SUCCESS;
308}
309
310
311static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
312{
313 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
314
315 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
316 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
317
318 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
319 return VINF_SUCCESS;
320
321 int rc;
322 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
323 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
324 else
325 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
326
327 if (RT_SUCCESS(rc))
328 {
329 PDMAudioStrmCfgFree(pStreamDbg->pCfg);
330 pStreamDbg->pCfg = NULL;
331 }
332
333 return rc;
334}
335
336static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
337 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
338{
339 RT_NOREF(enmStreamCmd);
340 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
341 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
342
343 return VINF_SUCCESS;
344}
345
346/**
347 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
348 */
349static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
350{
351 RT_NOREF(pInterface, pStream);
352
353 return UINT32_MAX;
354}
355
356
357/**
358 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
359 */
360static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
361{
362 RT_NOREF(pInterface, pStream);
363
364 return UINT32_MAX;
365}
366
367static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostValKitAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
368 PPDMAUDIOBACKENDSTREAM pStream)
369{
370 RT_NOREF(pInterface, pStream);
371
372 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
373}
374
375static DECLCALLBACK(int) drvHostValKitAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
376{
377 RT_NOREF(pInterface, pStream);
378 return VINF_SUCCESS;
379}
380
381
382/**
383 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
384 */
385static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
386{
387 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
388 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
389
390 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
391 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
392 return NULL;
393}
394
395
396/**
397 * Constructs a VaKit audio driver instance.
398 *
399 * @copydoc FNPDMDRVCONSTRUCT
400 */
401static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
402{
403 RT_NOREF(pCfg, fFlags);
404 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
405 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
406 LogRel(("Audio: Initializing VAKIT driver\n"));
407
408 /*
409 * Init the static parts.
410 */
411 pThis->pDrvIns = pDrvIns;
412 /* IBase */
413 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
414 /* IHostAudio */
415 pThis->IHostAudio.pfnInit = drvHostValKitAudioHA_Init;
416 pThis->IHostAudio.pfnShutdown = drvHostValKitAudioHA_Shutdown;
417 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
418 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
419 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
420 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
421 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
422 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
423 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
424 pThis->IHostAudio.pfnStreamGetStatus = drvHostValKitAudioHA_StreamGetStatus;
425 pThis->IHostAudio.pfnStreamIterate = drvHostValKitAudioHA_StreamIterate;
426 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
427 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
428 pThis->IHostAudio.pfnSetCallback = NULL;
429 pThis->IHostAudio.pfnGetDevices = NULL;
430 pThis->IHostAudio.pfnStreamGetPending = NULL;
431 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
432 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
433 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
434 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
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 drvHostValKitAudioConstruct,
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