VirtualBox

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

Last change on this file since 63488 was 63488, checked in by vboxsync, 8 years ago

svn properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/* $Id: DrvHostDebugAudio.cpp 63488 2016-08-15 15:49:05Z vboxsync $ */
2/** @file
3 * Debug audio driver -- host backend for dumping and injecting audio data
4 * from/to the device emulation.
5 */
6
7/*
8 * Copyright (C) 2016 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 * --------------------------------------------------------------------
18 */
19
20#include <iprt/alloc.h>
21#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
22
23#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
24#include <VBox/log.h>
25#include <VBox/vmm/pdmaudioifs.h>
26
27#include "DrvAudio.h"
28#include "AudioMixBuffer.h"
29#include "VBoxDD.h"
30
31
32/**
33 * Structure for keeping a debug input/output stream.
34 */
35typedef struct DEBUGAUDIOSTREAM
36{
37 /** Note: Always must come first! */
38 PDMAUDIOSTREAM Stream;
39 /** Audio file to dump output to or read input from. */
40 PDMAUDIOFILE File;
41 union
42 {
43 struct
44 {
45 /** Timestamp of last captured samples. */
46 uint64_t tsLastCaptured;
47 } In;
48 struct
49 {
50 /** Timestamp of last played samples. */
51 uint64_t tsLastPlayed;
52 uint64_t cMaxSamplesInPlayBuffer;
53 uint8_t *pu8PlayBuffer;
54 } Out;
55 };
56
57} DEBUGAUDIOSTREAM, *PDEBUGAUDIOSTREAM;
58
59/**
60 * Debug audio driver instance data.
61 * @implements PDMIAUDIOCONNECTOR
62 */
63typedef struct DRVHOSTDEBUGAUDIO
64{
65 /** Pointer to the driver instance structure. */
66 PPDMDRVINS pDrvIns;
67 /** Pointer to host audio interface. */
68 PDMIHOSTAUDIO IHostAudio;
69} DRVHOSTDEBUGAUDIO, *PDRVHOSTDEBUGAUDIO;
70
71/*******************************************PDM_AUDIO_DRIVER******************************/
72
73
74/**
75 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
76 */
77static DECLCALLBACK(int) drvHostDebugAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
78{
79 NOREF(pInterface);
80 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
81
82 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
83 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
84
85 /* The NULL backend has exactly one input source and one output sink. */
86 pBackendCfg->cSources = 1;
87 pBackendCfg->cSinks = 1;
88
89 pBackendCfg->cMaxStreamsOut = 1; /* Output */
90 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
91
92 return VINF_SUCCESS;
93}
94
95
96/**
97 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
98 */
99static DECLCALLBACK(int) drvHostDebugAudioInit(PPDMIHOSTAUDIO pInterface)
100{
101 NOREF(pInterface);
102
103 LogFlowFuncLeaveRC(VINF_SUCCESS);
104 return VINF_SUCCESS;
105}
106
107
108/**
109 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
110 */
111static DECLCALLBACK(void) drvHostDebugAudioShutdown(PPDMIHOSTAUDIO pInterface)
112{
113 NOREF(pInterface);
114}
115
116
117/**
118 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
119 */
120static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
121{
122 RT_NOREF(enmDir);
123 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
124
125 return PDMAUDIOBACKENDSTS_RUNNING;
126}
127
128
129static int debugCreateStreamIn(PPDMIHOSTAUDIO pInterface,
130 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
131{
132 NOREF(pInterface);
133
134 /* Just adopt the wanted stream configuration. */
135 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStream->Props);
136 if (RT_SUCCESS(rc))
137 {
138 if (pCfgAcq)
139 pCfgAcq->cSamples = _1K;
140 }
141
142 LogFlowFuncLeaveRC(rc);
143 return rc;
144}
145
146
147static int debugCreateStreamOut(PPDMIHOSTAUDIO pInterface,
148 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
149{
150 NOREF(pInterface);
151
152 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
153
154 /* Just adopt the wanted stream configuration. */
155 PDMAUDIOPCMPROPS Props;
156 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &Props);
157 if (RT_SUCCESS(rc))
158 {
159 pDbgStream->Out.tsLastPlayed = 0;
160 pDbgStream->Out.cMaxSamplesInPlayBuffer = _1K;
161 pDbgStream->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pDbgStream->Out.cMaxSamplesInPlayBuffer << Props.cShift);
162 if (!pDbgStream->Out.pu8PlayBuffer)
163 rc = VERR_NO_MEMORY;
164 }
165
166 if (RT_SUCCESS(rc))
167 {
168 char szFile[RTPATH_MAX];
169 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), "/tmp/", NULL, PDMAUDIOFILETYPE_WAV);
170 if (RT_SUCCESS(rc))
171 {
172 LogRel(("DebugAudio: Creating output file '%s'\n", szFile));
173 rc = DrvAudioHlpWAVFileOpen(&pDbgStream->File, szFile,
174 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
175 &Props, PDMAUDIOFILEFLAG_NONE);
176 if (RT_FAILURE(rc))
177 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
178 }
179 }
180
181 if (RT_SUCCESS(rc))
182 {
183 if (pCfgAcq)
184 pCfgAcq->cSamples = pDbgStream->Out.cMaxSamplesInPlayBuffer;
185 }
186
187 LogFlowFuncLeaveRC(rc);
188 return rc;
189}
190
191
192/**
193 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
194 */
195static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
196{
197 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
198 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
199 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
200
201 int rc;
202 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
203 rc = debugCreateStreamIn( pInterface, pStream, pCfgReq, pCfgAcq);
204 else
205 rc = debugCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
206
207 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
208 return rc;
209}
210
211
212/**
213 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
214 */
215static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
216{
217 RT_NOREF(pvBuf, cbBuf);
218
219 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
220 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
221
222 /* Consume as many samples as would be played at the current frequency since last call. */
223 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
224
225 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
226 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
227 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
228
229 /*
230 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
231 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
232 */
233 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
234
235 /* Don't play more than available. */
236 /*if (cSamplesPlayed > cLive)
237 cSamplesPlayed = cLive;*/
238
239 uint32_t cSamplesPlayed = 0;
240 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
241 while (cSamplesAvail)
242 {
243 uint32_t cSamplesRead = 0;
244 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
245 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
246
247 if (RT_FAILURE(rc2))
248 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
249
250 if (!cSamplesRead)
251 break;
252#if 0
253 RTFILE fh;
254 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
255 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
256 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
257 RTFileClose(fh);
258#endif
259 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
260 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
261 0 /* fFlags */);
262 if (RT_FAILURE(rc2))
263 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
264
265 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
266
267 Assert(cSamplesAvail >= cSamplesRead);
268 cSamplesAvail -= cSamplesRead;
269
270 cSamplesPlayed += cSamplesRead;
271 }
272
273 /* Remember when samples were consumed. */
274 pDbgStream->Out.tsLastPlayed = u64TicksNow;
275
276 if (pcbWritten)
277 *pcbWritten = cSamplesPlayed;
278
279 return VINF_SUCCESS;
280}
281
282
283/**
284 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
285 */
286static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
287{
288 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
289
290 /* Never capture anything. */
291 if (pcbRead)
292 *pcbRead = 0;
293
294 return VINF_SUCCESS;
295}
296
297
298static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
299{
300 RT_NOREF(pInterface, pStream);
301 LogFlowFuncLeaveRC(VINF_SUCCESS);
302 return VINF_SUCCESS;
303}
304
305
306static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
307{
308 RT_NOREF(pInterface);
309 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
310 if ( pDbgStream
311 && pDbgStream->Out.pu8PlayBuffer)
312 {
313 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
314 pDbgStream->Out.pu8PlayBuffer = NULL;
315 }
316
317 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
318
319 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
320 if (RT_SUCCESS(rc))
321 {
322 /* Delete the file again if nothing but the header was written to it. */
323 if (!cbDataSize)
324 rc = RTFileDelete(pDbgStream->File.szName); /** @todo Make deletion configurable? */
325 }
326
327 LogFlowFuncLeaveRC(rc);
328 return rc;
329}
330
331
332static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
333{
334 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
335 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
336
337 int rc;
338 if (pStream->enmDir == PDMAUDIODIR_IN)
339 rc = debugDestroyStreamIn(pInterface, pStream);
340 else
341 rc = debugDestroyStreamOut(pInterface, pStream);
342
343 return rc;
344}
345
346static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
347 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
348{
349 RT_NOREF(enmStreamCmd);
350 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
351 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
352
353 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
354
355 return VINF_SUCCESS;
356}
357
358static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
359{
360 NOREF(pInterface);
361 NOREF(pStream);
362
363 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
364 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
365}
366
367static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
368{
369 NOREF(pInterface);
370 NOREF(pStream);
371
372 return VINF_SUCCESS;
373}
374
375
376/**
377 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
378 */
379static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
380{
381 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
382 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
383
384 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
385 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
386 return NULL;
387}
388
389
390/**
391 * Constructs a Null audio driver instance.
392 *
393 * @copydoc FNPDMDRVCONSTRUCT
394 */
395static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
396{
397 RT_NOREF(pCfg, fFlags);
398 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
399 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
400 LogRel(("Audio: Initializing DEBUG driver\n"));
401
402 /*
403 * Init the static parts.
404 */
405 pThis->pDrvIns = pDrvIns;
406 /* IBase */
407 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
408 /* IHostAudio */
409 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
410
411 return VINF_SUCCESS;
412}
413
414/**
415 * Char driver registration record.
416 */
417const PDMDRVREG g_DrvHostDebugAudio =
418{
419 /* u32Version */
420 PDM_DRVREG_VERSION,
421 /* szName */
422 "DebugAudio",
423 /* szRCMod */
424 "",
425 /* szR0Mod */
426 "",
427 /* pszDescription */
428 "Debug audio host driver",
429 /* fFlags */
430 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
431 /* fClass. */
432 PDM_DRVREG_CLASS_AUDIO,
433 /* cMaxInstances */
434 ~0U,
435 /* cbInstance */
436 sizeof(DRVHOSTDEBUGAUDIO),
437 /* pfnConstruct */
438 drvHostDebugAudioConstruct,
439 /* pfnDestruct */
440 NULL,
441 /* pfnRelocate */
442 NULL,
443 /* pfnIOCtl */
444 NULL,
445 /* pfnPowerOn */
446 NULL,
447 /* pfnReset */
448 NULL,
449 /* pfnSuspend */
450 NULL,
451 /* pfnResume */
452 NULL,
453 /* pfnAttach */
454 NULL,
455 /* pfnDetach */
456 NULL,
457 /* pfnPowerOff */
458 NULL,
459 /* pfnSoftReset */
460 NULL,
461 /* u32EndVersion */
462 PDM_DRVREG_VERSION
463};
464
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