VirtualBox

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

Last change on this file since 100240 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.5 KB
Line 
1/* $Id: vkatDriverStack.cpp 99775 2023-05-12 12:21:58Z 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 pDrvReg The backend driver to use.
614 * @param fEnabledIn Whether input is enabled or not on creation time.
615 * @param fEnabledOut Whether output is enabled or not on creation time.
616 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
617 */
618int audioTestDriverStackProbe(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fEnabledIn, bool fEnabledOut, bool fWithDrvAudio)
619{
620 int rc = VERR_IPE_UNINITIALIZED_STATUS; /* Shut up MSVC. */
621
622 for (size_t i = 0; i < g_cBackends; i++)
623 {
624 pDrvReg = g_aBackends[i].pDrvReg;
625 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing for backend '%s' ...\n", g_aBackends[i].pszName);
626
627 rc = audioTestDriverStackInitEx(pDrvStack, pDrvReg, fEnabledIn, fEnabledOut, fWithDrvAudio); /** @todo Make in/out configurable, too. */
628 if (RT_SUCCESS(rc))
629 {
630 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' successful\n", g_aBackends[i].pszName);
631 return rc;
632 }
633
634 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing backend '%s' failed with %Rrc, trying next one\n",
635 g_aBackends[i].pszName, rc);
636 continue;
637 }
638
639 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Probing all backends failed\n");
640 return rc;
641}
642
643/**
644 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
645 */
646int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
647{
648 int rc;
649 if ( pDrvStack->pIHostAudio
650 && pDrvStack->pIHostAudio->pfnSetDevice)
651 rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId);
652 else if (!pszDevId || *pszDevId)
653 rc = VINF_SUCCESS;
654 else
655 rc = VERR_INVALID_FUNCTION;
656 return rc;
657}
658
659
660/**
661 * Common stream creation code.
662 *
663 * @returns VBox status code.
664 * @param pDrvStack The audio driver stack to create it via.
665 * @param pCfgReq The requested config.
666 * @param ppStream Where to return the stream pointer on success.
667 * @param pCfgAcq Where to return the actual (well, not necessarily when
668 * using DrvAudio, but probably the same) stream config on
669 * success (not used as input).
670 */
671static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOSTREAMCFG pCfgReq,
672 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
673{
674 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16];
675 int rc;
676 *ppStream = NULL;
677
678 if (pDrvStack->pIAudioConnector)
679 {
680 /*
681 * DrvAudio does most of the work here.
682 */
683 rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, 0 /*fFlags*/, pCfgReq, ppStream);
684 if (RT_SUCCESS(rc))
685 {
686 *pCfgAcq = (*ppStream)->Cfg;
687 RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp)));
688 return rc;
689 }
690 /* else: Don't set RTTestFailed(...) here, as test boxes (servers) don't have any audio hardware.
691 * Caller has check the rc then. */
692 }
693 else
694 {
695 /*
696 * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM
697 * structure actually is for this backend.
698 */
699 PDMAUDIOBACKENDCFG BackendCfg;
700 rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg);
701 if (RT_SUCCESS(rc))
702 {
703 if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM))
704 {
705 /*
706 * Allocate and initialize the stream.
707 */
708 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream;
709 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream);
710 if (pStreamAt)
711 {
712 pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
713 pStreamAt->Core.Cfg = *pCfgReq;
714 pStreamAt->Core.cbBackend = cbStream;
715
716 pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC;
717 pStreamAt->Backend.pStream = &pStreamAt->Core;
718
719 /*
720 * Call the backend to create the stream.
721 */
722 rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend,
723 pCfgReq, &pStreamAt->Core.Cfg);
724 if (RT_SUCCESS(rc))
725 {
726 if (g_uVerbosity > 1)
727 RTMsgInfo("Created backend stream: %s\n",
728 PDMAudioStrmCfgToString(&pStreamAt->Core.Cfg, szTmp, sizeof(szTmp)));
729
730 /* Return if stream is ready: */
731 if (rc == VINF_SUCCESS)
732 {
733 *ppStream = &pStreamAt->Core;
734 *pCfgAcq = pStreamAt->Core.Cfg;
735 return VINF_SUCCESS;
736 }
737 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
738 {
739 /*
740 * Do async init right here and now.
741 */
742 rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend,
743 false /*fDestroyed*/);
744 if (RT_SUCCESS(rc))
745 {
746 *ppStream = &pStreamAt->Core;
747 *pCfgAcq = pStreamAt->Core.Cfg;
748 return VINF_SUCCESS;
749 }
750
751 RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc);
752 }
753 else
754 {
755 RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc);
756 rc = VERR_IPE_UNEXPECTED_INFO_STATUS;
757 }
758 pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
759 }
760 /* else: Don't set RTTestFailed(...) here, as test boxes (servers) don't have any audio hardware.
761 * Caller has check the rc then. */
762 }
763 else
764 {
765 RTTestFailed(g_hTest, "Out of memory!\n");
766 rc = VERR_NO_MEMORY;
767 }
768 RTMemFree(pStreamAt);
769 }
770 else
771 {
772 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM));
773 rc = VERR_OUT_OF_RANGE;
774 }
775 }
776 else
777 RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc);
778 }
779 return rc;
780}
781
782
783/**
784 * Creates an output stream.
785 *
786 * @returns VBox status code.
787 * @param pDrvStack The audio driver stack to create it via.
788 * @param pProps The audio properties to use.
789 * @param cMsBufferSize The buffer size in milliseconds.
790 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
791 * @param cMsSchedulingHint The scheduling hint in milliseconds.
792 * @param ppStream Where to return the stream pointer on success.
793 * @param pCfgAcq Where to return the actual (well, not
794 * necessarily when using DrvAudio, but probably
795 * the same) stream config on success (not used as
796 * input).
797 */
798int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
799 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
800 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
801{
802 /*
803 * Calculate the stream config.
804 */
805 PDMAUDIOSTREAMCFG CfgReq;
806 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
807 AssertRC(rc);
808 CfgReq.enmDir = PDMAUDIODIR_OUT;
809 CfgReq.enmPath = PDMAUDIOPATH_OUT_FRONT;
810 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
811 ? 10 : cMsSchedulingHint;
812 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
813 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
814 else
815 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
816 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
817 ? 300 : cMsBufferSize);
818 if (cMsPreBuffer == UINT32_MAX)
819 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */
820 : CfgReq.Backend.cFramesBufferSize * 2 / 3;
821 else
822 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
823 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16
824 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
825 {
826 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
827 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
828 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
829 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
830 }
831
832 static uint32_t s_idxStream = 0;
833 uint32_t const idxStream = s_idxStream++;
834 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream);
835
836 /*
837 * Call common code to do the actual work.
838 */
839 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
840}
841
842
843/**
844 * Creates an input stream.
845 *
846 * @returns VBox status code.
847 * @param pDrvStack The audio driver stack to create it via.
848 * @param pProps The audio properties to use.
849 * @param cMsBufferSize The buffer size in milliseconds.
850 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
851 * @param cMsSchedulingHint The scheduling hint in milliseconds.
852 * @param ppStream Where to return the stream pointer on success.
853 * @param pCfgAcq Where to return the actual (well, not
854 * necessarily when using DrvAudio, but probably
855 * the same) stream config on success (not used as
856 * input).
857 */
858int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
859 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
860 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
861{
862 /*
863 * Calculate the stream config.
864 */
865 PDMAUDIOSTREAMCFG CfgReq;
866 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
867 AssertRC(rc);
868 CfgReq.enmDir = PDMAUDIODIR_IN;
869 CfgReq.enmPath = PDMAUDIOPATH_IN_LINE;
870 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
871 ? 10 : cMsSchedulingHint;
872 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
873 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
874 else
875 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
876 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
877 ? 300 : cMsBufferSize);
878 if (cMsPreBuffer == UINT32_MAX)
879 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */
880 : CfgReq.Backend.cFramesBufferSize / 2;
881 else
882 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
883 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */
884 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
885 {
886 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
887 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
888 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
889 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
890 }
891
892 static uint32_t s_idxStream = 0;
893 uint32_t const idxStream = s_idxStream++;
894 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream);
895
896 /*
897 * Call common code to do the actual work.
898 */
899 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
900}
901
902
903/**
904 * Destroys a stream.
905 *
906 * @param pDrvStack Driver stack the stream to destroy is assigned to.
907 * @param pStream Stream to destroy. Pointer will be NULL (invalid) after successful return.
908 */
909void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
910{
911 if (!pStream)
912 return;
913
914 if (pDrvStack->pIAudioConnector)
915 {
916 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IAudioConnector) ...\n", pStream->Cfg.szName);
917 int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/);
918 if (RT_FAILURE(rc))
919 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc);
920 }
921 else
922 {
923 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' (IHostAudio) ...\n", pStream->Cfg.szName);
924 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
925 int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
926 if (RT_SUCCESS(rc))
927 {
928 pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC;
929 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC;
930
931 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Destroying stream '%s' done\n", pStream->Cfg.szName);
932
933 RTMemFree(pStreamAt);
934
935 pStreamAt = NULL;
936 pStream = NULL;
937 }
938 else
939 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc);
940 }
941}
942
943
944/**
945 * Enables a stream.
946 */
947int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
948{
949 int rc;
950 if (pDrvStack->pIAudioConnector)
951 {
952 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE);
953 if (RT_FAILURE(rc))
954 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
955 }
956 else
957 {
958 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
959 rc = pDrvStack->pIHostAudio->pfnStreamEnable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
960 if (RT_FAILURE(rc))
961 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamEnable failed: %Rrc", rc);
962 }
963 return rc;
964}
965
966
967/**
968 * Disables a stream.
969 */
970int AudioTestDriverStackStreamDisable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
971{
972 int rc;
973 if (pDrvStack->pIAudioConnector)
974 {
975 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DISABLE);
976 if (RT_FAILURE(rc))
977 RTTestFailed(g_hTest, "pfnStreamControl/DISABLE failed: %Rrc", rc);
978 }
979 else
980 {
981 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
982 rc = pDrvStack->pIHostAudio->pfnStreamDisable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
983 if (RT_FAILURE(rc))
984 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDisable failed: %Rrc", rc);
985 }
986 return rc;
987}
988
989
990/**
991 * Drains an output stream.
992 */
993int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
994{
995 int rc;
996 if (pDrvStack->pIAudioConnector)
997 {
998 /*
999 * Issue the drain request.
1000 */
1001 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN);
1002 if (RT_SUCCESS(rc) && fSync)
1003 {
1004 /*
1005 * This is a synchronous drain, so wait for the driver to change state to inactive.
1006 */
1007 PDMAUDIOSTREAMSTATE enmState;
1008 while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream))
1009 >= PDMAUDIOSTREAMSTATE_ENABLED)
1010 {
1011 RTThreadSleep(2);
1012 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream);
1013 if (RT_FAILURE(rc))
1014 {
1015 RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc);
1016 break;
1017 }
1018 }
1019 if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE)
1020 {
1021 RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState));
1022 rc = VERR_AUDIO_STREAM_NOT_READY;
1023 }
1024 }
1025 else if (RT_FAILURE(rc))
1026 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
1027 }
1028 else
1029 {
1030 /*
1031 * Issue the drain request.
1032 */
1033 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1034 rc = pDrvStack->pIHostAudio->pfnStreamDrain(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1035 if (RT_SUCCESS(rc) && fSync)
1036 {
1037 RTMSINTERVAL const msTimeout = RT_MS_5MIN; /* 5 minutes should be really enough for draining our stuff. */
1038 uint64_t const tsStart = RTTimeMilliTS();
1039
1040 /*
1041 * This is a synchronous drain, so wait for the driver to change state to inactive.
1042 */
1043 PDMHOSTAUDIOSTREAMSTATE enmHostState;
1044 while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend))
1045 == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
1046 {
1047 RTThreadSleep(2);
1048 uint32_t cbWritten = UINT32_MAX;
1049 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend,
1050 NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten);
1051 if (RT_FAILURE(rc))
1052 {
1053 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc);
1054 break;
1055 }
1056 if (cbWritten != 0)
1057 {
1058 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten);
1059 rc = VERR_MISSING;
1060 break;
1061 }
1062
1063 /* Fail-safe for audio stacks and/or implementations which mess up draining.
1064 *
1065 * Note: On some testboxes draining never seems to finish and thus is getting aborted, no clue why.
1066 * The test result in the end still could be correct, although the actual draining problem
1067 * needs to be investigated further.
1068 *
1069 * So don't make this (and the stream state check below) an error for now and just warn about it.
1070 *
1071 ** @todo Investigate draining issues on testboxes.
1072 */
1073 if (RTTimeMilliTS() - tsStart > msTimeout)
1074 {
1075 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
1076 "Warning: Draining stream took too long (timeout is %RU32ms), giving up", msTimeout);
1077 break;
1078 }
1079 }
1080 if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY)
1081 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
1082 "Warning: Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState));
1083 }
1084 else if (RT_FAILURE(rc))
1085 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
1086 }
1087 return rc;
1088}
1089
1090
1091/**
1092 * Checks if the stream is okay.
1093 * @returns true if okay, false if not.
1094 */
1095bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1096{
1097 /*
1098 * Get the stream status and check if it means is okay or not.
1099 */
1100 bool fRc = false;
1101 if (pDrvStack->pIAudioConnector)
1102 {
1103 PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream);
1104 switch (enmState)
1105 {
1106 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
1107 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
1108 break;
1109 case PDMAUDIOSTREAMSTATE_INACTIVE:
1110 case PDMAUDIOSTREAMSTATE_ENABLED:
1111 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
1112 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
1113 fRc = true;
1114 break;
1115 /* no default */
1116 case PDMAUDIOSTREAMSTATE_INVALID:
1117 case PDMAUDIOSTREAMSTATE_END:
1118 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
1119 break;
1120 }
1121 }
1122 else
1123 {
1124 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1125 PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio,
1126 &pStreamAt->Backend);
1127 switch (enmHostState)
1128 {
1129 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
1130 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
1131 break;
1132 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
1133 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
1134 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
1135 fRc = true;
1136 break;
1137 /* no default */
1138 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
1139 case PDMHOSTAUDIOSTREAMSTATE_END:
1140 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
1141 break;
1142 }
1143 }
1144 return fRc;
1145}
1146
1147
1148/**
1149 * Gets the number of bytes it's currently possible to write to the stream.
1150 */
1151uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1152{
1153 uint32_t cbWritable;
1154 if (pDrvStack->pIAudioConnector)
1155 cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream);
1156 else
1157 {
1158 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1159 cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1160 }
1161 return cbWritable;
1162}
1163
1164
1165/**
1166 * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
1167 */
1168int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1169 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
1170{
1171 int rc;
1172 if (pDrvStack->pIAudioConnector)
1173 {
1174 rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed);
1175 if (RT_FAILURE(rc))
1176 RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
1177 }
1178 else
1179 {
1180 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1181 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed);
1182 if (RT_FAILURE(rc))
1183 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x,) failed: %Rrc", cbBuf, rc);
1184 }
1185 return rc;
1186}
1187
1188
1189/**
1190 * Gets the number of bytes it's currently possible to write to the stream.
1191 */
1192uint32_t audioTestDriverStackStreamGetReadable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1193{
1194 uint32_t cbReadable;
1195 if (pDrvStack->pIAudioConnector)
1196 cbReadable = pDrvStack->pIAudioConnector->pfnStreamGetReadable(pDrvStack->pIAudioConnector, pStream);
1197 else
1198 {
1199 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1200 cbReadable = pDrvStack->pIHostAudio->pfnStreamGetReadable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1201 }
1202 return cbReadable;
1203}
1204
1205
1206/**
1207 * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
1208 */
1209int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1210 void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
1211{
1212 int rc;
1213 if (pDrvStack->pIAudioConnector)
1214 {
1215 rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured);
1216 if (RT_FAILURE(rc))
1217 RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
1218 }
1219 else
1220 {
1221 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1222 rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured);
1223 if (RT_FAILURE(rc))
1224 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x,) failed: %Rrc", cbBuf, rc);
1225 }
1226 return rc;
1227}
1228
1229
1230/*********************************************************************************************************************************
1231* Mixed streams *
1232*********************************************************************************************************************************/
1233
1234/**
1235 * Initializing mixing for a stream.
1236 *
1237 * This can be used as a do-nothing wrapper for the stack.
1238 *
1239 * @returns VBox status code.
1240 * @param pMix The mixing state.
1241 * @param pStream The stream to mix to/from.
1242 * @param pProps The mixer properties. Pass NULL for no mixing, just
1243 * wrap the driver stack functionality.
1244 * @param cMsBuffer The buffer size.
1245 */
1246int AudioTestMixStreamInit(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1247 PCPDMAUDIOPCMPROPS pProps, uint32_t cMsBuffer)
1248{
1249 RT_ZERO(*pMix);
1250
1251 AssertReturn(pDrvStack, VERR_INVALID_PARAMETER);
1252 AssertReturn(pStream, VERR_INVALID_PARAMETER);
1253
1254 pMix->pDrvStack = pDrvStack;
1255 pMix->pStream = pStream;
1256 if (!pProps)
1257 {
1258 pMix->pProps = &pStream->Cfg.Props;
1259 return VINF_SUCCESS;
1260 }
1261
1262 /*
1263 * Okay, we're doing mixing so we need to set up the mixer buffer
1264 * and associated states.
1265 */
1266 pMix->fDoMixing = true;
1267 int rc = AudioMixBufInit(&pMix->MixBuf, "mixer", pProps, PDMAudioPropsMilliToFrames(pProps, cMsBuffer));
1268 if (RT_SUCCESS(rc))
1269 {
1270 pMix->pProps = &pMix->MixBuf.Props;
1271
1272 if (pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1273 {
1274 rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pMix->MixBuf.Props);
1275 if (RT_SUCCESS(rc))
1276 {
1277 rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pStream->Cfg.Props);
1278 if (RT_SUCCESS(rc))
1279 return rc;
1280 }
1281 }
1282 else if (pStream->Cfg.enmDir == PDMAUDIODIR_OUT)
1283 {
1284 rc = AudioMixBufInitWriteState(&pMix->MixBuf, &pMix->WriteState, &pMix->MixBuf.Props);
1285 if (RT_SUCCESS(rc))
1286 {
1287 rc = AudioMixBufInitPeekState(&pMix->MixBuf, &pMix->PeekState, &pStream->Cfg.Props);
1288 if (RT_SUCCESS(rc))
1289 return rc;
1290 }
1291 }
1292 else
1293 {
1294 RTTestFailed(g_hTest, "Bogus stream direction!");
1295 rc = VERR_INVALID_STATE;
1296 }
1297 }
1298 else
1299 RTTestFailed(g_hTest, "AudioMixBufInit failed: %Rrc", rc);
1300 RT_ZERO(*pMix);
1301 return rc;
1302}
1303
1304
1305/**
1306 * Terminate mixing (leaves the stream untouched).
1307 *
1308 * @param pMix The mixing state.
1309 */
1310void AudioTestMixStreamTerm(PAUDIOTESTDRVMIXSTREAM pMix)
1311{
1312 if (pMix->fDoMixing)
1313 {
1314 AudioMixBufTerm(&pMix->MixBuf);
1315 pMix->pStream = NULL;
1316 }
1317 RT_ZERO(*pMix);
1318}
1319
1320
1321/**
1322 * Worker that transports data between the mixer buffer and the drivers.
1323 *
1324 * @returns VBox status code.
1325 * @param pMix The mixer stream setup to do transfers for.
1326 */
1327static int audioTestMixStreamTransfer(PAUDIOTESTDRVMIXSTREAM pMix)
1328{
1329 uint8_t abBuf[16384];
1330 if (pMix->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
1331 {
1332 /*
1333 * Try fill up the mixer buffer as much as possible.
1334 *
1335 * Slight fun part is that we have to calculate conversion
1336 * ratio and be rather pessimistic about it.
1337 */
1338 uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->pStream->Cfg.Props, sizeof(abBuf));
1339 for (;;)
1340 {
1341 /*
1342 * Figure out how much we can move in this iteration.
1343 */
1344 uint32_t cDstFrames = AudioMixBufFree(&pMix->MixBuf);
1345 if (!cDstFrames)
1346 break;
1347
1348 uint32_t cbReadable = audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
1349 if (!cbReadable)
1350 break;
1351
1352 uint32_t cbToRead;
1353 if (PDMAudioPropsHz(&pMix->pStream->Cfg.Props) == PDMAudioPropsHz(&pMix->MixBuf.Props))
1354 cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props, cDstFrames);
1355 else
1356 cbToRead = PDMAudioPropsFramesToBytes(&pMix->pStream->Cfg.Props,
1357 (uint64_t)cDstFrames * PDMAudioPropsHz(&pMix->pStream->Cfg.Props)
1358 / PDMAudioPropsHz(&pMix->MixBuf.Props));
1359 cbToRead = RT_MIN(cbToRead, RT_MIN(cbReadable, cbBuf));
1360 if (!cbToRead)
1361 break;
1362
1363 /*
1364 * Get the data.
1365 */
1366 uint32_t cbCaptured = 0;
1367 int rc = audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, abBuf, cbToRead, &cbCaptured);
1368 if (RT_FAILURE(rc))
1369 return rc;
1370 Assert(cbCaptured == cbToRead);
1371 AssertBreak(cbCaptured > 0);
1372
1373 /*
1374 * Feed it to the mixer.
1375 */
1376 uint32_t cDstFramesWritten = 0;
1377 if ((abBuf[0] >> 4) & 1) /* some cheap random */
1378 AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
1379 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
1380 else
1381 {
1382 AudioMixBufSilence(&pMix->MixBuf, &pMix->WriteState, 0 /*offFrame*/, cDstFrames);
1383 AudioMixBufBlend(&pMix->MixBuf, &pMix->WriteState, abBuf, cbCaptured,
1384 0 /*offDstFrame*/, cDstFrames, &cDstFramesWritten);
1385 }
1386 AudioMixBufCommit(&pMix->MixBuf, cDstFramesWritten);
1387 }
1388 }
1389 else
1390 {
1391 /*
1392 * The goal here is to empty the mixer buffer by transfering all
1393 * the data to the drivers.
1394 */
1395 uint32_t const cbBuf = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, sizeof(abBuf));
1396 for (;;)
1397 {
1398 uint32_t cFrames = AudioMixBufUsed(&pMix->MixBuf);
1399 if (!cFrames)
1400 break;
1401
1402 uint32_t cbWritable = audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
1403 if (!cbWritable)
1404 break;
1405
1406 uint32_t cSrcFramesPeeked;
1407 uint32_t cbDstPeeked;
1408 AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cSrcFramesPeeked,
1409 &pMix->PeekState, abBuf, RT_MIN(cbBuf, cbWritable), &cbDstPeeked);
1410 AudioMixBufAdvance(&pMix->MixBuf, cSrcFramesPeeked);
1411
1412 if (!cbDstPeeked)
1413 break;
1414
1415 uint32_t offBuf = 0;
1416 while (offBuf < cbDstPeeked)
1417 {
1418 uint32_t cbPlayed = 0;
1419 int rc = audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream,
1420 &abBuf[offBuf], cbDstPeeked - offBuf, &cbPlayed);
1421 if (RT_FAILURE(rc))
1422 return rc;
1423 if (!cbPlayed)
1424 RTThreadSleep(1);
1425 offBuf += cbPlayed;
1426 }
1427 }
1428 }
1429 return VINF_SUCCESS;
1430}
1431
1432
1433/**
1434 * Same as audioTestDriverStackStreamEnable.
1435 */
1436int AudioTestMixStreamEnable(PAUDIOTESTDRVMIXSTREAM pMix)
1437{
1438 return audioTestDriverStackStreamEnable(pMix->pDrvStack, pMix->pStream);
1439}
1440
1441
1442/**
1443 * Same as audioTestDriverStackStreamDrain.
1444 */
1445int AudioTestMixStreamDrain(PAUDIOTESTDRVMIXSTREAM pMix, bool fSync)
1446{
1447 /*
1448 * If we're mixing, we must first make sure the buffer is empty.
1449 */
1450 if (pMix->fDoMixing)
1451 {
1452 audioTestMixStreamTransfer(pMix);
1453 while (AudioMixBufUsed(&pMix->MixBuf) > 0)
1454 {
1455 RTThreadSleep(1);
1456 audioTestMixStreamTransfer(pMix);
1457 }
1458 }
1459
1460 /*
1461 * Then we do the regular work.
1462 */
1463 return audioTestDriverStackStreamDrain(pMix->pDrvStack, pMix->pStream, fSync);
1464}
1465
1466/**
1467 * Same as audioTestDriverStackStreamDisable.
1468 */
1469int AudioTestMixStreamDisable(PAUDIOTESTDRVMIXSTREAM pMix)
1470{
1471 return AudioTestDriverStackStreamDisable(pMix->pDrvStack, pMix->pStream);
1472}
1473
1474
1475/**
1476 * Same as audioTestDriverStackStreamIsOkay.
1477 */
1478bool AudioTestMixStreamIsOkay(PAUDIOTESTDRVMIXSTREAM pMix)
1479{
1480 return audioTestDriverStackStreamIsOkay(pMix->pDrvStack, pMix->pStream);
1481}
1482
1483
1484/**
1485 * Same as audioTestDriverStackStreamGetWritable
1486 */
1487uint32_t AudioTestMixStreamGetWritable(PAUDIOTESTDRVMIXSTREAM pMix)
1488{
1489 if (!pMix->fDoMixing)
1490 return audioTestDriverStackStreamGetWritable(pMix->pDrvStack, pMix->pStream);
1491 uint32_t cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
1492 if (!cbRet)
1493 {
1494 audioTestMixStreamTransfer(pMix);
1495 cbRet = AudioMixBufFreeBytes(&pMix->MixBuf);
1496 }
1497 return cbRet;
1498}
1499
1500
1501
1502
1503/**
1504 * Same as audioTestDriverStackStreamPlay.
1505 */
1506int AudioTestMixStreamPlay(PAUDIOTESTDRVMIXSTREAM pMix, void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
1507{
1508 if (!pMix->fDoMixing)
1509 return audioTestDriverStackStreamPlay(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbPlayed);
1510
1511 *pcbPlayed = 0;
1512
1513 int rc = audioTestMixStreamTransfer(pMix);
1514 if (RT_FAILURE(rc))
1515 return rc;
1516
1517 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
1518 while (cbBuf >= cbFrame)
1519 {
1520 uint32_t const cFrames = AudioMixBufFree(&pMix->MixBuf);
1521 if (!cFrames)
1522 break;
1523 uint32_t cbToWrite = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
1524 cbToWrite = RT_MIN(cbToWrite, cbBuf);
1525 cbToWrite = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToWrite);
1526
1527 uint32_t cFramesWritten = 0;
1528 AudioMixBufWrite(&pMix->MixBuf, &pMix->WriteState, pvBuf, cbToWrite, 0 /*offDstFrame*/, cFrames, &cFramesWritten);
1529 Assert(cFramesWritten == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbToWrite));
1530 AudioMixBufCommit(&pMix->MixBuf, cFramesWritten);
1531
1532 *pcbPlayed += cbToWrite;
1533 cbBuf -= cbToWrite;
1534 pvBuf = (uint8_t const *)pvBuf + cbToWrite;
1535
1536 rc = audioTestMixStreamTransfer(pMix);
1537 if (RT_FAILURE(rc))
1538 return *pcbPlayed ? VINF_SUCCESS : rc;
1539 }
1540
1541 return VINF_SUCCESS;
1542}
1543
1544
1545/**
1546 * Same as audioTestDriverStackStreamGetReadable
1547 */
1548uint32_t AudioTestMixStreamGetReadable(PAUDIOTESTDRVMIXSTREAM pMix)
1549{
1550 if (!pMix->fDoMixing)
1551 return audioTestDriverStackStreamGetReadable(pMix->pDrvStack, pMix->pStream);
1552
1553 audioTestMixStreamTransfer(pMix);
1554 uint32_t cbRet = AudioMixBufUsedBytes(&pMix->MixBuf);
1555 return cbRet;
1556}
1557
1558
1559
1560
1561/**
1562 * Same as audioTestDriverStackStreamCapture.
1563 */
1564int AudioTestMixStreamCapture(PAUDIOTESTDRVMIXSTREAM pMix, void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
1565{
1566 if (!pMix->fDoMixing)
1567 return audioTestDriverStackStreamCapture(pMix->pDrvStack, pMix->pStream, pvBuf, cbBuf, pcbCaptured);
1568
1569 *pcbCaptured = 0;
1570
1571 int rc = audioTestMixStreamTransfer(pMix);
1572 if (RT_FAILURE(rc))
1573 return rc;
1574
1575 uint32_t const cbFrame = PDMAudioPropsFrameSize(&pMix->MixBuf.Props);
1576 while (cbBuf >= cbFrame)
1577 {
1578 uint32_t const cFrames = AudioMixBufUsed(&pMix->MixBuf);
1579 if (!cFrames)
1580 break;
1581 uint32_t cbToRead = PDMAudioPropsFramesToBytes(&pMix->MixBuf.Props, cFrames);
1582 cbToRead = RT_MIN(cbToRead, cbBuf);
1583 cbToRead = PDMAudioPropsFloorBytesToFrame(&pMix->MixBuf.Props, cbToRead);
1584
1585 uint32_t cFramesPeeked = 0;
1586 uint32_t cbPeeked = 0;
1587 AudioMixBufPeek(&pMix->MixBuf, 0 /*offSrcFrame*/, cFrames, &cFramesPeeked, &pMix->PeekState, pvBuf, cbToRead, &cbPeeked);
1588 Assert(cFramesPeeked == PDMAudioPropsBytesToFrames(&pMix->MixBuf.Props, cbPeeked));
1589 AudioMixBufAdvance(&pMix->MixBuf, cFramesPeeked);
1590
1591 *pcbCaptured += cbToRead;
1592 cbBuf -= cbToRead;
1593 pvBuf = (uint8_t *)pvBuf + cbToRead;
1594
1595 rc = audioTestMixStreamTransfer(pMix);
1596 if (RT_FAILURE(rc))
1597 return *pcbCaptured ? VINF_SUCCESS : rc;
1598 }
1599
1600 return VINF_SUCCESS;
1601}
1602
1603/**
1604 * Sets the volume of a mixing stream.
1605 *
1606 * @param pMix Mixing stream to set volume for.
1607 * @param uVolumePercent Volume to set (in percent, 0-100).
1608 */
1609void AudioTestMixStreamSetVolume(PAUDIOTESTDRVMIXSTREAM pMix, uint8_t uVolumePercent)
1610{
1611 AssertReturnVoid(pMix->fDoMixing);
1612
1613 uint8_t const uVol = (PDMAUDIO_VOLUME_MAX / 100) * uVolumePercent;
1614
1615 PDMAUDIOVOLUME Vol;
1616 RT_ZERO(Vol);
1617 for (size_t i = 0; i < RT_ELEMENTS(Vol.auChannels); i++)
1618 Vol.auChannels[i] = uVol;
1619 AudioMixBufSetVolume(&pMix->MixBuf, &Vol);
1620}
1621
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