VirtualBox

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

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

Audio: PDMPCMPROPS -> PDMAUDIOPCMPROPS.

  • Property svn:executable set to *
File size: 13.4 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
74/**
75 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
76 */
77PDMAUDIO_IHOSTAUDIO_EMIT_GETCONFIG(drvHostDebugAudio)
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 */
99PDMAUDIO_IHOSTAUDIO_EMIT_INIT(drvHostDebugAudio)
100{
101 NOREF(pInterface);
102
103 LogFlowFuncLeaveRC(VINF_SUCCESS);
104 return VINF_SUCCESS;
105}
106
107
108/**
109 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
110 */
111PDMAUDIO_IHOSTAUDIO_EMIT_SHUTDOWN(drvHostDebugAudio)
112{
113 NOREF(pInterface);
114}
115
116
117/**
118 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
119 */
120PDMAUDIO_IHOSTAUDIO_EMIT_GETSTATUS(drvHostDebugAudio)
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 */
195PDMAUDIO_IHOSTAUDIO_EMIT_STREAMCREATE(drvHostDebugAudio)
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 */
215PDMAUDIO_IHOSTAUDIO_EMIT_STREAMPLAY(drvHostDebugAudio)
216{
217 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
218 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
219
220 /* Consume as many samples as would be played at the current frequency since last call. */
221 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
222
223 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
224 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
225 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
226
227 /*
228 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
229 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
230 */
231 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
232
233 /* Don't play more than available. */
234 /*if (cSamplesPlayed > cLive)
235 cSamplesPlayed = cLive;*/
236
237 uint32_t cSamplesPlayed = 0;
238 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
239 while (cSamplesAvail)
240 {
241 uint32_t cSamplesRead = 0;
242 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
243 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
244
245 if (RT_FAILURE(rc2))
246 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
247
248 if (!cSamplesRead)
249 break;
250#if 0
251 RTFILE fh;
252 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
253 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
254 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
255 RTFileClose(fh);
256#endif
257 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
258 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
259 0 /* fFlags */);
260 if (RT_FAILURE(rc2))
261 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
262
263 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
264
265 Assert(cSamplesAvail >= cSamplesRead);
266 cSamplesAvail -= cSamplesRead;
267
268 cSamplesPlayed += cSamplesRead;
269 }
270
271 /* Remember when samples were consumed. */
272 pDbgStream->Out.tsLastPlayed = u64TicksNow;
273
274 if (pcbWritten)
275 *pcbWritten = cSamplesPlayed;
276
277 return VINF_SUCCESS;
278}
279
280
281/**
282 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
283 */
284PDMAUDIO_IHOSTAUDIO_EMIT_STREAMCAPTURE(drvHostDebugAudio)
285{
286 RT_NOREF(pInterface, pStream);
287
288 /* Never capture anything. */
289 if (pcbRead)
290 *pcbRead = 0;
291
292 return VINF_SUCCESS;
293}
294
295
296static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
297{
298 RT_NOREF(pInterface, pStream);
299 LogFlowFuncLeaveRC(VINF_SUCCESS);
300 return VINF_SUCCESS;
301}
302
303
304static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
305{
306 RT_NOREF(pInterface);
307 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
308 if ( pDbgStream
309 && pDbgStream->Out.pu8PlayBuffer)
310 {
311 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
312 pDbgStream->Out.pu8PlayBuffer = NULL;
313 }
314
315 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
316
317 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
318 if (RT_SUCCESS(rc))
319 {
320 /* Delete the file again if nothing but the header was written to it. */
321 if (!cbDataSize)
322 rc = RTFileDelete(pDbgStream->File.szName); /** @todo Make deletion configurable? */
323 }
324
325 LogFlowFuncLeaveRC(rc);
326 return rc;
327}
328
329
330static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
331{
332 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
333 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
334
335 int rc;
336 if (pStream->enmDir == PDMAUDIODIR_IN)
337 rc = debugDestroyStreamIn(pInterface, pStream);
338 else
339 rc = debugDestroyStreamOut(pInterface, pStream);
340
341 return rc;
342}
343
344static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
345 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
346{
347 RT_NOREF(enmStreamCmd);
348 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
349 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
350
351 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
352
353 return VINF_SUCCESS;
354}
355
356static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
357{
358 NOREF(pInterface);
359 NOREF(pStream);
360
361 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
362 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
363}
364
365static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
366{
367 NOREF(pInterface);
368 NOREF(pStream);
369
370 return VINF_SUCCESS;
371}
372
373
374/**
375 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
376 */
377static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
378{
379 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
380 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
381
382 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
383 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
384 return NULL;
385}
386
387
388/**
389 * Constructs a Null audio driver instance.
390 *
391 * @copydoc FNPDMDRVCONSTRUCT
392 */
393static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
394{
395 RT_NOREF(pCfg, fFlags);
396 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
397 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
398 LogRel(("Audio: Initializing DEBUG driver\n"));
399
400 /*
401 * Init the static parts.
402 */
403 pThis->pDrvIns = pDrvIns;
404 /* IBase */
405 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
406 /* IHostAudio */
407 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
408
409 return VINF_SUCCESS;
410}
411
412/**
413 * Char driver registration record.
414 */
415const PDMDRVREG g_DrvHostDebugAudio =
416{
417 /* u32Version */
418 PDM_DRVREG_VERSION,
419 /* szName */
420 "DebugAudio",
421 /* szRCMod */
422 "",
423 /* szR0Mod */
424 "",
425 /* pszDescription */
426 "Debug audio host driver",
427 /* fFlags */
428 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
429 /* fClass. */
430 PDM_DRVREG_CLASS_AUDIO,
431 /* cMaxInstances */
432 ~0U,
433 /* cbInstance */
434 sizeof(DRVHOSTDEBUGAUDIO),
435 /* pfnConstruct */
436 drvHostDebugAudioConstruct,
437 /* pfnDestruct */
438 NULL,
439 /* pfnRelocate */
440 NULL,
441 /* pfnIOCtl */
442 NULL,
443 /* pfnPowerOn */
444 NULL,
445 /* pfnReset */
446 NULL,
447 /* pfnSuspend */
448 NULL,
449 /* pfnResume */
450 NULL,
451 /* pfnAttach */
452 NULL,
453 /* pfnDetach */
454 NULL,
455 /* pfnPowerOff */
456 NULL,
457 /* pfnSoftReset */
458 NULL,
459 /* u32EndVersion */
460 PDM_DRVREG_VERSION
461};
462
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