VirtualBox

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

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

Audio: Added Validation Kit skeleton driver. Use with VBOX_WITH_AUDIO_VALIDATIONKIT set. Needs to be modifiable at runtime later.

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