Index: trunk/src/VBox/ValidationKit/utils/audio/Makefile.kmk
===================================================================
--- trunk/src/VBox/ValidationKit/utils/audio/Makefile.kmk	(revision 89541)
+++ trunk/src/VBox/ValidationKit/utils/audio/Makefile.kmk	(revision 89544)
@@ -55,4 +55,6 @@
 vkat_SOURCES  = \
 	vkat.cpp \
+	vkatCommon.cpp \
+	vkatCmdPlayRec.cpp \
 	vkatDriverStack.cpp \
 	$(VKAT_PATH_AUDIO)/AudioTest.cpp \
@@ -67,4 +69,5 @@
 vkat_DEFS     += VBOX_WITH_AUDIO_VALIDATIONKIT
 vkat_SOURCES  += \
+	vkatCmdSelfTest.cpp \
 	$(VKAT_PATH_AUDIO)/DrvHostAudioValidationKit.cpp \
 	$(VKAT_PATH_AUDIO)/AudioTestService.cpp \
Index: trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp
===================================================================
--- trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp	(revision 89541)
+++ trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp	(revision 89544)
@@ -111,140 +111,4 @@
 
 
-/*********************************************************************************************************************************
-*   Structures and Typedefs                                                                                                      *
-*********************************************************************************************************************************/
-/**
- * Enumeration specifying the current audio test mode.
- */
-typedef enum AUDIOTESTMODE
-{
-    /** Unknown mode. */
-    AUDIOTESTMODE_UNKNOWN = 0,
-    /** VKAT is running on the guest side. */
-    AUDIOTESTMODE_GUEST,
-    /** VKAT is running on the host side. */
-    AUDIOTESTMODE_HOST
-} AUDIOTESTMODE;
-
-struct AUDIOTESTENV;
-/** Pointer a audio test environment. */
-typedef AUDIOTESTENV *PAUDIOTESTENV;
-
-struct AUDIOTESTDESC;
-/** Pointer a audio test descriptor. */
-typedef AUDIOTESTDESC *PAUDIOTESTDESC;
-
-/**
- * Callback to set up the test parameters for a specific test.
- *
- * @returns IPRT status code.
- * @retval  VINF_SUCCESS    if setting the parameters up succeeded. Any other error code
- *                          otherwise indicating the kind of error.
- * @param   pszTest         Test name.
- * @param   pTstParmsAcq    The audio test parameters to set up.
- */
-typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
-/** Pointer to an audio test setup callback. */
-typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
-
-typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
-/** Pointer to an audio test exec callback. */
-typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
-
-typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx));
-/** Pointer to an audio test destroy callback. */
-typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
-
-/**
- * Structure for keeping an audio test audio stream.
- */
-typedef struct AUDIOTESTSTREAM
-{
-    /** The PDM stream. */
-    PPDMAUDIOSTREAM         pStream;
-    /** The backend stream. */
-    PPDMAUDIOBACKENDSTREAM  pBackend;
-    /** The stream config. */
-    PDMAUDIOSTREAMCFG       Cfg;
-} AUDIOTESTSTREAM;
-/** Pointer to audio test stream. */
-typedef AUDIOTESTSTREAM *PAUDIOTESTSTREAM;
-
-/** Maximum audio streams a test environment can handle. */
-#define AUDIOTESTENV_MAX_STREAMS 8
-
-/**
- * Audio test environment parameters.
- * Not necessarily bound to a specific test (can be reused).
- */
-typedef struct AUDIOTESTENV
-{
-    /** Audio testing mode. */
-    AUDIOTESTMODE           enmMode;
-    /** Whether self test mode is active or not. */
-    bool                    fSelftest;
-    /** Output path for storing the test environment's final test files. */
-    char                    szTag[AUDIOTEST_TAG_MAX];
-    /** Output path for storing the test environment's final test files. */
-    char                    szPathOut[RTPATH_MAX];
-    /** Temporary path for this test environment. */
-    char                    szPathTemp[RTPATH_MAX];
-    /** Buffer size (in ms). */
-    RTMSINTERVAL            cMsBufferSize;
-    /** Pre-buffering time (in ms). */
-    RTMSINTERVAL            cMsPreBuffer;
-    /** Scheduling hint (in ms). */
-    RTMSINTERVAL            cMsSchedulingHint;
-    /** The audio test driver stack. */
-    AUDIOTESTDRVSTACK       DrvStack;
-    /** The current (last) audio device enumeration to use. */
-    PDMAUDIOHOSTENUM        DevEnum;
-    /** Audio stream. */
-    AUDIOTESTSTREAM         aStreams[AUDIOTESTENV_MAX_STREAMS];
-    /** The audio test set to use. */
-    AUDIOTESTSET            Set;
-    union
-    {
-        struct
-        {
-            /** ATS instance to use. */
-            ATSSERVER       Srv;
-        } Guest;
-        struct
-        {
-            /** Client connected to the ATS on the guest side. */
-            ATSCLIENT       AtsClGuest;
-            /** Client connected to the ATS on the Validation Kit. */
-            ATSCLIENT       AtsClValKit;
-        } Host;
-    } u;
-} AUDIOTESTENV;
-
-/**
- * Audio test descriptor.
- */
-typedef struct AUDIOTESTDESC
-{
-    /** (Sort of) Descriptive test name. */
-    const char             *pszName;
-    /** Flag whether the test is excluded. */
-    bool                    fExcluded;
-    /** The setup callback. */
-    PFNAUDIOTESTSETUP       pfnSetup;
-    /** The exec callback. */
-    PFNAUDIOTESTEXEC        pfnExec;
-    /** The destruction callback. */
-    PFNAUDIOTESTDESTROY     pfnDestroy;
-} AUDIOTESTDESC;
-
-/**
- * Structure for keeping a user context for the test service callbacks.
- */
-typedef struct ATSCALLBACKCTX
-{
-    PAUDIOTESTENV pTstEnv;
-} ATSCALLBACKCTX;
-typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
-
 
 /*********************************************************************************************************************************
@@ -252,10 +116,4 @@
 *********************************************************************************************************************************/
 static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms);
-static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
-static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
-static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
-static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev);
-static int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv);
-
 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm);
 static RTEXITCODE audioTestVersion(void);
@@ -359,60 +217,16 @@
 };
 
-/**
- * Backends.
- *
- * @note The first backend in the array is the default one for the platform.
- */
-static struct
-{
-    /** The driver registration structure. */
-    PCPDMDRVREG pDrvReg;
-    /** The backend name.
-     * Aliases are implemented by having multiple entries for the same backend.  */
-    const char *pszName;
-} const g_aBackends[] =
-{
-#if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
-    {   &g_DrvHostALSAAudio,          "alsa" },
-#endif
-#ifdef VBOX_WITH_AUDIO_PULSE
-    {   &g_DrvHostPulseAudio,         "pulseaudio" },
-    {   &g_DrvHostPulseAudio,         "pulse" },
-    {   &g_DrvHostPulseAudio,         "pa" },
-#endif
-#ifdef VBOX_WITH_AUDIO_OSS
-    {   &g_DrvHostOSSAudio,           "oss" },
-#endif
-#if defined(RT_OS_DARWIN)
-    {   &g_DrvHostCoreAudio,          "coreaudio" },
-    {   &g_DrvHostCoreAudio,          "core" },
-    {   &g_DrvHostCoreAudio,          "ca" },
-#endif
-#if defined(RT_OS_WINDOWS)
-    {   &g_DrvHostAudioWas,           "wasapi" },
-    {   &g_DrvHostAudioWas,           "was" },
-    {   &g_DrvHostDSound,             "directsound" },
-    {   &g_DrvHostDSound,             "dsound" },
-    {   &g_DrvHostDSound,             "ds" },
-#endif
-    {   &g_DrvHostValidationKitAudio, "valkit" }
-};
-AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
-
-
 /** Terminate ASAP if set.  Set on Ctrl-C. */
-static bool volatile    g_fTerminate = false;
+bool volatile    g_fTerminate = false;
 /** The release logger. */
-static PRTLOGGER        g_pRelLogger = NULL;
-
-
+PRTLOGGER        g_pRelLogger = NULL;
 /** The test handle. */
-RTTEST        g_hTest;
+RTTEST           g_hTest;
 /** The current verbosity level. */
-unsigned      g_uVerbosity = 0;
+unsigned         g_uVerbosity = 0;
 /** DrvAudio: Enable debug (or not). */
-bool          g_fDrvAudioDebug = 0;
+bool             g_fDrvAudioDebug = 0;
 /** DrvAudio: The debug output path. */
-const char   *g_pszDrvAudioDebug = NULL;
+const char      *g_pszDrvAudioDebug = NULL;
 
 
@@ -424,5 +238,5 @@
  * @param   pszBackend      The backend option value.
  */
-static PCPDMDRVREG audioTestFindBackendOpt(const char *pszBackend)
+PCPDMDRVREG audioTestFindBackendOpt(const char *pszBackend)
 {
     for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)
@@ -434,630 +248,4 @@
 }
 
