VirtualBox

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

Last change on this file since 68132 was 68132, checked in by vboxsync, 7 years ago

Audio: Renamed audio samples to audio frame because that's what they really are. No actual code changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: DrvHostDebugAudio.cpp 68132 2017-07-27 08:15:43Z 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-2017 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 "VBoxDD.h"
29
30
31/**
32 * Structure for keeping a debug input/output stream.
33 */
34typedef struct DEBUGAUDIOSTREAM
35{
36 /** The stream's acquired configuration. */
37 PPDMAUDIOSTREAMCFG pCfg;
38 /** Audio file to dump output to or read input from. */
39 PDMAUDIOFILE File;
40 union
41 {
42 struct
43 {
44 /** Timestamp of last captured samples. */
45 uint64_t tsLastCaptured;
46 } In;
47 struct
48 {
49 uint8_t *auPlayBuffer;
50 uint32_t cbPlayBuffer;
51 } Out;
52 };
53} DEBUGAUDIOSTREAM, *PDEBUGAUDIOSTREAM;
54
55/**
56 * Debug audio driver instance data.
57 * @implements PDMIAUDIOCONNECTOR
58 */
59typedef struct DRVHOSTDEBUGAUDIO
60{
61 /** Pointer to the driver instance structure. */
62 PPDMDRVINS pDrvIns;
63 /** Pointer to host audio interface. */
64 PDMIHOSTAUDIO IHostAudio;
65} DRVHOSTDEBUGAUDIO, *PDRVHOSTDEBUGAUDIO;
66
67/*******************************************PDM_AUDIO_DRIVER******************************/
68
69
70/**
71 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
72 */
73static DECLCALLBACK(int) drvHostDebugAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
74{
75 RT_NOREF(pInterface);
76 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
77
78 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
79 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
80
81 pBackendCfg->cMaxStreamsOut = 1; /* Output */
82 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
83
84 return VINF_SUCCESS;
85}
86
87
88/**
89 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
90 */
91static DECLCALLBACK(int) drvHostDebugAudioInit(PPDMIHOSTAUDIO pInterface)
92{
93 RT_NOREF(pInterface);
94
95 LogFlowFuncLeaveRC(VINF_SUCCESS);
96 return VINF_SUCCESS;
97}
98
99
100/**
101 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
102 */
103static DECLCALLBACK(void) drvHostDebugAudioShutdown(PPDMIHOSTAUDIO pInterface)
104{
105 RT_NOREF(pInterface);
106}
107
108
109/**
110 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
111 */
112static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
113{
114 RT_NOREF(enmDir);
115 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
116
117 return PDMAUDIOBACKENDSTS_RUNNING;
118}
119
120
121static int debugCreateStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
122 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
123{
124 RT_NOREF(pDrv, pStreamDbg, pCfgReq);
125
126 if (pCfgAcq)
127 pCfgAcq->cFrameBufferHint = _1K;
128
129 return VINF_SUCCESS;
130}
131
132
133static int debugCreateStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
134 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
135{
136 RT_NOREF(pDrv);
137
138 int rc = VINF_SUCCESS;
139
140 pStreamDbg->Out.cbPlayBuffer = _1K * PDMAUDIOSTREAMCFG_F2B(pCfgReq, 1); /** @todo Make this configurable? */
141 pStreamDbg->Out.auPlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
142 if (!pStreamDbg->Out.auPlayBuffer)
143 rc = VERR_NO_MEMORY;
144
145 if (RT_SUCCESS(rc))
146 {
147 char szTemp[RTPATH_MAX];
148 rc = RTPathTemp(szTemp, sizeof(szTemp));
149 if (RT_SUCCESS(rc))
150 {
151 char szFile[RTPATH_MAX];
152 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, NULL, PDMAUDIOFILETYPE_WAV);
153 if (RT_SUCCESS(rc))
154 {
155 LogFlowFunc(("%s\n", szFile));
156 rc = DrvAudioHlpWAVFileOpen(&pStreamDbg->File, szFile,
157 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
158 &pCfgReq->Props, PDMAUDIOFILEFLAG_NONE);
159 if (RT_FAILURE(rc))
160 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
161 }
162 else
163 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
164 }
165 else
166 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
167 }
168
169 if (RT_SUCCESS(rc))
170 {
171 if (pCfgAcq)
172 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
173 }
174
175 return rc;
176}
177
178
179/**
180 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
181 */
182static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
183 PPDMAUDIOBACKENDSTREAM pStream,
184 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
185{
186 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
187 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
188 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
189 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
190
191 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
192 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
193
194 int rc;
195 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
196 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
197 else
198 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
199
200 if (RT_SUCCESS(rc))
201 {
202 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
203 if (!pStreamDbg->pCfg)
204 rc = VERR_NO_MEMORY;
205 }
206
207 return rc;
208}
209
210
211/**
212 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
213 */
214static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
215 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
216 uint32_t *pcxWritten)
217{
218 RT_NOREF(pInterface);
219 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
220
221 uint32_t cbWrittenTotal = 0;
222
223 uint32_t cbAvail = cxBuf;
224 while (cbAvail)
225 {
226 uint32_t cbChunk = RT_MIN(cbAvail, pStreamDbg->Out.cbPlayBuffer);
227
228 memcpy(pStreamDbg->Out.auPlayBuffer, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
229
230#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
231 RTFILE fh;
232 RTFileOpen(&fh, VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "AudioDebugOutput.pcm",
233 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
234 RTFileWrite(fh, pStreamDbg->Out.auPlayBuffer, cbChunk, NULL);
235 RTFileClose(fh);
236#endif
237 int rc2 = DrvAudioHlpWAVFileWrite(&pStreamDbg->File,
238 pStreamDbg->Out.auPlayBuffer, cbChunk, 0 /* fFlags */);
239 if (RT_FAILURE(rc2))
240 {
241 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
242 break;
243 }
244
245 Assert(cbAvail >= cbAvail);
246 cbAvail -= cbChunk;
247
248 cbWrittenTotal += cbChunk;
249 }
250
251 if (pcxWritten)
252 *pcxWritten = cbWrittenTotal;
253
254 return VINF_SUCCESS;
255}
256
257
258/**
259 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
260 */
261static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
262 void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
263{
264 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
265
266 /* Never capture anything. */
267 if (pcxRead)
268 *pcxRead = 0;
269
270 return VINF_SUCCESS;
271}
272
273
274static int debugDestroyStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
275{
276 RT_NOREF(pDrv, pStreamDbg);
277 return VINF_SUCCESS;
278}
279
280
281static int debugDestroyStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
282{
283 RT_NOREF(pDrv);
284
285 if (pStreamDbg->Out.auPlayBuffer)
286 {
287 RTMemFree(pStreamDbg->Out.auPlayBuffer);
288 pStreamDbg->Out.auPlayBuffer = NULL;
289 }
290
291 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pStreamDbg->File);
292
293 int rc = DrvAudioHlpWAVFileClose(&pStreamDbg->File);
294 if (RT_SUCCESS(rc))
295 {
296 /* Delete the file again if nothing but the header was written to it. */
297 bool fDeleteEmptyFiles = true; /** @todo Make deletion configurable? */
298
299 if ( !cbDataSize
300 && fDeleteEmptyFiles)
301 {
302 rc = RTFileDelete(pStreamDbg->File.szName);
303 }
304 else
305 LogRel(("DebugAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->File.szName, cbDataSize));
306 }
307
308 return rc;
309}
310
311
312/**
313 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
314 */
315static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
316{
317 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
318
319 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
320 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
321
322 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
323 return VINF_SUCCESS;
324
325 int rc;
326 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
327 rc = debugDestroyStreamIn (pDrv, pStreamDbg);
328 else
329 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
330
331 if (RT_SUCCESS(rc))
332 {
333 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
334 pStreamDbg->pCfg = NULL;
335 }
336
337 return rc;
338}
339
340
341/**
342 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
343 */
344static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
345 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
346{
347 RT_NOREF(enmStreamCmd);
348 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
349 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
350
351 return VINF_SUCCESS;
352}
353
354
355/**
356 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
357 */
358static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
359{
360 RT_NOREF(pInterface, pStream);
361
362 return 0; /* Never capture anything. */
363}
364
365
366/**
367 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
368 */
369static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
370{
371 RT_NOREF(pInterface, pStream);
372
373 return UINT32_MAX;
374}
375
376
377/**
378 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
379 */
380static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
381{
382 RT_NOREF(pInterface, pStream);
383
384 return (PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED);
385}
386
387
388/**
389 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
390 */
391static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
392{
393 RT_NOREF(pInterface, pStream);
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