VirtualBox

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

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

Audio/DrvHostDebugAudio.cpp: Use RTPathTemp() for retrieving the temporary directory, logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: DrvHostDebugAudio.cpp 63629 2016-08-24 14:33:27Z 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 RT_NOREF(pInterface, pStream);
133
134 /* Just adopt the wanted stream configuration. */
135 PDMAUDIOPCMPROPS Props;
136 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &Props);
137 if (RT_SUCCESS(rc))
138 {
139 if (pCfgAcq)
140 pCfgAcq->cSampleBufferSize = _1K;
141 }
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147
148static int debugCreateStreamOut(PPDMIHOSTAUDIO pInterface,
149 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
150{
151 NOREF(pInterface);
152
153 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
154
155 /* Just adopt the wanted stream configuration. */
156 PDMAUDIOPCMPROPS Props;
157 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &Props);
158 if (RT_SUCCESS(rc))
159 {
160 pDbgStream->Out.tsLastPlayed = 0;
161 pDbgStream->Out.cMaxSamplesInPlayBuffer = _1K;
162 pDbgStream->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pDbgStream->Out.cMaxSamplesInPlayBuffer << Props.cShift);
163 if (!pDbgStream->Out.pu8PlayBuffer)
164 rc = VERR_NO_MEMORY;
165 }
166
167 if (RT_SUCCESS(rc))
168 {
169 char szTemp[RTPATH_MAX];
170 rc = RTPathTemp(szTemp, sizeof(szTemp));
171 if (RT_SUCCESS(rc))
172 {
173 char szFile[RTPATH_MAX];
174 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, NULL, PDMAUDIOFILETYPE_WAV);
175 if (RT_SUCCESS(rc))
176 {
177 LogFlowFunc(("%s\n", szFile));
178 rc = DrvAudioHlpWAVFileOpen(&pDbgStream->File, szFile,
179 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
180 &Props, PDMAUDIOFILEFLAG_NONE);
181 if (RT_FAILURE(rc))
182 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
183 }
184 else
185 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
186 }
187 else
188 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
189 }
190
191 if (RT_SUCCESS(rc))
192 {
193 if (pCfgAcq)
194 pCfgAcq->cSampleBufferSize = pDbgStream->Out.cMaxSamplesInPlayBuffer;
195 }
196
197 LogFlowFuncLeaveRC(rc);
198 return rc;
199}
200
201
202/**
203 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
204 */
205static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
206 PPDMAUDIOSTREAM pStream,
207 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
208{
209 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
210 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
211 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
212
213 int rc;
214 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
215 rc = debugCreateStreamIn( pInterface, pStream, pCfgReq, pCfgAcq);
216 else
217 rc = debugCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
218
219 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
220 return rc;
221}
222
223
224/**
225 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
226 */
227static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
228 PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
229 uint32_t *pcbWritten)
230{
231 RT_NOREF(pvBuf, cbBuf);
232
233 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
234 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
235
236 /* Consume as many samples as would be played at the current frequency since last call. */
237 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
238
239 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
240 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
241 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
242
243 /*
244 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
245 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
246 */
247 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
248
249 /* Don't play more than available. */
250 /*if (cSamplesPlayed > cLive)
251 cSamplesPlayed = cLive;*/
252
253 uint32_t cSamplesPlayed = 0;
254 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
255 while (cSamplesAvail)
256 {
257 uint32_t cSamplesRead = 0;
258 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
259 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
260
261 if (RT_FAILURE(rc2))
262 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
263
264 if (!cSamplesRead)
265 break;
266#if 0
267 RTFILE fh;
268 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
269 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
270 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
271 RTFileClose(fh);
272#endif
273 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
274 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
275 0 /* fFlags */);
276 if (RT_FAILURE(rc2))
277 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
278
279 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
280
281 Assert(cSamplesAvail >= cSamplesRead);
282 cSamplesAvail -= cSamplesRead;
283
284 cSamplesPlayed += cSamplesRead;
285 }
286
287 /* Remember when samples were consumed. */
288 pDbgStream->Out.tsLastPlayed = u64TicksNow;
289
290 if (pcbWritten)
291 *pcbWritten = cSamplesPlayed;
292
293 return VINF_SUCCESS;
294}
295
296
297/**
298 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
299 */
300static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
301 PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
302{
303 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
304
305 /* Never capture anything. */
306 if (pcbRead)
307 *pcbRead = 0;
308
309 return VINF_SUCCESS;
310}
311
312
313static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
314{
315 RT_NOREF(pInterface, pStream);
316 LogFlowFuncLeaveRC(VINF_SUCCESS);
317 return VINF_SUCCESS;
318}
319
320
321static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
322{
323 RT_NOREF(pInterface);
324 PDEBUGAUDIOSTREAM pDbgStream = (PDEBUGAUDIOSTREAM)pStream;
325 if ( pDbgStream
326 && pDbgStream->Out.pu8PlayBuffer)
327 {
328 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
329 pDbgStream->Out.pu8PlayBuffer = NULL;
330 }
331
332 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
333
334 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
335 if (RT_SUCCESS(rc))
336 {
337 /* Delete the file again if nothing but the header was written to it. */
338 bool fDeleteEmptyFiles = true; /** @todo Make deletion configurable? */
339
340 if ( !cbDataSize
341 && fDeleteEmptyFiles)
342 {
343 rc = RTFileDelete(pDbgStream->File.szName);
344 }
345 else
346 LogRel(("DebugAudio: Created output file '%s' (%zu bytes)\n", pDbgStream->File.szName, cbDataSize));
347 }
348
349 LogFlowFuncLeaveRC(rc);
350 return rc;
351}
352
353
354static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
355{
356 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
357 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
358
359 int rc;
360 if (pStream->enmDir == PDMAUDIODIR_IN)
361 rc = debugDestroyStreamIn(pInterface, pStream);
362 else
363 rc = debugDestroyStreamOut(pInterface, pStream);
364
365 return rc;
366}
367
368static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
369 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
370{
371 RT_NOREF(enmStreamCmd);
372 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
373 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
374
375 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
376
377 return VINF_SUCCESS;
378}
379
380static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
381{
382 NOREF(pInterface);
383 NOREF(pStream);
384
385 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
386 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
387}
388
389static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
390{
391 NOREF(pInterface);
392 NOREF(pStream);
393
394 return VINF_SUCCESS;
395}
396
397
398/**
399 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
400 */
401static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
402{
403 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
404 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
405
406 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
407 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
408 return NULL;
409}
410
411
412/**
413 * Constructs a Null audio driver instance.
414 *
415 * @copydoc FNPDMDRVCONSTRUCT
416 */
417static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
418{
419 RT_NOREF(pCfg, fFlags);
420 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
421 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
422 LogRel(("Audio: Initializing DEBUG driver\n"));
423
424 /*
425 * Init the static parts.
426 */
427 pThis->pDrvIns = pDrvIns;
428 /* IBase */
429 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
430 /* IHostAudio */
431 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
432
433 return VINF_SUCCESS;
434}
435
436/**
437 * Char driver registration record.
438 */
439const PDMDRVREG g_DrvHostDebugAudio =
440{
441 /* u32Version */
442 PDM_DRVREG_VERSION,
443 /* szName */
444 "DebugAudio",
445 /* szRCMod */
446 "",
447 /* szR0Mod */
448 "",
449 /* pszDescription */
450 "Debug audio host driver",
451 /* fFlags */
452 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
453 /* fClass. */
454 PDM_DRVREG_CLASS_AUDIO,
455 /* cMaxInstances */
456 ~0U,
457 /* cbInstance */
458 sizeof(DRVHOSTDEBUGAUDIO),
459 /* pfnConstruct */
460 drvHostDebugAudioConstruct,
461 /* pfnDestruct */
462 NULL,
463 /* pfnRelocate */
464 NULL,
465 /* pfnIOCtl */
466 NULL,
467 /* pfnPowerOn */
468 NULL,
469 /* pfnReset */
470 NULL,
471 /* pfnSuspend */
472 NULL,
473 /* pfnResume */
474 NULL,
475 /* pfnAttach */
476 NULL,
477 /* pfnDetach */
478 NULL,
479 /* pfnPowerOff */
480 NULL,
481 /* pfnSoftReset */
482 NULL,
483 /* u32EndVersion */
484 PDM_DRVREG_VERSION
485};
486
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