VirtualBox

Changeset 89401 in vbox for trunk/src/VBox


Ignore:
Timestamp:
May 31, 2021 1:17:26 PM (4 years ago)
Author:
vboxsync
Message:

Audio/ValKit: Moved the VKAT driver stack code to an own file; main file got too big already. bugref:10008

Location:
trunk/src/VBox/ValidationKit/utils/audio
Files:
1 added
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp

    r89399 r89401  
    7272#include "../../../Devices/Audio/AudioTestService.h"
    7373#include "../../../Devices/Audio/AudioTestServiceClient.h"
     74#include "vkatInternal.h"
    7475#include "VBoxDD.h"
    7576
     
    153154
    154155/**
    155  * Audio driver stack.
    156  *
    157  * This can be just be backend driver alone or DrvAudio with a backend.
    158  * @todo add automatic resampling via mixer so we can test more of the audio
    159  *       stack used by the device emulations.
    160  */
    161 typedef struct AUDIOTESTDRVSTACK
    162 {
    163     /** The device registration record for the backend. */
    164     PCPDMDRVREG             pDrvReg;
    165     /** The backend driver instance. */
    166     PPDMDRVINS              pDrvBackendIns;
    167     /** The backend's audio interface. */
    168     PPDMIHOSTAUDIO          pIHostAudio;
    169 
    170     /** The DrvAudio instance. */
    171     PPDMDRVINS              pDrvAudioIns;
    172     /** This is NULL if we don't use DrvAudio. */
    173     PPDMIAUDIOCONNECTOR     pIAudioConnector;
    174 } AUDIOTESTDRVSTACK;
    175 /** Pointer to an audio driver stack. */
    176 typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK;
    177 
    178 /**
    179  * Backend-only stream structure.
    180  */
    181 typedef struct AUDIOTESTDRVSTACKSTREAM
    182 {
    183     /** The public stream data. */
    184     PDMAUDIOSTREAM          Core;
    185     /** The acquired config. */
    186     PDMAUDIOSTREAMCFG       Cfg;
    187     /** The backend data (variable size). */
    188     PDMAUDIOBACKENDSTREAM   Backend;
    189 } AUDIOTESTDRVSTACKSTREAM;
    190 /** Pointer to a backend-only stream structure. */
    191 typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM;
    192 
    193 /** Maximum audio streams a test environment can handle. */
    194 #define AUDIOTESTENV_MAX_STREAMS 8
    195 
    196 /**
    197156 * Structure for keeping an audio test audio stream.
    198157 */
     
    276235static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms);
    277236static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
    278 
    279 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg,
    280                                  PPDMDRVINS pParentDrvIns, PPPDMDRVINS ppDrvIns);
    281237
    282238static RTEXITCODE audioTestUsage(PRTSTREAM pStrm);
     
    420376
    421377static volatile bool g_fTerminated = false;
    422 /** The test handle. */
    423 static RTTEST        g_hTest;
    424378/** The release logger. */
    425379static PRTLOGGER    g_pRelLogger = NULL;
     380
     381
     382/** The test handle. */
     383RTTEST        g_hTest;
    426384/** The current verbosity level. */
    427 static unsigned      g_uVerbosity = 0;
     385unsigned      g_uVerbosity = 0;
    428386/** DrvAudio: Enable debug (or not). */
    429 static bool          g_fDrvAudioDebug = 0;
     387bool          g_fDrvAudioDebug = 0;
    430388/** DrvAudio: The debug output path. */
    431 static const char   *g_pszDrvAudioDebug = NULL;
    432 
    433 
    434 /*********************************************************************************************************************************
    435 *   Fake PDM Driver Handling.                                                                                                    *
    436 *********************************************************************************************************************************/
    437 
    438 /** @name Driver Fakes/Stubs
    439  *
    440  * @note The VMM functions defined here will turn into driver helpers before
    441  *       long, as the drivers aren't supposed to import directly from the VMM in
    442  *       the future.
    443  *
    444  * @{  */
    445 
    446 VMMR3DECL(PCFGMNODE) CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath)
    447 {
    448     RT_NOREF(pNode, pszPath);
    449     return NULL;
    450 }
    451 
    452 VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString)
    453 {
    454     if (pNode != NULL)
    455     {
    456         PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
    457         if (g_uVerbosity > 2)
    458             RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString);
    459 
    460         if (   (   strcmp(pDrvReg->szName, "PulseAudio") == 0
    461                 || strcmp(pDrvReg->szName, "HostAudioWas") == 0)
    462             && strcmp(pszName, "VmName") == 0)
    463             return RTStrCopy(pszString, cchString, "vkat");
    464 
    465         if (   strcmp(pDrvReg->szName, "HostAudioWas") == 0
    466             && strcmp(pszName, "VmUuid") == 0)
    467             return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e");
    468     }
    469     else if (g_uVerbosity > 2)
    470         RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString);
    471 
    472     return VERR_CFGM_VALUE_NOT_FOUND;
    473 }
    474 
    475 VMMR3DECL(int) CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString)
    476 {
    477     char szStr[128];
    478     int rc = CFGMR3QueryString(pNode, pszName, szStr, sizeof(szStr));
    479     if (RT_SUCCESS(rc))
    480         *ppszString = RTStrDup(szStr);
    481 
    482     return rc;
    483 }
    484 
    485 VMMR3DECL(void) MMR3HeapFree(void *pv)
    486 {
    487     /* counterpart to CFGMR3QueryStringAlloc */
    488     RTStrFree((char *)pv);
    489 }
    490 
    491 VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
    492 {
    493     PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
    494     if (RT_VALID_PTR(pDrvReg))
    495     {
    496         const char *pszRet = pszDef;
    497         if (   g_pszDrvAudioDebug
    498             && strcmp(pDrvReg->szName, "AUDIO") == 0
    499             && strcmp(pszName, "DebugPathOut") == 0)
    500             pszRet = g_pszDrvAudioDebug;
    501 
    502         int rc = RTStrCopy(pszString, cchString, pszRet);
    503 
    504         if (g_uVerbosity > 2)
    505             RTPrintf("debug: CFGMR3QueryStringDef([%s], %s, %p, %#x, %s) -> '%s' + %Rrc\n",
    506                      pDrvReg->szName, pszName, pszString, cchString, pszDef, pszRet, rc);
    507         return rc;
    508     }
    509 
    510     if (g_uVerbosity > 2)
    511         RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef);
    512     return RTStrCopy(pszString, cchString, pszDef);
    513 }
    514 
    515 VMMR3DECL(int) CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef)
    516 {
    517     PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
    518     if (RT_VALID_PTR(pDrvReg))
    519     {
    520         *pf = fDef;
    521         if (   strcmp(pDrvReg->szName, "AUDIO") == 0
    522             && strcmp(pszName, "DebugEnabled") == 0)
    523             *pf = g_fDrvAudioDebug;
    524 
    525         if (g_uVerbosity > 2)
    526             RTPrintf("debug: CFGMR3QueryBoolDef([%s], %s, %p, %RTbool) -> %RTbool\n", pDrvReg->szName, pszName, pf, fDef, *pf);
    527         return VINF_SUCCESS;
    528     }
    529     *pf = fDef;
    530     return VINF_SUCCESS;
    531 }
    532 
    533 VMMR3DECL(int) CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8)
    534 {
    535     RT_NOREF(pNode, pszName, pu8);
    536     return VERR_CFGM_VALUE_NOT_FOUND;
    537 }
    538 
    539 VMMR3DECL(int) CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32)
    540 {
    541     RT_NOREF(pNode, pszName, pu32);
    542     return VERR_CFGM_VALUE_NOT_FOUND;
    543 }
    544 
    545 VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode,
    546                                     const char *pszValidValues, const char *pszValidNodes,
    547                                     const char *pszWho, uint32_t uInstance)
    548 {
    549     RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance);
    550     return VINF_SUCCESS;
    551 }
    552 
    553 /** @} */
    554 
    555 /** @name Driver Helper Fakes
    556  * @{ */
    557 
    558 static DECLCALLBACK(int) audioTestDrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface)
    559 {
    560     /* DrvAudio must be allowed to attach the backend driver (paranoid
    561        backend drivers may call us to check that nothing is attached). */
    562     if (strcmp(pDrvIns->pReg->szName, "AUDIO") == 0)
    563     {
    564         PAUDIOTESTDRVSTACK pDrvStack = pDrvIns->Internal.s.pStack;
    565         AssertReturn(pDrvStack->pDrvBackendIns == NULL, VERR_PDM_DRIVER_ALREADY_ATTACHED);
    566 
    567         if (g_uVerbosity > 1)
    568             RTMsgInfo("Attaching backend '%s' to DrvAudio...\n", pDrvStack->pDrvReg->szName);
    569         int rc = audioTestDrvConstruct(pDrvStack, pDrvStack->pDrvReg, pDrvIns, &pDrvStack->pDrvBackendIns);
    570         if (RT_SUCCESS(rc))
    571         {
    572             if (ppBaseInterface)
    573                 *ppBaseInterface = &pDrvStack->pDrvBackendIns->IBase;
    574         }
    575         else
    576             RTMsgError("Failed to attach backend: %Rrc", rc);
    577         return rc;
    578     }
    579     RT_NOREF(fFlags);
    580     return VERR_PDM_NO_ATTACHED_DRIVER;
    581 }
    582 
    583 static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
    584                                                         STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
    585                                                         const char *pszName, ...)
    586 {
    587     RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName);
    588 }
    589 
    590 static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
    591                                                         STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
    592                                                         const char *pszName, va_list args)
    593 {
    594     RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
    595 }
    596 
    597 static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample)
    598 {
    599     RT_NOREF(pDrvIns, pvSample);
    600     return VINF_SUCCESS;
    601 }
    602 
    603 static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix)
    604 {
    605     RT_NOREF(pDrvIns, pszPrefix);
    606     return VINF_SUCCESS;
    607 }
    608 
    609 /**
    610  * Get the driver helpers.
    611  */
    612 static const PDMDRVHLPR3 *audioTestFakeGetDrvHlp(void)
    613 {
    614     /*
    615      * Note! No initializer for s_DrvHlp (also why it's not a file global).
    616      *       We do not want to have to update this code every time PDMDRVHLPR3
    617      *       grows new entries or are otherwise modified.  Only when the
    618      *       entries used by the audio driver changes do we want to change
    619      *       our code.
    620      */
    621     static PDMDRVHLPR3 s_DrvHlp;
    622     if (s_DrvHlp.u32Version != PDM_DRVHLPR3_VERSION)
    623     {
    624         s_DrvHlp.u32Version                     = PDM_DRVHLPR3_VERSION;
    625         s_DrvHlp.u32TheEnd                      = PDM_DRVHLPR3_VERSION;
    626         s_DrvHlp.pfnAttach                      = audioTestDrvHlp_Attach;
    627         s_DrvHlp.pfnSTAMRegisterF               = audioTestDrvHlp_STAMRegisterF;
    628         s_DrvHlp.pfnSTAMRegisterV               = audioTestDrvHlp_STAMRegisterV;
    629         s_DrvHlp.pfnSTAMDeregister              = audioTestDrvHlp_STAMDeregister;
    630         s_DrvHlp.pfnSTAMDeregisterByPrefix      = audioTestDrvHlp_STAMDeregisterByPrefix;
    631     }
    632     return &s_DrvHlp;
    633 }
    634 
    635 /** @} */
    636 
    637 
    638 /**
    639  * Implementation of PDMIBASE::pfnQueryInterface for a fake device above
    640  * DrvAudio.
    641  */
    642 static DECLCALLBACK(void *) audioTestFakeDeviceIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
    643 {
    644     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
    645     RTMsgWarning("audioTestFakeDeviceIBaseQueryInterface: Unknown interface: %s\n", pszIID);
    646     return NULL;
    647 }
    648 
    649 /** IBase interface for a fake device above DrvAudio. */
    650 static PDMIBASE g_AudioTestFakeDeviceIBase =  { audioTestFakeDeviceIBaseQueryInterface };
    651 
    652 
    653 static DECLCALLBACK(int) audioTestIHostAudioPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream,
    654                                                                   uintptr_t uUser, void *pvUser)
    655 {
    656     RT_NOREF(pInterface, pStream, uUser, pvUser);
    657     RTMsgWarning("audioTestIHostAudioPort_DoOnWorkerThread was called\n");
    658     return VERR_NOT_IMPLEMENTED;
    659 }
    660 
    661 DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser)
    662 {
    663     RT_NOREF(pInterface, enmDir, pvUser);
    664     RTMsgWarning("audioTestIHostAudioPort_NotifyDeviceChanged was called\n");
    665 }
    666 
    667 static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface,
    668                                                                                     PPDMAUDIOBACKENDSTREAM pStream)
    669 {
    670     RT_NOREF(pInterface, pStream);
    671     RTMsgWarning("audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch was called\n");
    672 }
    673 
    674 static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface,
    675                                                                             PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
    676 {
    677     RT_NOREF(pInterface, pStream, fReInit);
    678     RTMsgWarning("audioTestIHostAudioPort_StreamNotifyDeviceChanged was called\n");
    679 }
    680 
    681 static DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface)
    682 {
    683     RT_NOREF(pInterface);
    684     RTMsgWarning("audioTestIHostAudioPort_NotifyDevicesChanged was called\n");
    685 }
    686 
    687 static PDMIHOSTAUDIOPORT g_AudioTestIHostAudioPort =
    688 {
    689     audioTestIHostAudioPort_DoOnWorkerThread,
    690     audioTestIHostAudioPort_NotifyDeviceChanged,
    691     audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch,
    692     audioTestIHostAudioPort_StreamNotifyDeviceChanged,
    693     audioTestIHostAudioPort_NotifyDevicesChanged,
    694 };
    695 
    696 /**
    697  * Implementation of PDMIBASE::pfnQueryInterface for a fake DrvAudio above a
    698  * backend.
    699  */
    700 static DECLCALLBACK(void *) audioTestFakeDrvAudioIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
    701 {
    702     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
    703     PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &g_AudioTestIHostAudioPort);
    704     RTMsgWarning("audioTestFakeDrvAudioIBaseQueryInterface: Unknown interface: %s\n", pszIID);
    705     return NULL;
    706 }
    707 
    708 /** IBase interface for a fake DrvAudio above a lonesome backend. */
    709 static PDMIBASE g_AudioTestFakeDrvAudioIBase =  { audioTestFakeDrvAudioIBaseQueryInterface };
    710 
    711 
    712 
    713 /**
    714  * Constructs a PDM audio driver instance.
    715  *
    716  * @returns VBox status code.
    717  * @param   pDrvStack       The stack this is associated with.
    718  * @param   pDrvReg         PDM driver registration record to use for construction.
    719  * @param   pParentDrvIns   The parent driver (if any).
    720  * @param   ppDrvIns        Where to return the driver instance structure.
    721  */
    722 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
    723                                  PPPDMDRVINS ppDrvIns)
    724 {
    725     /* The destruct function must have valid data to work with. */
    726     *ppDrvIns   = NULL;
    727 
    728     /*
    729      * Check registration structure validation (doesn't need to be too
    730      * thorough, PDM check it in detail on every VM startup).
    731      */
    732     AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER);
    733     RTMsgInfo("Initializing backend '%s' ...\n", pDrvReg->szName);
    734     AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER);
    735 
    736     /*
    737      * Create the instance data structure.
    738      */
    739     PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance]));
    740     RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY);
    741 
    742     pDrvIns->u32Version         = PDM_DRVINS_VERSION;
    743     pDrvIns->iInstance          = 0;
    744     pDrvIns->pHlpR3             = audioTestFakeGetDrvHlp();
    745     pDrvIns->pvInstanceDataR3   = &pDrvIns->achInstanceData[0];
    746     pDrvIns->pReg               = pDrvReg;
    747     pDrvIns->pCfg               = (PCFGMNODE)pDrvReg;
    748     pDrvIns->Internal.s.pStack  = pDrvStack;
    749     pDrvIns->pUpBase            = NULL;
    750     pDrvIns->pDownBase          = NULL;
    751     if (pParentDrvIns)
    752     {
    753         Assert(pParentDrvIns->pDownBase == NULL);
    754         pParentDrvIns->pDownBase = &pDrvIns->IBase;
    755         pDrvIns->pUpBase         = &pParentDrvIns->IBase;
    756     }
    757     else if (strcmp(pDrvReg->szName, "AUDIO") == 0)
    758         pDrvIns->pUpBase         = &g_AudioTestFakeDeviceIBase;
    759     else
    760         pDrvIns->pUpBase         = &g_AudioTestFakeDrvAudioIBase;
    761 
    762     /*
    763      * Invoke the constructor.
    764      */
    765     int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/);
    766     if (RT_SUCCESS(rc))
    767     {
    768         *ppDrvIns   = pDrvIns;
    769         return VINF_SUCCESS;
    770     }
    771 
    772     RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc);
    773     if (pDrvReg->pfnDestruct)
    774         pDrvReg->pfnDestruct(pDrvIns);
    775     RTMemFree(pDrvIns);
    776     return rc;
    777 }
    778 
    779 /**
    780  * Destructs a PDM audio driver instance.
    781  *
    782  * @param   pDrvIns             Driver instance to destruct.
    783  */
    784 static void audioTestDrvDestruct(PPDMDRVINS pDrvIns)
    785 {
    786     if (pDrvIns)
    787     {
    788         Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
    789 
    790         if (pDrvIns->pReg->pfnDestruct)
    791             pDrvIns->pReg->pfnDestruct(pDrvIns);
    792 
    793         pDrvIns->u32Version = 0;
    794         pDrvIns->pReg = NULL;
    795         RTMemFree(pDrvIns);
    796     }
    797 }
    798 
    799 /**
    800  * Sends the PDM driver a power off notification.
    801  *
    802  * @param   pDrvIns             Driver instance to notify.
    803  */
    804 static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns)
    805 {
    806     if (pDrvIns)
    807     {
    808         Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
    809         if (pDrvIns->pReg->pfnPowerOff)
    810             pDrvIns->pReg->pfnPowerOff(pDrvIns);
    811     }
    812 }
    813 
    814 /**
    815  * Deletes a driver stack.
    816  *
    817  * This will power off and destroy the drivers.
    818  */
    819 static void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
    820 {
    821     /*
    822      * Do power off notifications (top to bottom).
    823      */
    824     audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns);
    825     audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns);
    826 
    827     /*
    828      * Drivers are destroyed from bottom to top (closest to the device).
    829      */
    830     audioTestDrvDestruct(pDrvStack->pDrvBackendIns);
    831     pDrvStack->pDrvBackendIns   = NULL;
    832     pDrvStack->pIHostAudio      = NULL;
    833 
    834     audioTestDrvDestruct(pDrvStack->pDrvAudioIns);
    835     pDrvStack->pDrvAudioIns     = NULL;
    836     pDrvStack->pIAudioConnector = NULL;
    837 }
    838 
    839 /**
    840  * Initializes a driver stack.
    841  *
    842  * @returns VBox status code.
    843  * @param   pDrvStack       The driver stack to initialize.
    844  * @param   pDrvReg         The backend driver to use.
    845  * @param   fWithDrvAudio   Whether to include DrvAudio in the stack or not.
    846  */
    847 static int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
    848 {
    849     RT_ZERO(*pDrvStack);
    850     pDrvStack->pDrvReg = pDrvReg;
    851 
    852     int rc;
    853     if (!fWithDrvAudio)
    854         rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns);
    855     else
    856     {
    857         rc = audioTestDrvConstruct(pDrvStack, &g_DrvAUDIO, NULL /*pParentDrvIns*/, &pDrvStack->pDrvAudioIns);
    858         if (RT_SUCCESS(rc))
    859         {
    860             Assert(pDrvStack->pDrvAudioIns);
    861             PPDMIBASE const pIBase = &pDrvStack->pDrvAudioIns->IBase;
    862             pDrvStack->pIAudioConnector = (PPDMIAUDIOCONNECTOR)pIBase->pfnQueryInterface(pIBase, PDMIAUDIOCONNECTOR_IID);
    863             if (pDrvStack->pIAudioConnector)
    864             {
    865                 /* Both input and output is disabled by default. Fix that: */
    866                 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_OUT, true);
    867                 if (RT_SUCCESS(rc))
    868                     rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_IN, true);
    869                 if (RT_FAILURE(rc))
    870                 {
    871                     RTTestFailed(g_hTest, "Failed to enabled input and output: %Rrc", rc);
    872                     audioTestDriverStackDelete(pDrvStack);
    873                 }
    874             }
    875             else
    876             {
    877                 RTTestFailed(g_hTest, "Failed to query PDMIAUDIOCONNECTOR");
    878                 audioTestDriverStackDelete(pDrvStack);
    879                 rc = VERR_PDM_MISSING_INTERFACE;
    880             }
    881         }
    882     }
    883 
    884     /*
    885      * Get the IHostAudio interface and check that the host driver is working.
    886      */
    887     if (RT_SUCCESS(rc))
    888     {
    889         PPDMIBASE const pIBase = &pDrvStack->pDrvBackendIns->IBase;
    890         pDrvStack->pIHostAudio = (PPDMIHOSTAUDIO)pIBase->pfnQueryInterface(pIBase, PDMIHOSTAUDIO_IID);
    891         if (pDrvStack->pIHostAudio)
    892         {
    893             PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT);
    894             if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING)
    895                 return VINF_SUCCESS;
    896 
    897             RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus);
    898         }
    899         else
    900             RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName);
    901         audioTestDriverStackDelete(pDrvStack);
    902     }
    903 
    904     return rc;
    905 }
    906 
    907 /**
    908  * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
    909  */
    910 static int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
    911 {
    912     int rc;
    913     if (   pDrvStack->pIHostAudio
    914         && pDrvStack->pIHostAudio->pfnSetDevice)
    915         rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId);
    916     else if (!pszDevId || *pszDevId)
    917         rc = VINF_SUCCESS;
    918     else
    919         rc = VERR_INVALID_FUNCTION;
    920     return rc;
    921 }
    922 
    923 /**
    924  * Common stream creation code.
    925  *
    926  * @returns VBox status code.
    927  * @param   pDrvStack           The audio driver stack to create it via.
    928  * @param   pCfgReq             The requested config.
    929  * @param   ppStream            Where to return the stream pointer on success.
    930  * @param   pCfgAcq             Where to return the actual (well, not
    931  *                              necessarily when using DrvAudio, but probably
    932  *                              the same) stream config on success (not used as
    933  *                              input).
    934  */
    935 static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAMCFG pCfgReq,
    936                                             PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
    937 {
    938     char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16];
    939     int  rc;
    940     *ppStream = NULL;
    941 
    942     if (pDrvStack->pIAudioConnector)
    943     {
    944         /*
    945          * DrvAudio does most of the work here.
    946          */
    947         PDMAUDIOSTREAMCFG CfgGst = *pCfgReq;
    948         rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF,
    949                                                           pCfgReq, &CfgGst, ppStream);
    950         if (RT_SUCCESS(rc))
    951         {
    952             *pCfgAcq = *pCfgReq; /** @todo PDMIAUDIOCONNECTOR::pfnStreamCreate only does one utterly pointless change to the two configs (enmLayout) from what I can tell... */
    953             pCfgAcq->Props = (*ppStream)->Props;
    954             RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp)));
    955             return rc;
    956         }
    957         RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc", rc);
    958     }
    959     else
    960     {
    961         /*
    962          * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM
    963          * structure actually is for this backend.
    964          */
    965         PDMAUDIOBACKENDCFG BackendCfg;
    966         rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg);
    967         if (RT_SUCCESS(rc))
    968         {
    969             if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM))
    970             {
    971                 /*
    972                  * Allocate and initialize the stream.
    973                  */
    974                 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream;
    975                 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream);
    976                 if (pStreamAt)
    977                 {
    978                     pStreamAt->Core.uMagic     = PDMAUDIOSTREAM_MAGIC;
    979                     pStreamAt->Core.enmDir     = PDMAUDIODIR_OUT;
    980                     pStreamAt->Core.cbBackend  = cbStream;
    981                     pStreamAt->Core.Props      = pCfgReq->Props;
    982                     RTStrPrintf(pStreamAt->Core.szName, sizeof(pStreamAt->Core.szName), pCfgReq->szName);
    983 
    984                     pStreamAt->Backend.uMagic  = PDMAUDIOBACKENDSTREAM_MAGIC;
    985                     pStreamAt->Backend.pStream = &pStreamAt->Core;
    986 
    987                     /*
    988                      * Call the backend to create the stream.
    989                      */
    990                     pStreamAt->Cfg = *pCfgReq;
    991 
    992                     rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend,
    993                                                                  pCfgReq, &pStreamAt->Cfg);
    994                     if (RT_SUCCESS(rc))
    995                     {
    996                         pStreamAt->Core.Props = pStreamAt->Cfg.Props;
    997                         if (g_uVerbosity > 1)
    998                             RTMsgInfo("Created backend stream: %s\n",
    999                                       PDMAudioStrmCfgToString(&pStreamAt->Cfg, szTmp, sizeof(szTmp)));
    1000 
    1001                         /* Return if stream is ready: */
    1002                         if (rc == VINF_SUCCESS)
    1003                         {
    1004                             *ppStream = &pStreamAt->Core;
    1005                             *pCfgAcq  = pStreamAt->Cfg;
    1006                             return VINF_SUCCESS;
    1007                         }
    1008                         if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
    1009                         {
    1010                             /*
    1011                              * Do async init right here and now.
    1012                              */
    1013                             rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend,
    1014                                                                             false /*fDestroyed*/);
    1015                             if (RT_SUCCESS(rc))
    1016                             {
    1017                                 *ppStream = &pStreamAt->Core;
    1018                                 *pCfgAcq  = pStreamAt->Cfg;
    1019                                 return VINF_SUCCESS;
    1020                             }
    1021 
    1022                             RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc);
    1023                         }
    1024                         else
    1025                         {
    1026                             RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc);
    1027                             rc = VERR_IPE_UNEXPECTED_INFO_STATUS;
    1028                         }
    1029                         pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
    1030                     }
    1031                     else
    1032                         RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc\n", rc);
    1033                 }
    1034                 else
    1035                 {
    1036                     RTTestFailed(g_hTest, "Out of memory!\n");
    1037                     rc = VERR_NO_MEMORY;
    1038                 }
    1039             }
    1040             else
    1041             {
    1042                 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM));
    1043                 rc = VERR_OUT_OF_RANGE;
    1044             }
    1045         }
    1046         else
    1047             RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc);
    1048     }
    1049     return rc;
    1050 }
    1051 
    1052 /**
    1053  * Creates an output stream.
    1054  *
    1055  * @returns VBox status code.
    1056  * @param   pDrvStack           The audio driver stack to create it via.
    1057  * @param   pProps              The audio properties to use.
    1058  * @param   cMsBufferSize       The buffer size in milliseconds.
    1059  * @param   cMsPreBuffer        The pre-buffering amount in milliseconds.
    1060  * @param   cMsSchedulingHint   The scheduling hint in milliseconds.
    1061  * @param   ppStream            Where to return the stream pointer on success.
    1062  * @param   pCfgAcq             Where to return the actual (well, not
    1063  *                              necessarily when using DrvAudio, but probably
    1064  *                              the same) stream config on success (not used as
    1065  *                              input).
    1066  */
    1067 static int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
    1068                                                   uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
    1069                                                   PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
    1070 {
    1071     /*
    1072      * Calculate the stream config.
    1073      */
    1074     PDMAUDIOSTREAMCFG CfgReq;
    1075     int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
    1076     AssertRC(rc);
    1077     CfgReq.enmDir                       = PDMAUDIODIR_OUT;
    1078     CfgReq.enmPath                      = PDMAUDIOPATH_OUT_FRONT;
    1079     CfgReq.Device.cMsSchedulingHint     = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
    1080                                         ? 10 : cMsSchedulingHint;
    1081     if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
    1082         CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
    1083     else
    1084         CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
    1085                                                                       cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
    1086                                                                       ? 300 : cMsBufferSize);
    1087     if (cMsPreBuffer == UINT32_MAX)
    1088         CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */
    1089                                            : CfgReq.Backend.cFramesBufferSize * 2 / 3;
    1090     else
    1091         CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
    1092     if (   CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16
    1093         && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
    1094     {
    1095         RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
    1096                      CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
    1097         CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
    1098             ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
    1099     }
    1100 
    1101     static uint32_t s_idxStream = 0;
    1102     uint32_t const idxStream = s_idxStream++;
    1103     RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream);
    1104 
    1105     /*
    1106      * Call common code to do the actual work.
    1107      */
    1108     return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
    1109 }
    1110 
    1111 /**
    1112  * Creates an input stream.
    1113  *
    1114  * @returns VBox status code.
    1115  * @param   pDrvStack           The audio driver stack to create it via.
    1116  * @param   pProps              The audio properties to use.
    1117  * @param   cMsBufferSize       The buffer size in milliseconds.
    1118  * @param   cMsPreBuffer        The pre-buffering amount in milliseconds.
    1119  * @param   cMsSchedulingHint   The scheduling hint in milliseconds.
    1120  * @param   ppStream            Where to return the stream pointer on success.
    1121  * @param   pCfgAcq             Where to return the actual (well, not
    1122  *                              necessarily when using DrvAudio, but probably
    1123  *                              the same) stream config on success (not used as
    1124  *                              input).
    1125  */
    1126 static int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
    1127                                                  uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
    1128                                                  PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
    1129 {
    1130     /*
    1131      * Calculate the stream config.
    1132      */
    1133     PDMAUDIOSTREAMCFG CfgReq;
    1134     int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
    1135     AssertRC(rc);
    1136     CfgReq.enmDir                       = PDMAUDIODIR_IN;
    1137     CfgReq.enmPath                      = PDMAUDIOPATH_IN_LINE;
    1138     CfgReq.Device.cMsSchedulingHint     = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
    1139                                         ? 10 : cMsSchedulingHint;
    1140     if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
    1141         CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
    1142     else
    1143         CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
    1144                                                                       cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
    1145                                                                       ? 300 : cMsBufferSize);
    1146     if (cMsPreBuffer == UINT32_MAX)
    1147         CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */
    1148                                            : CfgReq.Backend.cFramesBufferSize / 2;
    1149     else
    1150         CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
    1151     if (   CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */
    1152         && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
    1153     {
    1154         RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
    1155                      CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
    1156         CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
    1157             ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
    1158     }
    1159 
    1160     static uint32_t s_idxStream = 0;
    1161     uint32_t const idxStream = s_idxStream++;
    1162     RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream);
    1163 
    1164     /*
    1165      * Call common code to do the actual work.
    1166      */
    1167     return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
    1168 }
    1169 
    1170 /**
    1171  * Destroys a stream.
    1172  */
    1173 static void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1174 {
    1175     if (pStream)
    1176     {
    1177         if (pDrvStack->pIAudioConnector)
    1178         {
    1179             int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/);
    1180             if (RT_FAILURE(rc))
    1181                 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc);
    1182         }
    1183         else
    1184         {
    1185             PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1186             int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
    1187             if (RT_SUCCESS(rc))
    1188             {
    1189                 pStreamAt->Core.uMagic    = ~PDMAUDIOSTREAM_MAGIC;
    1190                 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC;
    1191                 RTMemFree(pStreamAt);
    1192             }
    1193             else
    1194                 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc);
    1195         }
    1196     }
    1197 }
    1198 
    1199 /**
    1200  * Enables a stream.
    1201  */
    1202 static int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1203 {
    1204     int rc;
    1205     if (pDrvStack->pIAudioConnector)
    1206     {
    1207         rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE);
    1208         if (RT_FAILURE(rc))
    1209             RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
    1210     }
    1211     else
    1212     {
    1213         PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1214         rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_ENABLE);
    1215         if (RT_FAILURE(rc))
    1216             RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
    1217     }
    1218     return rc;
    1219 }
    1220 
    1221 /**
    1222  * Drains an output stream.
    1223  */
    1224 static int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
    1225 {
    1226     int rc;
    1227     if (pDrvStack->pIAudioConnector)
    1228     {
    1229         /*
    1230          * Issue the drain request.
    1231          */
    1232         rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN);
    1233         if (RT_SUCCESS(rc) && fSync)
    1234         {
    1235             /*
    1236              * This is a synchronous drain, so wait for the driver to change state to inactive.
    1237              */
    1238             PDMAUDIOSTREAMSTATE enmState;
    1239             while (   (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream))
    1240                    >= PDMAUDIOSTREAMSTATE_ENABLED)
    1241             {
    1242                 RTThreadSleep(2);
    1243                 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream);
    1244                 if (RT_FAILURE(rc))
    1245                 {
    1246                     RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc);
    1247                     break;
    1248                 }
    1249             }
    1250             if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE)
    1251             {
    1252                 RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState));
    1253                 rc = VERR_AUDIO_STREAM_NOT_READY;
    1254             }
    1255         }
    1256         else if (RT_FAILURE(rc))
    1257             RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
    1258     }
    1259     else
    1260     {
    1261         /*
    1262          * Issue the drain request.
    1263          */
    1264         PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1265         rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_DRAIN);
    1266         if (RT_SUCCESS(rc) && fSync)
    1267         {
    1268             /*
    1269              * This is a synchronous drain, so wait for the driver to change state to inactive.
    1270              */
    1271             PDMHOSTAUDIOSTREAMSTATE enmHostState;
    1272             while (   (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend))
    1273                    == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
    1274             {
    1275                 RTThreadSleep(2);
    1276                 uint32_t cbWritten = UINT32_MAX;
    1277                 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend,
    1278                                                            NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten);
    1279                 if (RT_FAILURE(rc))
    1280                 {
    1281                     RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc);
    1282                     break;
    1283                 }
    1284                 if (cbWritten != 0)
    1285                 {
    1286                     RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten);
    1287                     rc = VERR_MISSING;
    1288                     break;
    1289                 }
    1290             }
    1291             if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY)
    1292             {
    1293                 RTTestFailed(g_hTest, "Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState));
    1294                 rc = VERR_AUDIO_STREAM_NOT_READY;
    1295             }
    1296         }
    1297         else if (RT_FAILURE(rc))
    1298             RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
    1299     }
    1300     return rc;
    1301 }
    1302 
    1303 /**
    1304  * Checks if the stream is okay.
    1305  * @returns true if okay, false if not.
    1306  */
    1307 static bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1308 {
    1309     /*
    1310      * Get the stream status and check if it means is okay or not.
    1311      */
    1312     bool fRc = false;
    1313     if (pDrvStack->pIAudioConnector)
    1314     {
    1315         PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream);
    1316         switch (enmState)
    1317         {
    1318             case PDMAUDIOSTREAMSTATE_NOT_WORKING:
    1319             case PDMAUDIOSTREAMSTATE_NEED_REINIT:
    1320                 break;
    1321             case PDMAUDIOSTREAMSTATE_INACTIVE:
    1322             case PDMAUDIOSTREAMSTATE_ENABLED:
    1323             case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
    1324             case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
    1325                 fRc = true;
    1326                 break;
    1327             /* no default */
    1328             case PDMAUDIOSTREAMSTATE_INVALID:
    1329             case PDMAUDIOSTREAMSTATE_END:
    1330             case PDMAUDIOSTREAMSTATE_32BIT_HACK:
    1331                 break;
    1332         }
    1333     }
    1334     else
    1335     {
    1336         PAUDIOTESTDRVSTACKSTREAM pStreamAt    = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1337         PDMHOSTAUDIOSTREAMSTATE  enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio,
    1338                                                                                           &pStreamAt->Backend);
    1339         switch (enmHostState)
    1340         {
    1341             case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
    1342             case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
    1343                 break;
    1344             case PDMHOSTAUDIOSTREAMSTATE_OKAY:
    1345             case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
    1346             case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
    1347                 fRc = true;
    1348                 break;
    1349             /* no default */
    1350             case PDMHOSTAUDIOSTREAMSTATE_INVALID:
    1351             case PDMHOSTAUDIOSTREAMSTATE_END:
    1352             case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
    1353                 break;
    1354         }
    1355     }
    1356     return fRc;
    1357 }
    1358 
    1359 /**
    1360  * Gets the number of bytes it's currently possible to write to the stream.
    1361  */
    1362 static uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1363 {
    1364     uint32_t cbWritable;
    1365     if (pDrvStack->pIAudioConnector)
    1366         cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream);
    1367     else
    1368     {
    1369         PAUDIOTESTDRVSTACKSTREAM pStreamAt    = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1370         cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
    1371     }
    1372     return cbWritable;
    1373 }
    1374 
    1375 /**
    1376  * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
    1377  */
    1378 static int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
    1379                                           void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
    1380 {
    1381     int rc;
    1382     if (pDrvStack->pIAudioConnector)
    1383     {
    1384         rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed);
    1385         if (RT_FAILURE(rc))
    1386             RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc);
    1387     }
    1388     else
    1389     {
    1390         PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1391         rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed);
    1392         if (RT_FAILURE(rc))
    1393             RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc);
    1394     }
    1395     return rc;
    1396 }
    1397 
    1398 /**
    1399  * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
    1400  */
    1401 static int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
    1402                                              void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
    1403 {
    1404     int rc;
    1405     if (pDrvStack->pIAudioConnector)
    1406     {
    1407         rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured);
    1408         if (RT_FAILURE(rc))
    1409             RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc);
    1410     }
    1411     else
    1412     {
    1413         PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
    1414         rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured);
    1415         if (RT_FAILURE(rc))
    1416             RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc);
    1417     }
    1418     return rc;
    1419 }
     389const char   *g_pszDrvAudioDebug = NULL;
    1420390
    1421391
  • trunk/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp

    r89399 r89401  
    11/* $Id$ */
    22/** @file
    3  * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.
     3 * Validation Kit Audio Test (VKAT) - Driver stack code.
    44 */
    55
     
    6565#define PDMDRVINSINT_DECLARED
    6666
    67 #include <VBox/vmm/pdmaudioinline.h>
    68 #include <VBox/vmm/pdmaudiohostenuminline.h>
    69 
    70 #include "../../../Devices/Audio/AudioHlp.h"
    71 #include "../../../Devices/Audio/AudioTest.h"
    72 #include "../../../Devices/Audio/AudioTestService.h"
    73 #include "../../../Devices/Audio/AudioTestServiceClient.h"
    74 #include "VBoxDD.h"
    75 
    76 
    77 /*********************************************************************************************************************************
    78 *   Defined Constants And Macros                                                                                                 *
    79 *********************************************************************************************************************************/
    80 /** For use in the option switch to handle common options. */
    81 #define AUDIO_TEST_COMMON_OPTION_CASES(a_ValueUnion) \
    82             case 'q': \
    83                 g_uVerbosity = 0; \
    84                 if (g_pRelLogger) \
    85                     RTLogGroupSettings(g_pRelLogger, "all=0 all.e"); \
    86                 break; \
    87             \
    88             case 'v': \
    89                 g_uVerbosity++; \
    90                 if (g_pRelLogger) \
    91                     RTLogGroupSettings(g_pRelLogger, g_uVerbosity == 1 ? "all.e.l" : g_uVerbosity == 2 ? "all.e.l.f" : "all=~0"); \
    92                 break; \
    93             \
    94             case 'V': \
    95                 return audioTestVersion(); \
    96             \
    97             case 'h': \
    98                 return audioTestUsage(g_pStdOut); \
    99             \
    100             case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE: \
    101                 g_fDrvAudioDebug = true; \
    102                 break; \
    103             \
    104             case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH: \
    105                 g_pszDrvAudioDebug = (a_ValueUnion).psz; \
    106                 break
    107 
    108 
    109 /*********************************************************************************************************************************
    110 *   Structures and Typedefs                                                                                                      *
    111 *********************************************************************************************************************************/
    112 /**
    113  * Enumeration specifying the current audio test mode.
    114  */
    115 typedef enum AUDIOTESTMODE
    116 {
    117     /** Unknown mode. */
    118     AUDIOTESTMODE_UNKNOWN = 0,
    119     /** VKAT is running on the guest side. */
    120     AUDIOTESTMODE_GUEST,
    121     /** VKAT is running on the host side. */
    122     AUDIOTESTMODE_HOST
    123 } AUDIOTESTMODE;
    124 
    125 struct AUDIOTESTENV;
    126 /** Pointer a audio test environment. */
    127 typedef AUDIOTESTENV *PAUDIOTESTENV;
    128 
    129 struct AUDIOTESTDESC;
    130 /** Pointer a audio test descriptor. */
    131 typedef AUDIOTESTDESC *PAUDIOTESTDESC;
    132 
    133 /**
    134  * Callback to set up the test parameters for a specific test.
    135  *
    136  * @returns IPRT status code.
    137  * @retval  VINF_SUCCESS    if setting the parameters up succeeded. Any other error code
    138  *                          otherwise indicating the kind of error.
    139  * @param   pszTest         Test name.
    140  * @param   pTstParmsAcq    The audio test parameters to set up.
    141  */
    142 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
    143 /** Pointer to an audio test setup callback. */
    144 typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
    145 
    146 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
    147 /** Pointer to an audio test exec callback. */
    148 typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
    149 
    150 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx));
    151 /** Pointer to an audio test destroy callback. */
    152 typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
    153 
    154 /**
    155  * Audio driver stack.
    156  *
    157  * This can be just be backend driver alone or DrvAudio with a backend.
    158  * @todo add automatic resampling via mixer so we can test more of the audio
    159  *       stack used by the device emulations.
    160  */
    161 typedef struct AUDIOTESTDRVSTACK
    162 {
    163     /** The device registration record for the backend. */
    164     PCPDMDRVREG             pDrvReg;
    165     /** The backend driver instance. */
    166     PPDMDRVINS              pDrvBackendIns;
    167     /** The backend's audio interface. */
    168     PPDMIHOSTAUDIO          pIHostAudio;
    169 
    170     /** The DrvAudio instance. */
    171     PPDMDRVINS              pDrvAudioIns;
    172     /** This is NULL if we don't use DrvAudio. */
    173     PPDMIAUDIOCONNECTOR     pIAudioConnector;
    174 } AUDIOTESTDRVSTACK;
    175 /** Pointer to an audio driver stack. */
    176 typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK;
    177 
    178 /**
    179  * Backend-only stream structure.
    180  */
    181 typedef struct AUDIOTESTDRVSTACKSTREAM
    182 {
    183     /** The public stream data. */
    184     PDMAUDIOSTREAM          Core;
    185     /** The acquired config. */
    186     PDMAUDIOSTREAMCFG       Cfg;
    187     /** The backend data (variable size). */
    188     PDMAUDIOBACKENDSTREAM   Backend;
    189 } AUDIOTESTDRVSTACKSTREAM;
    190 /** Pointer to a backend-only stream structure. */
    191 typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM;
    192 
    193 /** Maximum audio streams a test environment can handle. */
    194 #define AUDIOTESTENV_MAX_STREAMS 8
    195 
    196 /**
    197  * Structure for keeping an audio test audio stream.
    198  */
    199 typedef struct AUDIOTESTSTREAM
    200 {
    201     /** The PDM stream. */
    202     PPDMAUDIOSTREAM         pStream;
    203     /** The backend stream. */
    204     PPDMAUDIOBACKENDSTREAM  pBackend;
    205     /** The stream config. */
    206     PDMAUDIOSTREAMCFG       Cfg;
    207 } AUDIOTESTSTREAM;
    208 /** Pointer to audio test stream. */
    209 typedef AUDIOTESTSTREAM *PAUDIOTESTSTREAM;
    210 
    211 /**
    212  * Audio test environment parameters.
    213  * Not necessarily bound to a specific test (can be reused).
    214  */
    215 typedef struct AUDIOTESTENV
    216 {
    217     /** Audio testing mode. */
    218     AUDIOTESTMODE           enmMode;
    219     /** Output path for storing the test environment's final test files. */
    220     char                    szPathOut[RTPATH_MAX];
    221     /** Temporary path for this test environment. */
    222     char                    szPathTemp[RTPATH_MAX];
    223     /** The audio test driver stack. */
    224     AUDIOTESTDRVSTACK       DrvStack;
    225     /** The current (last) audio device enumeration to use. */
    226     PDMAUDIOHOSTENUM        DevEnum;
    227     /** Audio stream. */
    228     AUDIOTESTSTREAM         aStreams[AUDIOTESTENV_MAX_STREAMS];
    229     /** The audio test set to use. */
    230     AUDIOTESTSET            Set;
    231     union
    232     {
    233         struct
    234         {
    235             /** ATS instance to use. */
    236             ATSSERVER       Srv;
    237         } Guest;
    238         struct
    239         {
    240             ATSCLIENT       Client;
    241         } Host;
    242     } u;
    243 } AUDIOTESTENV;
    244 
    245 /**
    246  * Audio test descriptor.
    247  */
    248 typedef struct AUDIOTESTDESC
    249 {
    250     /** (Sort of) Descriptive test name. */
    251     const char             *pszName;
    252     /** Flag whether the test is excluded. */
    253     bool                    fExcluded;
    254     /** The setup callback. */
    255     PFNAUDIOTESTSETUP       pfnSetup;
    256     /** The exec callback. */
    257     PFNAUDIOTESTEXEC        pfnExec;
    258     /** The destruction callback. */
    259     PFNAUDIOTESTDESTROY     pfnDestroy;
    260 } AUDIOTESTDESC;
    261 
    262 /**
    263  * Structure for keeping a user context for the test service callbacks.
    264  */
    265 typedef struct ATSCALLBACKCTX
    266 {
    267     PAUDIOTESTENV pTstEnv;
    268 } ATSCALLBACKCTX;
    269 typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
    270 
    271 
    272 /*********************************************************************************************************************************
    273 *   Internal Functions                                                                                                           *
    274 *********************************************************************************************************************************/
    275 static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms);
    276 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms);
    277 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
    278 
    279 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg,
    280                                  PPDMDRVINS pParentDrvIns, PPPDMDRVINS ppDrvIns);
    281 
    282 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm);
    283 static RTEXITCODE audioTestVersion(void);
    284 
    285 
    286 /*********************************************************************************************************************************
    287 *   Global Variables                                                                                                             *
    288 *********************************************************************************************************************************/
    289 /**
    290  * Common long options values.
    291  */
    292 enum
    293 {
    294     AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE = 256,
    295     AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH
    296 };
    297 
    298 /**
    299  * Long option values for the 'test' command.
    300  */
    301 enum
    302 {
    303     VKAT_TEST_OPT_COUNT = 900,
    304     VKAT_TEST_OPT_DEV,
    305     VKAT_TEST_OPT_ATS_ADDR,
    306     VKAT_TEST_OPT_ATS_PORT,
    307     VKAT_TEST_OPT_MODE,
    308     VKAT_TEST_OPT_OUTDIR,
    309     VKAT_TEST_OPT_PAUSE,
    310     VKAT_TEST_OPT_PCM_HZ,
    311     VKAT_TEST_OPT_PCM_BIT,
    312     VKAT_TEST_OPT_PCM_CHAN,
    313     VKAT_TEST_OPT_PCM_SIGNED,
    314     VKAT_TEST_OPT_TAG,
    315     VKAT_TEST_OPT_TEMPDIR,
    316     VKAT_TEST_OPT_VOL
    317 };
    318 
    319 /**
    320  * Long option values for the 'verify' command.
    321  */
    322 enum
    323 {
    324     VKAT_VERIFY_OPT_TAG = 900
    325 };
    326 
    327 /**
    328  * Long option values for the 'selftest' command.
    329  */
    330 enum
    331 {
    332     VKAT_SELFTEST_OPT_ATS_HOST = 900
    333 };
    334 
    335 /**
    336  * Common command line parameters.
    337  */
    338 static const RTGETOPTDEF g_aCmdCommonOptions[] =
    339 {
    340     { "--quiet",            'q',                                        RTGETOPT_REQ_NOTHING },
    341     { "--verbose",          'v',                                        RTGETOPT_REQ_NOTHING },
    342     { "--debug-audio",      AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE,      RTGETOPT_REQ_NOTHING },
    343     { "--debug-audio-path", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH,        RTGETOPT_REQ_STRING  },
    344 };
    345 
    346 /**
    347  * Command line parameters for test mode.
    348  */
    349 static const RTGETOPTDEF g_aCmdTestOptions[] =
    350 {
    351     { "--backend",          'b',                          RTGETOPT_REQ_STRING  },
    352     { "--drvaudio",         'd',                          RTGETOPT_REQ_NOTHING },
    353     { "--exclude",          'e',                          RTGETOPT_REQ_UINT32  },
    354     { "--exclude-all",      'a',                          RTGETOPT_REQ_NOTHING },
    355     { "--mode",             VKAT_TEST_OPT_MODE,           RTGETOPT_REQ_STRING  },
    356     { "--ats-address",      VKAT_TEST_OPT_ATS_ADDR,       RTGETOPT_REQ_STRING  },
    357     { "--ats-port",         VKAT_TEST_OPT_ATS_PORT,       RTGETOPT_REQ_UINT32  },
    358     { "--include",          'i',                          RTGETOPT_REQ_UINT32  },
    359     { "--outdir",           VKAT_TEST_OPT_OUTDIR,         RTGETOPT_REQ_STRING  },
    360     { "--count",            VKAT_TEST_OPT_COUNT,          RTGETOPT_REQ_UINT32  },
    361     { "--device",           VKAT_TEST_OPT_DEV,            RTGETOPT_REQ_STRING  },
    362     { "--pause",            VKAT_TEST_OPT_PAUSE,          RTGETOPT_REQ_UINT32  },
    363     { "--pcm-bit",          VKAT_TEST_OPT_PCM_BIT,        RTGETOPT_REQ_UINT8   },
    364     { "--pcm-chan",         VKAT_TEST_OPT_PCM_CHAN,       RTGETOPT_REQ_UINT8   },
    365     { "--pcm-hz",           VKAT_TEST_OPT_PCM_HZ,         RTGETOPT_REQ_UINT16  },
    366     { "--pcm-signed",       VKAT_TEST_OPT_PCM_SIGNED,     RTGETOPT_REQ_BOOL    },
    367     { "--tag",              VKAT_TEST_OPT_TAG,            RTGETOPT_REQ_STRING  },
    368     { "--tempdir",          VKAT_TEST_OPT_TEMPDIR,        RTGETOPT_REQ_STRING  },
    369     { "--volume",           VKAT_TEST_OPT_VOL,            RTGETOPT_REQ_UINT8   }
    370 };
    371 
    372 /**
    373  * Command line parameters for verification mode.
    374  */
    375 static const RTGETOPTDEF g_aCmdVerifyOptions[] =
    376 {
    377     { "--tag",              VKAT_VERIFY_OPT_TAG,          RTGETOPT_REQ_STRING  }
    378 };
    379 
    380 /**
    381  * Backends.
    382  *
    383  * @note The first backend in the array is the default one for the platform.
    384  */
    385 static struct
    386 {
    387     /** The driver registration structure. */
    388     PCPDMDRVREG pDrvReg;
    389     /** The backend name.
    390      * Aliases are implemented by having multiple entries for the same backend.  */
    391     const char *pszName;
    392 } const g_aBackends[] =
    393 {
    394 #if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
    395     {   &g_DrvHostALSAAudio,          "alsa" },
    396 #endif
    397 #ifdef VBOX_WITH_AUDIO_PULSE
    398     {   &g_DrvHostPulseAudio,         "pulseaudio" },
    399     {   &g_DrvHostPulseAudio,         "pulse" },
    400     {   &g_DrvHostPulseAudio,         "pa" },
    401 #endif
    402 #ifdef VBOX_WITH_AUDIO_OSS
    403     {   &g_DrvHostOSSAudio,           "oss" },
    404 #endif
    405 #if defined(RT_OS_DARWIN)
    406     {   &g_DrvHostCoreAudio,          "coreaudio" },
    407     {   &g_DrvHostCoreAudio,          "core" },
    408     {   &g_DrvHostCoreAudio,          "ca" },
    409 #endif
    410 #if defined(RT_OS_WINDOWS)
    411     {   &g_DrvHostAudioWas,           "wasapi" },
    412     {   &g_DrvHostAudioWas,           "was" },
    413     {   &g_DrvHostDSound,             "directsound" },
    414     {   &g_DrvHostDSound,             "dsound" },
    415     {   &g_DrvHostDSound,             "ds" },
    416 #endif
    417     {   &g_DrvHostValidationKitAudio, "valkit" }
    418 };
    419 AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
    420 
    421 static volatile bool g_fTerminated = false;
    422 /** The test handle. */
    423 static RTTEST        g_hTest;
    424 /** The release logger. */
    425 static PRTLOGGER    g_pRelLogger = NULL;
    426 /** The current verbosity level. */
    427 static unsigned      g_uVerbosity = 0;
    428 /** DrvAudio: Enable debug (or not). */
    429 static bool          g_fDrvAudioDebug = 0;
    430 /** DrvAudio: The debug output path. */
    431 static const char   *g_pszDrvAudioDebug = NULL;
     67#include "vkatInternal.h"
     68
    43269
    43370
     
    720357 * @param   ppDrvIns        Where to return the driver instance structure.
    721358 */
    722 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
    723                                  PPPDMDRVINS ppDrvIns)
     359int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
     360                          PPPDMDRVINS ppDrvIns)
    724361{
    725362    /* The destruct function must have valid data to work with. */
     
    817454 * This will power off and destroy the drivers.
    818455 */
    819 static void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
     456void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
    820457{
    821458    /*
     
    845482 * @param   fWithDrvAudio   Whether to include DrvAudio in the stack or not.
    846483 */
    847 static int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
     484int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
    848485{
    849486    RT_ZERO(*pDrvStack);
     
    908545 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
    909546 */
    910 static int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
     547int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
    911548{
    912549    int rc;
     
    1065702 *                              input).
    1066703 */
    1067 static int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
    1068                                                   uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
    1069                                                   PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
     704int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
     705                                           uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
     706                                           PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
    1070707{
    1071708    /*
     
    1124761 *                              input).
    1125762 */
    1126 static int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
    1127                                                  uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
    1128                                                  PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
     763int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
     764                                          uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
     765                                          PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
    1129766{
    1130767    /*
     
    1171808 * Destroys a stream.
    1172809 */
    1173 static void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
     810void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1174811{
    1175812    if (pStream)
     
    1200837 * Enables a stream.
    1201838 */
    1202 static int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
     839int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1203840{
    1204841    int rc;
     
    1222859 * Drains an output stream.
    1223860 */
    1224 static int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
     861int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
    1225862{
    1226863    int rc;
     
    1305942 * @returns true if okay, false if not.
    1306943 */
    1307 static bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
     944bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    1308945{
    1309946    /*
     
    1360997 * Gets the number of bytes it's currently possible to write to the stream.
    1361998 */
    1362 static uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
     999uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
    13631000{
    13641001    uint32_t cbWritable;
     
    13761013 * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
    13771014 */
    1378 static int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
    1379                                           void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
     1015int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
     1016                                   void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
    13801017{
    13811018    int rc;
     
    13991036 * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
    14001037 */
    1401 static int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
    1402                                              void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
     1038int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
     1039                                      void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
    14031040{
    14041041    int rc;
     
    14191056}
    14201057
    1421 
    1422 /*********************************************************************************************************************************
    1423 *   ATS Callback Implementations                                                                                                 *
    1424 *********************************************************************************************************************************/
    1425 
    1426 /**
    1427  * Note: Called within server (client serving) thread.
    1428  */
    1429 static DECLCALLBACK(int) audioTestSvcTonePlayCallback(void const *pvUser, PPDMAUDIOSTREAMCFG pStreamCfg, PAUDIOTESTTONEPARMS pToneParms)
    1430 {
    1431     PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
    1432     PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
    1433 
    1434     AUDIOTESTTONE TstTone;
    1435     AudioTestToneInitRandom(&TstTone, &pStreamCfg->Props);
    1436 
    1437     int rc;
    1438 
    1439     const PPDMAUDIOSTREAM pStream = pTstEnv->aStreams[0].pStream; /** @todo Make this dynamic. */
    1440 
    1441     if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream))
    1442     {
    1443         uint32_t cbBuf;
    1444         uint8_t  abBuf[_4K];
    1445 
    1446         const uint64_t tsStartMs     = RTTimeMilliTS();
    1447         const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Chose a random scheduling (in ms). */
    1448         const uint32_t cbPerMs       = PDMAudioPropsMilliToBytes(&pStream->Props, cSchedulingMs);
    1449 
    1450         do
    1451         {
    1452             rc = AudioTestToneGenerate(&TstTone, abBuf, RT_MIN(cbPerMs, sizeof(abBuf)), &cbBuf);
    1453             if (RT_SUCCESS(rc))
    1454             {
    1455                 uint32_t cbWritten;
    1456                 rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream, abBuf, cbBuf, &cbWritten);
    1457             }
    1458 
    1459             if (RTTimeMilliTS() - tsStartMs >= pToneParms->msDuration)
    1460                 break;
    1461 
    1462             if (RT_FAILURE(rc))
    1463                 break;
    1464 
    1465             RTThreadSleep(cSchedulingMs);
    1466 
    1467         } while (RT_SUCCESS(rc));
    1468     }
    1469     else
    1470         rc = VERR_AUDIO_STREAM_NOT_READY;
    1471 
    1472     return rc;
    1473 }
    1474 
    1475 
    1476 /*********************************************************************************************************************************
    1477 *   Implementation of audio test environment handling                                                                            *
    1478 *********************************************************************************************************************************/
    1479 
    1480 /**
    1481  * Initializes an audio test environment.
    1482  *
    1483  * @param   pTstEnv             Audio test environment to initialize.
    1484  * @param   pDrvReg             Audio driver to use.
    1485  * @param   fWithDrvAudio       Whether to include DrvAudio in the stack or not.
    1486  * @param   pszTag              Tag name to use. If NULL, a generated UUID will be used.
    1487  * @param   pszTcpAddr          TCP/IP address to connect to.
    1488  * @param   uTcpPort            TCP/IP port to connect to.
    1489  */
    1490 static int audioTestEnvInit(PAUDIOTESTENV pTstEnv,
    1491                             PCPDMDRVREG pDrvReg, bool fWithDrvAudio, const char *pszTag,
    1492                             const char *pszTcpAddr, uint32_t uTcpPort)
    1493 {
    1494     PDMAudioHostEnumInit(&pTstEnv->DevEnum);
    1495 
    1496     int rc = audioTestDriverStackInit(&pTstEnv->DrvStack, pDrvReg, fWithDrvAudio);
    1497     if (RT_FAILURE(rc))
    1498         return rc;
    1499 
    1500     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
    1501     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pszTag);
    1502     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
    1503     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
    1504 
    1505     char szPathTemp[RTPATH_MAX];
    1506     if (   !strlen(pTstEnv->szPathTemp)
    1507         || !strlen(pTstEnv->szPathOut))
    1508         rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
    1509 
    1510     if (   RT_SUCCESS(rc)
    1511         && !strlen(pTstEnv->szPathTemp))
    1512         rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
    1513 
    1514     if (   RT_SUCCESS(rc)
    1515         && !strlen(pTstEnv->szPathOut))
    1516         rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
    1517 
    1518     if (RT_SUCCESS(rc))
    1519         rc = AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
    1520 
    1521     if (RT_FAILURE(rc))
    1522         return rc;
    1523 
    1524     /** @todo Implement NAT mode like we do for TxS later? */
    1525     if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
    1526     {
    1527         ATSCALLBACKCTX Ctx;
    1528         Ctx.pTstEnv = pTstEnv;
    1529 
    1530         ATSCALLBACKS Callbacks;
    1531         Callbacks.pfnTonePlay = audioTestSvcTonePlayCallback;
    1532         Callbacks.pvUser      = &Ctx;
    1533 
    1534         RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Starting ATS ...\n");
    1535         rc = AudioTestSvcInit(&pTstEnv->u.Guest.Srv, &Callbacks);
    1536         if (RT_SUCCESS(rc))
    1537             rc = AudioTestSvcStart(&pTstEnv->u.Guest.Srv);
    1538 
    1539         if (RT_FAILURE(rc))
    1540         {
    1541             RTTestFailed(g_hTest, "Initializing ATS failed with %Rrc\n", rc);
    1542             return rc;
    1543         }
    1544 
    1545         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "ATS running\n");
    1546 
    1547         while (!g_fTerminated) /** @todo Implement signal handling. */
    1548         {
    1549             RTThreadSleep(100);
    1550         }
    1551 
    1552         RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Shutting down ATS ...\n");
    1553 
    1554         int rc2 = AudioTestSvcShutdown(&pTstEnv->u.Guest.Srv);
    1555         if (RT_SUCCESS(rc))
    1556             rc = rc2;
    1557     }
    1558     else
    1559     {
    1560         RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to ATS at %s:%RU32 ...\n", pszTcpAddr, uTcpPort);
    1561         rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.Client, pszTcpAddr, uTcpPort);
    1562         if (RT_FAILURE(rc))
    1563         {
    1564             RTTestFailed(g_hTest, "Connecting to ATS failed with %Rrc\n", rc);
    1565             return rc;
    1566         }
    1567     }
    1568 
    1569     audioTestDriverStackDelete(&pTstEnv->DrvStack);
    1570 
    1571     return rc;
    1572 }
    1573 
    1574 /**
    1575  * Destroys an audio test environment.
    1576  *
    1577  * @param   pTstEnv             Audio test environment to destroy.
    1578  */
    1579 static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
    1580 {
    1581     if (!pTstEnv)
    1582         return;
    1583 
    1584     PDMAudioHostEnumDelete(&pTstEnv->DevEnum);
    1585 
    1586     for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
    1587     {
    1588         int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
    1589         if (RT_FAILURE(rc2))
    1590             RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
    1591     }
    1592 
    1593     AudioTestSetDestroy(&pTstEnv->Set);
    1594     audioTestDriverStackDelete(&pTstEnv->DrvStack);
    1595 }
    1596 
    1597 /**
    1598  * Initializes an audio test parameters set.
    1599  *
    1600  * @param   pTstParms           Test parameters set to initialize.
    1601  */
    1602 static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
    1603 {
    1604     RT_ZERO(*pTstParms);
    1605 }
    1606 
    1607 /**
    1608  * Destroys an audio test parameters set.
    1609  *
    1610  * @param   pTstParms           Test parameters set to destroy.
    1611  */
    1612 static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
    1613 {
    1614     if (!pTstParms)
    1615         return;
    1616 
    1617     return;
    1618 }
    1619 
    1620 
    1621 /*********************************************************************************************************************************
    1622 *   Device enumeration + handling.                                                                                               *
    1623 *********************************************************************************************************************************/
    1624 
    1625 /**
    1626  * Enumerates audio devices and optionally searches for a specific device.
    1627  *
    1628  * @returns VBox status code.
    1629  * @param   pTstEnv             Test env to use for enumeration.
    1630  * @param   pszDev              Device name to search for. Can be NULL if the default device shall be used.
    1631  * @param   ppDev               Where to return the pointer of the device enumeration of \a pTstEnv when a
    1632  *                              specific device was found.
    1633  */
    1634 static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
    1635 {
    1636     RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
    1637 
    1638     if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)
    1639     {
    1640         RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
    1641         return VINF_NOT_SUPPORTED;
    1642     }
    1643 
    1644     Assert(pszDev == NULL || ppDev);
    1645 
    1646     if (ppDev)
    1647         *ppDev = NULL;
    1648 
    1649     int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);
    1650     if (RT_SUCCESS(rc))
    1651     {
    1652         PPDMAUDIOHOSTDEV pDev;
    1653         RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
    1654         {
    1655             char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
    1656             if (pDev->pszId)
    1657                 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->szName, pDev->pszId);
    1658             else
    1659                 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->szName);
    1660             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage));
    1661             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
    1662             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Input channels  = %RU8\n", pDev->cMaxInputChannels);
    1663             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Output channels = %RU8\n", pDev->cMaxOutputChannels);
    1664 
    1665             if (   pszDev
    1666                 && !RTStrCmp(pDev->szName, pszDev))
    1667             {
    1668                 *ppDev = pDev;
    1669             }
    1670         }
    1671     }
    1672     else
    1673         RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
    1674 
    1675     RTTestSubDone(g_hTest);
    1676 
    1677     if (   pszDev
    1678         && *ppDev == NULL)
    1679     {
    1680         RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
    1681         return VERR_NOT_FOUND;
    1682     }
    1683 
    1684     return VINF_SUCCESS;
    1685 }
    1686 
    1687 /**
    1688  * Opens an audio device.
    1689  *
    1690  * @returns VBox status code.
    1691  * @param   pDev                Audio device to open.
    1692  */
    1693 static int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
    1694 {
    1695     int rc = VINF_SUCCESS;
    1696 
    1697     RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->szName);
    1698 
    1699     /** @todo Detect + open device here. */
    1700 
    1701     RTTestSubDone(g_hTest);
    1702 
    1703     return rc;
    1704 }
    1705 
    1706 /**
    1707  * Closes an audio device.
    1708  *
    1709  * @returns VBox status code.
    1710  * @param   pDev                Audio device to close.
    1711  */
    1712 static int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
    1713 {
    1714     int rc = VINF_SUCCESS;
    1715 
    1716     RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->szName);
    1717 
    1718     /** @todo Close device here. */
    1719 
    1720     RTTestSubDone(g_hTest);
    1721 
    1722     return rc;
    1723 }
    1724 
    1725 /**
    1726  * Destroys an audio test stream.
    1727  *
    1728  * @returns VBox status code.
    1729  * @param   pTstEnv             Test environment the stream to destroy contains.
    1730  * @param   pStream             Audio stream to destroy.
    1731  */
    1732 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
    1733 {
    1734     int rc = VINF_SUCCESS;
    1735     if (pStream && pStream->pStream)
    1736     {
    1737         /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
    1738 
    1739         audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);
    1740         pStream->pStream  = NULL;
    1741         pStream->pBackend = NULL;
    1742     }
    1743 
    1744     return rc;
    1745 }
    1746 
    1747 /**
    1748  * Creates an audio default input (recording) test stream.
    1749  * Convenience function.
    1750  *
    1751  * @returns VBox status code.
    1752  * @param   pTstEnv             Test environment to use for creating the stream.
    1753  * @param   pStream             Audio stream to create.
    1754  * @param   pProps              PCM properties to use for creation.
    1755  */
    1756 static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
    1757 {
    1758     pStream->pBackend = NULL;
    1759     int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, 300 /*cMsBufferSize*/, 150 /*cMsPreBuffer*/,
    1760                                                    10 /*cMsSchedulingHint*/, &pStream->pStream, &pStream->Cfg);
    1761     if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
    1762         pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
    1763     return rc;
    1764 }
    1765 
    1766 /**
    1767  * Records a test tone from a specific audio test stream.
    1768  *
    1769  * @returns VBox status code.
    1770  * @param   pTstEnv             Test environment to use for running the test.
    1771  * @param   pStream             Stream to use for recording the tone.
    1772  * @param   pParms              Tone parameters to use.
    1773  *
    1774  * @note    Blocking function.
    1775  */
    1776 static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
    1777 {
    1778     const char *pcszPathOut = pTstEnv->Set.szPathAbs;
    1779 
    1780     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);
    1781     RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
    1782 
    1783     /** @todo Use .WAV here? */
    1784     PAUDIOTESTOBJ pObj;
    1785     int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-rec.pcm", &pObj);
    1786     AssertRCReturn(rc, rc);
    1787 
    1788     if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
    1789     {
    1790         uint8_t abBuf[_4K];
    1791 
    1792         const uint64_t tsStartMs     = RTTimeMilliTS();
    1793         const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
    1794 
    1795         do
    1796         {
    1797             uint32_t cbRead = 0;
    1798             rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, sizeof(abBuf), &cbRead);
    1799             if (RT_SUCCESS(rc))
    1800                 rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);
    1801 
    1802             if (RT_FAILURE(rc))
    1803                 break;
    1804 
    1805             if (RTTimeMilliTS() - tsStartMs >= pParms->msDuration)
    1806                 break;
    1807 
    1808             RTThreadSleep(cSchedulingMs);
    1809 
    1810         } while (RT_SUCCESS(rc));
    1811     }
    1812     else
    1813         rc = VERR_AUDIO_STREAM_NOT_READY;
    1814 
    1815     int rc2 = AudioTestSetObjClose(pObj);
    1816     if (RT_SUCCESS(rc))
    1817         rc = rc2;
    1818 
    1819     return rc;
    1820 }
    1821 
    1822 /**
    1823  * Creates an audio default output (playback) test stream.
    1824  * Convenience function.
    1825  *
    1826  * @returns VBox status code.
    1827  * @param   pTstEnv             Test environment to use for creating the stream.
    1828  * @param   pStream             Audio stream to create.
    1829  * @param   pProps              PCM properties to use for creation.
    1830  */
    1831 static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
    1832 {
    1833     pStream->pBackend = NULL;
    1834     int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, 300 /*cMsBufferSize*/, 200 /*cMsPreBuffer*/,
    1835                                                     10 /*cMsSchedulingHint*/, &pStream->pStream, &pStream->Cfg);
    1836     if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
    1837         pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
    1838     return rc;
    1839 }
    1840 
    1841 /**
    1842  * Plays a test tone on a specific audio test stream.
    1843  *
    1844  * @returns VBox status code.
    1845  * @param   pTstEnv             Test environment to use for running the test.
    1846  * @param   pStream             Stream to use for playing the tone.
    1847  * @param   pParms              Tone parameters to use.
    1848  *
    1849  * @note    Blocking function.
    1850  */
    1851 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
    1852 {
    1853     AUDIOTESTTONE TstTone;
    1854     AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);
    1855 
    1856     const char *pcszPathOut = pTstEnv->Set.szPathAbs;
    1857 
    1858     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
    1859     RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
    1860 
    1861     /** @todo Use .WAV here? */
    1862     PAUDIOTESTOBJ pObj;
    1863     int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-play.pcm", &pObj);
    1864     AssertRCReturn(rc, rc);
    1865 
    1866     if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
    1867     {
    1868         uint32_t cbBuf;
    1869         uint8_t  abBuf[_4K];
    1870 
    1871         const uint64_t tsStartMs     = RTTimeMilliTS();
    1872         const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
    1873         const uint32_t cbPerMs       = PDMAudioPropsMilliToBytes(&pParms->Props, cSchedulingMs);
    1874 
    1875         do
    1876         {
    1877             rc = AudioTestToneGenerate(&TstTone, abBuf, RT_MIN(cbPerMs, sizeof(abBuf)), &cbBuf);
    1878             if (RT_SUCCESS(rc))
    1879             {
    1880                 /* Write stuff to disk before trying to play it. Help analysis later. */
    1881                 rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
    1882                 if (RT_SUCCESS(rc))
    1883                 {
    1884                     uint32_t cbWritten;
    1885                     rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,
    1886                                                         abBuf, cbBuf, &cbWritten);
    1887                 }
    1888             }
    1889 
    1890             if (RTTimeMilliTS() - tsStartMs >= pParms->msDuration)
    1891                 break;
    1892 
    1893             if (RT_FAILURE(rc))
    1894                 break;
    1895 
    1896             RTThreadSleep(cSchedulingMs);
    1897 
    1898         } while (RT_SUCCESS(rc));
    1899     }
    1900     else
    1901         rc = VERR_AUDIO_STREAM_NOT_READY;
    1902 
    1903     int rc2 = AudioTestSetObjClose(pObj);
    1904     if (RT_SUCCESS(rc))
    1905         rc = rc2;
    1906 
    1907     return rc;
    1908 }
    1909 
    1910 /**
    1911  * Overrides audio test base parameters with another set.
    1912  *
    1913  * @returns VBox status code.
    1914  * @param   pBaseParms          Base parameters to override.
    1915  * @param   pOverrideParms      Override parameters to use for overriding the base parameters.
    1916  *
    1917  * @note    Overriding a parameter depends on its type / default values.
    1918  */
    1919 static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms)
    1920 {
    1921     RT_NOREF(pBaseParms, pOverrideParms);
    1922 
    1923     /** @todo Implement parameter overriding. */
    1924     return VERR_NOT_IMPLEMENTED;
    1925 }
    1926 
    1927 
    1928 /*********************************************************************************************************************************
    1929 *   Test callbacks                                                                                                               *
    1930 *********************************************************************************************************************************/
    1931 
    1932 /**
    1933  * @copydoc FNAUDIOTESTSETUP
    1934  */
    1935 static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
    1936 {
    1937     RT_NOREF(pTstEnv, pTstDesc, ppvCtx);
    1938 
    1939     pTstParmsAcq->enmType     = AUDIOTESTTYPE_TESTTONE_PLAY;
    1940 
    1941     PDMAudioPropsInit(&pTstParmsAcq->Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);
    1942 
    1943     pTstParmsAcq->enmDir      = PDMAUDIODIR_OUT;
    1944 #ifdef DEBUG
    1945     pTstParmsAcq->cIterations = 2;
    1946 #else
    1947     pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);
    1948 #endif
    1949     pTstParmsAcq->idxCurrent  = 0;
    1950 
    1951     return VINF_SUCCESS;
    1952 }
    1953 
    1954 /**
    1955  * @copydoc FNAUDIOTESTEXEC
    1956  */
    1957 static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
    1958 {
    1959     RT_NOREF(pvCtx);
    1960 
    1961     int rc = VINF_SUCCESS;
    1962 
    1963     PAUDIOTESTSTREAM pStream = &pTstEnv->aStreams[0];
    1964 
    1965     for (uint32_t i = 0; i < pTstParms->cIterations; i++)
    1966     {
    1967         AudioTestToneParamsInitRandom(&pTstParms->TestTone, &pTstParms->Props);
    1968 
    1969         PAUDIOTESTENTRY pTst;
    1970         rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", pTstParms, &pTst);
    1971         if (RT_SUCCESS(rc))
    1972         {
    1973             rc = audioTestCreateStreamDefaultOut(pTstEnv, pStream, &pTstParms->TestTone.Props);
    1974             if (RT_SUCCESS(rc))
    1975             {
    1976                 rc = audioTestPlayTone(pTstEnv, pStream, &pTstParms->TestTone);
    1977             }
    1978 
    1979             int rc2 = audioTestStreamDestroy(pTstEnv, pStream);
    1980             if (RT_SUCCESS(rc))
    1981                 rc = rc2;
    1982 
    1983             if (RT_SUCCESS(rc))
    1984             {
    1985                AudioTestSetTestDone(pTst);
    1986             }
    1987             else
    1988                 AudioTestSetTestFailed(pTst, rc, "Playing test tone failed");
    1989         }
    1990 
    1991         if (RT_FAILURE(rc))
    1992             RTTestFailed(g_hTest, "Playing tone failed\n");
    1993     }
    1994 
    1995     return rc;
    1996 }
    1997 
    1998 /**
    1999  * @copydoc FNAUDIOTESTDESTROY
    2000  */
    2001 static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
    2002 {
    2003     RT_NOREF(pTstEnv, pvCtx);
    2004 
    2005     return VINF_SUCCESS;
    2006 }
    2007 
    2008 /**
    2009  * @copydoc FNAUDIOTESTSETUP
    2010  */
    2011 static DECLCALLBACK(int) audioTestRecordToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)
    2012 {
    2013     RT_NOREF(pTstEnv, pTstDesc, ppvCtx);
    2014 
    2015     pTstParmsAcq->enmType     = AUDIOTESTTYPE_TESTTONE_RECORD;
    2016 
    2017     RT_ZERO(pTstParmsAcq->TestTone);
    2018     PDMAudioPropsInit(&pTstParmsAcq->TestTone.Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);
    2019 
    2020     pTstParmsAcq->enmDir      = PDMAUDIODIR_IN;
    2021 #ifdef DEBUG
    2022     pTstParmsAcq->cIterations = 2;
    2023 #else
    2024     pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);
    2025 #endif
    2026     pTstParmsAcq->idxCurrent  = 0;
    2027 
    2028     return VINF_SUCCESS;
    2029 }
    2030 
    2031 /**
    2032  * @copydoc FNAUDIOTESTEXEC
    2033  */
    2034 static DECLCALLBACK(int) audioTestRecordToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)
    2035 {
    2036     RT_NOREF(pvCtx);
    2037 
    2038     int rc = VINF_SUCCESS;
    2039 
    2040     PAUDIOTESTSTREAM pStream = &pTstEnv->aStreams[0];
    2041 
    2042     for (uint32_t i = 0; i < pTstParms->cIterations; i++)
    2043     {
    2044         pTstParms->TestTone.msDuration = RTRandU32Ex(50 /* ms */, RT_MS_10SEC); /** @todo Record even longer? */
    2045 
    2046         PAUDIOTESTENTRY pTst;
    2047         rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone", pTstParms, &pTst);
    2048         if (RT_SUCCESS(rc))
    2049         {
    2050             /** @todo  For now we're (re-)creating the recording stream for each iteration. Change that to be random. */
    2051             rc = audioTestCreateStreamDefaultIn(pTstEnv, pStream, &pTstParms->TestTone.Props);
    2052             if (RT_SUCCESS(rc))
    2053                 rc = audioTestRecordTone(pTstEnv, pStream, &pTstParms->TestTone);
    2054 
    2055             int rc2 = audioTestStreamDestroy(pTstEnv, pStream);
    2056             if (RT_SUCCESS(rc))
    2057                 rc = rc2;
    2058 
    2059             if (RT_SUCCESS(rc))
    2060             {
    2061                AudioTestSetTestDone(pTst);
    2062             }
    2063             else
    2064                 AudioTestSetTestFailed(pTst, rc, "Recording test tone failed");
    2065         }
    2066 
    2067         if (RT_FAILURE(rc))
    2068             RTTestFailed(g_hTest, "Recording tone failed\n");
    2069     }
    2070 
    2071     return rc;
    2072 }
    2073 
    2074 /**
    2075  * @copydoc FNAUDIOTESTDESTROY
    2076  */
    2077 static DECLCALLBACK(int) audioTestRecordToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)
    2078 {
    2079     RT_NOREF(pTstEnv, pvCtx);
    2080 
    2081     return VINF_SUCCESS;
    2082 }
    2083 
    2084 
    2085 /*********************************************************************************************************************************
    2086 *   Test execution                                                                                                               *
    2087 *********************************************************************************************************************************/
    2088 
    2089 static AUDIOTESTDESC g_aTests[] =
    2090 {
    2091     /* pszTest      fExcluded      pfnSetup */
    2092     { "PlayTone",   false,         audioTestPlayToneSetup,       audioTestPlayToneExec,      audioTestPlayToneDestroy },
    2093     { "RecordTone", false,         audioTestRecordToneSetup,     audioTestRecordToneExec,    audioTestRecordToneDestroy }
    2094 };
    2095 
    2096 /**
    2097  * Runs one specific audio test.
    2098  *
    2099  * @returns VBox status code.
    2100  * @param   pTstEnv             Test environment to use for running the test.
    2101  * @param   pTstDesc            Test to run.
    2102  * @param   uSeq                Test sequence # in case there are more tests.
    2103  * @param   pOverrideParms      Test parameters for overriding the actual test parameters. Optional.
    2104  */
    2105 static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc,
    2106                         unsigned uSeq, PAUDIOTESTPARMS pOverrideParms)
    2107 {
    2108     RT_NOREF(uSeq);
    2109 
    2110     int rc;
    2111 
    2112     AUDIOTESTPARMS TstParms;
    2113     audioTestParmsInit(&TstParms);
    2114 
    2115     RTTestSub(g_hTest, pTstDesc->pszName);
    2116 
    2117     if (pTstDesc->fExcluded)
    2118     {
    2119         RTTestSkipped(g_hTest, "Excluded from list");
    2120         return VINF_SUCCESS;
    2121     }
    2122 
    2123     void *pvCtx = NULL; /* Test-specific opaque context. Optional and can be NULL. */
    2124 
    2125     if (pTstDesc->pfnSetup)
    2126     {
    2127         rc = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);
    2128         if (RT_FAILURE(rc))
    2129         {
    2130             RTTestFailed(g_hTest, "Test setup failed\n");
    2131             return rc;
    2132         }
    2133     }
    2134 
    2135     audioTestCombineParms(&TstParms, pOverrideParms);
    2136 
    2137     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%u: %RU32 iterations\n", uSeq, TstParms.cIterations);
    2138 
    2139     if (strlen(TstParms.Dev.szName)) /** @todo Refine this check. */
    2140         rc = audioTestDeviceOpen(&TstParms.Dev);
    2141 
    2142     AssertPtr(pTstDesc->pfnExec);
    2143     rc = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);
    2144 
    2145     RTTestSubDone(g_hTest);
    2146 
    2147     if (pTstDesc->pfnDestroy)
    2148     {
    2149         int rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);
    2150         if (RT_SUCCESS(rc))
    2151             rc = rc2;
    2152 
    2153         if (RT_FAILURE(rc2))
    2154             RTTestFailed(g_hTest, "Test destruction failed\n");
    2155     }
    2156 
    2157     rc = audioTestDeviceClose(&TstParms.Dev);
    2158 
    2159     audioTestParmsDestroy(&TstParms);
    2160 
    2161     return rc;
    2162 }
    2163 
    2164 /**
    2165  * Runs all specified tests in a row.
    2166  *
    2167  * @returns VBox status code.
    2168  * @param   pTstEnv             Test environment to use for running all tests.
    2169  * @param   pOverrideParms      Test parameters for (some / all) specific test parameters. Optional.
    2170  */
    2171 static int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)
    2172 {
    2173     int rc = VINF_SUCCESS;
    2174 
    2175     unsigned uSeq = 0;
    2176     for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
    2177     {
    2178         int rc2 = audioTestOne(pTstEnv, &g_aTests[i], uSeq, pOverrideParms);
    2179         if (RT_SUCCESS(rc))
    2180             rc = rc2;
    2181 
    2182         if (!g_aTests[i].fExcluded)
    2183             uSeq++;
    2184     }
    2185 
    2186     return rc;
    2187 }
    2188 
    2189 /** Option help for the 'test' command.   */
    2190 static DECLCALLBACK(const char *) audioTestCmdTestHelp(PCRTGETOPTDEF pOpt)
    2191 {
    2192     switch (pOpt->iShort)
    2193     {
    2194         case 'd':                 return "Go via DrvAudio instead of directly interfacing with the backend.";
    2195         case VKAT_TEST_OPT_DEV:   return "Use the specified audio device";
    2196         case 'e':                 return "Exclude the given test id from the list";
    2197         case 'a':                 return "Exclude all tests from the list (useful to enable single tests later with --include)";
    2198         case 'i':                 return "Include the given test id in the list";
    2199     }
    2200     return NULL;
    2201 }
    2202 
    2203 /**
    2204  * Main (entry) function for the testing functionality of VKAT.
    2205  *
    2206  * @returns Program exit code.
    2207  * @param   pGetState   RTGetOpt state.
    2208  */
    2209 static DECLCALLBACK(RTEXITCODE) audioTestMain(PRTGETOPTSTATE pGetState)
    2210 {
    2211     AUDIOTESTENV TstEnv;
    2212     RT_ZERO(TstEnv);
    2213 
    2214     AUDIOTESTPARMS TstCust;
    2215     audioTestParmsInit(&TstCust);
    2216 
    2217     const char *pszDevice     = NULL; /* Custom device to use. Can be NULL if not being used. */
    2218     const char *pszTag        = NULL; /* Custom tag to use. Can be NULL if not being used. */
    2219     PCPDMDRVREG pDrvReg       = g_aBackends[0].pDrvReg;
    2220     bool        fWithDrvAudio = false;
    2221     uint8_t     cPcmSampleBit = 0;
    2222     uint8_t     cPcmChannels  = 0;
    2223     uint32_t    uPcmHz        = 0;
    2224     bool        fPcmSigned    = true;
    2225     const char *pszTcpAddr    = NULL;
    2226     uint16_t    uTcpPort      = 0;
    2227 
    2228     int           rc;
    2229     RTGETOPTUNION ValueUnion;
    2230     while ((rc = RTGetOpt(pGetState, &ValueUnion)))
    2231     {
    2232         switch (rc)
    2233         {
    2234             case 'a':
    2235                 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)
    2236                     g_aTests[i].fExcluded = true;
    2237                 break;
    2238 
    2239             case 'b':
    2240                 pDrvReg = NULL;
    2241                 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
    2242                     if (   strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 0
    2243                         || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)
    2244                     {
    2245                         pDrvReg = g_aBackends[i].pDrvReg;
    2246                         break;
    2247                     }
    2248                 if (pDrvReg == NULL)
    2249                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);
    2250                 break;
    2251 
    2252             case 'd':
    2253                 fWithDrvAudio = true;
    2254                 break;
    2255 
    2256             case 'e':
    2257                 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
    2258                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);
    2259                 g_aTests[ValueUnion.u32].fExcluded = true;
    2260                 break;
    2261 
    2262             case VKAT_TEST_OPT_ATS_ADDR:
    2263                 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)
    2264                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must specify a test mode first!");
    2265                 pszTcpAddr = ValueUnion.psz;
    2266                 break;
    2267 
    2268             case VKAT_TEST_OPT_ATS_PORT:
    2269                 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)
    2270                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must specify a test mode first!");
    2271                 uTcpPort = ValueUnion.u32;
    2272                 break;
    2273 
    2274             case VKAT_TEST_OPT_MODE:
    2275                 if (TstEnv.enmMode != AUDIOTESTMODE_UNKNOWN)
    2276                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Test mode (guest / host) already specified");
    2277                 TstEnv.enmMode = RTStrICmp(ValueUnion.psz, "guest") == 0 ? AUDIOTESTMODE_GUEST : AUDIOTESTMODE_HOST;
    2278                 break;
    2279 
    2280             case 'i':
    2281                 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))
    2282                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);
    2283                 g_aTests[ValueUnion.u32].fExcluded = false;
    2284                 break;
    2285 
    2286             case VKAT_TEST_OPT_COUNT:
    2287                 return RTMsgErrorExitFailure("Not yet implemented!");
    2288 
    2289             case VKAT_TEST_OPT_DEV:
    2290                 pszDevice = ValueUnion.psz;
    2291                 break;
    2292 
    2293             case VKAT_TEST_OPT_PAUSE:
    2294                 return RTMsgErrorExitFailure("Not yet implemented!");
    2295 
    2296             case VKAT_TEST_OPT_OUTDIR:
    2297                 rc = RTStrCopy(TstEnv.szPathOut, sizeof(TstEnv.szPathOut), ValueUnion.psz);
    2298                 if (RT_FAILURE(rc))
    2299                     return RTMsgErrorExitFailure("Failed to copy out directory: %Rrc", rc);
    2300                 break;
    2301 
    2302             case VKAT_TEST_OPT_PCM_BIT:
    2303                 cPcmSampleBit = ValueUnion.u8;
    2304                 break;
    2305 
    2306             case VKAT_TEST_OPT_PCM_CHAN:
    2307                 cPcmChannels = ValueUnion.u8;
    2308                 break;
    2309 
    2310             case VKAT_TEST_OPT_PCM_HZ:
    2311                 uPcmHz = ValueUnion.u32;
    2312                 break;
    2313 
    2314             case VKAT_TEST_OPT_PCM_SIGNED:
    2315                 fPcmSigned = ValueUnion.f;
    2316                 break;
    2317 
    2318             case VKAT_TEST_OPT_TAG:
    2319                 pszTag = ValueUnion.psz;
    2320                 break;
    2321 
    2322             case VKAT_TEST_OPT_TEMPDIR:
    2323                 rc = RTStrCopy(TstEnv.szPathTemp, sizeof(TstEnv.szPathTemp), ValueUnion.psz);
    2324                 if (RT_FAILURE(rc))
    2325                     return RTMsgErrorExit(RTEXITCODE_FAILURE, "Temp dir invalid, rc=%Rrc", rc);
    2326                 break;
    2327 
    2328             case VKAT_TEST_OPT_VOL:
    2329                 TstCust.TestTone.uVolumePercent = ValueUnion.u8;
    2330                 break;
    2331 
    2332             AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
    2333 
    2334             default:
    2335                 return RTGetOptPrintError(rc, &ValueUnion);
    2336         }
    2337     }
    2338 
    2339     /*
    2340      * Start testing.
    2341      */
    2342     RTTestBanner(g_hTest);
    2343 
    2344     /* Initialize the custom test parameters with sensible defaults if nothing else is given. */
    2345     PDMAudioPropsInit(&TstCust.TestTone.Props,
    2346                       cPcmSampleBit ? cPcmSampleBit / 8 : 2 /* 16-bit */, fPcmSigned, cPcmChannels ? cPcmChannels : 2,
    2347                       uPcmHz ? uPcmHz : 44100);
    2348 
    2349     if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)
    2350         return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No test mode specified!\n");
    2351 
    2352     if (TstEnv.enmMode == AUDIOTESTMODE_HOST)
    2353     {
    2354         /* Use the default port is none is specified. */
    2355         if (!uTcpPort)
    2356             uTcpPort = ATS_DEFAULT_PORT;
    2357 
    2358         if (!pszTcpAddr)
    2359             return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--ats-address missing\n");
    2360     }
    2361 
    2362     /* For now all tests have the same test environment. */
    2363     rc = audioTestEnvInit(&TstEnv, pDrvReg, fWithDrvAudio, pszTag, pszTcpAddr, uTcpPort);
    2364     if (RT_SUCCESS(rc))
    2365     {
    2366         PPDMAUDIOHOSTDEV pDev;
    2367         rc = audioTestDevicesEnumerateAndCheck(&TstEnv, pszDevice, &pDev);
    2368         if (RT_SUCCESS(rc))
    2369             audioTestWorker(&TstEnv, &TstCust);
    2370 
    2371         /* Before destroying the test environment, pack up the test set so
    2372          * that it's ready for transmission. */
    2373         char szFileOut[RTPATH_MAX];
    2374         rc = AudioTestSetPack(&TstEnv.Set, TstEnv.szPathOut, szFileOut, sizeof(szFileOut));
    2375         if (RT_SUCCESS(rc))
    2376             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", szFileOut);
    2377 
    2378 #ifndef DEBUG_andy
    2379         /* Clean up. */
    2380         AudioTestSetClose(&TstEnv.Set); /* wipe fails on windows if the manifest file is open*/
    2381 
    2382         int rc2 = AudioTestSetWipe(&TstEnv.Set);
    2383         AssertRC(rc2); /* Annoying, but not test-critical. */
    2384 #endif
    2385         audioTestEnvDestroy(&TstEnv);
    2386     }
    2387 
    2388     audioTestParmsDestroy(&TstCust);
    2389 
    2390     if (RT_FAILURE(rc)) /* Let us know that something went wrong in case we forgot to mention it. */
    2391         RTTestFailed(g_hTest, "Tested failed with %Rrc\n", rc);
    2392 
    2393     /*
    2394      * Print summary and exit.
    2395      */
    2396     return RTTestSummaryAndDestroy(g_hTest);
    2397 }
    2398 
    2399 
    2400 /*********************************************************************************************************************************
    2401 *   Command: verify                                                                                                              *
    2402 *********************************************************************************************************************************/
    2403 
    2404 static int audioVerifyOpenTestSet(const char *pszPathSet, PAUDIOTESTSET pSet)
    2405 {
    2406     int rc;
    2407 
    2408     char szPathExtracted[RTPATH_MAX];
    2409 
    2410     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Opening test set '%s'\n", pszPathSet);
    2411 
    2412     const bool fPacked = AudioTestSetIsPacked(pszPathSet);
    2413     if (fPacked)
    2414     {
    2415         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set is an archive and needs to be unpacked\n");
    2416 
    2417         char szPathTemp[RTPATH_MAX];
    2418         rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
    2419         if (RT_SUCCESS(rc))
    2420         {
    2421             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using temporary directory '%s'\n", szPathTemp);
    2422 
    2423             rc = RTPathJoin(szPathExtracted, sizeof(szPathExtracted), szPathTemp, "vkat-XXXX");
    2424             if (RT_SUCCESS(rc))
    2425             {
    2426                 rc = RTDirCreateTemp(szPathExtracted, 0755);
    2427                 if (RT_SUCCESS(rc))
    2428                 {
    2429                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unpacking archive to '%s'\n", szPathExtracted);
    2430                     rc = AudioTestSetUnpack(pszPathSet, szPathExtracted);
    2431                     if (RT_SUCCESS(rc))
    2432                         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Archive successfully unpacked\n");
    2433                 }
    2434             }
    2435         }
    2436     }
    2437     else
    2438         rc = VINF_SUCCESS;
    2439 
    2440     if (RT_SUCCESS(rc))
    2441         rc = AudioTestSetOpen(pSet, fPacked ? szPathExtracted : pszPathSet);
    2442 
    2443     if (RT_FAILURE(rc))
    2444         RTTestFailed(g_hTest, "Unable to open / unpack test set archive: %Rrc", rc);
    2445 
    2446     return rc;
    2447 }
    2448 
    2449 /**
    2450  * Verifies one single test set.
    2451  *
    2452  * @returns VBox status code.
    2453  * @param   pszPathSetA         Absolute path to test set A.
    2454  * @param   pszPathSetB         Absolute path to test set B.
    2455  */
    2456 static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB)
    2457 {
    2458     RTTestSubF(g_hTest, "Verifying");
    2459     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verifying test set '%s' with test set '%s'\n", pszPathSetA, pszPathSetB);
    2460 
    2461     AUDIOTESTSET SetA, SetB;
    2462     int rc = audioVerifyOpenTestSet(pszPathSetA, &SetA);
    2463     if (RT_SUCCESS(rc))
    2464         rc = audioVerifyOpenTestSet(pszPathSetB, &SetB);
    2465 
    2466     if (RT_SUCCESS(rc))
    2467     {
    2468         AUDIOTESTERRORDESC errDesc;
    2469         rc = AudioTestSetVerify(&SetA, &SetB, &errDesc);
    2470         if (RT_SUCCESS(rc))
    2471         {
    2472             if (AudioTestErrorDescFailed(&errDesc))
    2473             {
    2474                 /** @todo Use some AudioTestErrorXXX API for enumeration here later. */
    2475                 PAUDIOTESTERRORENTRY pErrEntry;
    2476                 RTListForEach(&errDesc.List, pErrEntry, AUDIOTESTERRORENTRY, Node)
    2477                     RTTestFailed(g_hTest, pErrEntry->szDesc);
    2478             }
    2479             else
    2480                 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification successful\n");
    2481 
    2482             AudioTestErrorDescDestroy(&errDesc);
    2483         }
    2484         else
    2485             RTTestFailed(g_hTest, "Verification failed with %Rrc", rc);
    2486     }
    2487 
    2488     AudioTestSetClose(&SetA);
    2489     AudioTestSetClose(&SetB);
    2490 
    2491     RTTestSubDone(g_hTest);
    2492 
    2493     return rc;
    2494 }
    2495 
    2496 /**
    2497  * Main (entry) function for the verification functionality of VKAT.
    2498  *
    2499  * @returns Program exit code.
    2500  * @param   pGetState   RTGetOpt state.
    2501  */
    2502 static DECLCALLBACK(RTEXITCODE) audioVerifyMain(PRTGETOPTSTATE pGetState)
    2503 {
    2504     /*
    2505      * Parse options and process arguments.
    2506      */
    2507     char     *pszSetA = NULL;
    2508     char     *pszSetB = NULL;
    2509     unsigned iTestSet = 0;
    2510 
    2511     int           rc;
    2512     RTGETOPTUNION ValueUnion;
    2513     while ((rc = RTGetOpt(pGetState, &ValueUnion)))
    2514     {
    2515         switch (rc)
    2516         {
    2517             case VKAT_VERIFY_OPT_TAG:
    2518                 break;
    2519 
    2520             case VINF_GETOPT_NOT_OPTION:
    2521             {
    2522                 char **ppszSet = iTestSet == 0 ? &pszSetA : &pszSetB;
    2523 
    2524                 if (iTestSet == 0)
    2525                     RTTestBanner(g_hTest);
    2526 
    2527                 *ppszSet = RTStrDup(ValueUnion.psz);
    2528                 AssertPtrReturn(*ppszSet, RTEXITCODE_FAILURE);
    2529 
    2530                 iTestSet++;
    2531                 break;
    2532             }
    2533 
    2534             AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
    2535 
    2536             default:
    2537                 return RTGetOptPrintError(rc, &ValueUnion);
    2538         }
    2539     }
    2540 
    2541     if (!iTestSet)
    2542         return RTMsgErrorExitFailure("At least one test set must be specified");
    2543 
    2544     if (iTestSet > 2)
    2545         return RTMsgErrorExitFailure("Only two test sets can be verified at one time");
    2546 
    2547     /*
    2548      * If only test set A is given, default to the current directory
    2549      * for test set B.
    2550      */
    2551     if (iTestSet == 1)
    2552     {
    2553         char szDirCur[RTPATH_MAX];
    2554         rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
    2555         if (RT_SUCCESS(rc))
    2556         {
    2557             Assert(pszSetB == NULL);
    2558             pszSetB = RTStrDup(szDirCur);
    2559             AssertPtrReturn(pszSetB, RTEXITCODE_FAILURE);
    2560         }
    2561         else
    2562             RTTestFailed(g_hTest, "Failed to retrieve current directory: %Rrc", rc);
    2563     }
    2564 
    2565     if (RT_SUCCESS(rc))
    2566         rc = audioVerifyOne(pszSetA, pszSetB);
    2567 
    2568     RTStrFree(pszSetA);
    2569     RTStrFree(pszSetB);
    2570 
    2571     /*
    2572      * Print summary and exit.
    2573      */
    2574     return RTTestSummaryAndDestroy(g_hTest);
    2575 }
    2576 
    2577 
    2578 /*********************************************************************************************************************************
    2579 *   Command: play                                                                                                                *
    2580 *********************************************************************************************************************************/
    2581 
    2582 /**
    2583  * Worker for audioTestCmdPlayHandler that plays one file.
    2584  */
    2585 static RTEXITCODE audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,
    2586                                    uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, bool fWithDrvAudio)
    2587 {
    2588     /*
    2589      * First we must open the file and determin the format.
    2590      */
    2591     RTERRINFOSTATIC ErrInfo;
    2592     AUDIOTESTWAVEFILE WaveFile;
    2593     int rc = AudioTestWaveFileOpen(pszFile, &WaveFile, RTErrInfoInitStatic(&ErrInfo));
    2594     if (RT_FAILURE(rc))
    2595         return RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
    2596 
    2597     if (g_uVerbosity > 0)
    2598     {
    2599         char szTmp[128];
    2600         RTMsgInfo("Opened '%s' for playing\n", pszFile);
    2601         RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));
    2602         RTMsgInfo("Size:   %'RU32 bytes / %#RX32 / %'RU32 frames / %'RU64 ns\n",
    2603                   WaveFile.cbSamples, WaveFile.cbSamples,
    2604                   PDMAudioPropsBytesToFrames(&WaveFile.Props, WaveFile.cbSamples),
    2605                   PDMAudioPropsBytesToNano(&WaveFile.Props, WaveFile.cbSamples));
    2606     }
    2607 
    2608     /*
    2609      * Construct the driver stack.
    2610      */
    2611     RTEXITCODE          rcExit = RTEXITCODE_FAILURE;
    2612     AUDIOTESTDRVSTACK   DrvStack;
    2613     rc = audioTestDriverStackInit(&DrvStack, pDrvReg, fWithDrvAudio);
    2614     if (RT_SUCCESS(rc))
    2615     {
    2616         /*
    2617          * Set the output device if one is specified.
    2618          */
    2619         rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_OUT, pszDevId);
    2620         if (RT_SUCCESS(rc))
    2621         {
    2622             /*
    2623              * Open a stream for the output.
    2624              */
    2625             PDMAUDIOSTREAMCFG CfgAcq;
    2626             PPDMAUDIOSTREAM   pStream = NULL;
    2627             rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &WaveFile.Props, cMsBufferSize,
    2628                                                         cMsPreBuffer, cMsSchedulingHint, &pStream, &CfgAcq);
    2629             if (RT_SUCCESS(rc))
    2630             {
    2631                 rc = audioTestDriverStackStreamEnable(&DrvStack, pStream);
    2632                 if (RT_SUCCESS(rc))
    2633                 {
    2634                     uint64_t const nsStarted = RTTimeNanoTS();
    2635 
    2636                     /*
    2637                      * Transfer data as quickly as we're allowed.
    2638                      */
    2639                     for (;;)
    2640                     {
    2641                         /* Read a chunk from the wave file. */
    2642                         uint8_t  abSamples[16384];
    2643                         size_t   cbSamples = 0;
    2644                         rc = AudioTestWaveFileRead(&WaveFile, abSamples, sizeof(abSamples), &cbSamples);
    2645                         if (RT_SUCCESS(rc) && cbSamples > 0)
    2646                         {
    2647                             /* Transfer the data to the audio stream. */
    2648                             for (uint32_t offSamples = 0; offSamples < cbSamples;)
    2649                             {
    2650                                 uint32_t const cbCanWrite = audioTestDriverStackStreamGetWritable(&DrvStack, pStream);
    2651                                 if (cbCanWrite > 0)
    2652                                 {
    2653                                     uint32_t const cbToPlay = RT_MIN(cbCanWrite, (uint32_t)cbSamples - offSamples);
    2654                                     uint32_t       cbPlayed = 0;
    2655                                     rc = audioTestDriverStackStreamPlay(&DrvStack, pStream, &abSamples[offSamples],
    2656                                                                         cbToPlay, &cbPlayed);
    2657                                     if (RT_SUCCESS(rc))
    2658                                     {
    2659                                         if (cbPlayed)
    2660                                             offSamples += cbPlayed;
    2661                                         else
    2662                                         {
    2663                                             rcExit = RTMsgErrorExitFailure("Played zero out of %#x bytes - %#x bytes reported playable!\n",
    2664                                                                            cbToPlay, cbCanWrite);
    2665                                             break;
    2666                                         }
    2667                                     }
    2668                                     else
    2669                                     {
    2670                                         rcExit = RTMsgErrorExitFailure("Failed to play %#x bytes: %Rrc\n", cbToPlay, rc);
    2671                                         break;
    2672                                     }
    2673                                 }
    2674                                 else if (audioTestDriverStackStreamIsOkay(&DrvStack, pStream))
    2675                                     RTThreadSleep(RT_MIN(RT_MAX(1, CfgAcq.Device.cMsSchedulingHint), 256));
    2676                                 else
    2677                                 {
    2678                                     rcExit = RTMsgErrorExitFailure("Stream is not okay!\n");
    2679                                     break;
    2680                                 }
    2681                             }
    2682                         }
    2683                         else if (RT_SUCCESS(rc) && cbSamples == 0)
    2684                         {
    2685                             rcExit = RTEXITCODE_SUCCESS;
    2686                             break;
    2687                         }
    2688                         else
    2689                         {
    2690                             rcExit = RTMsgErrorExitFailure("Error reading wav file '%s': %Rrc", pszFile, rc);
    2691                             break;
    2692                         }
    2693                     }
    2694 
    2695                     /*
    2696                      * Drain the stream.
    2697                      */
    2698                     if (rcExit == RTEXITCODE_SUCCESS)
    2699                     {
    2700                         if (g_uVerbosity > 0)
    2701                             RTMsgInfo("%'RU64 ns: Draining...\n", RTTimeNanoTS() - nsStarted);
    2702                         rc = audioTestDriverStackStreamDrain(&DrvStack, pStream, true /*fSync*/);
    2703                         if (RT_SUCCESS(rc))
    2704                         {
    2705                             if (g_uVerbosity > 0)
    2706                                 RTMsgInfo("%'RU64 ns: Done\n", RTTimeNanoTS() - nsStarted);
    2707                         }
    2708                         else
    2709                             rcExit = RTMsgErrorExitFailure("Draining failed: %Rrc", rc);
    2710                     }
    2711                 }
    2712                 else
    2713                     rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc);
    2714                 audioTestDriverStackStreamDestroy(&DrvStack, pStream);
    2715             }
    2716             else
    2717                 rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
    2718         }
    2719         else
    2720             rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
    2721         audioTestDriverStackDelete(&DrvStack);
    2722     }
    2723     else
    2724         rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
    2725     AudioTestWaveFileClose(&WaveFile);
    2726     return rcExit;
    2727 }
    2728 
    2729 /**
    2730  * Command line parameters for test mode.
    2731  */
    2732 static const RTGETOPTDEF g_aCmdPlayOptions[] =
    2733 {
    2734     { "--backend",          'b',                          RTGETOPT_REQ_STRING  },
    2735     { "--output-device",    'o',                          RTGETOPT_REQ_STRING  },
    2736     { "--with-drv-audio",   'd',                          RTGETOPT_REQ_NOTHING },
    2737 };
    2738 
    2739 /** the 'play' command option help. */
    2740 static DECLCALLBACK(const char *) audioTestCmdPlayHelp(PCRTGETOPTDEF pOpt)
    2741 {
    2742     switch (pOpt->iShort)
    2743     {
    2744         case 'b': return "The audio backend to use.";
    2745         case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";
    2746         case 'o': return "The ID of the output device to use.";
    2747         default:  return NULL;
    2748     }
    2749 }
    2750 
    2751 /**
    2752  * The 'play' command handler.
    2753  *
    2754  * @returns Program exit code.
    2755  * @param   pGetState   RTGetOpt state.
    2756  */
    2757 static DECLCALLBACK(RTEXITCODE) audioTestCmdPlayHandler(PRTGETOPTSTATE pGetState)
    2758 {
    2759     /* Option values: */
    2760     PCPDMDRVREG pDrvReg           = g_aBackends[0].pDrvReg;
    2761     uint32_t    cMsBufferSize     = UINT32_MAX;
    2762     uint32_t    cMsPreBuffer      = UINT32_MAX;
    2763     uint32_t    cMsSchedulingHint = UINT32_MAX;
    2764     const char *pszDevId          = NULL;
    2765     bool        fWithDrvAudio     = false;
    2766 
    2767     /* Argument processing loop: */
    2768     int           rc;
    2769     RTGETOPTUNION ValueUnion;
    2770     while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)
    2771     {
    2772         switch (rc)
    2773         {
    2774             case 'b':
    2775                 pDrvReg = NULL;
    2776                 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
    2777                     if (   strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 0
    2778                         || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)
    2779                     {
    2780                         pDrvReg = g_aBackends[i].pDrvReg;
    2781                         break;
    2782                     }
    2783                 if (pDrvReg == NULL)
    2784                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);
    2785                 break;
    2786 
    2787             case 'd':
    2788                 fWithDrvAudio = true;
    2789                 break;
    2790 
    2791             case 'o':
    2792                 pszDevId = ValueUnion.psz;
    2793                 break;
    2794 
    2795             case VINF_GETOPT_NOT_OPTION:
    2796             {
    2797                 RTEXITCODE rcExit = audioTestPlayOne(ValueUnion.psz, pDrvReg, pszDevId, cMsBufferSize, cMsPreBuffer,
    2798                                                      cMsSchedulingHint, fWithDrvAudio);
    2799                 if (rcExit != RTEXITCODE_SUCCESS)
    2800                     return rcExit;
    2801                 break;
    2802             }
    2803 
    2804             AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
    2805 
    2806             default:
    2807                 return RTGetOptPrintError(rc, &ValueUnion);
    2808         }
    2809     }
    2810     return RTEXITCODE_SUCCESS;
    2811 }
    2812 
    2813 /**
    2814  * Command line parameters for self-test mode.
    2815  */
    2816 static const RTGETOPTDEF g_aCmdSelftestOptions[] =
    2817 {
    2818     { "--ats-host",         VKAT_SELFTEST_OPT_ATS_HOST,   RTGETOPT_REQ_STRING },
    2819     { "--backend",          'b',                          RTGETOPT_REQ_STRING  },
    2820     { "--with-drv-audio",   'd',                          RTGETOPT_REQ_NOTHING },
    2821 };
    2822 
    2823 /** the 'selftest' command option help. */
    2824 static DECLCALLBACK(const char *) audioTestCmdSelftestHelp(PCRTGETOPTDEF pOpt)
    2825 {
    2826     switch (pOpt->iShort)
    2827     {
    2828         case 'b': return "The audio backend to use.";
    2829         case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";
    2830         default:  return NULL;
    2831     }
    2832 }
    2833 
    2834 /**
    2835  * Tests the Audio Test Service (ATS).
    2836  *
    2837  * @returns VBox status code.
    2838  * @param   pDrvReg             Backend driver to use.
    2839  * @param   pszAdr              Address of ATS server to connect to.
    2840  *                              If NULL, an own (local) ATS server will be created.
    2841  */
    2842 static int audioTestDoSelftestAts(PCPDMDRVREG pDrvReg, const char *pszAdr)
    2843 {
    2844     AUDIOTESTENV TstEnv;
    2845     int rc = audioTestDriverStackInit(&TstEnv.DrvStack, pDrvReg, true /* fWithDrvAudio */);
    2846     if (RT_SUCCESS(rc))
    2847     {
    2848         /** @todo Make stream parameters configurable. */
    2849         PDMAUDIOPCMPROPS  Props;
    2850         PDMAudioPropsInit(&Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);
    2851 
    2852         PDMAUDIOSTREAMCFG CfgAcq;
    2853         PPDMAUDIOSTREAM   pStream = NULL;
    2854         rc = audioTestDriverStackStreamCreateOutput(&TstEnv.DrvStack, &Props,
    2855                                                     UINT32_MAX /* cMsBufferSize */,
    2856                                                     UINT32_MAX /* cMsPreBuffer */,
    2857                                                     UINT32_MAX /* cMsSchedulingHint */, &pStream, &CfgAcq);
    2858         if (RT_SUCCESS(rc))
    2859         {
    2860             rc = audioTestDriverStackStreamEnable(&TstEnv.DrvStack, pStream);
    2861             if (RT_SUCCESS(rc))
    2862             {
    2863                 ATSCALLBACKCTX Ctx;
    2864                 Ctx.pTstEnv = &TstEnv;
    2865 
    2866                 ATSCALLBACKS Callbacks;
    2867                 Callbacks.pfnTonePlay = audioTestSvcTonePlayCallback;
    2868                 Callbacks.pvUser      = &Ctx;
    2869 
    2870                 /* Start an own ATS instance if no address to connect was specified. */
    2871                 ATSSERVER Srv;
    2872                 if (pszAdr == NULL)
    2873                 {
    2874                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting ATS ...\n");
    2875 
    2876                     rc = AudioTestSvcInit(&Srv, &Callbacks);
    2877                     if (RT_SUCCESS(rc))
    2878                     {
    2879                         rc = AudioTestSvcStart(&Srv);
    2880                         if (RT_SUCCESS(rc))
    2881                             RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "ATS running\n");
    2882                     }
    2883                 }
    2884                 else
    2885                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting to ATS at '%s' ...\n", pszAdr);
    2886 
    2887                 if (RT_SUCCESS(rc))
    2888                 {
    2889                     ATSCLIENT Conn;
    2890                     rc = AudioTestSvcClientConnect(&Conn, NULL, 0);
    2891                     if (RT_SUCCESS(rc))
    2892                     {
    2893                         RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to ATS, testing ...\n");
    2894 
    2895                         /* Do the bare minimum here to get a test tone out. */
    2896                         AUDIOTESTTONEPARMS ToneParms;
    2897                         RT_ZERO(ToneParms);
    2898                         ToneParms.msDuration = RTRandU32Ex(250, 1000 * 5);
    2899                         memcpy(&ToneParms.Props, &CfgAcq.Props, sizeof(PDMAUDIOPCMPROPS));
    2900 
    2901                         rc = AudioTestSvcClientTonePlay(&Conn, &CfgAcq, &ToneParms);
    2902 
    2903                         int rc2 = AudioTestSvcClientClose(&Conn);
    2904                         if (RT_SUCCESS(rc))
    2905                             rc = rc2;
    2906                     }
    2907                     else
    2908                         RTTestFailed(g_hTest, "Connecting to ATS failed, rc=%Rrc\n", rc);
    2909 
    2910                     int rc2 = AudioTestSvcShutdown(&Srv);
    2911                     if (RT_SUCCESS(rc))
    2912                         rc = rc2;
    2913                 }
    2914 
    2915                 if (pszAdr == NULL)
    2916                 {
    2917                     RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down ATS ...\n");
    2918 
    2919                     int rc2 = AudioTestSvcDestroy(&Srv);
    2920                         if (RT_SUCCESS(rc))
    2921                             rc = rc2;
    2922                 }
    2923             }
    2924         }
    2925     }
    2926 
    2927     if (RT_FAILURE(rc))
    2928         RTTestFailed(g_hTest, "Testing ATS failed with %Rrc\n", rc);
    2929 
    2930     return rc;
    2931 }
    2932 
    2933 /**
    2934  * Main function for performing the self-tests.
    2935  *
    2936  * @returns VBox status code.
    2937  * @param   pDrvReg             Backend driver to use.
    2938  * @param   pszAtsAdr           Address of ATS server to connect to.
    2939  *                              If NULL, an own (local) ATS server will be created.
    2940  */
    2941 static int audioTestDoSelftest(PCPDMDRVREG pDrvReg, const char *pszAtsAdr)
    2942 {
    2943     int rc = audioTestDoSelftestAts(pDrvReg, pszAtsAdr);
    2944     if (RT_FAILURE(rc))
    2945         RTTestFailed(g_hTest, "Self-test failed with: %Rrc", rc);
    2946 
    2947     return rc;
    2948 }
    2949 
    2950 /**
    2951  * The 'selftest' command handler.
    2952  *
    2953  * @returns Program exit code.
    2954  * @param   pGetState   RTGetOpt state.
    2955  */
    2956 static DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
    2957 {
    2958     /* Option values: */
    2959     PCPDMDRVREG pDrvReg           = g_aBackends[0].pDrvReg;
    2960     bool        fWithDrvAudio     = false;
    2961     char       *pszAtsAddr        = NULL;
    2962 
    2963     /* Argument processing loop: */
    2964     int           rc;
    2965     RTGETOPTUNION ValueUnion;
    2966     while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)
    2967     {
    2968         switch (rc)
    2969         {
    2970             case VKAT_SELFTEST_OPT_ATS_HOST:
    2971             {
    2972                 pszAtsAddr = RTStrDup(ValueUnion.psz);
    2973                 break;
    2974             }
    2975 
    2976             case 'b':
    2977             {
    2978                 pDrvReg = NULL;
    2979                 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
    2980                     if (   strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 0
    2981                         || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)
    2982                     {
    2983                         pDrvReg = g_aBackends[i].pDrvReg;
    2984                         break;
    2985                     }
    2986                 if (pDrvReg == NULL)
    2987                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);
    2988                 break;
    2989             }
    2990 
    2991             case 'd':
    2992             {
    2993                 fWithDrvAudio = true;
    2994                 break;
    2995             }
    2996 
    2997             case VINF_GETOPT_NOT_OPTION:
    2998             {
    2999                 break;
    3000             }
    3001 
    3002             AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);
    3003 
    3004             default:
    3005                 return RTGetOptPrintError(rc, &ValueUnion);
    3006         }
    3007     }
    3008 
    3009     audioTestDoSelftest(pDrvReg, pszAtsAddr);
    3010 
    3011     RTStrFree(pszAtsAddr);
    3012 
    3013     /*
    3014      * Print summary and exit.
    3015      */
    3016     return RTTestSummaryAndDestroy(g_hTest);
    3017 }
    3018 
    3019 
    3020 /**
    3021  * Commands.
    3022  */
    3023 static struct
    3024 {
    3025     /** The command name. */
    3026     const char     *pszCommand;
    3027     /** The command handler.   */
    3028     DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(PRTGETOPTSTATE pGetState));
    3029 
    3030     /** Command description.   */
    3031     const char     *pszDesc;
    3032     /** Options array.  */
    3033     PCRTGETOPTDEF   paOptions;
    3034     /** Number of options in the option array. */
    3035     size_t          cOptions;
    3036     /** Gets help for an option. */
    3037     DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt));
    3038 } const g_aCommands[] =
    3039 {
    3040     {
    3041         "test",     audioTestMain,
    3042         "Runs audio tests and creates an audio test set.",
    3043         g_aCmdTestOptions,      RT_ELEMENTS(g_aCmdTestOptions),     audioTestCmdTestHelp
    3044     },
    3045     {
    3046         "verify",   audioVerifyMain,
    3047         "Verifies a formerly created audio test set.",
    3048         g_aCmdVerifyOptions,    RT_ELEMENTS(g_aCmdVerifyOptions),   NULL,
    3049     },
    3050     {
    3051         "play",     audioTestCmdPlayHandler,
    3052         "Plays one or more wave files.",
    3053         g_aCmdPlayOptions,      RT_ELEMENTS(g_aCmdPlayOptions),     audioTestCmdPlayHelp,
    3054     },
    3055     {
    3056         "selftest", audioTestCmdSelftestHandler,
    3057         "Performs self-tests.",
    3058         g_aCmdSelftestOptions,  RT_ELEMENTS(g_aCmdSelftestOptions), audioTestCmdSelftestHelp,
    3059     }
    3060 };
    3061 
    3062 /**
    3063  * Shows tool usage text.
    3064  */
    3065 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm)
    3066 {
    3067     RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n",
    3068                  RTPathFilename(RTProcExecutablePath()));
    3069     RTStrmPrintf(pStrm,
    3070                  "\n"
    3071                  "Global Options:\n"
    3072                  "  --debug-audio\n"
    3073                  "    Enables DrvAudio debugging.\n"
    3074                  "  --debug-audio-path=<path>\n"
    3075                  "    Tells DrvAudio where to put its debug output (wav-files).\n"
    3076                  "  -q, --quiet\n"
    3077                  "    Sets verbosity to zero.\n"
    3078                  "  -v, --verbose\n"
    3079                  "    Increase verbosity.\n"
    3080                  "  -V, --version\n"
    3081                  "    Displays version.\n"
    3082                  "  -h, -?, --help\n"
    3083                  "    Displays help.\n"
    3084                  );
    3085 
    3086     for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
    3087     {
    3088         RTStrmPrintf(pStrm,
    3089                      "\n"
    3090                      "Command '%s':\n"
    3091                      "    %s\n"
    3092                      "Options for '%s':\n",
    3093                      g_aCommands[iCmd].pszCommand, g_aCommands[iCmd].pszDesc, g_aCommands[iCmd].pszCommand);
    3094         PCRTGETOPTDEF const paOptions = g_aCommands[iCmd].paOptions;
    3095         for (unsigned i = 0; i < g_aCommands[iCmd].cOptions; i++)
    3096         {
    3097             if (RT_C_IS_PRINT(paOptions[i].iShort))
    3098                 RTStrmPrintf(pStrm, "  -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
    3099             else
    3100                 RTStrmPrintf(pStrm, "  %s\n", paOptions[i].pszLong);
    3101 
    3102             const char *pszHelp = NULL;
    3103             if (g_aCommands[iCmd].pfnOptionHelp)
    3104                 pszHelp = g_aCommands[iCmd].pfnOptionHelp(&paOptions[i]);
    3105             if (pszHelp)
    3106                 RTStrmPrintf(pStrm, "    %s\n", pszHelp);
    3107         }
    3108     }
    3109     return RTEXITCODE_SUCCESS;
    3110 }
    3111 
    3112 /**
    3113  * Shows tool version.
    3114  */
    3115 static RTEXITCODE audioTestVersion(void)
    3116 {
    3117     RTPrintf("v0.0.1\n");
    3118     return RTEXITCODE_SUCCESS;
    3119 }
    3120 
    3121 /**
    3122  * Shows the logo.
    3123  *
    3124  * @param   pStream             Output stream to show logo on.
    3125  */
    3126 static void audioTestShowLogo(PRTSTREAM pStream)
    3127 {
    3128     RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) Version " VBOX_VERSION_STRING " - r%s\n"
    3129                  "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
    3130                  "All rights reserved.\n\n", RTBldCfgRevisionStr());
    3131 }
    3132 
    3133 int main(int argc, char **argv)
    3134 {
    3135     /*
    3136      * Init IPRT and globals.
    3137      */
    3138     RTEXITCODE rcExit = RTTestInitAndCreate("AudioTest", &g_hTest);
    3139     if (rcExit != RTEXITCODE_SUCCESS)
    3140         return rcExit;
    3141 
    3142 #ifdef RT_OS_WINDOWS
    3143     HRESULT hrc = CoInitializeEx(NULL /*pReserved*/, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE);
    3144     if (FAILED(hrc))
    3145         RTMsgWarning("CoInitializeEx failed: %#x", hrc);
    3146 #endif
    3147 
    3148     /*
    3149      * Configure release logging to go to stderr.
    3150      */
    3151     static const char * const g_apszLogGroups[] = VBOX_LOGGROUP_NAMES;
    3152     int rc = RTLogCreate(&g_pRelLogger, RTLOGFLAGS_PREFIX_THREAD, "all.e.l", "VKAT_RELEASE_LOG",
    3153                          RT_ELEMENTS(g_apszLogGroups), g_apszLogGroups, RTLOGDEST_STDERR, "vkat-release.log");
    3154     if (RT_SUCCESS(rc))
    3155         RTLogRelSetDefaultInstance(g_pRelLogger);
    3156     else
    3157         RTMsgWarning("Failed to create release logger: %Rrc", rc);
    3158 
    3159     /*
    3160      * Process common options.
    3161      */
    3162     RTGETOPTSTATE GetState;
    3163     rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,
    3164                       RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
    3165     AssertRCReturn(rc, RTEXITCODE_INIT);
    3166 
    3167     RTGETOPTUNION ValueUnion;
    3168     while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
    3169     {
    3170         switch (rc)
    3171         {
    3172             case 'q':
    3173                 g_uVerbosity = 0;
    3174                 if (g_pRelLogger)
    3175                     RTLogGroupSettings(g_pRelLogger, "all=0 all.e");
    3176                 break;
    3177 
    3178             case 'v':
    3179                 g_uVerbosity++;
    3180                 if (g_pRelLogger)
    3181                     RTLogGroupSettings(g_pRelLogger, g_uVerbosity == 1 ? "all.e.l" : g_uVerbosity == 2 ? "all.e.l.f" : "all=~0");
    3182                 break;
    3183 
    3184             case 'V':
    3185                 return audioTestVersion();
    3186 
    3187             case 'h':
    3188                 audioTestShowLogo(g_pStdOut);
    3189                 return audioTestUsage(g_pStdOut);
    3190 
    3191             case VINF_GETOPT_NOT_OPTION:
    3192             {
    3193                 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)
    3194                     if (strcmp(ValueUnion.psz, g_aCommands[i].pszCommand) == 0)
    3195                     {
    3196                         size_t const cCombinedOptions  = g_aCommands[i].cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
    3197                         PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
    3198                         if (paCombinedOptions)
    3199                         {
    3200                             memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
    3201                             memcpy(&paCombinedOptions[RT_ELEMENTS(g_aCmdCommonOptions)],
    3202                                    g_aCommands[i].paOptions, g_aCommands[i].cOptions * sizeof(RTGETOPTDEF));
    3203 
    3204                             rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
    3205                                               GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    3206                             if (RT_SUCCESS(rc))
    3207                             {
    3208 
    3209                                 rcExit = g_aCommands[i].pfnHandler(&GetState);
    3210                                 RTMemFree(paCombinedOptions);
    3211                                 return rcExit;
    3212                             }
    3213                             return RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
    3214                         }
    3215                         return RTMsgErrorExitFailure("Out of memory!");
    3216                     }
    3217                 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
    3218                 audioTestUsage(g_pStdErr);
    3219                 return RTEXITCODE_SYNTAX;
    3220             }
    3221 
    3222             default:
    3223                 return RTGetOptPrintError(rc, &ValueUnion);
    3224         }
    3225     }
    3226 
    3227     RTMsgError("No command specified!\n");
    3228     audioTestUsage(g_pStdErr);
    3229     return RTEXITCODE_SYNTAX;
    3230 }
    3231 
Note: See TracChangeset for help on using the changeset viewer.

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