-
-/*********************************************************************************************************************************
-*   Test Primitives                                                                                                              *
-*********************************************************************************************************************************/
-
-/**
- * Plays a test tone on a specific audio test stream.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment to use for running the test.
- * @param   pStream             Stream to use for playing the tone.
- * @param   pParms              Tone parameters to use.
- *
- * @note    Blocking function.
- */
-static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
-{
-    AUDIOTESTTONE TstTone;
-    AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);
-
-    const char *pcszPathOut = pTstEnv->Set.szPathAbs;
-
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
-    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
-
-    /** @todo Use .WAV here? */
-    PAUDIOTESTOBJ pObj;
-    int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-play.pcm", &pObj);
-    AssertRCReturn(rc, rc);
-
-    if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
-    {
-        uint32_t cbBuf;
-        uint8_t  abBuf[_4K];
-
-        const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
-        AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
-              uint32_t cbToWrite  = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
-        AssertStmt(cbToWrite,  rc = VERR_INVALID_PARAMETER);
-
-        if (RT_SUCCESS(rc))
-        {
-            AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
-            AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
-            AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
-
-            while (cbToWrite)
-            {
-                uint32_t cbWritten    = 0;
-                uint32_t cbToGenerate = RT_MIN(cbToWrite, RT_MIN(cbPerSched, sizeof(abBuf)));
-                Assert(cbToGenerate);
-
-                rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbBuf);
-                if (RT_SUCCESS(rc))
-                {
-                    /* Write stuff to disk before trying to play it. Help analysis later. */
-                    rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
-                    if (RT_SUCCESS(rc))
-                        rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,
-                                                            abBuf, cbBuf, &cbWritten);
-                }
-
-                if (RT_FAILURE(rc))
-                    break;
-
-                RTThreadSleep(pTstEnv->cMsSchedulingHint);
-
-                Assert(cbToWrite >= cbWritten);
-                cbToWrite -= cbWritten;
-            }
-        }
-    }
-    else
-        rc = VERR_AUDIO_STREAM_NOT_READY;
-
-    int rc2 = AudioTestSetObjClose(pObj);
-    if (RT_SUCCESS(rc))
-        rc = rc2;
-
-    if (RT_FAILURE(rc))
-        RTTestFailed(g_hTest, "Playing tone done failed with %Rrc\n", rc);
-
-    return rc;
-}
-
-/**
- * Records a test tone from a specific audio test stream.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment to use for running the test.
- * @param   pStream             Stream to use for recording the tone.
- * @param   pParms              Tone parameters to use.
- *
- * @note    Blocking function.
- */
-static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
-{
-    const char *pcszPathOut = pTstEnv->Set.szPathAbs;
-
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);
-    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
-
-    /** @todo Use .WAV here? */
-    PAUDIOTESTOBJ pObj;
-    int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-rec.pcm", &pObj);
-    AssertRCReturn(rc, rc);
-
-    if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
-    {
-        const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
-        AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
-              uint32_t cbToRead   = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
-        AssertStmt(cbToRead,   rc = VERR_INVALID_PARAMETER);
-
-        if (RT_SUCCESS(rc))
-        {
-            AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
-            AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
-            AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
-
-            uint8_t abBuf[_4K];
-
-            while (cbToRead)
-            {
-                const uint32_t cbChunk = RT_MIN(cbToRead, RT_MIN(cbPerSched, sizeof(abBuf)));
-
-                uint32_t cbRead = 0;
-                rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, cbChunk, &cbRead);
-                if (RT_SUCCESS(rc))
-                    rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);
-
-                if (RT_FAILURE(rc))
-                    break;
-
-                RTThreadSleep(pTstEnv->cMsSchedulingHint);
-
-                Assert(cbToRead >= cbRead);
-                cbToRead -= cbRead;
-            }
-        }
-    }
-    else
-        rc = VERR_AUDIO_STREAM_NOT_READY;
-
-    int rc2 = AudioTestSetObjClose(pObj);
-    if (RT_SUCCESS(rc))
-        rc = rc2;
-
-    if (RT_FAILURE(rc))
-        RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
-
-    return rc;
-}
-
-
-/*********************************************************************************************************************************
-*   ATS Callback Implementations                                                                                                 *
-*********************************************************************************************************************************/
-
-/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
-static DECLCALLBACK(int) audioTestSvcTestSetBeginCallback(void const *pvUser, const char *pszTag)
-{
-    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
-    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
-
-    char szTag[AUDIOTEST_TAG_MAX];
-    int rc = RTStrPrintf2(szTag, sizeof(szTag), "%s-guest", pszTag);
-    AssertRCReturn(rc, rc);
-
-    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Beginning test set '%s'\n", szTag);
-
-    return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, szTag);
-}
-
-/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
-static DECLCALLBACK(int) audioTestSvcTestSetEndCallback(void const *pvUser, const char *pszTag)
-{
-    RT_NOREF(pszTag);
-
-    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
-    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
-
-    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set '%s'\n", pszTag);
-
-    return audioTestEnvPrologue(pTstEnv);
-}
-
-/** @copydoc ATSCALLBACKS::pfnTonePlay */
-static DECLCALLBACK(int) audioTestSvcTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
-{
-    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
-    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
-
-    AUDIOTESTTONE TstTone;
-    AudioTestToneInitRandom(&TstTone, &pToneParms->Props);
-
-    const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
-
-    int rc = audioTestCreateStreamDefaultOut(pTstEnv, pTstStream, &pToneParms->Props);
-    if (RT_SUCCESS(rc))
-    {
-        AUDIOTESTPARMS TstParms;
-        RT_ZERO(TstParms);
-        TstParms.enmType  = AUDIOTESTTYPE_TESTTONE_PLAY;
-        TstParms.enmDir   = PDMAUDIODIR_OUT;
-        TstParms.TestTone = *pToneParms;
-
-        PAUDIOTESTENTRY pTst;
-        rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
-        if (RT_SUCCESS(rc))
-        {
-            rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
-            if (RT_SUCCESS(rc))
-            {
-                AudioTestSetTestDone(pTst);
-            }
-            else
-                AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
-        }
-
-        int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    else
-        RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
-
-    return rc;
-}
-
-/** @copydoc ATSCALLBACKS::pfnToneRecord */
-static DECLCALLBACK(int) audioTestSvcToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
-{
-    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
-    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
-
-    const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
-
-    int rc = audioTestCreateStreamDefaultIn(pTstEnv, pTstStream, &pToneParms->Props);
-    if (RT_SUCCESS(rc))
-    {
-        AUDIOTESTPARMS TstParms;
-        RT_ZERO(TstParms);
-        TstParms.enmType  = AUDIOTESTTYPE_TESTTONE_RECORD;
-        TstParms.enmDir   = PDMAUDIODIR_IN;
-        TstParms.Props    = pToneParms->Props;
-        TstParms.TestTone = *pToneParms;
-
-        PAUDIOTESTENTRY pTst;
-        rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone", &TstParms, &pTst);
-        if (RT_SUCCESS(rc))
-        {
-            rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
-            if (RT_SUCCESS(rc))
-            {
-                AudioTestSetTestDone(pTst);
-            }
-            else
-                AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
-        }
-
-        int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-    else
-        RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
-
-    return rc;
-}
-
-
-/*********************************************************************************************************************************
-*   Implementation of audio test environment handling                                                                            *
-*********************************************************************************************************************************/
-
-/**
- * Initializes an audio test environment.
- *
- * @param   pTstEnv             Audio test environment to initialize.
- * @param   pDrvReg             Audio driver to use.
- * @param   fWithDrvAudio       Whether to include DrvAudio in the stack or not.
- * @param   pszTcpAddr          TCP/IP address to connect to.
- *                              If NULL, localhost (127.0.0.1) will be used.
- * @param   uTcpPort            TCP/IP port to connect to.
- *                              If 0, ATS_DEFAULT_PORT will be used.
- */
-static int audioTestEnvInit(PAUDIOTESTENV pTstEnv,
-                            PCPDMDRVREG pDrvReg, bool fWithDrvAudio,
-                            const char *pszTcpAddr, uint32_t uTcpPort)
-{
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
-
-    int rc = VINF_SUCCESS;
-
-    PDMAudioHostEnumInit(&pTstEnv->DevEnum);
-
-    pTstEnv->cMsBufferSize     = 300; /* ms */ /** @todo Randomize this also? */
-    pTstEnv->cMsPreBuffer      = 150; /* ms */ /** @todo Ditto. */
-    pTstEnv->cMsSchedulingHint = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
-
-    /* Only the guest mode needs initializing the driver stack. */
-    const bool fUseDriverStack =    pTstEnv->enmMode == AUDIOTESTMODE_GUEST
-                                 /* The self test mode drives the ValKit audio driver locally,
-                                  * so also use the driver stack here. */
-                                 || pTstEnv->fSelftest;
-    if (fUseDriverStack)
-    {
-        rc = audioTestDriverStackInit(&pTstEnv->DrvStack, pDrvReg, fWithDrvAudio);
-        if (RT_FAILURE(rc))
-            return rc;
-
-        PPDMAUDIOHOSTDEV pDev;
-        rc = audioTestDevicesEnumerateAndCheck(pTstEnv, NULL /* pszDevice */, &pDev); /** @todo Implement device checking. */
-        if (RT_FAILURE(rc))
-            return rc;
-    }
-
-    char szPathTemp[RTPATH_MAX];
-    if (   !strlen(pTstEnv->szPathTemp)
-        || !strlen(pTstEnv->szPathOut))
-        rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
-
-    if (   RT_SUCCESS(rc)
-        && !strlen(pTstEnv->szPathTemp))
-        rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
-
-    if (   RT_SUCCESS(rc)
-        && !strlen(pTstEnv->szPathOut))
-        rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
-
-    if (RT_FAILURE(rc))
-        return rc;
-
-    /** @todo Implement NAT mode like we do for TxS later? */
-    if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
-    {
-        ATSCALLBACKCTX Ctx;
-        Ctx.pTstEnv = pTstEnv;
-
-        ATSCALLBACKS Callbacks;
-        Callbacks.pfnTestSetBegin = audioTestSvcTestSetBeginCallback;
-        Callbacks.pfnTestSetEnd   = audioTestSvcTestSetEndCallback;
-        Callbacks.pfnTonePlay     = audioTestSvcTonePlayCallback;
-        Callbacks.pfnToneRecord   = audioTestSvcToneRecordCallback;
-        Callbacks.pvUser          = &Ctx;
-
-        RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Starting guest ATS at %s:%RU32...\n", pszTcpAddr, uTcpPort);
-        rc = AudioTestSvcInit(&pTstEnv->u.Guest.Srv, pszTcpAddr, uTcpPort, &Callbacks);
-        if (RT_SUCCESS(rc))
-            rc = AudioTestSvcStart(&pTstEnv->u.Guest.Srv);
-
-        if (RT_FAILURE(rc))
-        {
-            RTTestFailed(g_hTest, "Starting ATS failed with %Rrc\n", rc);
-            return rc;
-        }
-    }
-    else /* Host mode */
-    {
-        RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to guest ATS at %s:%RU32 ...\n",
-                     (pszTcpAddr && *pszTcpAddr) ? pszTcpAddr : "127.0.0.1", uTcpPort ? uTcpPort : ATS_TCP_DEFAULT_PORT);
-
-        rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClGuest, pszTcpAddr, uTcpPort);
-        if (RT_FAILURE(rc))
-        {
-            RTTestFailed(g_hTest, "Connecting to ATS failed with %Rrc\n", rc);
-            return rc;
-        }
-
-        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to ATS\n");
-    }
-
-    if (   RT_FAILURE(rc)
-        && fUseDriverStack)
-        audioTestDriverStackDelete(&pTstEnv->DrvStack);
-
-    return rc;
-}
-
-/**
- * Destroys an audio test environment.
- *
- * @param   pTstEnv             Audio test environment to destroy.
- */
-static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
-{
-    if (!pTstEnv)
-        return;
-
-    PDMAudioHostEnumDelete(&pTstEnv->DevEnum);
-
-    for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
-    {
-        int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
-        if (RT_FAILURE(rc2))
-            RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
-    }
-
-    audioTestDriverStackDelete(&pTstEnv->DrvStack);
-}
-
-/**
- * Closes, packs up and destroys a test environment.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment to handle.
- */
-static int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv)
-{
-    /* Close the test set first. */
-    AudioTestSetClose(&pTstEnv->Set);
-
-    /* Before destroying the test environment, pack up the test set so
-     * that it's ready for transmission. */
-    char szFileOut[RTPATH_MAX];
-    int rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, szFileOut, sizeof(szFileOut));
-    if (RT_SUCCESS(rc))
-        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", szFileOut);
-
-    int rc2 = AudioTestSetWipe(&pTstEnv->Set);
-    if (RT_SUCCESS(rc))
-        rc = rc2;
-
-    AudioTestSetDestroy(&pTstEnv->Set);
-
-    if (RT_FAILURE(rc))
-        RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
-
-    return rc;
-}
-
-/**
- * Initializes an audio test parameters set.
- *
- * @param   pTstParms           Test parameters set to initialize.
- */
-static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
-{
-    RT_ZERO(*pTstParms);
-}
-
-/**
- * Destroys an audio test parameters set.
- *
- * @param   pTstParms           Test parameters set to destroy.
- */
-static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
-{
-    if (!pTstParms)
-        return;
-
-    return;
-}
-
-
-/*********************************************************************************************************************************
-*   Device enumeration + handling.                                                                                               *
-*********************************************************************************************************************************/
-
-/**
- * Enumerates audio devices and optionally searches for a specific device.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test env to use for enumeration.
- * @param   pszDev              Device name to search for. Can be NULL if the default device shall be used.
- * @param   ppDev               Where to return the pointer of the device enumeration of \a pTstEnv when a
- *                              specific device was found.
- */
-static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
-{
-#ifdef DEBUG_andy
-    return VINF_SUCCESS;
-#endif
-
-    RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
-
-    if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)
-    {
-        RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
-        return VINF_NOT_SUPPORTED;
-    }
-
-    Assert(pszDev == NULL || ppDev);
-
-    if (ppDev)
-        *ppDev = NULL;
-
-    int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);
-    if (RT_SUCCESS(rc))
-    {
-        PPDMAUDIOHOSTDEV pDev;
-        RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
-        {
-            char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
-            if (pDev->pszId)
-                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
-            else
-                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
-            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage));
-            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
-            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Input channels  = %RU8\n", pDev->cMaxInputChannels);
-            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Output channels = %RU8\n", pDev->cMaxOutputChannels);
-
-            if (   pszDev
-                && !RTStrCmp(pDev->pszName, pszDev))
-            {
-                *ppDev = pDev;
-            }
-        }
-    }
-    else
-        RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
-
-    RTTestSubDone(g_hTest);
-
-    if (   pszDev
-        && *ppDev == NULL)
-    {
-        RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
-        return VERR_NOT_FOUND;
-    }
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Opens an audio device.
- *
- * @returns VBox status code.
- * @param   pDev                Audio device to open.
- */
-static int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
-{
-    int rc = VINF_SUCCESS;
-
-    RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->pszName);
-
-    /** @todo Detect + open device here. */
-
-    RTTestSubDone(g_hTest);
-
-    return rc;
-}
-
-/**
- * Closes an audio device.
- *
- * @returns VBox status code.
- * @param   pDev                Audio device to close.
- */
-static int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
-{
-    int rc = VINF_SUCCESS;
-
-    RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->pszName);
-
-    /** @todo Close device here. */
-
-    RTTestSubDone(g_hTest);
-
-    return rc;
-}
-
-/**
- * Destroys an audio test stream.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment the stream to destroy contains.
- * @param   pStream             Audio stream to destroy.
- */
-static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
-{
-    int rc = VINF_SUCCESS;
-    if (pStream && pStream->pStream)
-    {
-        /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
-
-        audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);
-        pStream->pStream  = NULL;
-        pStream->pBackend = NULL;
-    }
-
-    return rc;
-}
-
-/**
- * Creates an audio default input (recording) test stream.
- * Convenience function.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment to use for creating the stream.
- * @param   pStream             Audio stream to create.
- * @param   pProps              PCM properties to use for creation.
- */
-static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
-{
-    pStream->pBackend = NULL;
-    int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
-                                                   pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
-    if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
-        pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
-    return rc;
-}
-
-/**
- * Creates an audio default output (playback) test stream.
- * Convenience function.
- *
- * @returns VBox status code.
- * @param   pTstEnv             Test environment to use for creating the stream.
- * @param   pStream             Audio stream to create.
- * @param   pProps              PCM properties to use for creation.
- */
-static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
-{
-    pStream->pBackend = NULL;
-    int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
-                                                    pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
-    if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
-        pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
-    return rc;
-}
 
 /**
@@ -1341,5 +529,5 @@
  * @param   pOverrideParms      Test parameters for (some / all) specific test parameters. Optional.
  */
