VirtualBox

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

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

Audio: Replaced the confusing 'cs' prefix with cSamples of similar. See earlier commit message for details.

  • Property svn:executable set to *
File size: 13.5 KB
Line 
1/* $Id$ */
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
74static DECLCALLBACK(int) drvHostDebugAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
75{
76 NOREF(pInterface);
77 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
78
79 pCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
80 pCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
81
82 /* The NULL backend has exactly one input source and one output sink. */
83 pCfg->cSources = 1;
84 pCfg->cSinks = 1;
85
86 pCfg->cMaxStreamsOut = 1; /* Output */
87 pCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
88
89 return VINF_SUCCESS;
90}
91
92static DECLCALLBACK(int) drvHostDebugAudioInit(PPDMIHOSTAUDIO pInterface)
93{
94 NOREF(pInterface);
95
96 LogFlowFuncLeaveRC(VINF_SUCCESS);
97 return VINF_SUCCESS;
98}
99
100static int debugCreateStreamIn(PPDMIHOSTAUDIO pInterface,
101 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
102{
103 NOREF(pInterface);
104
105 /* Just adopt the wanted stream configuration. */
106 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &pStream->Props);
107 if (RT_SUCCESS(rc))
108 {
109 if (pcSamples)
110 *pcSamples = _1K;
111 }
112
113 LogFlowFuncLeaveRC(rc);
114 return rc;
115}
116
117static int debugCreateStreamOut(PPDMIHOSTAUDIO pInterface,
118 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg,
119 uint32_t *pcSamples)
120{
121 NOREF(pInterface);
122
123 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
124
125 /* Just adopt the wanted stream configuration. */
126 PDMPCMPROPS Props;
127 int rc = DrvAudioHlpStreamCfgToProps(pCfg, &Props);
128 if (RT_SUCCESS(rc))
129 {
130 pDbgStream->Out.tsLastPlayed = 0;
131 pDbgStream->Out.cMaxSamplesInPlayBuffer = _1K;
132 pDbgStream->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pDbgStream->Out.cMaxSamplesInPlayBuffer << Props.cShift);
133 if (!pDbgStream->Out.pu8PlayBuffer)
134 rc = VERR_NO_MEMORY;
135 }
136
137 if (RT_SUCCESS(rc))
138 {
139 char szFile[RTPATH_MAX];
140 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), "/tmp/", NULL, PDMAUDIOFILETYPE_WAV);
141 if (RT_SUCCESS(rc))
142 {
143 LogRel(("DebugAudio: Creating output file '%s'\n", szFile));
144 rc = DrvAudioHlpWAVFileOpen(&pDbgStream->File, szFile,
145 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
146 &Props, PDMAUDIOFILEFLAG_NONE);
147 if (RT_FAILURE(rc))
148 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
149 }
150 }
151
152 if (RT_SUCCESS(rc))
153 {
154 if (pcSamples)
155 *pcSamples = pDbgStream->Out.cMaxSamplesInPlayBuffer;
156 }
157
158 LogFlowFuncLeaveRC(rc);
159 return rc;
160}
161
162static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
163 uint32_t *pcSamplesPlayed)
164{
165 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
166 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
167
168 /* Consume as many samples as would be played at the current frequency since last call. */
169 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
170
171 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
172 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
173 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
174
175 /*
176 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
177 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
178 */
179 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
180
181 /* Don't play more than available. */
182 /*if (cSamplesPlayed > cLive)
183 cSamplesPlayed = cLive;*/
184
185 uint32_t cSamplesPlayed = 0;
186 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
187 while (cSamplesAvail)
188 {
189 uint32_t cSamplesRead = 0;
190 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
191 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
192
193 if (RT_FAILURE(rc2))
194 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
195
196 if (!cSamplesRead)
197 break;
198#if 0
199 RTFILE fh;
200 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
201 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
202 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
203 RTFileClose(fh);
204#endif
205 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
206 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
207 0 /* fFlags */);
208 if (RT_FAILURE(rc2))
209 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
210
211 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
212
213 Assert(cSamplesAvail >= cSamplesRead);
214 cSamplesAvail -= cSamplesRead;
215
216 cSamplesPlayed += cSamplesRead;
217 };
218
219 /* Remember when samples were consumed. */
220 pDbgStream->Out.tsLastPlayed = u64TicksNow;
221
222 if (pcSamplesPlayed)
223 *pcSamplesPlayed = cSamplesPlayed;
224
225 return VINF_SUCCESS;
226}
227
228static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
229 uint32_t *pcSamplesCaptured)
230{
231 RT_NOREF(pInterface, pStream);
232
233 /* Never capture anything. */
234 if (pcSamplesCaptured)
235 *pcSamplesCaptured = 0;
236
237 return VINF_SUCCESS;
238}
239
240static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
241{
242 RT_NOREF(pInterface, pStream);
243 LogFlowFuncLeaveRC(VINF_SUCCESS);
244 return VINF_SUCCESS;
245}
246
247static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
248{
249 RT_NOREF(pInterface);
250 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
251 if ( pDbgStream
252 && pDbgStream->Out.pu8PlayBuffer)
253 {
254 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
255 pDbgStream->Out.pu8PlayBuffer = NULL;
256 }
257
258 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
259
260 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
261 if (RT_SUCCESS(rc))
262 {
263 /* Delete the file again if nothing but the header was written to it. */
264 if (!cbDataSize)
265 rc = RTFileDelete(pDbgStream->File.szName); /** @todo Make deletion configurable? */
266 }
267
268 LogFlowFuncLeaveRC(rc);
269 return rc;
270}
271
272static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
273{
274 RT_NOREF(enmDir);
275 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
276
277 return PDMAUDIOBACKENDSTS_RUNNING;
278}
279
280static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
281 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfg, uint32_t *pcSamples)
282{
283 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
284 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
285 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
286
287 int rc;
288 if (pCfg->enmDir == PDMAUDIODIR_IN)
289 rc = debugCreateStreamIn(pInterface, pStream, pCfg, pcSamples);
290 else
291 rc = debugCreateStreamOut(pInterface, pStream, pCfg, pcSamples);
292
293 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
294 return rc;
295}
296
297static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
298{
299 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
300 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
301
302 int rc;
303 if (pStream->enmDir == PDMAUDIODIR_IN)
304 rc = debugDestroyStreamIn(pInterface, pStream);
305 else
306 rc = debugDestroyStreamOut(pInterface, pStream);
307
308 return rc;
309}
310
311static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
312 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
313{
314 RT_NOREF(enmStreamCmd);
315 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
316 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
317
318 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
319
320 return VINF_SUCCESS;
321}
322
323static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
324{
325 NOREF(pInterface);
326 NOREF(pStream);
327
328 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
329 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
330}
331
332static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
333{
334 NOREF(pInterface);
335 NOREF(pStream);
336
337 return VINF_SUCCESS;
338}
339
340/**
341 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
342 */
343static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
344{
345 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
346 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
347
348 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
349 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
350 return NULL;
351}
352
353static DECLCALLBACK(void) drvHostDebugAudioShutdown(PPDMIHOSTAUDIO pInterface)
354{
355 NOREF(pInterface);
356}
357
358/**
359 * Constructs a Null audio driver instance.
360 *
361 * @copydoc FNPDMDRVCONSTRUCT
362 */
363static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
364{
365 RT_NOREF(pCfg, fFlags);
366 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
367 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
368 LogRel(("Audio: Initializing DEBUG driver\n"));
369
370 /*
371 * Init the static parts.
372 */
373 pThis->pDrvIns = pDrvIns;
374 /* IBase */
375 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
376 /* IHostAudio */
377 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
378
379 return VINF_SUCCESS;
380}
381
382/**
383 * Char driver registration record.
384 */
385const PDMDRVREG g_DrvHostDebugAudio =
386{
387 /* u32Version */
388 PDM_DRVREG_VERSION,
389 /* szName */
390 "DebugAudio",
391 /* szRCMod */
392 "",
393 /* szR0Mod */
394 "",
395 /* pszDescription */
396 "Debug audio host driver",
397 /* fFlags */
398 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
399 /* fClass. */
400 PDM_DRVREG_CLASS_AUDIO,
401 /* cMaxInstances */
402 ~0U,
403 /* cbInstance */
404 sizeof(DRVHOSTDEBUGAUDIO),
405 /* pfnConstruct */
406 drvHostDebugAudioConstruct,
407 /* pfnDestruct */
408 NULL,
409 /* pfnRelocate */
410 NULL,
411 /* pfnIOCtl */
412 NULL,
413 /* pfnPowerOn */
414 NULL,
415 /* pfnReset */
416 NULL,
417 /* pfnSuspend */
418 NULL,
419 /* pfnResume */
420 NULL,
421 /* pfnAttach */
422 NULL,
423 /* pfnDetach */
424 NULL,
425 /* pfnPowerOff */
426 NULL,
427 /* pfnSoftReset */
428 NULL,
429 /* u32EndVersion */
430 PDM_DRVREG_VERSION
431};
432
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