VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostDebugAudio.cpp@ 87875

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

Audio: Unified debug .WAV path file generation so that all data lands at the same place.

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