-static int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)
+int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)
 {
     int rc = VINF_SUCCESS;
@@ -1862,208 +1050,4 @@
 }
 
-
-/*********************************************************************************************************************************
-*   Command: play                                                                                                                *
-*********************************************************************************************************************************/
-
-/**
- * Worker for audioTestPlayOne implementing the play loop.
- */
-static RTEXITCODE audioTestPlayOneInner(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTWAVEFILE pWaveFile,
-                                        PCPDMAUDIOSTREAMCFG pCfgAcq, const char *pszFile)
-{
-    uint32_t const  cbPreBuffer        = PDMAudioPropsFramesToBytes(pMix->pProps, pCfgAcq->Backend.cFramesPreBuffering);
-    uint64_t const  nsStarted          = RTTimeNanoTS();
-    uint64_t        nsDonePreBuffering = 0;
-
-    /*
-     * Transfer data as quickly as we're allowed.
-     */
-    uint8_t         abSamples[16384];
-    uint32_t const  cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
-    uint64_t        offStream        = 0;
-    while (!g_fTerminate)
-    {
-        /* Read a chunk from the wave file. */
-        size_t      cbSamples = 0;
-        int rc = AudioTestWaveFileRead(pWaveFile, abSamples, cbSamplesAligned, &cbSamples);
-        if (RT_SUCCESS(rc) && cbSamples > 0)
-        {
-            /* Pace ourselves a little. */
-            if (offStream >= cbPreBuffer)
-            {
-                if (!nsDonePreBuffering)
-                    nsDonePreBuffering = RTTimeNanoTS();
-                uint64_t const cNsWritten = PDMAudioPropsBytesToNano64(pMix->pProps, offStream - cbPreBuffer);
-                uint64_t const cNsElapsed = RTTimeNanoTS() - nsStarted;
-                if (cNsWritten > cNsElapsed + RT_NS_10MS)
-                    RTThreadSleep((cNsWritten - cNsElapsed - RT_NS_10MS / 2) / RT_NS_1MS);
-            }
-
-            /* Transfer the data to the audio stream. */
-            for (uint32_t offSamples = 0; offSamples < cbSamples;)
-            {
-                uint32_t const cbCanWrite = AudioTestMixStreamGetWritable(pMix);
-                if (cbCanWrite > 0)
-                {
-                    uint32_t const cbToPlay = RT_MIN(cbCanWrite, (uint32_t)cbSamples - offSamples);
-                    uint32_t       cbPlayed = 0;
-                    rc = AudioTestMixStreamPlay(pMix, &abSamples[offSamples], cbToPlay, &cbPlayed);
-                    if (RT_SUCCESS(rc))
-                    {
-                        if (cbPlayed)
-                        {
-                            offSamples += cbPlayed;
-                            offStream  += cbPlayed;
-                        }
-                        else
-                            return RTMsgErrorExitFailure("Played zero bytes - %#x bytes reported playable!\n", cbCanWrite);
-                    }
-                    else
-                        return RTMsgErrorExitFailure("Failed to play %#x bytes: %Rrc\n", cbToPlay, rc);
-                }
-                else if (AudioTestMixStreamIsOkay(pMix))
-                    RTThreadSleep(RT_MIN(RT_MAX(1, pCfgAcq->Device.cMsSchedulingHint), 256));
-                else
-                    return RTMsgErrorExitFailure("Stream is not okay!\n");
-            }
-        }
-        else if (RT_SUCCESS(rc) && cbSamples == 0)
-            break;
-        else
-            return RTMsgErrorExitFailure("Error reading wav file '%s': %Rrc", pszFile, rc);
-    }
-
-    /*
-     * Drain the stream.
-     */
-    if (g_uVerbosity > 0)
-        RTMsgInfo("%'RU64 ns: Draining...\n", RTTimeNanoTS() - nsStarted);
-    int rc = AudioTestMixStreamDrain(pMix, true /*fSync*/);
-    if (RT_SUCCESS(rc))
-    {
-        if (g_uVerbosity > 0)
-            RTMsgInfo("%'RU64 ns: Done\n", RTTimeNanoTS() - nsStarted);
-    }
-    else
-        return RTMsgErrorExitFailure("Draining failed: %Rrc", rc);
-
-    return RTEXITCODE_SUCCESS;
-}
-
-
-/**
- * Worker for audioTestCmdPlayHandler that plays one file.
- */
-static RTEXITCODE audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,
-                                   uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
-                                   uint8_t cChannels, uint8_t cbSample, uint32_t uHz,
-                                   bool fWithDrvAudio, bool fWithMixer)
-{
-    char szTmp[128];
-
-    /*
-     * First we must open the file and determin the format.
-     */
-    RTERRINFOSTATIC ErrInfo;
-    AUDIOTESTWAVEFILE WaveFile;
-    int rc = AudioTestWaveFileOpen(pszFile, &WaveFile, RTErrInfoInitStatic(&ErrInfo));
-    if (RT_FAILURE(rc))
-        return RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
-
-    if (g_uVerbosity > 0)
-    {
-        RTMsgInfo("Opened '%s' for playing\n", pszFile);
-        RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));
-        RTMsgInfo("Size:   %'RU32 bytes / %#RX32 / %'RU32 frames / %'RU64 ns\n",
-                  WaveFile.cbSamples, WaveFile.cbSamples,
-                  PDMAudioPropsBytesToFrames(&WaveFile.Props, WaveFile.cbSamples),
-                  PDMAudioPropsBytesToNano(&WaveFile.Props, WaveFile.cbSamples));
-    }
-
-    /*
-     * Construct the driver stack.
-     */
-    RTEXITCODE          rcExit = RTEXITCODE_FAILURE;
-    AUDIOTESTDRVSTACK   DrvStack;
-    rc = audioTestDriverStackInit(&DrvStack, pDrvReg, fWithDrvAudio);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Set the output device if one is specified.
-         */
-        rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_OUT, pszDevId);
-        if (RT_SUCCESS(rc))
-        {
-            /*
-             * Open a stream for the output.
-             */
-            PDMAUDIOPCMPROPS ReqProps = WaveFile.Props;
-            if (cChannels != 0 && PDMAudioPropsChannels(&ReqProps) != cChannels)
-                PDMAudioPropsSetChannels(&ReqProps, cChannels);
-            if (cbSample != 0)
-                PDMAudioPropsSetSampleSize(&ReqProps, cbSample);
-            if (uHz != 0)
-                ReqProps.uHz = uHz;
-
-            PDMAUDIOSTREAMCFG CfgAcq;
-            PPDMAUDIOSTREAM   pStream  = NULL;
-            rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &ReqProps, cMsBufferSize,
-                                                        cMsPreBuffer, cMsSchedulingHint, &pStream, &CfgAcq);
-            if (RT_SUCCESS(rc))
-            {
-                /*
-                 * Automatically enable the mixer if the wave file and the
-                 * output parameters doesn't match.
-                 */
-                if (   !fWithMixer
-                    && !PDMAudioPropsAreEqual(&WaveFile.Props, &pStream->Cfg.Props))
-                {
-                    RTMsgInfo("Enabling the mixer buffer.\n");
-                    fWithMixer = true;
-                }
-
-                /*
-                 * Create a mixer wrapper.  This is just a thin wrapper if fWithMixer
-                 * is false, otherwise it's doing mixing, resampling and recoding.
-                 */
-                AUDIOTESTDRVMIXSTREAM Mix;
-                rc = AudioTestMixStreamInit(&Mix, &DrvStack, pStream, fWithMixer ? &WaveFile.Props : NULL, 100 /*ms*/);
-                if (RT_SUCCESS(rc))
-                {
-                    if (g_uVerbosity > 0)
-                        RTMsgInfo("Stream: %s cbBackend=%#RX32%s\n",
-                                  PDMAudioPropsToString(&pStream->Cfg.Props, szTmp, sizeof(szTmp)),
-                                  pStream->cbBackend, fWithMixer ? " mixed" : "");
-
-                    /*
-                     * Enable the stream and start playing.
-                     */
-                    rc = AudioTestMixStreamEnable(&Mix);
-                    if (RT_SUCCESS(rc))
-                        rcExit = audioTestPlayOneInner(&Mix, &WaveFile, &CfgAcq, pszFile);
-                    else
-                        rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc);
-
-                    /*
-                     * Clean up.
-                     */
-                    AudioTestMixStreamTerm(&Mix);
-                }
-                audioTestDriverStackStreamDestroy(&DrvStack, pStream);
-            }
-            else
-                rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
-        }
-        else
-            rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
-        audioTestDriverStackDelete(&DrvStack);
-    }
-    else
-        rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
-    AudioTestWaveFileClose(&WaveFile);
-    return rcExit;
-}
-
 /**
  * Options for 'play'.
@@ -2176,203 +1160,4 @@
 *   Command: rec                                                                                                                 *
 *********************************************************************************************************************************/
