VirtualBox

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

Last change on this file since 86075 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * ValidationKit audio driver - host backend for dumping and injecting audio data
4 * from/to the device emulation.
5 */
6
7/*
8 * Copyright (C) 2016-2020 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#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 VAKIT input/output stream.
32 */
33typedef struct VAKITAUDIOSTREAM
34{
35 /** The stream's acquired configuration. */
36 PPDMAUDIOSTREAMCFG pCfg;
37 /** Audio file to dump output to or read input from. */
38 PPDMAUDIOFILE pFile;
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 uint8_t *pu8PlayBuffer;
57 uint32_t cbPlayBuffer;
58 } Out;
59 };
60} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
61
62/**
63 * VAKIT audio driver instance data.
64 * @implements PDMIAUDIOCONNECTOR
65 */
66typedef struct DRVHOSTVAKITAUDIO
67{
68 /** Pointer to the driver instance structure. */
69 PPDMDRVINS pDrvIns;
70 /** Pointer to host audio interface. */
71 PDMIHOSTAUDIO IHostAudio;
72} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
73
74/*******************************************PDM_AUDIO_DRIVER******************************/
75
76
77/**
78 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
79 */
80static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
81{
82 RT_NOREF(pInterface);
83 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
84
85 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
86
87 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
88 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
89
90 pBackendCfg->cMaxStreamsOut = 1; /* Output */
91 pBackendCfg->cMaxStreamsIn = 0; /* No input supported yet. */
92
93 return VINF_SUCCESS;
94}
95
96
97/**
98 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
99 */
100static DECLCALLBACK(int) drvHostValKitAudioHA_Init(PPDMIHOSTAUDIO pInterface)
101{
102 RT_NOREF(pInterface);
103
104 LogFlowFuncLeaveRC(VINF_SUCCESS);
105 return VINF_SUCCESS;
106}
107
108
109/**
110 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
111 */
112static DECLCALLBACK(void) drvHostValKitAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
113{
114 RT_NOREF(pInterface);
115}
116
117
118/**
119 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
120 */
121static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
122{
123 RT_NOREF(enmDir);
124 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
125
126 return PDMAUDIOBACKENDSTS_RUNNING;
127}
128
129
130static int drvHostValKitAudioCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
131 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
132{
133 RT_NOREF(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
134
135 return VINF_SUCCESS;
136}
137
138
139static int drvHostValKitAudioCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
140 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
141{
142 RT_NOREF(pDrv, pCfgAcq);
143
144 pStreamDbg->tsStarted = 0;
145 pStreamDbg->uSamplesSinceStarted = 0;
146 pStreamDbg->Out.tsLastPlayed = 0;
147 pStreamDbg->Out.cbPlayBuffer = DrvAudioHlpFramesToBytes(pCfgReq->Backend.cFramesBufferSize, &pCfgReq->Props);
148 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
149 AssertReturn(pStreamDbg->Out.pu8PlayBuffer, VERR_NO_MEMORY);
150
151 char szTemp[RTPATH_MAX];
152 int rc = RTPathTemp(szTemp, sizeof(szTemp));
153 if (RT_SUCCESS(rc))
154 rc = RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
155 if (RT_SUCCESS(rc))
156 {
157 char szFile[RTPATH_MAX];
158 rc = DrvAudioHlpFileNameGet(szFile, sizeof(szFile), szTemp, "VaKit",
159 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAGS_NONE);
160 if (RT_SUCCESS(rc))
161 {
162 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAGS_NONE, &pStreamDbg->pFile);
163 if (RT_SUCCESS(rc))
164 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
165 }
166
167 if (RT_FAILURE(rc))
168 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
169 else
170 {
171 size_t cch;
172 char szTimingInfo[128];
173 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %uHz %uch %ubit\n",
174 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cbSample * 8);
175
176 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
177 }
178 }
179 else
180 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
181 return rc;
182}
183
184
185/**
186 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
187 */
188static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
189 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
190{
191 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
192 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
193 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
194 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
195
196 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
197 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
198
199 int rc;
200 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
201 rc = drvHostValKitAudioCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
202 else
203 rc = drvHostValKitAudioCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
204
205 if (RT_SUCCESS(rc))
206 {
207 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
208 if (!pStreamDbg->pCfg)
209 rc = VERR_NO_MEMORY;
210 }
211
212 return rc;
213}
214
215
216/**
217 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
218 */
219static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
220 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
221{
222 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
223 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
224 RT_NOREF(pDrv);
225
226 uint64_t tsSinceStart;
227 size_t cch;
228 char szTimingInfo[128];
229
230 if (pStreamDbg->tsStarted == 0)
231 {
232 pStreamDbg->tsStarted = RTTimeNanoTS();
233 tsSinceStart = 0;
234 }
235 else
236 {
237 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
238 }
239
240 // Microseconds are used everythere below
241 uint32_t sBuf = uBufSize >> pStreamDbg->pCfg->Props.cShift;
242 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
243 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
244 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
245 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
246 sBuf);
247 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
248 pStreamDbg->uSamplesSinceStarted += sBuf;
249
250 /* Remember when samples were consumed. */
251 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
252
253 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, uBufSize, 0 /* fFlags */);
254 if (RT_FAILURE(rc2))
255 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
256
257 *puWritten = uBufSize;
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
265 */
266static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
267 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
268{
269 RT_NOREF(pInterface, pStream, pvBuf, uBufSize);
270
271 /* Never capture anything. */
272 if (puRead)
273 *puRead = 0;
274
275 return VINF_SUCCESS;
276}
277
278
279static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
280{
281 RT_NOREF(pDrv, pStreamDbg);
282 return VINF_SUCCESS;
283}
284
285
286static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
287{
288 RT_NOREF(pDrv);
289
290 if (pStreamDbg->Out.pu8PlayBuffer)
291 {
292 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
293 pStreamDbg->Out.pu8PlayBuffer = NULL;
294 }
295
296 if (pStreamDbg->pFile)
297 {
298 size_t cbDataSize = DrvAudioHlpFileGetDataSize(pStreamDbg->pFile);
299 if (cbDataSize)
300 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
301
302 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
303 pStreamDbg->pFile = NULL;
304 }
305
306 return VINF_SUCCESS;
307}
308
309
310static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
311{
312 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
313
314 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
315 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
316
317 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
318 return VINF_SUCCESS;
319
320 int rc;
321 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
322 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
323 else
324 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
325
326 if (RT_SUCCESS(rc))
327 {
328 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
329 pStreamDbg->pCfg = NULL;
330 }
331
332 return rc;
333}
334
335static DECLCALLBACK(int) drvHostValKitAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
336 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
337{
338 RT_NOREF(enmStreamCmd);
339 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
340 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
341
342 return VINF_SUCCESS;
343}
344
345/**
346 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
347 */
348static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
349{
350 RT_NOREF(pInterface, pStream);
351
352 return UINT32_MAX;
353}
354
355
356/**
357 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
358 */
359static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
360{
361 RT_NOREF(pInterface, pStream);
362
363 return UINT32_MAX;
364}
365
366static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostValKitAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface,
367 PPDMAUDIOBACKENDSTREAM pStream)
368{
369 RT_NOREF(pInterface, pStream);
370
371 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
372}
373
374static DECLCALLBACK(int) drvHostValKitAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
375{
376 RT_NOREF(pInterface, pStream);
377 return VINF_SUCCESS;
378}
379
380
381/**
382 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
383 */
384static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
385{
386 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
387 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
388
389 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
390 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
391 return NULL;
392}
393
394
395/**
396 * Constructs a VaKit audio driver instance.
397 *
398 * @copydoc FNPDMDRVCONSTRUCT
399 */
400static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
401{
402 RT_NOREF(pCfg, fFlags);
403 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
404 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
405 LogRel(("Audio: Initializing VAKIT driver\n"));
406
407 /*
408 * Init the static parts.
409 */
410 pThis->pDrvIns = pDrvIns;
411 /* IBase */
412 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
413 /* IHostAudio */
414 pThis->IHostAudio.pfnInit = drvHostValKitAudioHA_Init;
415 pThis->IHostAudio.pfnShutdown = drvHostValKitAudioHA_Shutdown;
416 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
417 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
418 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
419 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
420 pThis->IHostAudio.pfnStreamControl = drvHostValKitAudioHA_StreamControl;
421 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
422 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
423 pThis->IHostAudio.pfnStreamGetStatus = drvHostValKitAudioHA_StreamGetStatus;
424 pThis->IHostAudio.pfnStreamIterate = drvHostValKitAudioHA_StreamIterate;
425 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
426 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
427 pThis->IHostAudio.pfnSetCallback = NULL;
428 pThis->IHostAudio.pfnGetDevices = NULL;
429 pThis->IHostAudio.pfnStreamGetPending = NULL;
430 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
431 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
432 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
433 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
434
435 return VINF_SUCCESS;
436}
437
438/**
439 * Char driver registration record.
440 */
441const PDMDRVREG g_DrvHostValidationKitAudio =
442{
443 /* u32Version */
444 PDM_DRVREG_VERSION,
445 /* szName */
446 "ValidationKitAudio",
447 /* szRCMod */
448 "",
449 /* szR0Mod */
450 "",
451 /* pszDescription */
452 "ValidationKitAudio audio host driver",
453 /* fFlags */
454 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
455 /* fClass. */
456 PDM_DRVREG_CLASS_AUDIO,
457 /* cMaxInstances */
458 ~0U,
459 /* cbInstance */
460 sizeof(DRVHOSTVAKITAUDIO),
461 /* pfnConstruct */
462 drvHostValKitAudioConstruct,
463 /* pfnDestruct */
464 NULL,
465 /* pfnRelocate */
466 NULL,
467 /* pfnIOCtl */
468 NULL,
469 /* pfnPowerOn */
470 NULL,
471 /* pfnReset */
472 NULL,
473 /* pfnSuspend */
474 NULL,
475 /* pfnResume */
476 NULL,
477 /* pfnAttach */
478 NULL,
479 /* pfnDetach */
480 NULL,
481 /* pfnPowerOff */
482 NULL,
483 /* pfnSoftReset */
484 NULL,
485 /* u32EndVersion */
486 PDM_DRVREG_VERSION
487};
488
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