VirtualBox

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

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

DrvHostAudioDebug,DrvHostAudioNull,DrvHostAudioValidationKit: Prepare splitting up pfnStreamControl. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: DrvHostAudioDebug.cpp 88462 2021-04-12 10:59:57Z 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25#include <VBox/vmm/pdmaudioinline.h>
26
27#include <iprt/rand.h>
28#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
29
30#define _USE_MATH_DEFINES
31#include <math.h> /* sin, M_PI */
32
33#include "AudioHlp.h"
34#include "VBoxDD.h"
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40/**
41 * Debug host audio stream.
42 */
43typedef struct DEBUGAUDIOSTREAM
44{
45 /** The stream's acquired configuration. */
46 PDMAUDIOSTREAMCFG Cfg;
47 /** Audio file to dump output to or read input from. */
48 PAUDIOHLPFILE pFile;
49 union
50 {
51 struct
52 {
53 /** Current sample index for generate the sine wave. */
54 uint64_t uSample;
55 /** The fixed portion of the sin() input. */
56 double rdFixed;
57 /** Timestamp of last captured samples. */
58 uint64_t tsLastCaptured;
59 /** Frequency (in Hz) of the sine wave to generate. */
60 double rdFreqHz;
61 } In;
62 };
63} DEBUGAUDIOSTREAM;
64/** Pointer to a debug host audio stream. */
65typedef DEBUGAUDIOSTREAM *PDEBUGAUDIOSTREAM;
66
67/**
68 * Debug audio driver instance data.
69 * @implements PDMIAUDIOCONNECTOR
70 */
71typedef struct DRVHOSTDEBUGAUDIO
72{
73 /** Pointer to the driver instance structure. */
74 PPDMDRVINS pDrvIns;
75 /** Pointer to host audio interface. */
76 PDMIHOSTAUDIO IHostAudio;
77} DRVHOSTDEBUGAUDIO;
78/** Pointer to a debug host audio driver. */
79typedef DRVHOSTDEBUGAUDIO *PDRVHOSTDEBUGAUDIO;
80
81
82/*********************************************************************************************************************************
83* Global Variables *
84*********************************************************************************************************************************/
85/** Frequency selection for input streams. */
86static const double s_ardInputFreqsHz[] =
87{
88 349.2282 /*F4*/,
89 440.0000 /*A4*/,
90 523.2511 /*C5*/,
91 698.4565 /*F5*/,
92 880.0000 /*A5*/,
93 1046.502 /*C6*/,
94 1174.659 /*D6*/,
95 1396.913 /*F6*/,
96 1760.0000 /*A6*/
97};
98
99
100/**
101 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
102 */
103static DECLCALLBACK(int) drvHostDebugAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
104{
105 RT_NOREF(pInterface);
106 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
107
108 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "DebugAudio");
109
110 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
111 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
112
113 pBackendCfg->cMaxStreamsOut = 1; /* Output; writing to a file. */
114 pBackendCfg->cMaxStreamsIn = 1; /* Input; generates a sine wave. */
115
116 return VINF_SUCCESS;
117}
118
119
120/**
121 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
122 */
123static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
124{
125 RT_NOREF(pInterface, enmDir);
126 return PDMAUDIOBACKENDSTS_RUNNING;
127}
128
129
130/**
131 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
132 */
133static DECLCALLBACK(int) drvHostDebugAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
134 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
135{
136 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
137 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
138 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
139 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
140 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
141
142 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
143
144 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
145 {
146 /* Pick a frequency from our selection, so that every time a recording starts
147 we'll hopfully generate a different note. */
148 pStreamDbg->In.rdFreqHz = s_ardInputFreqsHz[RTRandU32Ex(0, RT_ELEMENTS(s_ardInputFreqsHz) - 1)];
149 pStreamDbg->In.rdFixed = 2.0 * M_PI * pStreamDbg->In.rdFreqHz / PDMAudioPropsHz(&pStreamDbg->Cfg.Props);
150 pStreamDbg->In.uSample = 0;
151 }
152
153 int rc = AudioHlpFileCreateAndOpenEx(&pStreamDbg->pFile, AUDIOHLPFILETYPE_WAV, NULL /*use temp dir*/,
154 pCfgReq->enmDir == PDMAUDIODIR_IN ? "DebugAudioIn" : "DebugAudioOut",
155 pDrv->pDrvIns->iInstance, AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
156 &pCfgReq->Props, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE);
157 if (RT_FAILURE(rc))
158 LogRel(("DebugAudio: Failed to creating debug file for %s stream '%s' in the temp directory: %Rrc\n",
159 pCfgReq->enmDir == PDMAUDIODIR_IN ? "input" : "output", pCfgReq->szName, rc));
160
161 return rc;
162}
163
164
165/**
166 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
167 */
168static DECLCALLBACK(int) drvHostDebugAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
169{
170 RT_NOREF(pInterface);
171 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
172 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
173
174 if (pStreamDbg->pFile)
175 {
176 AudioHlpFileDestroy(pStreamDbg->pFile);
177 pStreamDbg->pFile = NULL;
178 }
179
180 return VINF_SUCCESS;
181}
182
183
184/**
185 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
186 */
187static DECLCALLBACK(int) drvHostDebugAudioHA_StreamControlStub(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
188{
189 RT_NOREF(pInterface, pStream);
190 return VINF_SUCCESS;
191}
192
193
194/**
195 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
196 */
197static DECLCALLBACK(int) drvHostDebugAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
198 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
199{
200 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
201 * replacing it with individual StreamXxxx methods. That would save us
202 * potentally huge switches and more easily see which drivers implement
203 * which operations (grep for pfnStreamXxxx). */
204 switch (enmStreamCmd)
205 {
206 case PDMAUDIOSTREAMCMD_ENABLE:
207 return drvHostDebugAudioHA_StreamControlStub(pInterface, pStream);
208 case PDMAUDIOSTREAMCMD_DISABLE:
209 return drvHostDebugAudioHA_StreamControlStub(pInterface, pStream);
210 case PDMAUDIOSTREAMCMD_PAUSE:
211 return drvHostDebugAudioHA_StreamControlStub(pInterface, pStream);
212 case PDMAUDIOSTREAMCMD_RESUME:
213 return drvHostDebugAudioHA_StreamControlStub(pInterface, pStream);
214 case PDMAUDIOSTREAMCMD_DRAIN:
215 return drvHostDebugAudioHA_StreamControlStub(pInterface, pStream);
216
217 case PDMAUDIOSTREAMCMD_END:
218 case PDMAUDIOSTREAMCMD_32BIT_HACK:
219 case PDMAUDIOSTREAMCMD_INVALID:
220 /* no default*/
221 break;
222 }
223 return VERR_NOT_SUPPORTED;
224}
225
226
227/**
228 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
229 */
230static DECLCALLBACK(uint32_t) drvHostDebugAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
231{
232 RT_NOREF(pInterface);
233 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
234
235 return PDMAudioPropsMilliToBytes(&pStreamDbg->Cfg.Props, 10 /*ms*/);
236}
237
238
239/**
240 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
241 */
242static DECLCALLBACK(uint32_t) drvHostDebugAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
243{
244 RT_NOREF(pInterface, pStream);
245 return UINT32_MAX;
246}
247
248
249/**
250 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
251 */
252static DECLCALLBACK(uint32_t) drvHostDebugAudioHA_StreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
253{
254 RT_NOREF(pInterface, pStream);
255 return 0;
256}
257
258
259/**
260 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
261 */
262static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDebugAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
263{
264 RT_NOREF(pInterface, pStream);
265 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
266}
267
268
269/**
270 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
271 */
272static DECLCALLBACK(int) drvHostDebugAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
273 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
274{
275 RT_NOREF(pInterface);
276 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
277
278 int rc = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
279 if (RT_SUCCESS(rc))
280 *pcbWritten = cbBuf;
281 else
282 LogRelMax(32, ("DebugAudio: Writing output failed with %Rrc\n", rc));
283 return rc;
284}
285
286
287/**
288 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
289 */
290static DECLCALLBACK(int) drvHostDebugAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
291 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
292{
293 RT_NOREF(pInterface);
294 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
295/** @todo rate limit this? */
296
297 /*
298 * Clear the buffer first so we don't need to thing about additional channels.
299 */
300 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStreamDbg->Cfg.Props, cbBuf);
301 PDMAudioPropsClearBuffer(&pStreamDbg->Cfg.Props, pvBuf, cbBuf, cFrames);
302
303 /*
304 * Generate the select sin wave in the first channel:
305 */
306 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pStreamDbg->Cfg.Props);
307 double const rdFixed = pStreamDbg->In.rdFixed;
308 uint64_t iSrcFrame = pStreamDbg->In.uSample;
309 switch (PDMAudioPropsSampleSize(&pStreamDbg->Cfg.Props))
310 {
311 case 1:
312 /* untested */
313 if (PDMAudioPropsIsSigned(&pStreamDbg->Cfg.Props))
314 {
315 int8_t *piSample = (int8_t *)pvBuf;
316 while (cFrames-- > 0)
317 {
318 *piSample = 126 /*Amplitude*/ * sin(rdFixed * iSrcFrame);
319 iSrcFrame++;
320 piSample += cbFrame;
321 }
322 }
323 else
324 {
325 /* untested */
326 uint16_t *pbSample = (uint16_t *)pvBuf;
327 while (cFrames-- > 0)
328 {
329 *pbSample = 126 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x80;
330 iSrcFrame++;
331 pbSample += cbFrame;
332 }
333 }
334 break;
335
336 case 2:
337 if (PDMAudioPropsIsSigned(&pStreamDbg->Cfg.Props))
338 {
339 int16_t *piSample = (int16_t *)pvBuf;
340 while (cFrames-- > 0)
341 {
342 *piSample = 32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame);
343 iSrcFrame++;
344 piSample = (int16_t *)((uint8_t *)piSample + cbFrame);
345 }
346 }
347 else
348 {
349 /* untested */
350 uint16_t *puSample = (uint16_t *)pvBuf;
351 while (cFrames-- > 0)
352 {
353 *puSample = 32760 /*Amplitude*/ * sin(rdFixed * iSrcFrame) + 0x8000;
354 iSrcFrame++;
355 puSample = (uint16_t *)((uint8_t *)puSample + cbFrame);
356 }
357 }
358 break;
359
360 case 4:
361 /* untested */
362 if (PDMAudioPropsIsSigned(&pStreamDbg->Cfg.Props))
363 {
364 int32_t *piSample = (int32_t *)pvBuf;
365 while (cFrames-- > 0)
366 {
367 *piSample = (32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame);
368 iSrcFrame++;
369 piSample = (int32_t *)((uint8_t *)piSample + cbFrame);
370 }
371 }
372 else
373 {
374 uint32_t *puSample = (uint32_t *)pvBuf;
375 while (cFrames-- > 0)
376 {
377 *puSample = (32760 << 16) /*Amplitude*/ * sin(rdFixed * iSrcFrame) + UINT32_C(0x80000000);
378 iSrcFrame++;
379 puSample = (uint32_t *)((uint8_t *)puSample + cbFrame);
380 }
381 }
382 break;
383
384 default:
385 AssertFailed();
386 }
387 pStreamDbg->In.uSample = iSrcFrame;
388
389 /*
390 * Write it.
391 */
392 int rc = AudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cbBuf, 0 /* fFlags */);
393 if (RT_SUCCESS(rc))
394 *pcbRead = cbBuf;
395 else
396 LogRelMax(32, ("DebugAudio: Writing input failed with %Rrc\n", rc));
397 return rc;
398}
399
400
401/**
402 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
403 */
404static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
405{
406 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
407 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
408
409 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
410 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
411 return NULL;
412}
413
414
415/**
416 * Constructs a Null audio driver instance.
417 *
418 * @copydoc FNPDMDRVCONSTRUCT
419 */
420static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
421{
422 RT_NOREF(pCfg, fFlags);
423 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
424 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
425 LogRel(("Audio: Initializing DEBUG driver\n"));
426
427 /*
428 * Init the static parts.
429 */
430 pThis->pDrvIns = pDrvIns;
431 /* IBase */
432 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
433 /* IHostAudio */
434 pThis->IHostAudio.pfnGetConfig = drvHostDebugAudioHA_GetConfig;
435 pThis->IHostAudio.pfnGetDevices = NULL;
436 pThis->IHostAudio.pfnGetStatus = drvHostDebugAudioHA_GetStatus;
437 pThis->IHostAudio.pfnStreamCreate = drvHostDebugAudioHA_StreamCreate;
438 pThis->IHostAudio.pfnStreamDestroy = drvHostDebugAudioHA_StreamDestroy;
439 pThis->IHostAudio.pfnStreamControl = drvHostDebugAudioHA_StreamControl;
440 pThis->IHostAudio.pfnStreamGetReadable = drvHostDebugAudioHA_StreamGetReadable;
441 pThis->IHostAudio.pfnStreamGetWritable = drvHostDebugAudioHA_StreamGetWritable;
442 pThis->IHostAudio.pfnStreamGetPending = drvHostDebugAudioHA_StreamGetPending;
443 pThis->IHostAudio.pfnStreamGetStatus = drvHostDebugAudioHA_StreamGetStatus;
444 pThis->IHostAudio.pfnStreamPlay = drvHostDebugAudioHA_StreamPlay;
445 pThis->IHostAudio.pfnStreamCapture = drvHostDebugAudioHA_StreamCapture;
446
447#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
448 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "AudioDebugOutput.pcm");
449#endif
450
451 return VINF_SUCCESS;
452}
453
454/**
455 * Char driver registration record.
456 */
457const PDMDRVREG g_DrvHostDebugAudio =
458{
459 /* u32Version */
460 PDM_DRVREG_VERSION,
461 /* szName */
462 "DebugAudio",
463 /* szRCMod */
464 "",
465 /* szR0Mod */
466 "",
467 /* pszDescription */
468 "Debug audio host driver",
469 /* fFlags */
470 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
471 /* fClass. */
472 PDM_DRVREG_CLASS_AUDIO,
473 /* cMaxInstances */
474 ~0U,
475 /* cbInstance */
476 sizeof(DRVHOSTDEBUGAUDIO),
477 /* pfnConstruct */
478 drvHostDebugAudioConstruct,
479 /* pfnDestruct */
480 NULL,
481 /* pfnRelocate */
482 NULL,
483 /* pfnIOCtl */
484 NULL,
485 /* pfnPowerOn */
486 NULL,
487 /* pfnReset */
488 NULL,
489 /* pfnSuspend */
490 NULL,
491 /* pfnResume */
492 NULL,
493 /* pfnAttach */
494 NULL,
495 /* pfnDetach */
496 NULL,
497 /* pfnPowerOff */
498 NULL,
499 /* pfnSoftReset */
500 NULL,
501 /* u32EndVersion */
502 PDM_DRVREG_VERSION
503};
504
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