-
-/**
- * Worker for audioTestRecOne implementing the recording loop.
- */
-static RTEXITCODE audioTestRecOneInner(PAUDIOTESTDRVMIXSTREAM pMix, PAUDIOTESTWAVEFILE pWaveFile,
-                                       PCPDMAUDIOSTREAMCFG pCfgAcq, uint64_t cMaxFrames, const char *pszFile)
-{
-    int             rc;
-    uint64_t const  nsStarted   = RTTimeNanoTS();
-
-    /*
-     * Transfer data as quickly as we're allowed.
-     */
-    uint8_t         abSamples[16384];
-    uint32_t const  cbSamplesAligned     = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
-    uint64_t        cFramesCapturedTotal = 0;
-    while (!g_fTerminate && cFramesCapturedTotal < cMaxFrames)
-    {
-        /*
-         * Anything we can read?
-         */
-        uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
-        if (cbCanRead)
-        {
-            uint32_t const cbToRead   = RT_MIN(cbCanRead, cbSamplesAligned);
-            uint32_t       cbCaptured = 0;
-            rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbCaptured);
-            if (RT_SUCCESS(rc))
-            {
-                if (cbCaptured)
-                {
-                    uint32_t cFramesCaptured = PDMAudioPropsBytesToFrames(pMix->pProps, cbCaptured);
-                    if (cFramesCaptured + cFramesCaptured < cMaxFrames)
-                    { /* likely */ }
-                    else
-                    {
-                        cFramesCaptured = cMaxFrames - cFramesCaptured;
-                        cbCaptured      = PDMAudioPropsFramesToBytes(pMix->pProps, cFramesCaptured);
-                    }
-
-                    rc = AudioTestWaveFileWrite(pWaveFile, abSamples, cbCaptured);
-                    if (RT_SUCCESS(rc))
-                        cFramesCapturedTotal += cFramesCaptured;
-                    else
-                        return RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszFile, rc);
-                }
-                else
-                    return RTMsgErrorExitFailure("Captured zero bytes - %#x bytes reported readable!\n", cbCanRead);
-            }
-            else
-                return RTMsgErrorExitFailure("Failed to capture %#x bytes: %Rrc (%#x available)\n", cbToRead, rc, cbCanRead);
-        }
-        else if (AudioTestMixStreamIsOkay(pMix))
-            RTThreadSleep(RT_MIN(RT_MAX(1, pCfgAcq->Device.cMsSchedulingHint), 256));
-        else
-            return RTMsgErrorExitFailure("Stream is not okay!\n");
-    }
-
-    /*
-     * Disable the stream.
-     */
-    rc = AudioTestMixStreamDisable(pMix);
-    if (RT_SUCCESS(rc) && g_uVerbosity > 0)
-        RTMsgInfo("%'RU64 ns: Stopped after recording %RU64 frames%s\n", RTTimeNanoTS() - nsStarted, cFramesCapturedTotal,
-                  g_fTerminate ? " - Ctrl-C" : ".");
-    else if (RT_FAILURE(rc))
-        return RTMsgErrorExitFailure("Disabling stream failed: %Rrc", rc);
-
-    return RTEXITCODE_SUCCESS;
-}
-
-
-/**
- * Worker for audioTestCmdRecHandler that recs one file.
- */
-static RTEXITCODE audioTestRecOne(const char *pszFile, uint8_t cWaveChannels, uint8_t cbWaveSample, uint32_t uWaveHz,
-                                  PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,
-                                  uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
-                                  uint8_t cChannels, uint8_t cbSample, uint32_t uHz, bool fWithDrvAudio, bool fWithMixer,
-                                  uint64_t cMaxFrames, uint64_t cNsMaxDuration)
-{
-    /*
-     * Construct the driver stack.
-     */
-    RTEXITCODE          rcExit = RTEXITCODE_FAILURE;
-    AUDIOTESTDRVSTACK   DrvStack;
-    int rc = audioTestDriverStackInit(&DrvStack, pDrvReg, fWithDrvAudio);
-    if (RT_SUCCESS(rc))
-    {
-        /*
-         * Set the input device if one is specified.
-         */
-        rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_IN, pszDevId);
-        if (RT_SUCCESS(rc))
-        {
-            /*
-             * Create an input stream.
-             */
-            PDMAUDIOPCMPROPS  ReqProps;
-            PDMAudioPropsInit(&ReqProps,
-                              cbSample ? cbSample : cbWaveSample ? cbWaveSample : 2,
-                              true /*fSigned*/,
-                              cChannels ? cChannels : cWaveChannels ? cWaveChannels : 2,
-                              uHz ? uHz : uWaveHz ? uWaveHz : 44100);
-            PDMAUDIOSTREAMCFG CfgAcq;
-            PPDMAUDIOSTREAM   pStream  = NULL;
-            rc = audioTestDriverStackStreamCreateInput(&DrvStack, &ReqProps, cMsBufferSize,
-                                                       cMsPreBuffer, cMsSchedulingHint, &pStream, &CfgAcq);
-            if (RT_SUCCESS(rc))
-            {
-                /*
-                 * Determine the wave file properties.  If it differs from the stream
-                 * properties, make sure the mixer is enabled.
-                 */
-                PDMAUDIOPCMPROPS WaveProps;
-                PDMAudioPropsInit(&WaveProps,
-                                  cbWaveSample ? cbWaveSample : PDMAudioPropsSampleSize(&CfgAcq.Props),
-                                  true /*fSigned*/,
-                                  cWaveChannels ? cWaveChannels : PDMAudioPropsChannels(&CfgAcq.Props),
-                                  uWaveHz ? uWaveHz : PDMAudioPropsHz(&CfgAcq.Props));
-                if (!fWithMixer && !PDMAudioPropsAreEqual(&WaveProps, &CfgAcq.Props))
-                {
-                    RTMsgInfo("Enabling the mixer buffer.\n");
-                    fWithMixer = true;
-                }
-
-                /* Console the max duration into frames now that we've got the wave file format. */
-                if (cMaxFrames != UINT64_MAX && cNsMaxDuration != UINT64_MAX)
-                {
-                    uint64_t cMaxFrames2 = PDMAudioPropsNanoToBytes64(&WaveProps, cNsMaxDuration);
-                    cMaxFrames = RT_MAX(cMaxFrames, cMaxFrames2);
-                }
-                else if (cNsMaxDuration != UINT64_MAX)
-                    cMaxFrames = PDMAudioPropsNanoToBytes64(&WaveProps, cNsMaxDuration);
-
-                /*
-                 * Create a mixer wrapper.  This is just a thin wrapper if fWithMixer
-                 * is false, otherwise it's doing mixing, resampling and recoding.
-                 */
-                AUDIOTESTDRVMIXSTREAM Mix;
-                rc = AudioTestMixStreamInit(&Mix, &DrvStack, pStream, fWithMixer ? &WaveProps : NULL, 100 /*ms*/);
-                if (RT_SUCCESS(rc))
-                {
-                    char szTmp[128];
-                    if (g_uVerbosity > 0)
-                        RTMsgInfo("Stream: %s cbBackend=%#RX32%s\n",
-                                  PDMAudioPropsToString(&pStream->Cfg.Props, szTmp, sizeof(szTmp)),
-                                  pStream->cbBackend, fWithMixer ? " mixed" : "");
-
-                    /*
-                     * Open the wave output file.
-                     */
-                    AUDIOTESTWAVEFILE WaveFile;
-                    RTERRINFOSTATIC ErrInfo;
-                    rc = AudioTestWaveFileCreate(pszFile, &WaveProps, &WaveFile, RTErrInfoInitStatic(&ErrInfo));
-                    if (RT_SUCCESS(rc))
-                    {
-                        if (g_uVerbosity > 0)
-                        {
-                            RTMsgInfo("Opened '%s' for playing\n", pszFile);
-                            RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));
-                        }
-
-                        /*
-                         * Enable the stream and start recording.
-                         */
-                        rc = AudioTestMixStreamEnable(&Mix);
-                        if (RT_SUCCESS(rc))
-                            rcExit = audioTestRecOneInner(&Mix, &WaveFile, &CfgAcq, cMaxFrames, pszFile);
-                        else
-                            rcExit = RTMsgErrorExitFailure("Enabling the input stream failed: %Rrc", rc);
-                        if (rcExit != RTEXITCODE_SUCCESS)
-                            AudioTestMixStreamDisable(&Mix);
-
-                        /*
-                         * Clean up.
-                         */
-                        rc = AudioTestWaveFileClose(&WaveFile);
-                        if (RT_FAILURE(rc))
-                            rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszFile, rc);
-                    }
-                    else
-                        rcExit = RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core.pszMsg);
-
-                    AudioTestMixStreamTerm(&Mix);
-                }
-                audioTestDriverStackStreamDestroy(&DrvStack, pStream);
-            }
-            else
-                rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);
-        }
-        else
-            rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);
-        audioTestDriverStackDelete(&DrvStack);
-    }
-    else
-        rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);
-    return rcExit;
-}
 
 /**
@@ -2540,6 +1325,4 @@
 *********************************************************************************************************************************/
 
