VirtualBox

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

Last change on this file since 89526 was 89511, checked in by vboxsync, 4 years ago

ValKit/AudioTest: Adapted to PDMIHOSTAUDIO changes. bugref:10008

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette