VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioDebug.cpp@ 88286

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

Audio: StreamPlay cleanups in the Debug and Null drivers. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: DrvHostAudioDebug.cpp 88286 2021-03-25 10:33:46Z vboxsync $ */
2/** @file
3 * Host audio driver - Debug - For dumping and injecting audio data from/to the device emulation.
4 */
5
6/*
7 * Copyright (C) 2016-2021 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/mem.h>
19#include <iprt/rand.h>
20#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
21
22#include <math.h>
23
24#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
25#include <VBox/log.h>
26#include <VBox/vmm/pdmaudioifs.h>
27#include <VBox/vmm/pdmaudioinline.h>
28
29#include "AudioHlp.h"
30#include "VBoxDD.h"
31
32
33/**
34 * Structure for keeping a debug input/output stream.
35 */
36typedef struct DEBUGAUDIOSTREAM
37{
38 /** The stream's acquired configuration. */
39 PPDMAUDIOSTREAMCFG pCfg;
40 /** Audio file to dump output to or read input from. */
41 PPDMAUDIOFILE pFile;
42 union
43 {
44 struct
45 {
46 /** Frequency (in Hz) of the sine wave to generate. */
47 uint16_t uFreqHz;
48 /** Current sample index for generate the sine wave. */
49 uint64_t uSample;
50 /** Timestamp of last captured samples. */
51 uint64_t tsLastCaptured;
52 } In;
53 };
54} DEBUGAUDIOSTREAM, *PDEBUGAUDIOSTREAM;
55
56/**
57 * Debug audio driver instance data.
58 * @implements PDMIAUDIOCONNECTOR
59 */
60typedef struct DRVHOSTDEBUGAUDIO
61{
62 /** Pointer to the driver instance structure. */
63 PPDMDRVINS pDrvIns;
64 /** Pointer to host audio interface. */
65 PDMIHOSTAUDIO IHostAudio;
66} DRVHOSTDEBUGAUDIO, *PDRVHOSTDEBUGAUDIO;
67
68/*******************************************PDM_AUDIO_DRIVER******************************/
69
70
71/**
72 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
73 */
74static DECLCALLBACK(int) drvHostDebugAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
75{
76 RT_NOREF(pInterface);
77 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
78
79 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DebugAudio");
80
81 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
82 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
83
84 pBackendCfg->cMaxStreamsOut = 1; /* Output; writing to a file. */
85 pBackendCfg->cMaxStreamsIn = 1; /* Input; generates a sine wave. */
86
87 return VINF_SUCCESS;
88}
89
90
91/**
92 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
93 */
94static DECLCALLBACK(int) drvHostDebugAudioHA_Init(PPDMIHOSTAUDIO pInterface)
95{
96 RT_NOREF(pInterface);
97
98 LogFlowFuncLeaveRC(VINF_SUCCESS);
99 return VINF_SUCCESS;
100}
101
102
103/**
104 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
105 */
106static DECLCALLBACK(void) drvHostDebugAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
107{
108 RT_NOREF(pInterface);
109}
110
111
112/**
113 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
114 */
115static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
116{
117 RT_NOREF(enmDir);
118 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
119
120 return PDMAUDIOBACKENDSTS_RUNNING;
121}
122
123
124/**
125 * Creates a debug output .WAV file on the host with the specified stream configuration.
126 *
127 * @returns VBox status code.
128 * @param pDrv Driver instance.
129 * @param pStreamDbg Debug audio stream to create file for.
130 * @param fIn Whether this is an input or output stream to create file for.
131 * @param pCfg Stream configuration to create .wAV file with.
132 */
133static int debugCreateFile(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg, bool fIn, PPDMAUDIOSTREAMCFG pCfg)
134{
135 char szFile[RTPATH_MAX];
136 int rc = AudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), NULL /* Use temporary directory */, fIn ? "DebugAudioIn" : "DebugAudioOut",
137 pDrv->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
138 if (RT_SUCCESS(rc))
139 {
140 rc = AudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pStreamDbg->pFile);
141 if (RT_SUCCESS(rc))
142 rc = AudioHlpFileOpen(pStreamDbg->pFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE, &pCfg->Props);
143 if (RT_FAILURE(rc))
144 LogRel(("DebugAudio: Creating %sput file '%s' failed with %Rrc\n", fIn ? "in" : "out", szFile, rc));
145 }
146 else
147 LogRel(("DebugAudio: Unable to build file name: %Rrc\n", rc));
148
149 return rc;
150}
151
152
153static int debugCreateStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
154 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
155{
156 RT_NOREF(pDrv, pCfgReq);
157
158 pStreamDbg->In.uSample = 0; /* Initialize sample index. */
159
160 const uint16_t auFreqsHz[] = { 400, 600, 750, 800, 1000, 1200, 1400, 1600 };
161
162 /* Chose a random frequency so that every time a recording is started (hopefully) another tone will be generated. */
163 pStreamDbg->In.uFreqHz = auFreqsHz[RTRandU32Ex(0, RT_ELEMENTS(auFreqsHz) - 1)];
164
165 return debugCreateFile(pDrv, pStreamDbg, true /* fIn */, pCfgAcq);
166}
167
168
169static int debugCreateStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
170 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
171{
172 RT_NOREF(pDrv, pCfgAcq);
173
174 return debugCreateFile(pDrv, pStreamDbg, false /* fIn */, pCfgReq);
175}
176
177
178/**
179 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
180 */
181static DECLCALLBACK(int) drvHostDebugAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
182 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
183{
184 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
185 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
186 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
187 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
188
189 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
190 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
191
192 int rc;
193 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
194 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
195 else
196 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
197
198 if (RT_SUCCESS(rc))
199 {
200 pStreamDbg->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
201 if (!pStreamDbg->pCfg)
202 rc = VERR_NO_MEMORY;
203 }
204
205 return rc;
206}
207
208
209/**
210 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
211 */
212static DECLCALLBACK(int) drvHostDebugAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
213 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
214{
215 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
216 RT_NOREF(pInterface);
217
218 int rc = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
219 if (RT_SUCCESS(rc))
220 *pcbWritten = cbBuf;
221 else
222 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc));
223 return rc;
224}
225
226
227/**
228 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
229 */
230static DECLCALLBACK(int) drvHostDebugAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
231 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
232{
233 RT_NOREF(pInterface);
234
235 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
236
237 PPDMAUDIOSTREAMCFG pCfg = pStreamDbg->pCfg;
238 AssertPtr(pCfg);
239
240 Assert(uBufSize % PDMAudioPropsSampleSize(&pCfg->Props) == 0);
241 size_t const cSamples = uBufSize / PDMAudioPropsSampleSize(&pCfg->Props);
242
243 uint16_t *paBuf = (uint16_t *)pvBuf;
244
245 /* Generate a simple mono sine wave. */
246 for (size_t i = 0; i < cSamples; i++)
247 {
248 paBuf[i] = 32760 * sin((2.f * float(3.1415) * pStreamDbg->In.uFreqHz) / pCfg->Props.uHz * pStreamDbg->In.uSample);
249 if (pStreamDbg->In.uSample == UINT64_MAX)
250 {
251 pStreamDbg->In.uSample = 0;
252 continue;
253 }
254
255 pStreamDbg->In.uSample++;
256 }
257
258 int rc = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, uBufSize, 0 /* fFlags */);
259 if (RT_FAILURE(rc))
260 {
261 LogRel(("DebugAudio: Writing input failed with %Rrc\n", rc));
262 return rc;
263 }
264
265 if (puRead)
266 *puRead = uBufSize;
267
268 return VINF_SUCCESS;
269}
270
271
272static int debugDestroyStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
273{
274 RT_NOREF(pDrv, pStreamDbg);
275 return VINF_SUCCESS;
276}
277
278
279static int debugDestroyStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
280{
281 RT_NOREF(pDrv, pStreamDbg);
282 return VINF_SUCCESS;
283}
284
285
286/**
287 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
288 */
289static DECLCALLBACK(int) drvHostDebugAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
290{
291 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
292
293 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
294 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)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 = debugDestroyStreamIn (pDrv, pStreamDbg);
302 else
303 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
304
305 if (RT_SUCCESS(rc))
306 {
307 AudioHlpFileDestroy(pStreamDbg->pFile);
308
309 PDMAudioStrmCfgFree(pStreamDbg->pCfg);
310 pStreamDbg->pCfg = NULL;
311 }
312
313 return rc;
314}
315
316
317/**
318 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
319 */
320static DECLCALLBACK(int) drvHostDebugAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
321 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
322{
323 RT_NOREF(enmStreamCmd);
324 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
325 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
326
327 return VINF_SUCCESS;
328}
329
330
331/**
332 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
333 */
334static DECLCALLBACK(uint32_t) drvHostDebugAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
335{
336 RT_NOREF(pInterface);
337
338 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
339
340 AssertPtr(pStreamDbg->pCfg);
341
342 return PDMAudioPropsMilliToBytes(&pStreamDbg->pCfg->Props, 10 /*ms*/);
343}
344
345
346/**
347 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
348 */
349static DECLCALLBACK(uint32_t) drvHostDebugAudioHA_StreamGetWritable(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(PDMAUDIOSTREAMSTS) drvHostDebugAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
361{
362 RT_NOREF(pInterface, pStream);
363
364 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
365}
366
367
368/**
369 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
370 */
371static DECLCALLBACK(int) drvHostDebugAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
372{
373 RT_NOREF(pInterface, pStream);
374 return VINF_SUCCESS;
375}
376
377
378/**
379 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
380 */
381static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
382{
383 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
384 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
385
386 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
387 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
388 return NULL;
389}
390
391
392/**
393 * Constructs a Null audio driver instance.
394 *
395 * @copydoc FNPDMDRVCONSTRUCT
396 */
397static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
398{
399 RT_NOREF(pCfg, fFlags);
400 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
401 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
402 LogRel(("Audio: Initializing DEBUG driver\n"));
403
404 /*
405 * Init the static parts.
406 */
407 pThis->pDrvIns = pDrvIns;
408 /* IBase */
409 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
410 /* IHostAudio */
411 pThis->IHostAudio.pfnInit = drvHostDebugAudioHA_Init;
412 pThis->IHostAudio.pfnShutdown = drvHostDebugAudioHA_Shutdown;
413 pThis->IHostAudio.pfnGetConfig = drvHostDebugAudioHA_GetConfig;
414 pThis->IHostAudio.pfnGetStatus = drvHostDebugAudioHA_GetStatus;
415 pThis->IHostAudio.pfnStreamCreate = drvHostDebugAudioHA_StreamCreate;
416 pThis->IHostAudio.pfnStreamDestroy = drvHostDebugAudioHA_StreamDestroy;
417 pThis->IHostAudio.pfnStreamControl = drvHostDebugAudioHA_StreamControl;
418 pThis->IHostAudio.pfnStreamGetReadable = drvHostDebugAudioHA_StreamGetReadable;
419 pThis->IHostAudio.pfnStreamGetWritable = drvHostDebugAudioHA_StreamGetWritable;
420 pThis->IHostAudio.pfnStreamGetStatus = drvHostDebugAudioHA_StreamGetStatus;
421 pThis->IHostAudio.pfnStreamIterate = drvHostDebugAudioHA_StreamIterate;
422 pThis->IHostAudio.pfnStreamPlay = drvHostDebugAudioHA_StreamPlay;
423 pThis->IHostAudio.pfnStreamCapture = drvHostDebugAudioHA_StreamCapture;
424 pThis->IHostAudio.pfnSetCallback = NULL;
425 pThis->IHostAudio.pfnGetDevices = NULL;
426 pThis->IHostAudio.pfnStreamGetPending = NULL;
427 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
428 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
429 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
430 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
431
432#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
433 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "AudioDebugOutput.pcm");
434#endif
435
436 return VINF_SUCCESS;
437}
438
439/**
440 * Char driver registration record.
441 */
442const PDMDRVREG g_DrvHostDebugAudio =
443{
444 /* u32Version */
445 PDM_DRVREG_VERSION,
446 /* szName */
447 "DebugAudio",
448 /* szRCMod */
449 "",
450 /* szR0Mod */
451 "",
452 /* pszDescription */
453 "Debug 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(DRVHOSTDEBUGAUDIO),
462 /* pfnConstruct */
463 drvHostDebugAudioConstruct,
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