-/** @todo Move this (all?) commands into separate files -- this file is too big already. */
-
 /**
  * Command line parameters for self-test mode.
@@ -2570,182 +1353,4 @@
 
 /**
- * Structure for keeping a VKAT self test context.
- */
-typedef struct SELFTESTCTX
-{
-    /** Common tag for guest and host side. */
-    char             szTag[AUDIOTEST_TAG_MAX];
-    /** Whether to use DrvAudio in the driver stack or not. */
-    bool             fWithDrvAudio;
-    struct
-    {
-        AUDIOTESTENV TstEnv;
-        /** Audio driver to use.
-         *  Defaults to the platform's default driver. */
-        PCPDMDRVREG  pDrvReg;
-    } Guest;
-    struct
-    {
-        AUDIOTESTENV TstEnv;
-        /** Address of the guest ATS instance.
-         *  Defaults to localhost (127.0.0.1) if not set. */
-        char         szGuestAtsAddr[64];
-        /** Port of the guest ATS instance.
-         *  Defaults to ATS_DEFAULT_PORT if not set. */
-        uint32_t     uGuestAtsPort;
-        /** Address of the Validation Kit audio driver ATS instance.
-         *  Defaults to localhost (127.0.0.1) if not set. */
-        char         szValKitAtsAddr[64];
-        /** Port of the Validation Kit audio driver ATS instance.
-         *  Defaults to ATS_ALT_PORT if not set. */
-        uint32_t     uValKitAtsPort;
-    } Host;
-} SELFTESTCTX;
-/** Pointer to a VKAT self test context. */
-typedef SELFTESTCTX *PSELFTESTCTX;
-
-static DECLCALLBACK(int) audioTestSelftestGuestAtsThread(RTTHREAD hThread, void *pvUser)
-{
-    RT_NOREF(hThread);
-    PSELFTESTCTX pCtx = (PSELFTESTCTX)pvUser;
-
-    AUDIOTESTPARMS TstCust;
-    audioTestParmsInit(&TstCust);
-
-    PAUDIOTESTENV pTstEnv = &pCtx->Guest.TstEnv;
-
-    /* Flag the environment for self test mode. */
-    pTstEnv->fSelftest = true;
-
-    /* Generate tag for guest side. */
-    int rc = RTStrCopy(pTstEnv->szTag, sizeof(pTstEnv->szTag), pCtx->szTag);
-    AssertRCReturn(rc, rc);
-
-    rc = AudioTestPathCreateTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), "selftest-guest");
-    AssertRCReturn(rc, rc);
-
-    rc = AudioTestPathCreateTemp(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), "selftest-out");
-    AssertRCReturn(rc, rc);
-
-    pTstEnv->enmMode = AUDIOTESTMODE_GUEST;
-
-    /** @todo Make this customizable. */
-    PDMAudioPropsInit(&TstCust.TestTone.Props,
-                      2 /* 16-bit */, true  /* fSigned */, 2 /* cChannels */, 44100 /* uHz */);
-
-    /* Use ATS_ALT_PORT, as on ATS_DEFAULT_PORT the
-     * Validation Kit audio driver ATS already is running on ATS_DEFAULT_PORT. */
-    rc = audioTestEnvInit(pTstEnv, pCtx->Guest.pDrvReg, pCtx->fWithDrvAudio,
-                          "127.0.0.1", ATS_TCP_ALT_PORT);
-    if (RT_SUCCESS(rc))
-    {
-        RTThreadUserSignal(hThread);
-
-        audioTestWorker(pTstEnv, &TstCust);
-        audioTestEnvDestroy(pTstEnv);
-    }
-
-    audioTestParmsDestroy(&TstCust);
-
-    return rc;
-}
-
-/**
- * Main function for performing the self test.
- *
- * @returns VBox status code.
- * @param   pCtx                Self test context to use.
- */
-static int audioTestDoSelftest(PSELFTESTCTX pCtx)
-{
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,  "Running self test ...\n");
-
-    /*
-     * The self-test does the following:
-     * - 1. Creates an ATS instance to emulate the guest mode ("--mode guest")
-     *      at port 6042 (ATS_ALT_PORT).
-     * - 2. Uses the Validation Kit audio backend, which in turn creates an ATS instance
-     *      at port 6052 (ATS_DEFAULT_PORT).
-     * - 3. Executes a complete test run locally (e.g. without any guest (VM) involved).
-     */
-
-    AUDIOTESTPARMS TstCust;
-    audioTestParmsInit(&TstCust);
-
-    /* Generate a common tag for guest and host side. */
-    int rc = AudioTestGenTag(pCtx->szTag, sizeof(pCtx->szTag));
-    AssertRCReturn(rc, rc);
-
-    PAUDIOTESTENV pTstEnv = &pCtx->Host.TstEnv;
-
-    /* Flag the environment for self test mode. */
-    pTstEnv->fSelftest = true;
-
-    /* Generate tag for host side. */
-    rc = RTStrCopy(pTstEnv->szTag, sizeof(pTstEnv->szTag), pCtx->szTag);
-    AssertRCReturn(rc, rc);
-
-    rc = AudioTestPathCreateTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), "selftest-host");
-    AssertRCReturn(rc, rc);
-
-    rc = AudioTestPathCreateTemp(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), "selftest-out");
-    AssertRCReturn(rc, rc);
-
-    /*
-     * Step 1.
-     * Creates a separate thread for the guest ATS.
-     */
-    RTTHREAD hThreadGstAts;
-    rc = RTThreadCreate(&hThreadGstAts, audioTestSelftestGuestAtsThread, pCtx, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
-                        "VKATGstAts");
-    if (RT_SUCCESS(rc))
-    {
-        rc = RTThreadUserWait(hThreadGstAts, RT_MS_30SEC);
-        if (RT_SUCCESS(rc))
-        {
-            /*
-             * Steps 2 + 3.
-             */
-            pTstEnv->enmMode = AUDIOTESTMODE_HOST;
-
-            if (!pCtx->Host.uGuestAtsPort)
-                pCtx->Host.uGuestAtsPort = ATS_TCP_ALT_PORT;
-
-            rc = audioTestEnvInit(pTstEnv, &g_DrvHostValidationKitAudio, true /* fWithDrvAudio */,
-                                  pCtx->Host.szGuestAtsAddr, pCtx->Host.uGuestAtsPort);
-            if (RT_SUCCESS(rc))
-            {
-                audioTestWorker(pTstEnv, &TstCust);
-                audioTestEnvDestroy(pTstEnv);
-            }
-        }
-    }
-
-    audioTestParmsDestroy(&TstCust);
-
-    /*
-     * Shutting down.
-     */
-    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,  "Shutting down self test\n");
-
-    ASMAtomicWriteBool(&g_fTerminate, true);
-
-    int rcThread;
-    int rc2 = RTThreadWait(hThreadGstAts, RT_MS_30SEC, &rcThread);
-    if (RT_SUCCESS(rc2))
-        rc2 = rcThread;
-    if (RT_FAILURE(rc2))
-        RTTestFailed(g_hTest, "Shutting down self test failed with %Rrc\n", rc2);
-
-    if (RT_SUCCESS(rc))
-        rc = rc2;
-
-    if (RT_FAILURE(rc))
-        RTTestFailed(g_hTest, "Self test failed with %Rrc\n", rc);
-
-    return rc;
-}
-
-/**
  * The 'selftest' command handler.
  *
@@ -2753,5 +1358,5 @@
  * @param   pGetState   RTGetOpt state.
  */
