VirtualBox

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

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

Audio: Made the pfnInit method of the PDMIHOSTAUDIO interface optional. Removed a bunch of stubs and moved the DSound init code to the constructor. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 88381 2021-04-07 08:43:55Z 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 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");
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->uSamplesSinceStarted = 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 uBufSize, uint32_t *puWritten)
200{
201 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
202 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
203 RT_NOREF(pDrv);
204
205 uint64_t tsSinceStart;
206 size_t cch;
207 char szTimingInfo[128];
208
209 if (pStreamDbg->tsStarted == 0)
210 {
211 pStreamDbg->tsStarted = RTTimeNanoTS();
212 tsSinceStart = 0;
213 }
214 else
215 {
216 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
217 }
218
219 // Microseconds are used everythere below
220 uint32_t sBuf = uBufSize >> pStreamDbg->pCfg->Props.cShift;
221 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
222 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
223 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
224 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
225 sBuf);
226 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
227 pStreamDbg->uSamplesSinceStarted += sBuf;
228
229 /* Remember when samples were consumed. */
230 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
231
232 int rc2 = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, uBufSize, 0 /* fFlags */);
233 if (RT_FAILURE(rc2))
234 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
235
236 *puWritten = uBufSize;
237
238 return VINF_SUCCESS;
239}
240
241
242/**
243 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
244 */
245static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
246 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
247{
248 RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
249
250 /* Never capture anything. */
251 if (puRead)
252 *puRead = 0;
253
254 return VINF_SUCCESS;
255}
256
257
258static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
259{
260 RT_NOREF(pDrv, pStreamDbg);
261 return VINF_SUCCESS;
262}
263
264
265static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
266{
267 RT_NOREF(pDrv);
268
269 if (pStreamDbg->Out.pu8PlayBuffer)
270 {
271 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
272 pStreamDbg->Out.pu8PlayBuffer = NULL;
273 }
274
275 if (pStreamDbg->pFile)
276 {
277 size_t cbDataSize = AudioHlpFileGetDataSize(pStreamDbg->pFile);
278 if (cbDataSize)
279 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
280
281 AudioHlpFileDestroy(pStreamDbg->pFile);
282 pStreamDbg->pFile = NULL;
283 }
284
285 return VINF_SUCCESS;
286}
287
288
289static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
290{
291 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
292
293 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
294 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
295
296 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
297 return VINF_SUCCESS;
298
299 int rc;
300 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
301 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
302 else
303 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
304
305 if (RT_SUCCESS(rc))
306 {
307 PDMAudioStrmCfgFree(pStreamDbg->pCfg);
308 pStreamDbg->pCfg = NULL;
309 }
310
311 return rc;
312}
313
314static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
315 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
316{
317 RT_NOREF(enmStreamCmd);
318 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
319 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
320
321 return VINF_SUCCESS;
322}
323
324/**
325 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
326 */
327static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
328{
329 RT_NOREF(pInterface, pStream);
330
331 return UINT32_MAX;
332}
333
334
335/**
336 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
337 */
338static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
339{
340 RT_NOREF(pInterface, pStream);
341
342 return UINT32_MAX;
343}
344
345static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostValKitAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
346 PPDMAUDIOBACKENDSTREAM pStream)
347{
348 RT_NOREF(pInterface, pStream);
349
350 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
351}
352
353/**
354 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
355 */
356static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
357{
358 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
359 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
360
361 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
362 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
363 return NULL;
364}
365
366
367/**
368 * Constructs a VaKit audio driver instance.
369 *
370 * @copydoc FNPDMDRVCONSTRUCT
371 */
372static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
373{
374 RT_NOREF(pCfg, fFlags);
375 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
376 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
377 LogRel(("Audio: Initializing VAKIT driver\n"));
378
379 /*
380 * Init the static parts.
381 */
382 pThis->pDrvIns = pDrvIns;
383 /* IBase */
384 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
385 /* IHostAudio */
386 pThis->IHostAudio.pfnInit = NULL;
387 pThis->IHostAudio.pfnShutdown = NULL;
388 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
389 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
390 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
391 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
392 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
393 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
394 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
395 pThis->IHostAudio.pfnStreamGetStatus = drvHostValKitAudioHA_StreamGetStatus;
396 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
397 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
398 pThis->IHostAudio.pfnGetDevices = NULL;
399 pThis->IHostAudio.pfnStreamGetPending = NULL;
400
401 return VINF_SUCCESS;
402}
403
404/**
405 * Char driver registration record.
406 */
407const PDMDRVREG g_DrvHostValidationKitAudio =
408{
409 /* u32Version */
410 PDM_DRVREG_VERSION,
411 /* szName */
412 "ValidationKitAudio",
413 /* szRCMod */
414 "",
415 /* szR0Mod */
416 "",
417 /* pszDescription */
418 "ValidationKitAudio audio host driver",
419 /* fFlags */
420 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
421 /* fClass. */
422 PDM_DRVREG_CLASS_AUDIO,
423 /* cMaxInstances */
424 ~0U,
425 /* cbInstance */
426 sizeof(DRVHOSTVAKITAUDIO),
427 /* pfnConstruct */
428 drvHostValKitAudioConstruct,
429 /* pfnDestruct */
430 NULL,
431 /* pfnRelocate */
432 NULL,
433 /* pfnIOCtl */
434 NULL,
435 /* pfnPowerOn */
436 NULL,
437 /* pfnReset */
438 NULL,
439 /* pfnSuspend */
440 NULL,
441 /* pfnResume */
442 NULL,
443 /* pfnAttach */
444 NULL,
445 /* pfnDetach */
446 NULL,
447 /* pfnPowerOff */
448 NULL,
449 /* pfnSoftReset */
450 NULL,
451 /* u32EndVersion */
452 PDM_DRVREG_VERSION
453};
454
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