VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp@ 90979

Last change on this file since 90979 was 90868, checked in by vboxsync, 3 years ago

Audio/VKAT: More logging adjustments. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.8 KB
Line 
1/* $Id: vkatDriverStack.cpp 90868 2021-08-25 10:12:34Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) - Driver stack code.
4 */
5
6/*
7 * Copyright (C) 2021 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP LOG_GROUP_AUDIO_TEST
32#include <iprt/log.h>
33
34#include <iprt/errcore.h>
35#include <iprt/message.h>
36#include <iprt/stream.h>
37#include <iprt/string.h>
38#include <iprt/uuid.h>
39#include <iprt/test.h>
40
41
42/**
43 * Internal driver instance data
44 * @note This must be put here as it's needed before pdmdrv.h is included.
45 */
46typedef struct PDMDRVINSINT
47{
48 /** The stack the drive belongs to. */
49 struct AUDIOTESTDRVSTACK *pStack;
50} PDMDRVINSINT;
51#define PDMDRVINSINT_DECLARED
52
53#include "vkatInternal.h"
54#include "VBoxDD.h"
55
56
57
58/*********************************************************************************************************************************
59* Fake PDM Driver Handling. *
60*********************************************************************************************************************************/
61
62/** @name Driver Fakes/Stubs
63 *
64 * @note The VMM functions defined here will turn into driver helpers before
65 * long, as the drivers aren't supposed to import directly from the VMM in
66 * the future.
67 *
68 * @{ */
69
70VMMR3DECL(PCFGMNODE) CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath)
71{
72 RT_NOREF(pNode, pszPath);
73 return NULL;
74}
75
76
77VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString)
78{
79 if (pNode != NULL)
80 {
81 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
82 if (g_uVerbosity > 2)
83 RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString);
84
85 if ( ( strcmp(pDrvReg->szName, "PulseAudio") == 0
86 || strcmp(pDrvReg->szName, "HostAudioWas") == 0)
87 && strcmp(pszName, "VmName") == 0)
88 return RTStrCopy(pszString, cchString, "vkat");
89
90 if ( strcmp(pDrvReg->szName, "HostAudioWas") == 0
91 && strcmp(pszName, "VmUuid") == 0)
92 return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e");
93 }
94 else if (g_uVerbosity > 2)
95 RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString);
96
97 return VERR_CFGM_VALUE_NOT_FOUND;
98}
99
100
101VMMR3DECL(int) CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString)
102{
103 char szStr[128];
104 int rc = CFGMR3QueryString(pNode, pszName, szStr, sizeof(szStr));
105 if (RT_SUCCESS(rc))
106 *ppszString = RTStrDup(szStr);
107
108 return rc;
109}
110
111
112VMMR3DECL(void) MMR3HeapFree(void *pv)
113{
114 /* counterpart to CFGMR3QueryStringAlloc */
115 RTStrFree((char *)pv);
116}
117
118
119VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
120{
121 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
122 if (RT_VALID_PTR(pDrvReg))
123 {
124 const char *pszRet = pszDef;
125 if ( g_pszDrvAudioDebug
126 && strcmp(pDrvReg->szName, "AUDIO") == 0
127 && strcmp(pszName, "DebugPathOut") == 0)
128 pszRet = g_pszDrvAudioDebug;
129
130 int rc = RTStrCopy(pszString, cchString, pszRet);
131
132 if (g_uVerbosity > 2)
133 RTPrintf("debug: CFGMR3QueryStringDef([%s], %s, %p, %#x, %s) -> '%s' + %Rrc\n",
134 pDrvReg->szName, pszName, pszString, cchString, pszDef, pszRet, rc);
135 return rc;
136 }
137
138 if (g_uVerbosity > 2)
139 RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef);
140 return RTStrCopy(pszString, cchString, pszDef);
141}
142
143
144VMMR3DECL(int) CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef)
145{
146 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
147 if (RT_VALID_PTR(pDrvReg))
148 {
149 *pf = fDef;
150 if ( strcmp(pDrvReg->szName, "AUDIO") == 0
151 && strcmp(pszName, "DebugEnabled") == 0)
152 *pf = g_fDrvAudioDebug;
153
154 if (g_uVerbosity > 2)
155 RTPrintf("debug: CFGMR3QueryBoolDef([%s], %s, %p, %RTbool) -> %RTbool\n", pDrvReg->szName, pszName, pf, fDef, *pf);
156 return VINF_SUCCESS;
157 }
158 *pf = fDef;
159 return VINF_SUCCESS;
160}
161
162
163VMMR3DECL(int) CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8)
164{
165 RT_NOREF(pNode, pszName, pu8);
166 return VERR_CFGM_VALUE_NOT_FOUND;
167}
168
169
170VMMR3DECL(int) CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32)
171{
172 RT_NOREF(pNode, pszName, pu32);
173 return VERR_CFGM_VALUE_NOT_FOUND;
174}
175
176
177VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode,
178 const char *pszValidValues, const char *pszValidNodes,
179 const char *pszWho, uint32_t uInstance)
180{
181 RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance);
182 return VINF_SUCCESS;
183}
184
185/** @} */
186
187/** @name Driver Helper Fakes
188 * @{ */
189
190static DECLCALLBACK(int) audioTestDrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface)
191{
192 /* DrvAudio must be allowed to attach the backend driver (paranoid
193 backend drivers may call us to check that nothing is attached). */
194 if (strcmp(pDrvIns->pReg->szName, "AUDIO") == 0)
195 {
196 PAUDIOTESTDRVSTACK pDrvStack = pDrvIns->Internal.s.pStack;
197 AssertReturn(pDrvStack->pDrvBackendIns == NULL, VERR_PDM_DRIVER_ALREADY_ATTACHED);
198
199 if (g_uVerbosity > 1)
200 RTMsgInfo("Attaching backend '%s' to DrvAudio...\n", pDrvStack->pDrvReg->szName);
201 int rc = audioTestDrvConstruct(pDrvStack, pDrvStack->pDrvReg, pDrvIns, &pDrvStack->pDrvBackendIns);
202 if (RT_SUCCESS(rc))
203 {
204 if (ppBaseInterface)
205 *ppBaseInterface = &pDrvStack->pDrvBackendIns->IBase;
206 }
207 else
208 RTMsgError("Failed to attach backend: %Rrc", rc);
209 return rc;
210 }
211 RT_NOREF(fFlags);
212 return VERR_PDM_NO_ATTACHED_DRIVER;
213}
214
215
216static DECLCALLBACK(void) audioTestDrvHlp_STAMRegister(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, const char *pszName,
217 STAMUNIT enmUnit, const char *pszDesc)
218{
219 RT_NOREF(pDrvIns, pvSample, enmType, pszName, enmUnit, pszDesc);
220}
221
222
223static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
224 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
225 const char *pszName, ...)
226{
227 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName);
228}
229
230
231static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
232 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
233 const char *pszName, va_list args)
234{
235 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
236}
237
238
239static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample)
240{
241 RT_NOREF(pDrvIns, pvSample);
242 return VINF_SUCCESS;
243}
244
245
246static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix)
247{
248 RT_NOREF(pDrvIns, pszPrefix);
249 return VINF_SUCCESS;
250}
251
252/**
253 * Get the driver helpers.
254 */
255static const PDMDRVHLPR3 *audioTestFakeGetDrvHlp(void)
256{
257 /*
258 * Note! No initializer for s_DrvHlp (also why it's not a file global).
259 * We do not want to have to update this code every time PDMDRVHLPR3
260 * grows new entries or are otherwise modified. Only when the
261 * entries used by the audio driver changes do we want to change
262 * our code.
263 */
264 static PDMDRVHLPR3 s_DrvHlp;
265 if (s_DrvHlp.u32Version != PDM_DRVHLPR3_VERSION)
266 {
267 s_DrvHlp.u32Version = PDM_DRVHLPR3_VERSION;
268 s_DrvHlp.u32TheEnd = PDM_DRVHLPR3_VERSION;
269 s_DrvHlp.pfnAttach = audioTestDrvHlp_Attach;
270 s_DrvHlp.pfnSTAMRegister = audioTestDrvHlp_STAMRegister;
271 s_DrvHlp.pfnSTAMRegisterF = audioTestDrvHlp_STAMRegisterF;
272 s_DrvHlp.pfnSTAMRegisterV = audioTestDrvHlp_STAMRegisterV;
273 s_DrvHlp.pfnSTAMDeregister = audioTestDrvHlp_STAMDeregister;
274 s_DrvHlp.pfnSTAMDeregisterByPrefix = audioTestDrvHlp_STAMDeregisterByPrefix;
275 }
276 return &s_DrvHlp;
277}
278
279/** @} */
280
281
282/**
283 * Implementation of PDMIBASE::pfnQueryInterface for a fake device above
284 * DrvAudio.
285 */
286static DECLCALLBACK(void *) audioTestFakeDeviceIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
287{
288 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
289 RTMsgWarning("audioTestFakeDeviceIBaseQueryInterface: Unknown interface: %s\n", pszIID);
290 return NULL;
291}
292
293/** IBase interface for a fake device above DrvAudio. */
294static PDMIBASE g_AudioTestFakeDeviceIBase = { audioTestFakeDeviceIBaseQueryInterface };
295
296
297static DECLCALLBACK(int) audioTestIHostAudioPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream,
298 uintptr_t uUser, void *pvUser)
299{
300 RT_NOREF(pInterface, pStream, uUser, pvUser);
301 RTMsgWarning("audioTestIHostAudioPort_DoOnWorkerThread was called\n");
302 return VERR_NOT_IMPLEMENTED;
303}
304
305
306DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser)
307{
308 RT_NOREF(pInterface, enmDir, pvUser);
309 RTMsgWarning("audioTestIHostAudioPort_NotifyDeviceChanged was called\n");
310}
311
312
313static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface,
314 PPDMAUDIOBACKENDSTREAM pStream)
315{
316 RT_NOREF(pInterface, pStream);
317 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch was called\n");
318}
319
320
321static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface,
322 PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
323{
324 RT_NOREF(pInterface, pStream, fReInit);
325 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyDeviceChanged was called\n");
326}
327
328
329static DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface)
330{
331 RT_NOREF(pInterface);
332 RTMsgWarning("audioTestIHostAudioPort_NotifyDevicesChanged was called\n");
333}
334
335
336static PDMIHOSTAUDIOPORT g_AudioTestIHostAudioPort =
337{
338 audioTestIHostAudioPort_DoOnWorkerThread,
339 audioTestIHostAudioPort_NotifyDeviceChanged,
340 audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch,
341 audioTestIHostAudioPort_StreamNotifyDeviceChanged,
342 audioTestIHostAudioPort_NotifyDevicesChanged,
343};
344
345
346/**
347 * Implementation of PDMIBASE::pfnQueryInterface for a fake DrvAudio above a
348 * backend.
349 */
350static DECLCALLBACK(void *) audioTestFakeDrvAudioIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
351{
352 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
353 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &g_AudioTestIHostAudioPort);
354 RTMsgWarning("audioTestFakeDrvAudioIBaseQueryInterface: Unknown interface: %s\n", pszIID);
355 return NULL;
356}
357
358
359/** IBase interface for a fake DrvAudio above a lonesome backend. */
360static PDMIBASE g_AudioTestFakeDrvAudioIBase = { audioTestFakeDrvAudioIBaseQueryInterface };
361
362
363
364/**
365 * Constructs a PDM audio driver instance.
366 *
367 * @returns VBox status code.
368 * @param pDrvStack The stack this is associated with.
369 * @param pDrvReg PDM driver registration record to use for construction.
370 * @param pParentDrvIns The parent driver (if any).
371 * @param ppDrvIns Where to return the driver instance structure.
372 */
373int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
374 PPPDMDRVINS ppDrvIns)
375{
376 /* The destruct function must have valid data to work with. */
377 *ppDrvIns = NULL;
378
379 /*
380 * Check registration structure validation (doesn't need to be too
381 * thorough, PDM check it in detail on every VM startup).
382 */
383 AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER);
384 RTMsgInfo("Initializing backend '%s' ...\n", pDrvReg->szName);
385 AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER);
386
387 /*
388 * Create the instance data structure.
389 */
390 PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance]));
391 RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY);
392
393 pDrvIns->u32Version = PDM_DRVINS_VERSION;
394 pDrvIns->iInstance = 0;
395 pDrvIns->pHlpR3 = audioTestFakeGetDrvHlp();
396 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0];
397 pDrvIns->pReg = pDrvReg;
398 pDrvIns->pCfg = (PCFGMNODE)pDrvReg;
399 pDrvIns->Internal.s.pStack = pDrvStack;
400 pDrvIns->pUpBase = NULL;
401 pDrvIns->pDownBase = NULL;
402 if (pParentDrvIns)
403 {
404 Assert(pParentDrvIns->pDownBase == NULL);
405 pParentDrvIns->pDownBase = &pDrvIns->IBase;
406 pDrvIns->pUpBase = &pParentDrvIns->IBase;
407 }
408 else if (strcmp(pDrvReg->szName, "AUDIO") == 0)
409 pDrvIns->pUpBase = &g_AudioTestFakeDeviceIBase;
410 else
411 pDrvIns->pUpBase = &g_AudioTestFakeDrvAudioIBase;
412
413 /*
414 * Invoke the constructor.
415 */
416 int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/);
417 if (RT_SUCCESS(rc))
418 {
419 *ppDrvIns = pDrvIns;
420 return VINF_SUCCESS;
421 }
422
423 if (pDrvReg->pfnDestruct)
424 pDrvReg->pfnDestruct(pDrvIns);
425 RTMemFree(pDrvIns);
426 return rc;
427}
428
429
430/**
431 * Destructs a PDM audio driver instance.
432 *
433 * @param pDrvIns Driver instance to destruct.
434 */
435static void audioTestDrvDestruct(PPDMDRVINS pDrvIns)
436{
437 if (pDrvIns)
438 {
439 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
440
441 if (pDrvIns->pReg->pfnDestruct)
442 pDrvIns->pReg->pfnDestruct(pDrvIns);
443
444 pDrvIns->u32Version = 0;
445 pDrvIns->pReg = NULL;
446 RTMemFree(pDrvIns);
447 }
448}
449
450
451/**
452 * Sends the PDM driver a power off notification.
453 *
454 * @param pDrvIns Driver instance to notify.
455 */
456static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns)
457{
458 if (pDrvIns)
459 {
460 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
461 if (pDrvIns->pReg->pfnPowerOff)
462 pDrvIns->pReg->pfnPowerOff(pDrvIns);
463 }
464}
465
466
467/**
468 * Deletes a driver stack.
469 *
470 * This will power off and destroy the drivers.
471 */
472void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
473{
474 /*
475 * Do power off notifications (top to bottom).
476 */
477 audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns);
478 audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns);
479
480 /*
481 * Drivers are destroyed from bottom to top (closest to the device).
482 */
483 audioTestDrvDestruct(pDrvStack->pDrvBackendIns);
484 pDrvStack->pDrvBackendIns = NULL;
485 pDrvStack->pIHostAudio = NULL;
486
487 audioTestDrvDestruct(pDrvStack->pDrvAudioIns);
488 pDrvStack->pDrvAudioIns = NULL;
489 pDrvStack->pIAudioConnector = NULL;
490
491 PDMAudioHostEnumDelete(&pDrvStack->DevEnum);
492}
493
494
495/**
496 * Initializes a driver stack, extended version.
497 *
498 * @returns VBox status code.
499 * @param pDrvStack The driver stack to initialize.
500 * @param pDrvReg The backend driver to use.
501 * @param fEnabledIn Whether input is enabled or not on creation time.
502 * @param fEnabledOut Whether output is enabled or not on creation time.
503 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
504 */
505int audioTestDriverStackInitEx(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio)
506{
507 int rc;
508
509 RT_ZERO(*pDrvStack);
510 pDrvStack->pDrvReg = pDrvReg;
511
512 PDMAudioHostEnumInit(&pDrvStack->DevEnum);
513
514 if (!fWithDrvAudio)
515 rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns);
516 else
517 {
518 rc = audioTestDrvConstruct(pDrvStack, &g_DrvAUDIO, NULL /*pParentDrvIns*/, &pDrvStack->pDrvAudioIns);
519 if (RT_SUCCESS(rc))
520 {
521 Assert(pDrvStack->pDrvAudioIns);
522 PPDMIBASE const pIBase = &pDrvStack->pDrvAudioIns->IBase;
523 pDrvStack->pIAudioConnector = (PPDMIAUDIOCONNECTOR)pIBase->pfnQueryInterface(pIBase, PDMIAUDIOCONNECTOR_IID);
524 if (pDrvStack->pIAudioConnector)
525 {
526 /* Both input and output is disabled by default. */
527 if (fEnabledIn)
528 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_IN, true);
529
530 if (RT_SUCCESS(rc))
531 {
532 if (fEnabledOut)
533 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_OUT, true);
534 }
535
536 if (RT_FAILURE(rc))
537 {
538 RTTestFailed(g_hTest, "Failed to enabled input and output: %Rrc", rc);
539 audioTestDriverStackDelete(pDrvStack);
540 }
541 }
542 else
543 {
544 RTTestFailed(g_hTest, "Failed to query PDMIAUDIOCONNECTOR");
545 audioTestDriverStackDelete(pDrvStack);
546 rc = VERR_PDM_MISSING_INTERFACE;
547 }
548 }
549 }
550
551 /*
552 * Get the IHostAudio interface and check that the host driver is working.
553 */
554 if (RT_SUCCESS(rc))
555 {
556 PPDMIBASE const pIBase = &pDrvStack->pDrvBackendIns->IBase;
557 pDrvStack->pIHostAudio = (PPDMIHOSTAUDIO)pIBase->pfnQueryInterface(pIBase, PDMIHOSTAUDIO_IID);
558 if (pDrvStack->pIHostAudio)
559 {
560 PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT);
561 if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING)
562 return VINF_SUCCESS;
563
564 RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus);
565 }
566 else
567 RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName);
568 audioTestDriverStackDelete(pDrvStack);
569 }
570
571 return rc;
572}
573
574
575/**
576 * Initializes a driver stack.
577 *
578 * @returns VBox status code.
579 * @param pDrvStack The driver stack to initialize.
580 * @param pDrvReg The backend driver to use.
581 * @param fEnabledIn Whether input is enabled or not on creation time.
582 * @param fEnabledOut Whether output is enabled or not on creation time.
583 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
584 */
585int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
586{
587 return audioTestDriverStackInitEx(pDrvStack, pDrvReg, true /* fEnabledIn */, true /* fEnabledOut */, fWithDrvAudio);
588}
589
590
591/**
592 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
593 */
594int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
595{
596 int rc;
597 if ( pDrvStack->pIHostAudio
598 && pDrvStack->pIHostAudio->pfnSetDevice)
599 rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId);
600 else if (!pszDevId || *pszDevId)
601 rc = VINF_SUCCESS;
602 else
603 rc = VERR_INVALID_FUNCTION;
604 return rc;
605}
606
607
608/**
609 * Common stream creation code.
610 *
611 * @returns VBox status code.
612 * @param pDrvStack The audio driver stack to create it via.
613 * @param pCfgReq The requested config.
614 * @param ppStream Where to return the stream pointer on success.
615 * @param pCfgAcq Where to return the actual (well, not necessarily when
616 * using DrvAudio, but probably the same) stream config on
617 * success (not used as input).
618 */
619static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOSTREAMCFG pCfgReq,
620 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
621{
622 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16];
623 int rc;
624 *ppStream = NULL;
625
626 if (pDrvStack->pIAudioConnector)
627 {
628 /*
629 * DrvAudio does most of the work here.
630 */
631 rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, 0 /*fFlags*/, pCfgReq, ppStream);
632 if (RT_SUCCESS(rc))
633 {
634 *pCfgAcq = (*ppStream)->Cfg;
635 RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp)));
636 return rc;
637 }
638 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc", rc);
639 }
640 else
641 {
642 /*
643 * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM
644 * structure actually is for this backend.
645 */
646 PDMAUDIOBACKENDCFG BackendCfg;
647 rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg);
648 if (RT_SUCCESS(rc))
649 {
650 if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM))
651 {
652 /*
653 * Allocate and initialize the stream.
654 */
655 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream;
656 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream);
657 if (pStreamAt)
658 {
659 pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
660 pStreamAt->Core.Cfg = *pCfgReq;
661 pStreamAt->Core.cbBackend = cbStream;
662
663 pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC;
664 pStreamAt->Backend.pStream = &pStreamAt->Core;
665
666 /*
667 * Call the backend to create the stream.
668 */
669 rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend,
670 pCfgReq, &pStreamAt->Core.Cfg);
671 if (RT_SUCCESS(rc))
672 {
673 if (g_uVerbosity > 1)
674 RTMsgInfo("Created backend stream: %s\n",
675 PDMAudioStrmCfgToString(&pStreamAt->Core.Cfg, szTmp, sizeof(szTmp)));
676
677 /* Return if stream is ready: */
678 if (rc == VINF_SUCCESS)
679 {
680 *ppStream = &pStreamAt->Core;
681 *pCfgAcq = pStreamAt->Core.Cfg;
682 return VINF_SUCCESS;
683 }
684 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
685 {
686 /*
687 * Do async init right here and now.
688 */
689 rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend,
690 false /*fDestroyed*/);
691 if (RT_SUCCESS(rc))
692 {
693 *ppStream = &pStreamAt->Core;
694 *pCfgAcq = pStreamAt->Core.Cfg;
695 return VINF_SUCCESS;
696 }
697
698 RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc);
699 }
700 else
701 {
702 RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc);
703 rc = VERR_IPE_UNEXPECTED_INFO_STATUS;
704 }
705 pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
706 }
707 else
708 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc\n", rc);
709 }
710 else
711 {
712 RTTestFailed(g_hTest, "Out of memory!\n");
713 rc = VERR_NO_MEMORY;
714 }
715 RTMemFree(pStreamAt);
716 }
717 else
718 {
719 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM));
720 rc = VERR_OUT_OF_RANGE;
721 }
722 }
723 else
724 RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc);
725 }
726 return rc;
727}
728
729
730/**
731 * Creates an output stream.
732 *
733 * @returns VBox status code.
734 * @param pDrvStack The audio driver stack to create it via.
735 * @param pProps The audio properties to use.
736 * @param cMsBufferSize The buffer size in milliseconds.
737 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
738 * @param cMsSchedulingHint The scheduling hint in milliseconds.
739 * @param ppStream Where to return the stream pointer on success.
740 * @param pCfgAcq Where to return the actual (well, not
741 * necessarily when using DrvAudio, but probably
742 * the same) stream config on success (not used as
743 * input).
744 */
745int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
746 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
747 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
748{
749 /*
750 * Calculate the stream config.
751 */
752 PDMAUDIOSTREAMCFG CfgReq;
753 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
754 AssertRC(rc);
755 CfgReq.enmDir = PDMAUDIODIR_OUT;
756 CfgReq.enmPath = PDMAUDIOPATH_OUT_FRONT;
757 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
758 ? 10 : cMsSchedulingHint;
759 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
760 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
761 else
762 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
763 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
764 ? 300 : cMsBufferSize);
765 if (cMsPreBuffer == UINT32_MAX)
766 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */
767 : CfgReq.Backend.cFramesBufferSize * 2 / 3;
768 else
769 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
770 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16
771 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
772 {
773 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
774 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
775 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
776 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
777 }
778
779 static uint32_t s_idxStream = 0;
780 uint32_t const idxStream = s_idxStream++;
781 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream);
782
783 /*
784 * Call common code to do the actual work.
785 */
786 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
787}
788
789
790/**
791 * Creates an input stream.
792 *
793 * @returns VBox status code.
794 * @param pDrvStack The audio driver stack to create it via.
795 * @param pProps The audio properties to use.
796 * @param cMsBufferSize The buffer size in milliseconds.
797 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
798 * @param cMsSchedulingHint The scheduling hint in milliseconds.
799 * @param ppStream Where to return the stream pointer on success.
800 * @param pCfgAcq Where to return the actual (well, not
801 * necessarily when using DrvAudio, but probably
802 * the same) stream config on success (not used as
803 * input).
804 */
805int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
806 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
807 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
808{
809 /*
810 * Calculate the stream config.
811 */
812 PDMAUDIOSTREAMCFG CfgReq;
813 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
814 AssertRC(rc);
815 CfgReq.enmDir = PDMAUDIODIR_IN;
816 CfgReq.enmPath = PDMAUDIOPATH_IN_LINE;
817 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
818 ? 10 : cMsSchedulingHint;
819 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
820 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
821 else
822 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
823 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
824 ? 300 : cMsBufferSize);
825 if (cMsPreBuffer == UINT32_MAX)
826 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */
827 : CfgReq.Backend.cFramesBufferSize / 2;
828 else
829 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
830 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */
831 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
832 {
833 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
834 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
835 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
836 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
837 }
838
839 static uint32_t s_idxStream = 0;
840 uint32_t const idxStream = s_idxStream++;
841 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream);
842
843 /*
844 * Call common code to do the actual work.
845 */
846 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
847}
848
849
850/**
851 * Destroys a stream.
852 */
853void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
854{
855 if (pStream)
856 {
857 if (pDrvStack->pIAudioConnector)
858 {
859 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IAudioConnector) ...\n", pStream->Cfg.szName);
860 int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/);
861 if (RT_FAILURE(rc))
862 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc);
863 }
864 else
865 {
866 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IHostAudio) ...\n", pStream->Cfg.szName);
867 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
868 int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
869 if (RT_SUCCESS(rc))
870 {
871 pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC;
872 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC;
873 RTMemFree(pStreamAt);
874 }
875 else
876 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc);
877 }
878
879 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' done\n", pStream->Cfg.szName);
880 }
881}
882
883
884/**
885 * Enables a stream.
886 */
887int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
888{
889 int rc;
890 if (pDrvStack->pIAudioConnector)
891 {
892 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE);
893 if (RT_FAILURE(rc))
894 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
895 }
896 else
897 {
898 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
899 rc = pDrvStack->pIHostAudio->pfnStreamEnable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
900 if (RT_FAILURE(rc))
901 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamEnable failed: %Rrc", rc);
902 }
903 return rc;
904}
905
906
907/**
908 * Disables a stream.
909 */
910int AudioTestDriverStackStreamDisable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
911{
912 int rc;
913 if (pDrvStack->pIAudioConnector)
914 {
915 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DISABLE);
916 if (RT_FAILURE(rc))
917 RTTestFailed(g_hTest, "pfnStreamControl/DISABLE failed: %Rrc", rc);
918 }
919 else
920 {
921 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
922 rc = pDrvStack->pIHostAudio->pfnStreamDisable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
923 if (RT_FAILURE(rc))
924 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDisable failed: %Rrc", rc);
925 }
926 return rc;
927}
928
929
930/**
931 * Drains an output stream.
932 */
933int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
934{
935 int rc;
936 if (pDrvStack->pIAudioConnector)
937 {
938 /*
939 * Issue the drain request.
940 */
941 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN);
942 if (RT_SUCCESS(rc) && fSync)
943 {
944 /*
945 * This is a synchronous drain, so wait for the driver to change state to inactive.
946 */
947 PDMAUDIOSTREAMSTATE enmState;
948 while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream))
949 >= PDMAUDIOSTREAMSTATE_ENABLED)
950 {
951 RTThreadSleep(2);
952 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream);
953 if (RT_FAILURE(rc))
954 {
955 RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc);
956 break;
957 }
958 }
959 if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE)
960 {
961 RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState));
962 rc = VERR_AUDIO_STREAM_NOT_READY;
963 }
964 }
965 else if (RT_FAILURE(rc))
966 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
967 }
968 else
969 {
970 /*
971 * Issue the drain request.
972 */
973 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
974 rc = pDrvStack->pIHostAudio->pfnStreamDrain(pDrvStack->pIHostAudio, &pStreamAt->Backend);
975 if (RT_SUCCESS(rc) && fSync)
976 {
977 /*
978 * This is a synchronous drain, so wait for the driver to change state to inactive.
979 */
980 PDMHOSTAUDIOSTREAMSTATE enmHostState;
981 while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend))
982 == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
983 {
984 RTThreadSleep(2);
985 uint32_t cbWritten = UINT32_MAX;
986 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend,
987 NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten);
988 if (RT_FAILURE(rc))
989 {
990 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc);
991 break;
992 }
993 if (cbWritten != 0)
994 {
995 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten);
996 rc = VERR_MISSING;
997 break;
998 }
999 }
1000 if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY)
1001 {
1002 RTTestFailed(g_hTest, "Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState));
1003 rc = VERR_AUDIO_STREAM_NOT_READY;
1004 }
1005 }
1006 else if (RT_FAILURE(rc))
1007 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
1008 }
1009 return rc;
1010}
1011
1012
1013/**
1014 * Checks if the stream is okay.
1015 * @returns true if okay, false if not.
1016 */
1017bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1018{
1019 /*
1020 * Get the stream status and check if it means is okay or not.
1021 */
1022 bool fRc = false;
1023 if (pDrvStack->pIAudioConnector)
1024 {
1025 PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream);
1026 switch (enmState)
1027 {
1028 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
1029 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
1030 break;
1031 case PDMAUDIOSTREAMSTATE_INACTIVE:
1032 case PDMAUDIOSTREAMSTATE_ENABLED:
1033 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
1034 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
1035 fRc = true;
1036 break;
1037 /* no default */
1038 case PDMAUDIOSTREAMSTATE_INVALID:
1039 case PDMAUDIOSTREAMSTATE_END:
1040 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
1041 break;
1042 }
1043 }
1044 else
1045 {
1046 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1047 PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio,
1048 &pStreamAt->Backend);
1049 switch (enmHostState)
1050 {
1051 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
1052 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
1053 break;
1054 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
1055 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
1056 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
1057 fRc = true;
1058 break;
1059 /* no default */
1060 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
1061 case PDMHOSTAUDIOSTREAMSTATE_END:
1062 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
1063 break;
1064 }
1065 }
1066 return fRc;
1067}
1068
1069
1070/**
1071 * Gets the number of bytes it's currently possible to write to the stream.
1072 */
1073uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1074{
1075 uint32_t cbWritable;
1076 if (pDrvStack->pIAudioConnector)
1077 cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream);
1078 else
1079 {
1080 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1081 cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1082 }
1083 return cbWritable;
1084}
1085
1086
1087/**
1088 * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
1089 */
1090int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1091 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
1092{
1093 int rc;
1094 if (pDrvStack->pIAudioConnector)
1095 {
1096 rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed);
1097 if (RT_FAILURE(rc))
1098 RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
1099 }
1100 else
1101 {
1102 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1103 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed);
1104 if (RT_FAILURE(rc))
1105 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
1106 }
1107 return rc;
1108}
1109
1110
1111/**
1112 * Gets the number of bytes it's currently possible to write to the stream.
1113 */
1114uint32_t audioTestDriverStackStreamGetReadable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1115{
1116 uint32_t cbReadable;
1117 if (pDrvStack->pIAudioConnector)
1118 cbReadable = pDrvStack->pIAudioConnector->pfnStreamGetReadable(pDrvStack->pIAudioConnector, pStream);
1119 else
1120 {
1121 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1122 cbReadable = pDrvStack->pIHostAudio->pfnStreamGetReadable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1123 }
1124 return cbReadable;
1125}
1126
1127
1128/**
1129 * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
1130 */
1131int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1132 void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
1133{
1134 int rc;
1135 if (pDrvStack->pIAudioConnector)
1136 {
1137 rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured);
1138 if (RT_FAILURE(rc))
1139 RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
1140 }
1141 else
1142 {
1143 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1144 rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured);
1145 if (RT_FAILURE(rc))
1146 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
1147 }
1148 return rc;
1149}
1150
1151
1152/*********************************************************************************************************************************
1153* Mixed streams *
1154*********************************************************************************************************************************/
1155
1156/**
1157 * Initializing mixing for a stream.
1158 *
1159 * This can be used as a do-nothing wrapper for the stack.
1160 *
1161 * @returns VBox status code.
1162 * @param pMix The mixing state.
1163 * @param pStream The stream to mix to/from.
1164 * @param pProps The mixer properties. Pass NULL for no mixing, just
1165 * wrap the driver stack functionality.
1166 * @param cMsBuffer The buffer size.
1167 */
1168int AudioTestMixStreamInit(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1169 PCPDMAUDIOPCMPROPS pProps, uint32_t cMsBuffer)
1170{
1171 RT_ZERO(*pMix);
1172
1173 AssertReturn(pDrvStack, VERR_INVALID_PARAMETER);
1174 AssertReturn(pStream, VERR_INVALID_PARAMETER);
1175
1176 pMix->pDrvStack = pDrvStack;
1177 pMix->pStream = pStream;
1178 if (!pProps)
1179 {
1180 pMix->pProps = &pStream->Cfg.Props;
1181 return VINF_SUCCESS;
1182 }
1183
1184 /*
1185 * Okay, we're doing mixing so we need to set up the mixer buffer
1186 * and associated states.
1187 */
1188 pMix->fDoMixing = true;
1189 int rc = AudioMixBufInit(&pMix->MixBuf, "mixer", pProps, PDMAudioPropsMilliToFrames(pProps, cMsBuffer));
1190 if (RT_SUCCESS(rc))
1191 {
1192 pMix->pProps = &pMix->MixBuf.Props;
1193
1194 if (pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1195 {
1196 rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pMix->MixBuf.Props);
1197 if (RT_SUCCESS(rc))
1198 {
1199 rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pStream->Cfg.Props);
1200 if (RT_SUCCESS(rc))
1201 return rc;
1202 }
1203 }
1204 else if (pStream->Cfg.enmDir == PDMAUDIODIR_OUT)
1205 {
1206 rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pMix->MixBuf.Props);
1207 if (RT_SUCCESS(rc))
1208 {
1209 rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pStream->Cfg.Props);
1210 if (RT_SUCCESS(rc))
1211 return rc;
1212 }
1213 }
1214 else
1215 {
1216 RTTestFailed(g_hTest, "Bogus stream direction!");
1217 rc = VERR_INVALID_STATE;
1218 }
1219 }
1220 else
1221 RTTestFailed(g_hTest, "AudioMixBufInit failed: %Rrc", rc);
1222 RT_ZERO(*pMix);
1223 return rc;
1224}
1225
1226
1227/**
1228 * Terminate mixing (leaves the stream untouched).
1229 *
1230 * @param pMix The mixing state.
1231 */
1232void AudioTestMixStreamTerm(PAUDIOTESTDRVMIXSTREAM pMix)
1233{
1234 if (pMix->fDoMixing)
1235 {
1236 AudioMixBufTerm(&pMix->MixBuf);
1237 pMix->pStream = NULL;
1238 }
1239 RT_ZERO(*pMix);
1240}
1241
1242
1243/**
1244 * Worker that transports data between the mixer buffer and the drivers.
1245 *
1246 * @returns VBox status code.
1247 * @param pMix The mixer stream setup to do transfers for.
1248 */
1249static int audioTestMixStreamTransfer(PAUDIOTESTDRVMIXSTREAM pMix)
1250{
1251 uint8_t abBuf[16384];
1252 if (pMix->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1253 {
1254 /*
1255 * Try fill up the mixer buffer as much as possible.
1256 *
1257 * Slight fun part is that we have to calculate conversion
1258 * ratio and be rather pessimistic about it.
1259 */
1260 uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->pStream->Cfg.Props, sizeof(abBuf));
1261 for (;;)
1262 {
1263 /*
1264 * Figure out how much we can move in this iteration.
1265 */
1266 uint32_t cDstFrames = AudioMixBufFree(&pMix->MixBuf);
1267 if (!cDstFrames)
1268 break;
1269
1270 uint32_t cbReadable = audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
1271 if (!cbReadable)
1272 break;
1273
1274 uint32_t cbToRead;
1275 if (PDMAudioPropsHz(&pMix->pStream->Cfg.Props) == PDMAudioPropsHz(&pMix->MixBuf.Props))
1276 cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props, cDstFrames);
1277 else
1278 cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props,
1279 (uint64_t)cDstFrames * PDMAudioPropsHz(&pMix->pStream->Cfg.Props)
1280 / PDMAudioPropsHz(&pMix->MixBuf.Props));
1281 cbToRead = RT_MIN(cbToRead, RT_MIN(cbReadable, cbBuf));
1282 if (!cbToRead)
1283 break;
1284
1285 /*
1286 * Get the data.
1287 */
1288 uint32_t cbCaptured = 0;
1289 int rc = audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, abBuf, cbToRead, &cbCaptured);
1290 if (RT_FAILURE(rc))
1291 return rc;
1292 Assert(cbCaptured == cbToRead);
1293 AssertBreak(cbCaptured > 0);
1294
1295 /*
1296 * Feed it to the mixer.
1297 */
1298 uint32_t cDstFramesWritten = 0;
1299 if ((abBuf[0] >> 4) & 1) /* some cheap random */
1300 AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
1301 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
1302 else
1303 {
1304 AudioMixBufSilence(&pMix->MixBuf, &pMix->WriteState, 0 /*offFrame*/, cDstFrames);
1305 AudioMixBufBlend(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
1306 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
1307 }
1308 AudioMixBufCommit(&pMix->MixBuf, cDstFramesWritten);
1309 }
1310 }
1311 else
1312 {
1313 /*
1314 * The goal here is to empty the mixer buffer by transfering all
1315 * the data to the drivers.
1316 */
1317 uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, sizeof(abBuf));
1318 for (;;)
1319 {
1320 uint32_t cFrames = AudioMixBufUsed(&pMix->MixBuf);
1321 if (!cFrames)
1322 break;
1323
1324 uint32_t cbWritable = audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
1325 if (!cbWritable)
1326 break;
1327
1328 uint32_t cSrcFramesPeeked;
1329 uint32_t cbDstPeeked;
1330 AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cSrcFramesPeeked,
1331 &pMix->PeekState, abBuf, RT_MIN(cbBuf, cbWritable), &cbDstPeeked);
1332 AudioMixBufAdvance(&pMix->MixBuf, cSrcFramesPeeked);
1333
1334 if (!cbDstPeeked)
1335 break;
1336
1337 uint32_t offBuf = 0;
1338 while (offBuf < cbDstPeeked)
1339 {
1340 uint32_t cbPlayed = 0;
1341 int rc = audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream,
1342 &abBuf[offBuf], cbDstPeeked - offBuf, &cbPlayed);
1343 if (RT_FAILURE(rc))
1344 return rc;
1345 if (!cbPlayed)
1346 RTThreadSleep(1);
1347 offBuf += cbPlayed;
1348 }
1349 }
1350 }
1351 return VINF_SUCCESS;
1352}
1353
1354
1355/**
1356 * Same as audioTestDriverStackStreamEnable.
1357 */
1358int AudioTestMixStreamEnable(PAUDIOTESTDRVMIXSTREAM pMix)
1359{
1360 return audioTestDriverStackStreamEnable(pMix->pDrvStack, pMix->pStream);
1361}
1362
1363
1364/**
1365 * Same as audioTestDriverStackStreamDrain.
1366 */
1367int AudioTestMixStreamDrain(PAUDIOTESTDRVMIXSTREAM pMix, bool fSync)
1368{
1369 /*
1370 * If we're mixing, we must first make sure the buffer is empty.
1371 */
1372 if (pMix->fDoMixing)
1373 {
1374 audioTestMixStreamTransfer(pMix);
1375 while (AudioMixBufUsed(&pMix->MixBuf) > 0)
1376 {
1377 RTThreadSleep(1);
1378 audioTestMixStreamTransfer(pMix);
1379 }
1380 }
1381
1382 /*
1383 * Then we do the regular workt.
1384 */
1385 return audioTestDriverStackStreamDrain(pMix->pDrvStack, pMix->pStream, fSync);
1386}
1387
1388/**
1389 * Same as audioTestDriverStackStreamDisable.
1390 */
1391int AudioTestMixStreamDisable(PAUDIOTESTDRVMIXSTREAM pMix)
1392{
1393 return AudioTestDriverStackStreamDisable(pMix->pDrvStack, pMix->pStream);
1394}
1395
1396
1397/**
1398 * Same as audioTestDriverStackStreamIsOkay.
1399 */
1400bool AudioTestMixStreamIsOkay(PAUDIOTESTDRVMIXSTREAM pMix)
1401{
1402 return audioTestDriverStackStreamIsOkay(pMix->pDrvStack, pMix->pStream);
1403}
1404
1405
1406/**
1407 * Same as audioTestDriverStackStreamGetWritable
1408 */
1409uint32_t AudioTestMixStreamGetWritable(PAUDIOTESTDRVMIXSTREAM pMix)
1410{
1411 if (!pMix->fDoMixing)
1412 return audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
1413 uint32_t cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
1414 if (!cbRet)
1415 {
1416 audioTestMixStreamTransfer(pMix);
1417 cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
1418 }
1419 return cbRet;
1420}
1421
1422
1423
1424
1425/**
1426 * Same as audioTestDriverStackStreamPlay.
1427 */
1428int AudioTestMixStreamPlay(PAUDIOTESTDRVMIXSTREAM pMix, void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
1429{
1430 if (!pMix->fDoMixing)
1431 return audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbPlayed);
1432
1433 *pcbPlayed = 0;
1434
1435 int rc = audioTestMixStreamTransfer(pMix);
1436 if (RT_FAILURE(rc))
1437 return rc;
1438
1439 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
1440 while (cbBuf >= cbFrame)
1441 {
1442 uint32_t const cFrames = AudioMixBufFree(&pMix->MixBuf);
1443 if (!cFrames)
1444 break;
1445 uint32_t cbToWrite = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
1446 cbToWrite = RT_MIN(cbToWrite, cbBuf);
1447 cbToWrite = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToWrite);
1448
1449 uint32_t cFramesWritten = 0;
1450 AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, pvBuf, cbToWrite, 0 /*offDstFrame*/, cFrames, &cFramesWritten);
1451 Assert(cFramesWritten == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbToWrite));
1452 AudioMixBufCommit(&pMix->MixBuf, cFramesWritten);
1453
1454 *pcbPlayed += cbToWrite;
1455 cbBuf -= cbToWrite;
1456 pvBuf = (uint8_t const *)pvBuf + cbToWrite;
1457
1458 rc = audioTestMixStreamTransfer(pMix);
1459 if (RT_FAILURE(rc))
1460 return *pcbPlayed ? VINF_SUCCESS : rc;
1461 }
1462
1463 return VINF_SUCCESS;
1464}
1465
1466
1467/**
1468 * Same as audioTestDriverStackStreamGetReadable
1469 */
1470uint32_t AudioTestMixStreamGetReadable(PAUDIOTESTDRVMIXSTREAM pMix)
1471{
1472 if (!pMix->fDoMixing)
1473 return audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
1474
1475 audioTestMixStreamTransfer(pMix);
1476 uint32_t cbRet = AudioMixBufUsedBytes(&pMix->MixBuf);
1477 return cbRet;
1478}
1479
1480
1481
1482
1483/**
1484 * Same as audioTestDriverStackStreamCapture.
1485 */
1486int AudioTestMixStreamCapture(PAUDIOTESTDRVMIXSTREAM pMix, void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
1487{
1488 if (!pMix->fDoMixing)
1489 return audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbCaptured);
1490
1491 *pcbCaptured = 0;
1492
1493 int rc = audioTestMixStreamTransfer(pMix);
1494 if (RT_FAILURE(rc))
1495 return rc;
1496
1497 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
1498 while (cbBuf >= cbFrame)
1499 {
1500 uint32_t const cFrames = AudioMixBufUsed(&pMix->MixBuf);
1501 if (!cFrames)
1502 break;
1503 uint32_t cbToRead = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
1504 cbToRead = RT_MIN(cbToRead, cbBuf);
1505 cbToRead = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToRead);
1506
1507 uint32_t cFramesPeeked = 0;
1508 uint32_t cbPeeked = 0;
1509 AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cFramesPeeked, &pMix->PeekState, pvBuf, cbToRead, &cbPeeked);
1510 Assert(cFramesPeeked == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbPeeked));
1511 AudioMixBufAdvance(&pMix->MixBuf, cFramesPeeked);
1512
1513 *pcbCaptured += cbToRead;
1514 cbBuf -= cbToRead;
1515 pvBuf = (uint8_t *)pvBuf + cbToRead;
1516
1517 rc = audioTestMixStreamTransfer(pMix);
1518 if (RT_FAILURE(rc))
1519 return *pcbCaptured ? VINF_SUCCESS : rc;
1520 }
1521
1522 return VINF_SUCCESS;
1523}
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