-static DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
+DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)
 {
     SELFTESTCTX Ctx;
@@ -2790,10 +1395,8 @@
 
             case 'b':
-            {
                 Ctx.Guest.pDrvReg = audioTestFindBackendOpt(ValueUnion.psz);
                 if (Ctx.Guest.pDrvReg == NULL)
                     return RTEXITCODE_SYNTAX;
                 break;
-            }
 
             case 'd':
Index: trunk/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp
===================================================================
--- trunk/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp	(revision 89544)
+++ trunk/src/VBox/ValidationKit/utils/audio/vkatCmdSelfTest.cpp	(revision 89544)
@@ -0,0 +1,187 @@
+/* $Id$ */
+/** @file
+ * Validation Kit Audio Test (VKAT) - Self test code.
+ */
+
+/*
+ * Copyright (C) 2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/test.h>
+
+#include "Audio/AudioHlp.h"
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "vkatInternal.h"
+
+
+static DECLCALLBACK(int) audioTestSelftestGuestAtsThread(RTTHREAD hThread, void *pvUser)
+{
+    RT_NOREF(hThread);
+    PSELFTESTCTX pCtx = (PSELFTESTCTX)pvUser;
+
+    AUDIOTESTPARMS TstCust;
+    audioTestParmsInit(&TstCust);
+
+    PAUDIOTESTENV pTstEnv = &pCtx->Guest.TstEnv;
+
+    /* Flag the environment for self test mode. */
+    pTstEnv->fSelftest = true;
+
+    /* Generate tag for guest side. */
+    int rc = RTStrCopy(pTstEnv->szTag, sizeof(pTstEnv->szTag), pCtx->szTag);
+    AssertRCReturn(rc, rc);
+
+    rc = AudioTestPathCreateTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), "selftest-guest");
+    AssertRCReturn(rc, rc);
+
+    rc = AudioTestPathCreateTemp(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), "selftest-out");
+    AssertRCReturn(rc, rc);
+
+    pTstEnv->enmMode = AUDIOTESTMODE_GUEST;
+
+    /** @todo Make this customizable. */
+    PDMAudioPropsInit(&TstCust.TestTone.Props,
+                      2 /* 16-bit */, true  /* fSigned */, 2 /* cChannels */, 44100 /* uHz */);
+
+    /* Use ATS_ALT_PORT, as on ATS_DEFAULT_PORT the
+     * Validation Kit audio driver ATS already is running on ATS_DEFAULT_PORT. */
+    rc = audioTestEnvInit(pTstEnv, pCtx->Guest.pDrvReg, pCtx->fWithDrvAudio,
+                          "127.0.0.1", ATS_TCP_ALT_PORT);
+    if (RT_SUCCESS(rc))
+    {
+        RTThreadUserSignal(hThread);
+
+        audioTestWorker(pTstEnv, &TstCust);
+        audioTestEnvDestroy(pTstEnv);
+    }
+
+    audioTestParmsDestroy(&TstCust);
+
+    return rc;
+}
+
+/**
+ * Main function for performing the self test.
+ *
+ * @returns RTEXITCODE
+ * @param   pCtx                Self test context to use.
+ */
+RTEXITCODE audioTestDoSelftest(PSELFTESTCTX pCtx)
+{
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,  "Running self test ...\n");
+
+    /*
+     * The self-test does the following:
+     * - 1. Creates an ATS instance to emulate the guest mode ("--mode guest")
+     *      at port 6042 (ATS_ALT_PORT).
+     * - 2. Uses the Validation Kit audio backend, which in turn creates an ATS instance
+     *      at port 6052 (ATS_DEFAULT_PORT).
+     * - 3. Executes a complete test run locally (e.g. without any guest (VM) involved).
+     */
+
+    AUDIOTESTPARMS TstCust;
+    audioTestParmsInit(&TstCust);
+
+    /* Generate a common tag for guest and host side. */
+    int rc = AudioTestGenTag(pCtx->szTag, sizeof(pCtx->szTag));
+    AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+    PAUDIOTESTENV pTstEnv = &pCtx->Host.TstEnv;
+
+    /* Flag the environment for self test mode. */
+    pTstEnv->fSelftest = true;
+
+    /* Generate tag for host side. */
+    rc = RTStrCopy(pTstEnv->szTag, sizeof(pTstEnv->szTag), pCtx->szTag);
+    AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+    rc = AudioTestPathCreateTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), "selftest-host");
+    AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+    rc = AudioTestPathCreateTemp(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), "selftest-out");
+    AssertRCReturn(rc, RTEXITCODE_FAILURE);
+
+    /*
+     * Step 1.
+     * Creates a separate thread for the guest ATS.
+     */
+    RTTHREAD hThreadGstAts;
+    rc = RTThreadCreate(&hThreadGstAts, audioTestSelftestGuestAtsThread, pCtx, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
+                        "VKATGstAts");
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTThreadUserWait(hThreadGstAts, RT_MS_30SEC);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Steps 2 + 3.
+             */
+            pTstEnv->enmMode = AUDIOTESTMODE_HOST;
+
+            if (!pCtx->Host.uGuestAtsPort)
+                pCtx->Host.uGuestAtsPort = ATS_TCP_ALT_PORT;
+
+            rc = audioTestEnvInit(pTstEnv, &g_DrvHostValidationKitAudio, true /* fWithDrvAudio */,
+                                  pCtx->Host.szGuestAtsAddr, pCtx->Host.uGuestAtsPort);
+            if (RT_SUCCESS(rc))
+            {
+                audioTestWorker(pTstEnv, &TstCust);
+                audioTestEnvDestroy(pTstEnv);
+            }
+        }
+    }
+
+    audioTestParmsDestroy(&TstCust);
+
+    /*
+     * Shutting down.
+     */
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,  "Shutting down self test\n");
+
+    ASMAtomicWriteBool(&g_fTerminate, true);
+
+    int rcThread;
+    int rc2 = RTThreadWait(hThreadGstAts, RT_MS_30SEC, &rcThread);
+    if (RT_SUCCESS(rc2))
+        rc2 = rcThread;
+    if (RT_FAILURE(rc2))
+        RTTestFailed(g_hTest, "Shutting down self test failed with %Rrc\n", rc2);
+
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Self test failed with %Rrc\n", rc);
+
+    return RT_SUCCESS(rc) ?  RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
Index: trunk/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp
===================================================================
--- trunk/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp	(revision 89544)
+++ trunk/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp	(revision 89544)
@@ -0,0 +1,695 @@
+/* $Id$ */
+/** @file
+ * Validation Kit Audio Test (VKAT) - Self test code.
+ */
+
+/*
+ * Copyright (C) 2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/rand.h>
+#include <iprt/test.h>
+
+#include "Audio/AudioHlp.h"
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "vkatInternal.h"
+
+
+/*********************************************************************************************************************************
+*   Prototypes                                                                                                                   *
+*********************************************************************************************************************************/
+
+static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
+static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
+static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
+static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev);
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/**
+ * Structure for keeping a user context for the test service callbacks.
+ */
+typedef struct ATSCALLBACKCTX
+{
+    PAUDIOTESTENV pTstEnv;
+} ATSCALLBACKCTX;
+typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
+
+
+/*********************************************************************************************************************************
+*   Device enumeration + handling.                                                                                               *
+*********************************************************************************************************************************/
+
+/**
+ * Enumerates audio devices and optionally searches for a specific device.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test env to use for enumeration.
+ * @param   pszDev              Device name to search for. Can be NULL if the default device shall be used.
+ * @param   ppDev               Where to return the pointer of the device enumeration of \a pTstEnv when a
+ *                              specific device was found.
+ */
+static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
+{
+#ifdef DEBUG_andy
+    return VINF_SUCCESS;
+#endif
+
+    RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
+
+    if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)
+    {
+        RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
+        return VINF_NOT_SUPPORTED;
+    }
+
+    Assert(pszDev == NULL || ppDev);
+
+    if (ppDev)
+        *ppDev = NULL;
+
+    int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);
+    if (RT_SUCCESS(rc))
+    {
+        PPDMAUDIOHOSTDEV pDev;
+        RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
+        {
+            char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
+            if (pDev->pszId)
+                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
+            else
+                RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
+            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Usage           = %s\n",   PDMAudioDirGetName(pDev->enmUsage));
+            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Flags           = %s\n",   PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
+            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Input channels  = %RU8\n", pDev->cMaxInputChannels);
+            RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum:   Output channels = %RU8\n", pDev->cMaxOutputChannels);
+
+            if (   pszDev
+                && !RTStrCmp(pDev->pszName, pszDev))
+            {
+                *ppDev = pDev;
+            }
+        }
+    }
+    else
+        RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
+
+    RTTestSubDone(g_hTest);
+
+    if (   pszDev
+        && *ppDev == NULL)
+    {
+        RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
+        return VERR_NOT_FOUND;
+    }
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Opens an audio device.
+ *
+ * @returns VBox status code.
+ * @param   pDev                Audio device to open.
+ */
+int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
+{
+    int rc = VINF_SUCCESS;
+
+    RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->pszName);
+
+    /** @todo Detect + open device here. */
+
+    RTTestSubDone(g_hTest);
+
+    return rc;
+}
+
+/**
+ * Closes an audio device.
+ *
+ * @returns VBox status code.
+ * @param   pDev                Audio device to close.
+ */
+int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
+{
+    int rc = VINF_SUCCESS;
+
+    RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->pszName);
+
+    /** @todo Close device here. */
+
+    RTTestSubDone(g_hTest);
+
+    return rc;
+}
+
+/**
+ * Destroys an audio test stream.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment the stream to destroy contains.
+ * @param   pStream             Audio stream to destroy.
+ */
+static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
+{
+    int rc = VINF_SUCCESS;
+    if (pStream && pStream->pStream)
+    {
+        /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
+
+        audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);
+        pStream->pStream  = NULL;
+        pStream->pBackend = NULL;
+    }
+
+    return rc;
+}
+
+/**
+ * Creates an audio default input (recording) test stream.
+ * Convenience function.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment to use for creating the stream.
+ * @param   pStream             Audio stream to create.
+ * @param   pProps              PCM properties to use for creation.
+ */
+static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
+{
+    pStream->pBackend = NULL;
+    int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
+                                                   pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
+    if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
+        pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
+    return rc;
+}
+
+/**
+ * Creates an audio default output (playback) test stream.
+ * Convenience function.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment to use for creating the stream.
+ * @param   pStream             Audio stream to create.
+ * @param   pProps              PCM properties to use for creation.
+ */
+static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
+{
+    pStream->pBackend = NULL;
+    int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
+                                                    pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
+    if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
+        pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
+    return rc;
+}
+
+
+/*********************************************************************************************************************************
+*   Test Primitives                                                                                                              *
+*********************************************************************************************************************************/
+
+/**
+ * Plays a test tone on a specific audio test stream.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment to use for running the test.
+ * @param   pStream             Stream to use for playing the tone.
+ * @param   pParms              Tone parameters to use.
+ *
+ * @note    Blocking function.
+ */
+static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
+{
+    AUDIOTESTTONE TstTone;
+    AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);
+
+    const char *pcszPathOut = pTstEnv->Set.szPathAbs;
+
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
+    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
+
+    /** @todo Use .WAV here? */
+    PAUDIOTESTOBJ pObj;
+    int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-play.pcm", &pObj);
+    AssertRCReturn(rc, rc);
+
+    if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
+    {
+        uint32_t cbBuf;
+        uint8_t  abBuf[_4K];
+
+        const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
+        AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
+              uint32_t cbToWrite  = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
+        AssertStmt(cbToWrite,  rc = VERR_INVALID_PARAMETER);
+
+        if (RT_SUCCESS(rc))
+        {
+            AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
+            AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
+            AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
+
+            while (cbToWrite)
+            {
+                uint32_t cbWritten    = 0;
+                uint32_t cbToGenerate = RT_MIN(cbToWrite, RT_MIN(cbPerSched, sizeof(abBuf)));
+                Assert(cbToGenerate);
+
+                rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbBuf);
+                if (RT_SUCCESS(rc))
+                {
+                    /* Write stuff to disk before trying to play it. Help analysis later. */
+                    rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
+                    if (RT_SUCCESS(rc))
+                        rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,
+                                                            abBuf, cbBuf, &cbWritten);
+                }
+
+                if (RT_FAILURE(rc))
+                    break;
+
+                RTThreadSleep(pTstEnv->cMsSchedulingHint);
+
+                Assert(cbToWrite >= cbWritten);
+                cbToWrite -= cbWritten;
+            }
+        }
+    }
+    else
+        rc = VERR_AUDIO_STREAM_NOT_READY;
+
+    int rc2 = AudioTestSetObjClose(pObj);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Playing tone done failed with %Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Records a test tone from a specific audio test stream.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment to use for running the test.
+ * @param   pStream             Stream to use for recording the tone.
+ * @param   pParms              Tone parameters to use.
+ *
+ * @note    Blocking function.
+ */
+static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
+{
+    const char *pcszPathOut = pTstEnv->Set.szPathAbs;
+
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);
+    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG,  "Writing to '%s'\n", pcszPathOut);
+
+    /** @todo Use .WAV here? */
+    PAUDIOTESTOBJ pObj;
+    int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-rec.pcm", &pObj);
+    AssertRCReturn(rc, rc);
+
+    if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
+    {
+        const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
+        AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
+              uint32_t cbToRead   = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
+        AssertStmt(cbToRead,   rc = VERR_INVALID_PARAMETER);
+
+        if (RT_SUCCESS(rc))
+        {
+            AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
+            AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
+            AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
+
+            uint8_t abBuf[_4K];
+
+            while (cbToRead)
+            {
+                const uint32_t cbChunk = RT_MIN(cbToRead, RT_MIN(cbPerSched, sizeof(abBuf)));
+
+                uint32_t cbRead = 0;
+                rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, cbChunk, &cbRead);
+                if (RT_SUCCESS(rc))
+                    rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);
+
+                if (RT_FAILURE(rc))
+                    break;
+
+                RTThreadSleep(pTstEnv->cMsSchedulingHint);
+
+                Assert(cbToRead >= cbRead);
+                cbToRead -= cbRead;
+            }
+        }
+    }
+    else
+        rc = VERR_AUDIO_STREAM_NOT_READY;
+
+    int rc2 = AudioTestSetObjClose(pObj);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
+
+    return rc;
+}
+
+
+/*********************************************************************************************************************************
+*   ATS Callback Implementations                                                                                                 *
+*********************************************************************************************************************************/
+
+/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
+static DECLCALLBACK(int) audioTestSvcTestSetBeginCallback(void const *pvUser, const char *pszTag)
+{
+    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
+    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
+
+    char szTag[AUDIOTEST_TAG_MAX];
+    int rc = RTStrPrintf2(szTag, sizeof(szTag), "%s-guest", pszTag);
+    AssertRCReturn(rc, rc);
+
+    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Beginning test set '%s'\n", szTag);
+
+    return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, szTag);
+}
+
+/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
+static DECLCALLBACK(int) audioTestSvcTestSetEndCallback(void const *pvUser, const char *pszTag)
+{
+    RT_NOREF(pszTag);
+
+    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
+    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
+
+    RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set '%s'\n", pszTag);
+
+    return audioTestEnvPrologue(pTstEnv);
+}
+
+/** @copydoc ATSCALLBACKS::pfnTonePlay */
+static DECLCALLBACK(int) audioTestSvcTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
+{
+    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
+    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
+
+    AUDIOTESTTONE TstTone;
+    AudioTestToneInitRandom(&TstTone, &pToneParms->Props);
+
+    const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
+
+    int rc = audioTestCreateStreamDefaultOut(pTstEnv, pTstStream, &pToneParms->Props);
+    if (RT_SUCCESS(rc))
+    {
+        AUDIOTESTPARMS TstParms;
+        RT_ZERO(TstParms);
+        TstParms.enmType  = AUDIOTESTTYPE_TESTTONE_PLAY;
+        TstParms.enmDir   = PDMAUDIODIR_OUT;
+        TstParms.TestTone = *pToneParms;
+
+        PAUDIOTESTENTRY pTst;
+        rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
+        if (RT_SUCCESS(rc))
+        {
+            rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
+            if (RT_SUCCESS(rc))
+            {
+                AudioTestSetTestDone(pTst);
+            }
+            else
+                AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
+        }
+
+        int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+    else
+        RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+/** @copydoc ATSCALLBACKS::pfnToneRecord */
+static DECLCALLBACK(int) audioTestSvcToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
+{
+    PATSCALLBACKCTX pCtx    = (PATSCALLBACKCTX)pvUser;
+    PAUDIOTESTENV   pTstEnv = pCtx->pTstEnv;
+
+    const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
+
+    int rc = audioTestCreateStreamDefaultIn(pTstEnv, pTstStream, &pToneParms->Props);
+    if (RT_SUCCESS(rc))
+    {
+        AUDIOTESTPARMS TstParms;
+        RT_ZERO(TstParms);
+        TstParms.enmType  = AUDIOTESTTYPE_TESTTONE_RECORD;
+        TstParms.enmDir   = PDMAUDIODIR_IN;
+        TstParms.Props    = pToneParms->Props;
+        TstParms.TestTone = *pToneParms;
+
+        PAUDIOTESTENTRY pTst;
+        rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone", &TstParms, &pTst);
+        if (RT_SUCCESS(rc))
+        {
+            rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
+            if (RT_SUCCESS(rc))
+            {
+                AudioTestSetTestDone(pTst);
+            }
+            else
+                AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
+        }
+
+        int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
+        if (RT_SUCCESS(rc))
+            rc = rc2;
+    }
+    else
+        RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
+
+    return rc;
+}
+
+
+/*********************************************************************************************************************************
+*   Implementation of audio test environment handling                                                                            *
+*********************************************************************************************************************************/
+
+/**
+ * Initializes an audio test environment.
+ *
+ * @param   pTstEnv             Audio test environment to initialize.
+ * @param   pDrvReg             Audio driver to use.
+ * @param   fWithDrvAudio       Whether to include DrvAudio in the stack or not.
+ * @param   pszTcpAddr          TCP/IP address to connect to.
+ *                              If NULL, localhost (127.0.0.1) will be used.
+ * @param   uTcpPort            TCP/IP port to connect to.
+ *                              If 0, ATS_DEFAULT_PORT will be used.
+ */
+int audioTestEnvInit(PAUDIOTESTENV pTstEnv,
+                     PCPDMDRVREG pDrvReg, bool fWithDrvAudio,
+                     const char *pszTcpAddr, uint32_t uTcpPort)
+{
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
+    RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
+
+    int rc = VINF_SUCCESS;
+
+    PDMAudioHostEnumInit(&pTstEnv->DevEnum);
+
+    pTstEnv->cMsBufferSize     = 300; /* ms */ /** @todo Randomize this also? */
+    pTstEnv->cMsPreBuffer      = 150; /* ms */ /** @todo Ditto. */
+    pTstEnv->cMsSchedulingHint = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
+
+    /* Only the guest mode needs initializing the driver stack. */
+    const bool fUseDriverStack =    pTstEnv->enmMode == AUDIOTESTMODE_GUEST
+                                 /* The self test mode drives the ValKit audio driver locally,
+                                  * so also use the driver stack here. */
+                                 || pTstEnv->fSelftest;
+    if (fUseDriverStack)
+    {
+        rc = audioTestDriverStackInit(&pTstEnv->DrvStack, pDrvReg, fWithDrvAudio);
+        if (RT_FAILURE(rc))
+            return rc;
+
+        PPDMAUDIOHOSTDEV pDev;
+        rc = audioTestDevicesEnumerateAndCheck(pTstEnv, NULL /* pszDevice */, &pDev); /** @todo Implement device checking. */
+        if (RT_FAILURE(rc))
+            return rc;
+    }
+
+    char szPathTemp[RTPATH_MAX];
+    if (   !strlen(pTstEnv->szPathTemp)
+        || !strlen(pTstEnv->szPathOut))
+        rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
+
+    if (   RT_SUCCESS(rc)
+        && !strlen(pTstEnv->szPathTemp))
+        rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
+
+    if (   RT_SUCCESS(rc)
+        && !strlen(pTstEnv->szPathOut))
+        rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
+
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /** @todo Implement NAT mode like we do for TxS later? */
+    if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
+    {
+        ATSCALLBACKCTX Ctx;
+        Ctx.pTstEnv = pTstEnv;
+
+        ATSCALLBACKS Callbacks;
+        Callbacks.pfnTestSetBegin = audioTestSvcTestSetBeginCallback;
+        Callbacks.pfnTestSetEnd   = audioTestSvcTestSetEndCallback;
+        Callbacks.pfnTonePlay     = audioTestSvcTonePlayCallback;
+        Callbacks.pfnToneRecord   = audioTestSvcToneRecordCallback;
+        Callbacks.pvUser          = &Ctx;
+
+        RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Starting guest ATS at %s:%RU32...\n", pszTcpAddr, uTcpPort);
+        rc = AudioTestSvcInit(&pTstEnv->u.Guest.Srv, pszTcpAddr, uTcpPort, &Callbacks);
+        if (RT_SUCCESS(rc))
+            rc = AudioTestSvcStart(&pTstEnv->u.Guest.Srv);
+
+        if (RT_FAILURE(rc))
+        {
+            RTTestFailed(g_hTest, "Starting ATS failed with %Rrc\n", rc);
+            return rc;
+        }
+    }
+    else /* Host mode */
+    {
+        RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to guest ATS at %s:%RU32 ...\n",
+                     (pszTcpAddr && *pszTcpAddr) ? pszTcpAddr : "127.0.0.1", uTcpPort ? uTcpPort : ATS_TCP_DEFAULT_PORT);
+
+        rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClGuest, pszTcpAddr, uTcpPort);
+        if (RT_FAILURE(rc))
+        {
+            RTTestFailed(g_hTest, "Connecting to ATS failed with %Rrc\n", rc);
+            return rc;
+        }
+
+        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to ATS\n");
+    }
+
+    if (   RT_FAILURE(rc)
+        && fUseDriverStack)
+        audioTestDriverStackDelete(&pTstEnv->DrvStack);
+
+    return rc;
+}
+
+/**
+ * Destroys an audio test environment.
+ *
+ * @param   pTstEnv             Audio test environment to destroy.
+ */
+void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
+{
+    if (!pTstEnv)
+        return;
+
+    PDMAudioHostEnumDelete(&pTstEnv->DevEnum);
+
+    for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
+    {
+        int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
+        if (RT_FAILURE(rc2))
+            RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
+    }
+
+    audioTestDriverStackDelete(&pTstEnv->DrvStack);
+}
+
+/**
+ * Closes, packs up and destroys a test environment.
+ *
+ * @returns VBox status code.
+ * @param   pTstEnv             Test environment to handle.
+ */
+int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv)
+{
+    /* Close the test set first. */
+    AudioTestSetClose(&pTstEnv->Set);
+
+    /* Before destroying the test environment, pack up the test set so
+     * that it's ready for transmission. */
+    char szFileOut[RTPATH_MAX];
+    int rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, szFileOut, sizeof(szFileOut));
+    if (RT_SUCCESS(rc))
+        RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", szFileOut);
+
+    int rc2 = AudioTestSetWipe(&pTstEnv->Set);
+    if (RT_SUCCESS(rc))
+        rc = rc2;
+
+    AudioTestSetDestroy(&pTstEnv->Set);
+
+    if (RT_FAILURE(rc))
+        RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
+
+    return rc;
+}
+
+/**
+ * Initializes an audio test parameters set.
+ *
+ * @param   pTstParms           Test parameters set to initialize.
+ */
+void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
+{
+    RT_ZERO(*pTstParms);
+}
+
+/**
+ * Destroys an audio test parameters set.
+ *
+ * @param   pTstParms           Test parameters set to destroy.
+ */
+void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
+{
+    if (!pTstParms)
+        return;
+
+    return;
+}
+
Index: trunk/src/VBox/ValidationKit/utils/audio/vkatInternal.h
===================================================================
--- trunk/src/VBox/ValidationKit/utils/audio/vkatInternal.h	(revision 89541)
+++ trunk/src/VBox/ValidationKit/utils/audio/vkatInternal.h	(revision 89544)
@@ -31,10 +31,50 @@
 #endif
 
