VirtualBox

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

Last change on this file since 73370 was 73370, checked in by vboxsync, 6 years ago

Audio: Implemented backend-independent (pre-)buffering support. Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.9 KB
Line 
1/* $Id: DrvHostDebugAudio.cpp 73370 2018-07-26 13:52:12Z vboxsync $ */
2/** @file
3 * Debug audio driver.
4 *
5 * Host backend for dumping and injecting audio data from/to the device emulation.
6 */
7
8/*
9 * Copyright (C) 2016-2018 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
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 PPDMAUDIOFILE pFile;
40 union
41 {
42 struct
43 {
44 /** Timestamp of last captured samples. */
45 uint64_t tsLastCaptured;
46 } In;
47 };
48} DEBUGAUDIOSTREAM, *PDEBUGAUDIOSTREAM;
49
50/**
51 * Debug audio driver instance data.
52 * @implements PDMIAUDIOCONNECTOR
53 */
54typedef struct DRVHOSTDEBUGAUDIO
55{
56 /** Pointer to the driver instance structure. */
57 PPDMDRVINS pDrvIns;
58 /** Pointer to host audio interface. */
59 PDMIHOSTAUDIO IHostAudio;
60} DRVHOSTDEBUGAUDIO, *PDRVHOSTDEBUGAUDIO;
61
62/*******************************************PDM_AUDIO_DRIVER******************************/
63
64
65/**
66 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
67 */
68static DECLCALLBACK(int) drvHostDebugAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
69{
70 RT_NOREF(pInterface);
71 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
72
73 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
74 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
75
76 pBackendCfg->cMaxStreamsOut = 1; /* Output */
77 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
78
79 return VINF_SUCCESS;
80}
81
82
83/**
84 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
85 */
86static DECLCALLBACK(int) drvHostDebugAudioInit(PPDMIHOSTAUDIO pInterface)
87{
88 RT_NOREF(pInterface);
89
90 LogFlowFuncLeaveRC(VINF_SUCCESS);
91 return VINF_SUCCESS;
92}
93
94
95/**
96 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
97 */
98static DECLCALLBACK(void) drvHostDebugAudioShutdown(PPDMIHOSTAUDIO pInterface)
99{
100 RT_NOREF(pInterface);
101}
102
103
104/**
105 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
106 */
107static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
108{
109 RT_NOREF(enmDir);
110 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
111
112 return PDMAUDIOBACKENDSTS_RUNNING;
113}
114
115
116static int debugCreateStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
117 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
118{
119 RT_NOREF(pDrv, pStreamDbg, pCfgReq);
120
121 if (pCfgAcq)
122 {
123 pCfgAcq->Backend.cfPeriod = DrvAudioHlpMsToFrames(&pCfgReq->Props, 1000 /* ms */);
124 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
125 }
126
127 return VINF_SUCCESS;
128}
129
130
131static int debugCreateStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
132 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
133{
134 RT_NOREF(pDrv);
135
136 char szTemp[RTPATH_MAX];
137 int rc = RTPathTemp(szTemp, sizeof(szTemp));
138 if (RT_SUCCESS(rc))
139 {
140 char szFile[RTPATH_MAX];
141 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, "DebugAudioOut",
142 pDrv->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
143 if (RT_SUCCESS(rc))
144 {
145 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE, &pStreamDbg->pFile);
146 if (RT_SUCCESS(rc))
147 {
148 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
149 &pCfgReq->Props);
150 }
151
152 if (RT_FAILURE(rc))
153 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
154 }
155 else
156 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
157 }
158 else
159 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
160
161 if (RT_SUCCESS(rc))
162 {
163 if (pCfgAcq)
164 {
165 pCfgAcq->Backend.cfPeriod = DrvAudioHlpBytesToFrames(&pCfgReq->Props, 50 /* ms */);
166 pCfgAcq->Backend.cfBufferSize = pCfgAcq->Backend.cfPeriod * 2; /* Use "double buffering". */
167 }
168 }
169
170 return rc;
171}
172
173
174/**
175 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
176 */
177static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
178 PPDMAUDIOBACKENDSTREAM pStream,
179 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
180{
181 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
182 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
183 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
184 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
185
186 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
187 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
188
189 int rc;
190 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
191 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
192 else
193 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
194
195 if (RT_SUCCESS(rc))
196 {
197 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
198 if (!pStreamDbg->pCfg)
199 rc = VERR_NO_MEMORY;
200 }
201
202 return rc;
203}
204
205
206/**
207 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
208 */
209static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
210 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
211 uint32_t *pcxWritten)
212{
213 RT_NOREF(pInterface);
214 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
215
216 int rc = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cxBuf, 0 /* fFlags */);
217 if (RT_FAILURE(rc))
218 {
219 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc));
220 return rc;
221 }
222
223 if (pcxWritten)
224 *pcxWritten = cxBuf;
225
226 return VINF_SUCCESS;
227}
228
229
230/**
231 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
232 */
233static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
234 void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
235{
236 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
237
238 /* Never capture anything. */
239 if (pcxRead)
240 *pcxRead = 0;
241
242 return VINF_SUCCESS;
243}
244
245
246static int debugDestroyStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
247{
248 RT_NOREF(pDrv, pStreamDbg);
249 return VINF_SUCCESS;
250}
251
252
253static int debugDestroyStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
254{
255 RT_NOREF(pDrv);
256
257 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
265 */
266static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
267{
268 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
269
270 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
271 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
272
273 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
274 return VINF_SUCCESS;
275
276 int rc;
277 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
278 rc = debugDestroyStreamIn (pDrv, pStreamDbg);
279 else
280 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
281
282 if (RT_SUCCESS(rc))
283 {
284 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
285 pStreamDbg->pCfg = NULL;
286 }
287
288 return rc;
289}
290
291
292/**
293 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
294 */
295static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
296 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
297{
298 RT_NOREF(enmStreamCmd);
299 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
300 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
301
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
308 */
309static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
310{
311 RT_NOREF(pInterface, pStream);
312
313 return 0; /* Never capture anything. */
314}
315
316
317/**
318 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
319 */
320static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
321{
322 RT_NOREF(pInterface, pStream);
323
324 return UINT32_MAX;
325}
326
327
328/**
329 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
330 */
331static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
332{
333 RT_NOREF(pInterface, pStream);
334
335 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
336}
337
338
339/**
340 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
341 */
342static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
343{
344 RT_NOREF(pInterface, pStream);
345 return VINF_SUCCESS;
346}
347
348
349/**
350 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
351 */
352static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
353{
354 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
355 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
356
357 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
358 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
359 return NULL;
360}
361
362
363/**
364 * Constructs a Null audio driver instance.
365 *
366 * @copydoc FNPDMDRVCONSTRUCT
367 */
368static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
369{
370 RT_NOREF(pCfg, fFlags);
371 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
372 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
373 LogRel(("Audio: Initializing DEBUG driver\n"));
374
375 /*
376 * Init the static parts.
377 */
378 pThis->pDrvIns = pDrvIns;
379 /* IBase */
380 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
381 /* IHostAudio */
382 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
383
384#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
385 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "AudioDebugOutput.pcm");
386#endif
387
388 return VINF_SUCCESS;
389}
390
391/**
392 * Char driver registration record.
393 */
394const PDMDRVREG g_DrvHostDebugAudio =
395{
396 /* u32Version */
397 PDM_DRVREG_VERSION,
398 /* szName */
399 "DebugAudio",
400 /* szRCMod */
401 "",
402 /* szR0Mod */
403 "",
404 /* pszDescription */
405 "Debug audio host driver",
406 /* fFlags */
407 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
408 /* fClass. */
409 PDM_DRVREG_CLASS_AUDIO,
410 /* cMaxInstances */
411 ~0U,
412 /* cbInstance */
413 sizeof(DRVHOSTDEBUGAUDIO),
414 /* pfnConstruct */
415 drvHostDebugAudioConstruct,
416 /* pfnDestruct */
417 NULL,
418 /* pfnRelocate */
419 NULL,
420 /* pfnIOCtl */
421 NULL,
422 /* pfnPowerOn */
423 NULL,
424 /* pfnReset */
425 NULL,
426 /* pfnSuspend */
427 NULL,
428 /* pfnResume */
429 NULL,
430 /* pfnAttach */
431 NULL,
432 /* pfnDetach */
433 NULL,
434 /* pfnPowerOff */
435 NULL,
436 /* pfnSoftReset */
437 NULL,
438 /* u32EndVersion */
439 PDM_DRVREG_VERSION
440};
441
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