VirtualBox

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

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

scm --update-copyright-year

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