+#include <iprt/getopt.h>
+
 #include <VBox/vmm/pdmdrv.h>
 #include <VBox/vmm/pdmaudioinline.h>
 #include <VBox/vmm/pdmaudiohostenuminline.h>
+
 #include "Audio/AudioMixBuffer.h"
-
-
+#include "Audio/AudioTest.h"
+#include "Audio/AudioTestService.h"
+#include "Audio/AudioTestServiceClient.h"
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+*   Externals                                                                                                                    *
+*********************************************************************************************************************************/
+/** Terminate ASAP if set.  Set on Ctrl-C. */
+extern bool volatile    g_fTerminate;
+/** The release logger. */
+extern PRTLOGGER        g_pRelLogger;
+
+/** The test handle. */
+extern RTTEST         g_hTest;
+extern unsigned       g_uVerbosity;
+extern bool           g_fDrvAudioDebug;
+extern const char    *g_pszDrvAudioDebug;
+
+/** The test handle. */
+extern RTTEST         g_hTest;
+/** The current verbosity level. */
+extern unsigned       g_uVerbosity;
+/** DrvAudio: Enable debug (or not). */
+extern bool           g_fDrvAudioDebug;
+/** DrvAudio: The debug output path. */
+extern const char    *g_pszDrvAudioDebug;
+
+
+/*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+
+
+/*********************************************************************************************************************************
+*   Structures and Typedefs                                                                                                      *
+*********************************************************************************************************************************/
 /**
  * Audio driver stack.
@@ -97,11 +137,210 @@
 typedef AUDIOTESTDRVMIXSTREAM *PAUDIOTESTDRVMIXSTREAM;
 
-
-/** The test handle. */
-extern RTTEST         g_hTest;
-extern unsigned       g_uVerbosity;
-extern bool           g_fDrvAudioDebug;
-extern const char    *g_pszDrvAudioDebug;
-
+/**
+ * Backends.
+ *
+ * @note The first backend in the array is the default one for the platform.
+ */
+struct
+{
+    /** The driver registration structure. */
+    PCPDMDRVREG pDrvReg;
+    /** The backend name.
+     * Aliases are implemented by having multiple entries for the same backend.  */
+    const char *pszName;
+} const g_aBackends[] =
+{
+#if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX)
+    {   &g_DrvHostALSAAudio,          "alsa" },
+#endif
+#ifdef VBOX_WITH_AUDIO_PULSE
+    {   &g_DrvHostPulseAudio,         "pulseaudio" },
+    {   &g_DrvHostPulseAudio,         "pulse" },
+    {   &g_DrvHostPulseAudio,         "pa" },
+#endif
+#ifdef VBOX_WITH_AUDIO_OSS
+    {   &g_DrvHostOSSAudio,           "oss" },
+#endif
+#if defined(RT_OS_DARWIN)
+    {   &g_DrvHostCoreAudio,          "coreaudio" },
+    {   &g_DrvHostCoreAudio,          "core" },
+    {   &g_DrvHostCoreAudio,          "ca" },
+#endif
+#if defined(RT_OS_WINDOWS)
+    {   &g_DrvHostAudioWas,           "wasapi" },
+    {   &g_DrvHostAudioWas,           "was" },
+    {   &g_DrvHostDSound,             "directsound" },
+    {   &g_DrvHostDSound,             "dsound" },
+    {   &g_DrvHostDSound,             "ds" },
+#endif
+    {   &g_DrvHostValidationKitAudio, "valkit" }
+};
+AssertCompile(sizeof(g_aBackends) > 0 /* port me */);
+
+
+
+/**
+ * Enumeration specifying the current audio test mode.
+ */
+typedef enum AUDIOTESTMODE
+{
+    /** Unknown mode. */
+    AUDIOTESTMODE_UNKNOWN = 0,
+    /** VKAT is running on the guest side. */
+    AUDIOTESTMODE_GUEST,
+    /** VKAT is running on the host side. */
+    AUDIOTESTMODE_HOST
+} AUDIOTESTMODE;
+
+struct AUDIOTESTENV;
+/** Pointer a audio test environment. */
+typedef AUDIOTESTENV *PAUDIOTESTENV;
+
+struct AUDIOTESTDESC;
+/** Pointer a audio test descriptor. */
+typedef AUDIOTESTDESC *PAUDIOTESTDESC;
+
+/**
+ * Callback to set up the test parameters for a specific test.
+ *
+ * @returns IPRT status code.
+ * @retval  VINF_SUCCESS    if setting the parameters up succeeded. Any other error code
+ *                          otherwise indicating the kind of error.
+ * @param   pszTest         Test name.
+ * @param   pTstParmsAcq    The audio test parameters to set up.
+ */
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx));
+/** Pointer to an audio test setup callback. */
+typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP;
+
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms));
+/** Pointer to an audio test exec callback. */
+typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC;
+
+typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx));
+/** Pointer to an audio test destroy callback. */
+typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY;
+
+/**
+ * Structure for keeping an audio test audio stream.
+ */
+typedef struct AUDIOTESTSTREAM
+{
+    /** The PDM stream. */
+    PPDMAUDIOSTREAM         pStream;
+    /** The backend stream. */
+    PPDMAUDIOBACKENDSTREAM  pBackend;
+    /** The stream config. */
+    PDMAUDIOSTREAMCFG       Cfg;
+} AUDIOTESTSTREAM;
+/** Pointer to audio test stream. */
+typedef AUDIOTESTSTREAM *PAUDIOTESTSTREAM;
+
+/** Maximum audio streams a test environment can handle. */
+#define AUDIOTESTENV_MAX_STREAMS 8
+
+/**
+ * Audio test environment parameters.
+ * Not necessarily bound to a specific test (can be reused).
+ */
+typedef struct AUDIOTESTENV
+{
+    /** Audio testing mode. */
+    AUDIOTESTMODE           enmMode;
+    /** Whether self test mode is active or not. */
+    bool                    fSelftest;
+    /** Output path for storing the test environment's final test files. */
+    char                    szTag[AUDIOTEST_TAG_MAX];
+    /** Output path for storing the test environment's final test files. */
+    char                    szPathOut[RTPATH_MAX];
+    /** Temporary path for this test environment. */
+    char                    szPathTemp[RTPATH_MAX];
+    /** Buffer size (in ms). */
+    RTMSINTERVAL            cMsBufferSize;
+    /** Pre-buffering time (in ms). */
+    RTMSINTERVAL            cMsPreBuffer;
+    /** Scheduling hint (in ms). */
+    RTMSINTERVAL            cMsSchedulingHint;
+    /** The audio test driver stack. */
+    AUDIOTESTDRVSTACK       DrvStack;
+    /** The current (last) audio device enumeration to use. */
+    PDMAUDIOHOSTENUM        DevEnum;
+    /** Audio stream. */
+    AUDIOTESTSTREAM         aStreams[AUDIOTESTENV_MAX_STREAMS];
+    /** The audio test set to use. */
+    AUDIOTESTSET            Set;
+    union
+    {
+        struct
+        {
+            /** ATS instance to use. */
+            ATSSERVER       Srv;
+        } Guest;
+        struct
+        {
+            /** Client connected to the ATS on the guest side. */
+            ATSCLIENT       AtsClGuest;
+            /** Client connected to the ATS on the Validation Kit. */
+            ATSCLIENT       AtsClValKit;
+        } Host;
+    } u;
+} AUDIOTESTENV;
+
+/**
+ * Audio test descriptor.
+ */
+typedef struct AUDIOTESTDESC
+{
+    /** (Sort of) Descriptive test name. */
+    const char             *pszName;
+    /** Flag whether the test is excluded. */
+    bool                    fExcluded;
+    /** The setup callback. */
+    PFNAUDIOTESTSETUP       pfnSetup;
+    /** The exec callback. */
+    PFNAUDIOTESTEXEC        pfnExec;
+    /** The destruction callback. */
+    PFNAUDIOTESTDESTROY     pfnDestroy;
+} AUDIOTESTDESC;
+
+/**
+ * Structure for keeping a VKAT self test context.
+ */
+typedef struct SELFTESTCTX
+{
+    /** Common tag for guest and host side. */
+    char             szTag[AUDIOTEST_TAG_MAX];
+    /** Whether to use DrvAudio in the driver stack or not. */
+    bool             fWithDrvAudio;
+    struct
+    {
+        AUDIOTESTENV TstEnv;
+        /** Audio driver to use.
+         *  Defaults to the platform's default driver. */
+        PCPDMDRVREG  pDrvReg;
+    } Guest;
+    struct
+    {
+        AUDIOTESTENV TstEnv;
+        /** Address of the guest ATS instance.
+         *  Defaults to localhost (127.0.0.1) if not set. */
+        char         szGuestAtsAddr[64];
+        /** Port of the guest ATS instance.
+         *  Defaults to ATS_DEFAULT_PORT if not set. */
+        uint32_t     uGuestAtsPort;
+        /** Address of the Validation Kit audio driver ATS instance.
+         *  Defaults to localhost (127.0.0.1) if not set. */
+        char         szValKitAtsAddr[64];
+        /** Port of the Validation Kit audio driver ATS instance.
+         *  Defaults to ATS_ALT_PORT if not set. */
+        uint32_t     uValKitAtsPort;
+    } Host;
+} SELFTESTCTX;
+/** Pointer to a VKAT self test context. */
+typedef SELFTESTCTX *PSELFTESTCTX;
+
+/*********************************************************************************************************************************
+*   Prototypes                                                                                                                   *
+*********************************************************************************************************************************/
 
 /** @name Driver stack
@@ -138,4 +377,8 @@
 /** @}  */
 
