VirtualBox

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

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

Audio: More abstraction for the backends: Now the backend stream's data is completely separate from the audio connector interface. That way the backends cannot mess with the audio connector's data (e.g. mixing buffers and friends) anymore, and those are forced to use the audio connector API as meant now. Needs more testing, partly work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 65624 2017-02-06 14:13:36Z 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 /** The stream's acquired configuration. */
36 PDMAUDIOSTREAMCFG Cfg;
37 /** Audio file to dump output to or read input from. */
38 PDMAUDIOFILE File;
39 /** Text file to store timing of audio buffers submittions**/
40 RTFILE hFileTiming;
41 /** Timestamp of the first play or record request**/
42 uint64_t tsStarted;
43 /** Total number of samples played or recorded so far**/
44 uint32_t uSamplesSinceStarted;
45 union
46 {
47 struct
48 {
49 /** Timestamp of last captured samples. */
50 uint64_t tsLastCaptured;
51 } In;
52 struct
53 {
54 /** Timestamp of last played samples. */
55 uint64_t tsLastPlayed;
56 uint64_t cMaxSamplesInPlayBuffer;
57 uint8_t *pu8PlayBuffer;
58 } Out;
59 };
60
61} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
62
63/**
64 * Validation Kit audio driver instance data.
65 * @implements PDMIAUDIOCONNECTOR
66 */
67typedef struct DRVHOSTVAKITAUDIO
68{
69 /** Pointer to the driver instance structure. */
70 PPDMDRVINS pDrvIns;
71 /** Pointer to host audio interface. */
72 PDMIHOSTAUDIO IHostAudio;
73} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
74
75/*******************************************PDM_AUDIO_DRIVER******************************/
76
77
78/**
79 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
80 */
81static DECLCALLBACK(int) drvHostVaKitAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
82{
83 NOREF(pInterface);
84 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
85
86 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
87 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
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) drvHostVaKitAudioInit(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) drvHostVaKitAudioShutdown(PPDMIHOSTAUDIO pInterface)
112{
113 NOREF(pInterface);
114}
115
116
117/**
118 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
119 */
120static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostVaKitAudioGetStatus(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 PPDMAUDIOBACKENDSTREAM 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 = DrvAudioHlpStreamCfgDup(pCfgReq, &Props);
137 if (RT_SUCCESS(rc))
138 {
139 if (pCfgAcq)
140 pCfgAcq->cSampleBufferHint = _1K;
141 }
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147
148static int debugCreateStreamOut(PPDMIHOSTAUDIO pInterface,
149 PPDMAUDIOBACKENDSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
150{
151 NOREF(pInterface);
152
153 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
154
155 /* Just adopt the wanted stream configuration. */
156 PDMAUDIOPCMPROPS Props;
157 int rc = DrvAudioHlpStreamCfgDup(pCfgReq, &Props);
158 if (RT_SUCCESS(rc))
159 {
160 pDbgStream->tsStarted = 0;
161 pDbgStream->uSamplesSinceStarted = 0;
162 pDbgStream->Out.tsLastPlayed = 0;
163 pDbgStream->Out.cMaxSamplesInPlayBuffer = 16 * _1K;
164 pDbgStream->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pDbgStream->Out.cMaxSamplesInPlayBuffer << Props.cShift);
165 if (!pDbgStream->Out.pu8PlayBuffer)
166 rc = VERR_NO_MEMORY;
167 }
168
169 if (RT_SUCCESS(rc))
170 {
171 char szTemp[RTPATH_MAX];
172 rc = RTPathTemp(szTemp, sizeof(szTemp));
173
174 RTPathAppend(szTemp, sizeof(szTemp), "VBoxAudioValKit");
175
176 if (RT_SUCCESS(rc))
177 {
178 char szFile[RTPATH_MAX];
179 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, NULL, PDMAUDIOFILETYPE_WAV);
180 if (RT_SUCCESS(rc))
181 {
182 LogFlowFunc(("%s\n", szFile));
183 rc = DrvAudioHlpWAVFileOpen(&pDbgStream->File, szFile,
184 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
185 &Props, PDMAUDIOFILEFLAG_NONE);
186 if (RT_FAILURE(rc))
187 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
188
189 RTStrCat(szFile, sizeof(szFile), ".timing");
190 rc = RTFileOpen(&pDbgStream->hFileTiming, szFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE);
191
192
193 if (RT_FAILURE(rc))
194 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
195 }
196 else
197 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
198 }
199 else
200 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
201 }
202
203 if (RT_SUCCESS(rc))
204 {
205 if (pCfgAcq)
206 pCfgAcq->cSampleBufferHint = pDbgStream->Out.cMaxSamplesInPlayBuffer;
207 }
208
209 LogFlowFuncLeaveRC(rc);
210 return rc;
211}
212
213
214/**
215 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
216 */
217static DECLCALLBACK(int) drvHostVaKitAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
218 PPDMAUDIOBACKENDSTREAM pStream,
219 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
220{
221 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
222 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
223 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
224
225 int rc;
226 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
227 rc = debugCreateStreamIn( pInterface, pStream, pCfgReq, pCfgAcq);
228 else
229 rc = debugCreateStreamOut(pInterface, pStream, pCfgReq, pCfgAcq);
230
231 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
232 return rc;
233}
234
235
236/**
237 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
238 */
239static DECLCALLBACK(int) drvHostVaKitAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
240 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
241 uint32_t *pcbWritten)
242{
243 RT_NOREF(pvBuf, cbBuf);
244
245 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
246 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
247
248 /* Consume as many samples as would be played at the current frequency since last call. */
249 /*uint32_t cLive = AudioMixBufLive(&pStream->MixBuf);*/
250
251 uint64_t u64TicksNow = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);
252 // uint64_t u64TicksElapsed = u64TicksNow - pDbgStream->Out.tsLastPlayed;
253 // uint64_t u64TicksFreq = PDMDrvHlpTMGetVirtualFreq(pDrv->pDrvIns);
254
255 /*
256 * Minimize the rounding error by adding 0.5: samples = int((u64TicksElapsed * samplesFreq) / u64TicksFreq + 0.5).
257 * If rounding is not taken into account then the playback rate will be consistently lower that expected.
258 */
259 // uint64_t cSamplesPlayed = (2 * u64TicksElapsed * pStream->Props.uHz + u64TicksFreq) / u64TicksFreq / 2;
260
261 /* Don't play more than available. */
262 /*if (cSamplesPlayed > cLive)
263 cSamplesPlayed = cLive;*/
264
265 uint64_t tsSinceStart;
266 size_t cch;
267 char szTimingInfo[128];
268
269 if (pDbgStream->tsStarted == 0)
270 {
271 pDbgStream->tsStarted = RTTimeNanoTS();
272 tsSinceStart = 0;
273 }
274 else
275 {
276 tsSinceStart = RTTimeNanoTS() - pDbgStream->tsStarted;
277 }
278
279 uint32_t uSamplesReady = AudioMixBufUsed(&pStream->MixBuf);
280 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
281 // Host time (in mcs) elapsed since Guest submitted the first buffer for playback
282 (uint32_t)(tsSinceStart / 1000),
283 // how long (in mcs) all the samples submitted previously were played
284 (uint32_t)(pDbgStream->uSamplesSinceStarted * 1.0E6 / pStream->Cfg.uHz),
285 // how long (in mcs) a new uSamplesReady samples should\will be played
286 (uint32_t)(uSamplesReady * 1.0E6 / pStream->Cfg.uHz),
287 uSamplesReady);
288 RTFileWrite(pDbgStream->hFileTiming, szTimingInfo, cch, NULL);
289 pDbgStream->uSamplesSinceStarted += uSamplesReady;
290
291 uint32_t cSamplesPlayed = 0;
292 uint32_t cSamplesAvail = RT_MIN(AudioMixBufUsed(&pStream->MixBuf), pDbgStream->Out.cMaxSamplesInPlayBuffer);
293
294 while (cSamplesAvail)
295 {
296 uint32_t cSamplesRead = 0;
297 int rc2 = AudioMixBufReadCirc(&pStream->MixBuf, pDbgStream->Out.pu8PlayBuffer,
298 AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesAvail), &cSamplesRead);
299
300 if (RT_FAILURE(rc2))
301 LogRel(("DebugAudio: Reading output failed with %Rrc\n", rc2));
302
303 if (!cSamplesRead)
304 break;
305#if 0
306 RTFILE fh;
307 RTFileOpen(&fh, "/tmp/AudioDebug-Output.pcm",
308 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);
309 RTFileWrite(fh, pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead), NULL);
310 RTFileClose(fh);
311#endif
312 rc2 = DrvAudioHlpWAVFileWrite(&pDbgStream->File,
313 pDbgStream->Out.pu8PlayBuffer, AUDIOMIXBUF_S2B(&pStream->MixBuf, cSamplesRead),
314 0 /* fFlags */);
315 if (RT_FAILURE(rc2))
316 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
317
318 AudioMixBufFinish(&pStream->MixBuf, cSamplesRead);
319
320 Assert(cSamplesAvail >= cSamplesRead);
321 cSamplesAvail -= cSamplesRead;
322
323 cSamplesPlayed += cSamplesRead;
324 }
325
326 /* Remember when samples were consumed. */
327 pDbgStream->Out.tsLastPlayed = u64TicksNow;
328
329 if (pcbWritten)
330 *pcbWritten = cSamplesPlayed;
331
332 return VINF_SUCCESS;
333}
334
335
336/**
337 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
338 */
339static DECLCALLBACK(int) drvHostVaKitAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
340 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
341{
342 RT_NOREF(pInterface, pStream, pvBuf, cbBuf);
343
344 /* Never capture anything. */
345 if (pcbRead)
346 *pcbRead = 0;
347
348 return VINF_SUCCESS;
349}
350
351
352static int debugDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
353{
354 RT_NOREF(pInterface, pStream);
355 LogFlowFuncLeaveRC(VINF_SUCCESS);
356 return VINF_SUCCESS;
357}
358
359
360static int debugDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
361{
362 RT_NOREF(pInterface);
363 PVAKITAUDIOSTREAM pDbgStream = (PVAKITAUDIOSTREAM)pStream;
364 if ( pDbgStream
365 && pDbgStream->Out.pu8PlayBuffer)
366 {
367 RTMemFree(pDbgStream->Out.pu8PlayBuffer);
368 pDbgStream->Out.pu8PlayBuffer = NULL;
369 }
370
371 size_t cbDataSize = DrvAudioHlpWAVFileGetDataSize(&pDbgStream->File);
372
373 int rc = DrvAudioHlpWAVFileClose(&pDbgStream->File);
374
375 RTFileClose(pDbgStream->hFileTiming);
376
377 if (RT_SUCCESS(rc))
378 {
379 /* Delete the file again if nothing but the header was written to it. */
380 bool fDeleteEmptyFiles = true; /** @todo Make deletion configurable? */
381
382 if ( !cbDataSize
383 && fDeleteEmptyFiles)
384 {
385 char szFile[RTPATH_MAX];
386
387 RTStrCopy(szFile, sizeof(szFile), pDbgStream->File.szName);
388 rc = RTFileDelete(szFile);
389
390 RTStrCat(szFile, sizeof(szFile), ".timing");
391 rc = RTFileDelete(szFile);
392 }
393 else
394 LogRel(("DebugAudio: Created output file '%s' (%zu bytes)\n", pDbgStream->File.szName, cbDataSize));
395 }
396
397 LogFlowFuncLeaveRC(rc);
398 return rc;
399}
400
401
402static DECLCALLBACK(int) drvHostVaKitAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
403{
404 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
405 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
406
407 int rc;
408 if (pStream->enmDir == PDMAUDIODIR_IN)
409 rc = debugDestroyStreamIn(pInterface, pStream);
410 else
411 rc = debugDestroyStreamOut(pInterface, pStream);
412
413 return rc;
414}
415
416static DECLCALLBACK(int) drvHostVaKitAudioStreamControl(PPDMIHOSTAUDIO pInterface,
417 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
418{
419 RT_NOREF(enmStreamCmd);
420
421 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
422 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
423
424 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
425
426 return VINF_SUCCESS;
427}
428
429static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostVaKitAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
430{
431 NOREF(pInterface);
432 NOREF(pStream);
433
434 return ( PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED
435 | PDMAUDIOSTRMSTS_FLAG_DATA_READABLE | PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE);
436}
437
438static DECLCALLBACK(int) drvHostVaKitAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
439{
440 NOREF(pInterface);
441 NOREF(pStream);
442
443 return VINF_SUCCESS;
444}
445
446
447/**
448 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
449 */
450static DECLCALLBACK(void *) drvHostVaKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
451{
452 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
453 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
454
455 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
456 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
457 return NULL;
458}
459
460
461/**
462 * Constructs a Null audio driver instance.
463 *
464 * @copydoc FNPDMDRVCONSTRUCT
465 */
466static DECLCALLBACK(int) drvHostVaKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
467{
468 RT_NOREF(pCfg, fFlags);
469 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
470 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
471 LogRel(("Audio: Initializing ValidationKit driver\n"));
472
473 /*
474 * Init the static parts.
475 */
476 pThis->pDrvIns = pDrvIns;
477 /* IBase */
478 pDrvIns->IBase.pfnQueryInterface = drvHostVaKitAudioQueryInterface;
479 /* IHostAudio */
480 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostVaKitAudio);
481
482 return VINF_SUCCESS;
483}
484
485/**
486 * Char driver registration record.
487 */
488const PDMDRVREG g_DrvHostValidationKitAudio =
489{
490 /* u32Version */
491 PDM_DRVREG_VERSION,
492 /* szName */
493 "ValidationKitAudio",
494 /* szRCMod */
495 "",
496 /* szR0Mod */
497 "",
498 /* pszDescription */
499 "ValidationKit audio host driver",
500 /* fFlags */
501 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
502 /* fClass. */
503 PDM_DRVREG_CLASS_AUDIO,
504 /* cMaxInstances */
505 1,
506 /* cbInstance */
507 sizeof(DRVHOSTVAKITAUDIO),
508 /* pfnConstruct */
509 drvHostVaKitAudioConstruct,
510 /* pfnDestruct */
511 NULL,
512 /* pfnRelocate */
513 NULL,
514 /* pfnIOCtl */
515 NULL,
516 /* pfnPowerOn */
517 NULL,
518 /* pfnReset */
519 NULL,
520 /* pfnSuspend */
521 NULL,
522 /* pfnResume */
523 NULL,
524 /* pfnAttach */
525 NULL,
526 /* pfnDetach */
527 NULL,
528 /* pfnPowerOff */
529 NULL,
530 /* pfnSoftReset */
531 NULL,
532 /* u32EndVersion */
533 PDM_DRVREG_VERSION
534};
535
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