VirtualBox

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

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