+/** @name Backend handling
+ * @{ */
+PCPDMDRVREG audioTestFindBackendOpt(const char *pszBackend);
+/** @}  */
 
 /** @name Mixing stream
@@ -154,4 +397,35 @@
 /** @}  */
 
+/** @name Device handling
+ * @{ */
+int         audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev);
+int         audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev);
+/** @}  */
+
+/** @name Test environment handling
+ * @{ */
+int         audioTestEnvInit(PAUDIOTESTENV pTstEnv, PCPDMDRVREG pDrvReg, bool fWithDrvAudio, const char *pszTcpAddr, uint32_t uTcpPort);
+void        audioTestEnvDestroy(PAUDIOTESTENV pTstEnv);
+int         audioTestEnvPrologue(PAUDIOTESTENV pTstEnv);
+
+void        audioTestParmsInit(PAUDIOTESTPARMS pTstParms);
+void        audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms);
+/** @}  */
+
+int         audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms);
+
+/** @name Command handlers
+ * @{ */
+RTEXITCODE   audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,
+                              uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+                              uint8_t cChannels, uint8_t cbSample, uint32_t uHz,
+                              bool fWithDrvAudio, bool fWithMixer);
+RTEXITCODE   audioTestRecOne(const char *pszFile, uint8_t cWaveChannels, uint8_t cbWaveSample, uint32_t uWaveHz,
+                             PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,
+                             uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
+                             uint8_t cChannels, uint8_t cbSample, uint32_t uHz, bool fWithDrvAudio, bool fWithMixer,
+                             uint64_t cMaxFrames, uint64_t cNsMaxDuration);
+RTEXITCODE   audioTestDoSelftest(PSELFTESTCTX pCtx);
+/** @}  */
 
 #endif /* !VBOX_INCLUDED_SRC_audio_vkatInternal_h */
