VirtualBox

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

Last change on this file since 103352 was 103352, checked in by vboxsync, 10 months ago

Audio/VKAT: Skip probing the same backend again when an alias for it exists.

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