VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostValidationKit.cpp@ 65565

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

Audio: Make use of pvBuf/cbBuf parameters in PDMIHOSTAUDIO::pfnStreamPlay() and PDMIHOSTAUDIO::pfnStreamCapture() to further abstract the backends from the audio connector. The backends now won't be allowed to operate on the audio connector's mixing buffers directly anymore that way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 65565 2017-02-01 14:11:10Z vboxsync $ */
2/** @file
3 * Validation Kit audio driver.
4 */
5
6/*
7 * Copyright (C) 2016-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 */
18
19#include <iprt/alloc.h>
20#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
21
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25
26#include "DrvAudio.h"
27#include "VBoxDD.h"
28
29
30/**
31 * Structure for keeping a debug input/output stream.
32 */
33typedef struct VAKITAUDIOSTREAM
34{
35 /** Note: Always must come first! */
36 PDMAUDIOSTREAM Stream;
37 /** Audio file to dump output to or read input from. */
38 PDMAUDIOFILE File;
39 union
40 {
41 struct
42 {
43 /** Timestamp of last captured samples. */
44 uint64_t tsLastCaptured;
45 } In;
46 struct
47 {
48 /** Timestamp of last played samples. */
49 uint64_t tsLastPlayed;
50 uint64_t cMaxSamplesInPlayBuffer;
51 uint8_t *pu8PlayBuffer;
52 } Out;
53 };
54
55} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
56
57/**
58 * Validation Kit audio driver instance data.
59 * @implements PDMIAUDIOCONNECTOR
60 */
61typedef struct DRVHOSTVAKITAUDIO
62{
63 /** Pointer to the driver instance structure. */
64 PPDMDRVINS pDrvIns;
65 /** Pointer to host audio interface. */
66 PDMIHOSTAUDIO IHostAudio;
67} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
68
69/*******************************************PDM_AUDIO_DRIVER******************************/
70
71
72/**
73 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
74 */
75static DECLCALLBACK(int) drvHostVaKitAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
76{
77 NOREF(pInterface);
78 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
79
80 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
81 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
82
83 pBackendCfg->cMaxStreamsOut = 1; /* Output */
84 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
85
86 return VINF_SUCCESS;
87}
88
89
90/**
91 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
92 */
93static DECLCALLBACK(int) drvHostVaKitAudioInit(PPDMIHOSTAUDIO pInterface)
94{
95 NOREF(pInterface);
96
97 LogFlowFuncLeaveRC(VINF_SUCCESS);
98 return VINF_SUCCESS;
99}
100
101
102/**
103 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
104 */
105static DECLCALLBACK(void) drvHostVaKitAudioShutdown(PPDMIHOSTAUDIO pInterface)
106{
107 NOREF(pInterface);
108}
109
110
111/**
112 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
113 */
114static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostVaKitAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
115{
116 RT_NOREF(enmDir);
117 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
118
119 return PDMAUDIOBACKENDSTS_RUNNING;
120}
121
122
123static int debugCreateStreamIn(PPDMIHOSTAUDIO pInterface,
124 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
125{
126 RT_NOREF(pInterface, pStream);
127
128 /* Just adopt the wanted stream configuration. */
129 PDMAUDIOPCMPROPS Props;
130 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &Props);
131 if (RT_SUCCESS(rc))
132 {
133 if (pCfgAcq)
134 pCfgAcq->cSampleBufferSize = _1K;
135 }
136
137 LogFlowFuncLeaveRC(rc);
138 return rc;
139}
140
141
142static int debugCreateStreamOut(PPDMIHOSTAUDIO pInterface,
143 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
144{
145 NOREF(pInterface);
146
147 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
148
149 /* Just adopt the wanted stream configuration. */
150 PDMAUDIOPCMPROPS Props;
151 int rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &Props);
152 if (RT_SUCCESS(rc))
153 {
154 pDbgStream->Out.tsLastPlayed = 0;
155 pDbgStream->Out.cMaxSamplesInPlayBuffer = _1K;
156 pDbgStream->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pDbgStream->Out.cMaxSamplesInPlayBuffer << Props.cShift);
157 if (!pDbgStream->Out.pu8PlayBuffer)
158 rc = VERR_NO_MEMORY;
159 }
160
161 if (RT_SUCCESS(rc))
162 {
163 char szTemp[RTPATH_MAX];
164 rc = RTPathTemp(szTemp, sizeof(szTemp));
165 if (RT_SUCCESS(rc))
166 {
167 char szFile[RTPATH_MAX];
168 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, NULL, PDMAUDIOFILETYPE_WAV);
169 if (RT_SUCCESS(rc))
170 {
171 LogFlowFunc(("%s\n", szFile));
172 rc = DrvAudioHlpWAVFileOpen(&pDbgStream->File, szFile,
173 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
174 &Props, PDMAUDIOFILEFLAG_NONE);
175 if (RT_FAILURE(rc))
176 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
177 }
178 else
179 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
180 }
181 else
182 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
183 }
184
185 if (RT_SUCCESS(rc))
186 {
187 if (pCfgAcq)
188 pCfgAcq->cSampleBufferSize = pDbgStream->Out.cMaxSamplesInPlayBuffer;
189 }
190
191 LogFlowFuncLeaveRC(rc);
192 return rc;
193}
194
195
196/**
197 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
198 */
199static DECLCALLBACK(int) drvHostVaKitAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
200 PPDMAUDIOSTREAM pStream,
201 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
202{
203 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
204 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
205 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
206
207 int rc;
208 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
209 rc = debugCreateStreamIn( pInterface, pStream, pCfgReq, pCfgAcq);
210 else
211 rc = debugCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
212
213 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
214 return rc;
215}
216
217
218/**
219 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
220 */
221static DECLCALLBACK(int) drvHostVaKitAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
222 PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
223 uint32_t *pcbWritten)
224{
225 RT_NOREF(pvBuf, cbBuf);
226
227 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
228 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
229
230 /* Consume as many samples as would be played at the current frequency since last call. */
231 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
232
233 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
234 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
235 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
236
237 /*
238 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
239 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
240 */
241 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
242
243 /* Don't play more than available. */
244 /*if (cSamplesPlayed > cLive)
245 cSamplesPlayed = cLive;*/
246
247 uint32_t cSamplesPlayed = 0;
248 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
249 while (cSamplesAvail)
250 {
251 uint32_t cSamplesRead = 0;
252 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
253 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
254
255 if (RT_FAILURE(rc2))
256 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
257
258 if (!cSamplesRead)
259 break;
260#if 0
261 RTFILE fh;
262 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
263 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
264 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
265 RTFileClose(fh);
266#endif
267 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
268 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
269 0 /* fFlags */);
270 if (RT_FAILURE(rc2))
271 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
272
273 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
274
275 Assert(cSamplesAvail >= cSamplesRead);
276 cSamplesAvail -= cSamplesRead;
277
278 cSamplesPlayed += cSamplesRead;
279 }
280
281 /* Remember when samples were consumed. */
282 pDbgStream->Out.tsLastPlayed = u64TicksNow;
283
284 if (pcbWritten)
285 *pcbWritten = cSamplesPlayed;
286
287 return VINF_SUCCESS;
288}
289
290
291/**
292 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
293 */
294static DECLCALLBACK(int) drvHostVaKitAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
295 PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
296{
297 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
298
299 /* Never capture anything. */
300 if (pcbRead)
301 *pcbRead = 0;
302
303 return VINF_SUCCESS;
304}
305
306
307static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
308{
309 RT_NOREF(pInterface, pStream);
310 LogFlowFuncLeaveRC(VINF_SUCCESS);
311 return VINF_SUCCESS;
312}
313
314
315static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
316{
317 RT_NOREF(pInterface);
318 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
319 if ( pDbgStream
320 && pDbgStream->Out.pu8PlayBuffer)
321 {
322 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
323 pDbgStream->Out.pu8PlayBuffer = NULL;
324 }
325
326 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
327
328 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
329 if (RT_SUCCESS(rc))
330 {
331 /* Delete the file again if nothing but the header was written to it. */
332 bool fDeleteEmptyFiles = true; /** @todo Make deletion configurable? */
333
334 if ( !cbDataSize
335 && fDeleteEmptyFiles)
336 {
337 rc = RTFileDelete(pDbgStream->File.szName);
338 }
339 else
340 LogRel(("DebugAudio: Created output file '%s' (%zu bytes)\n", pDbgStream->File.szName, cbDataSize));
341 }
342
343 LogFlowFuncLeaveRC(rc);
344 return rc;
345}
346
347
348static DECLCALLBACK(int) drvHostVaKitAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
349{
350 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
351 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
352
353 int rc;
354 if (pStream->enmDir == PDMAUDIODIR_IN)
355 rc = debugDestroyStreamIn(pInterface, pStream);
356 else
357 rc = debugDestroyStreamOut(pInterface, pStream);
358
359 return rc;
360}
361
362static DECLCALLBACK(int) drvHostVaKitAudioStreamControl(PPDMIHOSTAUDIO pInterface,
363 PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
364{
365 RT_NOREF(enmStreamCmd);
366
367 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
368 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
369
370 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
371
372 return VINF_SUCCESS;
373}
374
375static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostVaKitAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
376{
377 NOREF(pInterface);
378 NOREF(pStream);
379
380 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
381 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
382}
383
384static DECLCALLBACK(int) drvHostVaKitAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
385{
386 NOREF(pInterface);
387 NOREF(pStream);
388
389 return VINF_SUCCESS;
390}
391
392
393/**
394 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
395 */
396static DECLCALLBACK(void *) drvHostVaKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
397{
398 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
399 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
400
401 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
402 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
403 return NULL;
404}
405
406
407/**
408 * Constructs a Null audio driver instance.
409 *
410 * @copydoc FNPDMDRVCONSTRUCT
411 */
412static DECLCALLBACK(int) drvHostVaKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
413{
414 RT_NOREF(pCfg, fFlags);
415 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
416 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
417 LogRel(("Audio: Initializing ValidationKit driver\n"));
418
419 /*
420 * Init the static parts.
421 */
422 pThis->pDrvIns = pDrvIns;
423 /* IBase */
424 pDrvIns->IBase.pfnQueryInterface = drvHostVaKitAudioQueryInterface;
425 /* IHostAudio */
426 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostVaKitAudio);
427
428 return VINF_SUCCESS;
429}
430
431/**
432 * Char driver registration record.
433 */
434const PDMDRVREG g_DrvHostValidationKitAudio =
435{
436 /* u32Version */
437 PDM_DRVREG_VERSION,
438 /* szName */
439 "ValidationKitAudio",
440 /* szRCMod */
441 "",
442 /* szR0Mod */
443 "",
444 /* pszDescription */
445 "ValidationKit audio host driver",
446 /* fFlags */
447 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
448 /* fClass. */
449 PDM_DRVREG_CLASS_AUDIO,
450 /* cMaxInstances */
451 1,
452 /* cbInstance */
453 sizeof(DRVHOSTVAKITAUDIO),
454 /* pfnConstruct */
455 drvHostVaKitAudioConstruct,
456 /* pfnDestruct */
457 NULL,
458 /* pfnRelocate */
459 NULL,
460 /* pfnIOCtl */
461 NULL,
462 /* pfnPowerOn */
463 NULL,
464 /* pfnReset */
465 NULL,
466 /* pfnSuspend */
467 NULL,
468 /* pfnResume */
469 NULL,
470 /* pfnAttach */
471 NULL,
472 /* pfnDetach */
473 NULL,
474 /* pfnPowerOff */
475 NULL,
476 /* pfnSoftReset */
477 NULL,
478 /* u32EndVersion */
479 PDM_DRVREG_VERSION
480};
481
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette