VirtualBox

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

Last change on this file since 99568 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.4 KB
Line 
1/* $Id: vkatDriverStack.cpp 98103 2023-01-17 14:15:46Z 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
76VMMR3DECL(PCFGMNODE) audioTestDrvHlp_CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath)
77{
78 RT_NOREF(pNode, pszPath);
79 return NULL;
80}
81
82
83VMMR3DECL(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
107VMMR3DECL(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
118VMMR3DECL(void) audioTestDrvHlp_MMR3HeapFree(PPDMDRVINS pDrvIns, void *pv)
119{
120 RT_NOREF(pDrvIns);
121
122 /* counterpart to CFGMR3QueryStringAlloc */
123 RTStrFree((char *)pv);
124}
125
126
127VMMR3DECL(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
152VMMR3DECL(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
171VMMR3DECL(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
178VMMR3DECL(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
185VMMR3DECL(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
323DECLCALLBACK(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