VirtualBox

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

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

Audio: Added geberuc asynchronous init to DrvAudio for use in WAS (and maybe others). bugref:9890

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