VirtualBox

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

Last change on this file since 88440 was 88394, checked in by vboxsync, 4 years ago

DrvAudioRec,DrvHostAudioValidationKit: doxygen fixes. some cleanups. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 88394 2021-04-07 11:03: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#include <iprt/alloc.h>
19#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
20
21#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
22#include <VBox/log.h>
23#include <VBox/vmm/pdmaudioifs.h>
24#include <VBox/vmm/pdmaudioinline.h>
25
26#include "AudioHlp.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 frames played or recorded so far. */
44 uint32_t cFramesSinceStarted;
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");
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,pfnGetStatus}
99 */
100static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
101{
102 RT_NOREF(enmDir);
103 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
104
105 return PDMAUDIOBACKENDSTS_RUNNING;
106}
107
108
109static int drvHostValKitAudioCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
110 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
111{
112 RT_NOREF(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
113
114 return VINF_SUCCESS;
115}
116
117
118static int drvHostValKitAudioCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
119 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
120{
121 RT_NOREF(pDrv, pCfgAcq);
122
123 pStreamDbg->tsStarted = 0;
124 pStreamDbg->cFramesSinceStarted = 0;
125 pStreamDbg->Out.tsLastPlayed = 0;
126 pStreamDbg->Out.cbPlayBuffer = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize);
127 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
128 AssertReturn(pStreamDbg->Out.pu8PlayBuffer, VERR_NO_MEMORY);
129
130 char szTemp[RTPATH_MAX];
131 int rc = RTPathTemp(szTemp, sizeof(szTemp));
132 if (RT_SUCCESS(rc))
133 rc = RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
134 if (RT_SUCCESS(rc))
135 {
136 char szFile[RTPATH_MAX];
137 rc = AudioHlpFileNameGet(szFile, sizeof(szFile), szTemp, "VaKit",
138 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
139 if (RT_SUCCESS(rc))
140 {
141 rc = AudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pStreamDbg->pFile);
142 if (RT_SUCCESS(rc))
143 rc = AudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
144 }
145
146 if (RT_FAILURE(rc))
147 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
148 else
149 {
150 size_t cch;
151 char szTimingInfo[128];
152 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %uHz %uch %ubit\n",
153 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cbSample * 8);
154
155 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
156 }
157 }
158 else
159 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
160 return rc;
161}
162
163
164/**
165 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
166 */
167static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
168 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
169{
170 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
171 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
172 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
173 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
174
175 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
176 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
177
178 int rc;
179 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
180 rc = drvHostValKitAudioCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
181 else
182 rc = drvHostValKitAudioCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
183
184 if (RT_SUCCESS(rc))
185 {
186 pStreamDbg->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
187 if (!pStreamDbg->pCfg)
188 rc = VERR_NO_MEMORY;
189 }
190
191 return rc;
192}
193
194
195/**
196 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
197 */
198static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
199 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
200{
201 PDRVHOSTVAKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
202 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
203 RT_NOREF(pThis);
204
205 uint64_t cNsSinceStart;
206 if (pStreamDbg->tsStarted != 0)
207 cNsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
208 else
209 {
210 pStreamDbg->tsStarted = RTTimeNanoTS();
211 cNsSinceStart = 0;
212 }
213
214 // Microseconds are used everythere below
215 uint32_t const cFrames = PDMAudioPropsBytesToFrames(&pStreamDbg->pCfg->Props, cbBuf);
216 char szTimingInfo[128];
217 size_t cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
218 // Host time elapsed since Guest submitted the first buffer for playback:
219 (uint32_t)(cNsSinceStart / 1000),
220 // how long all the samples submitted previously were played:
221 (uint32_t)(pStreamDbg->cFramesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz),
222 // how long a new uSamplesReady samples should/will be played:
223 (uint32_t)(cFrames * 1.0E6 / pStreamDbg->pCfg->Props.uHz),
224 cFrames);
225 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
226 pStreamDbg->cFramesSinceStarted += cFrames;
227
228 /* Remember when samples were consumed. */
229 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pThis->pDrvIns);
230
231 int rc2 = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
232 if (RT_FAILURE(rc2))
233 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
234
235 *pcbWritten = cbBuf;
236
237 return VINF_SUCCESS;
238}
239
240
241/**
242 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
243 */
244static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
245 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
246{
247 RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
248
249 /* Never capture anything. */
250 if (puRead)
251 *puRead = 0;
252
253 return VINF_SUCCESS;
254}
255
256
257static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
258{
259 RT_NOREF(pDrv, pStreamDbg);
260 return VINF_SUCCESS;
261}
262
263
264static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
265{
266 RT_NOREF(pDrv);
267
268 if (pStreamDbg->Out.pu8PlayBuffer)
269 {
270 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
271 pStreamDbg->Out.pu8PlayBuffer = NULL;
272 }
273
274 if (pStreamDbg->pFile)
275 {
276 size_t cbDataSize = AudioHlpFileGetDataSize(pStreamDbg->pFile);
277 if (cbDataSize)
278 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
279
280 AudioHlpFileDestroy(pStreamDbg->pFile);
281 pStreamDbg->pFile = NULL;
282 }
283
284 return VINF_SUCCESS;
285}
286
287
288static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
289{
290 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
291
292 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
293 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
294
295 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
296 return VINF_SUCCESS;
297
298 int rc;
299 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
300 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
301 else
302 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
303
304 if (RT_SUCCESS(rc))
305 {
306 PDMAudioStrmCfgFree(pStreamDbg->pCfg);
307 pStreamDbg->pCfg = NULL;
308 }
309
310 return rc;
311}
312
313static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
314 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
315{
316 RT_NOREF(enmStreamCmd);
317 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
318 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
319
320 return VINF_SUCCESS;
321}
322
323/**
324 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
325 */
326static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
327{
328 RT_NOREF(pInterface, pStream);
329
330 return UINT32_MAX;
331}
332
333
334/**
335 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
336 */
337static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
338{
339 RT_NOREF(pInterface, pStream);
340
341 return UINT32_MAX;
342}
343
344static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostValKitAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
345 PPDMAUDIOBACKENDSTREAM pStream)
346{
347 RT_NOREF(pInterface, pStream);
348
349 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
350}
351
352/**
353 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
354 */
355static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
356{
357 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
358 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
359
360 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
361 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
362 return NULL;
363}
364
365
366/**
367 * Constructs a VaKit audio driver instance.
368 *
369 * @copydoc FNPDMDRVCONSTRUCT
370 */
371static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
372{
373 RT_NOREF(pCfg, fFlags);
374 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
375 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
376 LogRel(("Audio: Initializing VAKIT driver\n"));
377
378 /*
379 * Init the static parts.
380 */
381 pThis->pDrvIns = pDrvIns;
382 /* IBase */
383 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
384 /* IHostAudio */
385 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
386 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
387 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
388 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
389 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
390 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
391 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
392 pThis->IHostAudio.pfnStreamGetStatus = drvHostValKitAudioHA_StreamGetStatus;
393 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
394 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
395 pThis->IHostAudio.pfnGetDevices = NULL;
396 pThis->IHostAudio.pfnStreamGetPending = NULL;
397
398 return VINF_SUCCESS;
399}
400
401/**
402 * Char driver registration record.
403 */
404const PDMDRVREG g_DrvHostValidationKitAudio =
405{
406 /* u32Version */
407 PDM_DRVREG_VERSION,
408 /* szName */
409 "ValidationKitAudio",
410 /* szRCMod */
411 "",
412 /* szR0Mod */
413 "",
414 /* pszDescription */
415 "ValidationKitAudio audio host driver",
416 /* fFlags */
417 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
418 /* fClass. */
419 PDM_DRVREG_CLASS_AUDIO,
420 /* cMaxInstances */
421 ~0U,
422 /* cbInstance */
423 sizeof(DRVHOSTVAKITAUDIO),
424 /* pfnConstruct */
425 drvHostValKitAudioConstruct,
426 /* pfnDestruct */
427 NULL,
428 /* pfnRelocate */
429 NULL,
430 /* pfnIOCtl */
431 NULL,
432 /* pfnPowerOn */
433 NULL,
434 /* pfnReset */
435 NULL,
436 /* pfnSuspend */
437 NULL,
438 /* pfnResume */
439 NULL,
440 /* pfnAttach */
441 NULL,
442 /* pfnDetach */
443 NULL,
444 /* pfnPowerOff */
445 NULL,
446 /* pfnSoftReset */
447 NULL,
448 /* u32EndVersion */
449 PDM_DRVREG_VERSION
450};
451
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette