- Timestamp:
- May 16, 2021 4:01:19 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp
r89058 r89059 41 41 #include <iprt/string.h> 42 42 #include <iprt/test.h> 43 #include <iprt/formats/riff.h> 43 44 44 45 #include <package-generated.h> … … 46 47 47 48 #include <VBox/version.h> 49 50 /** 51 * Internal driver instance data 52 * @note This must be put here as it's needed before pdmdrv.h is included. 53 */ 54 typedef struct PDMDRVINSINT 55 { 56 /** The stack the drive belongs to. */ 57 struct AUDIOTESTDRVSTACK *pStack; 58 } PDMDRVINSINT; 59 #define PDMDRVINSINT_DECLARED 60 48 61 #include <VBox/vmm/pdmaudioinline.h> 49 62 #include <VBox/vmm/pdmaudiohostenuminline.h> … … 127 140 128 141 142 /** 143 * Audio driver stack. 144 * 145 * This can be just be backend driver alone or DrvAudio with a backend. 146 * @todo add automatic resampling via mixer so we can test more of the audio 147 * stack used by the device emulations. 148 */ 149 typedef struct AUDIOTESTDRVSTACK 150 { 151 /** The device registration record for the backend. */ 152 PCPDMDRVREG pDrvReg; 153 /** The backend driver instance. */ 154 PPDMDRVINS pDrvBackendIns; 155 /** The backend's audio interface. */ 156 PPDMIHOSTAUDIO pIHostAudio; 157 158 /** The DrvAudio instance. */ 159 PPDMDRVINS pDrvAudioIns; 160 /** This is NULL if we don't use DrvAudio. */ 161 PPDMIAUDIOCONNECTOR pIAudioConnector; 162 } AUDIOTESTDRVSTACK; 163 /** Pointer to an audio driver stack. */ 164 typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK; 165 166 /** 167 * Backend-only stream structure. 168 */ 169 typedef struct AUDIOTESTDRVSTACKSTREAM 170 { 171 /** The public stream data. */ 172 PDMAUDIOSTREAM Core; 173 /** The acquired config. */ 174 PDMAUDIOSTREAMCFG Cfg; 175 /** The backend data (variable size). */ 176 PDMAUDIOBACKENDSTREAM Backend; 177 } AUDIOTESTDRVSTACKSTREAM; 178 /** Pointer to a backend-only stream structure. */ 179 typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM; 180 181 /** 182 * An open wave file. 183 */ 184 typedef struct AUDIOTESTWAVEFILE 185 { 186 /** The file handle. */ 187 RTFILE hFile; 188 /** The absolute file offset of the first sample */ 189 uint32_t offSamples; 190 /** Number of bytes of samples. */ 191 uint32_t cbSamples; 192 /** The current read position relative to @a offSamples. */ 193 uint32_t offCur; 194 /** The PCM properties for the file format. */ 195 PDMAUDIOPCMPROPS Props; 196 } AUDIOTESTWAVEFILE; 197 /** Pointer to an open wave file. */ 198 typedef AUDIOTESTWAVEFILE *PAUDIOTESTWAVEFILE; 199 200 129 201 /********************************************************************************************************************************* 130 202 * Internal Functions * … … 133 205 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms); 134 206 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream); 135 static DECLCALLBACK(RTEXITCODE) audioTestMain(int argc, char **argv); 136 static DECLCALLBACK(RTEXITCODE) audioVerifyMain(int argc, char **argv); 207 208 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm); 209 static RTEXITCODE audioTestVersion(void); 137 210 138 211 … … 203 276 { 204 277 { "--tag", VKAT_VERIFY_OPT_TAG, RTGETOPT_REQ_STRING } 205 };206 207 /**208 * Commands.209 */210 static struct211 {212 const char *pszCommand;213 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(int argc, char **argv));214 PCRTGETOPTDEF paOptions;215 size_t cOptions;216 const char *pszDesc;217 } const g_aCommands[] =218 {219 {220 "test", audioTestMain, g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions),221 "Does some kind of testing, I guess..."222 },223 {224 "verify", audioVerifyMain, g_aCmdVerifyOptions, RT_ELEMENTS(g_aCmdVerifyOptions),225 "Verfies something, I guess..."226 },227 278 }; 228 279 … … 274 325 275 326 /********************************************************************************************************************************* 276 * Implementation*327 * Fake PDM driver handling. * 277 328 *********************************************************************************************************************************/ 278 279 /**280 * Initializes an audio test environment.281 *282 * @param pTstEnv Audio test environment to initialize.283 * @param pDrvAudio Audio driver to use.284 * @param pszPathOut Output path to use. If NULL, the system's temp directory will be used.285 * @param pszPathTemp Temporary path to use. If NULL, the system's temp directory will be used.286 * @param pszTag Tag name to use. If NULL, a generated UUID will be used.287 */288 static int audioTestEnvInit(PAUDIOTESTENV pTstEnv, PPDMIHOSTAUDIO pDrvAudio, const char *pszTag)289 {290 pTstEnv->pDrvAudio = pDrvAudio;291 PDMAudioHostEnumInit(&pTstEnv->DevEnum);292 293 int rc = VINF_SUCCESS;294 295 char szPathTemp[RTPATH_MAX];296 if ( !strlen(pTstEnv->szPathTemp)297 || !strlen(pTstEnv->szPathOut))298 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));299 300 if ( RT_SUCCESS(rc)301 && !strlen(pTstEnv->szPathTemp))302 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");303 304 if ( RT_SUCCESS(rc)305 && !strlen(pTstEnv->szPathOut))306 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");307 308 if (RT_SUCCESS(rc))309 rc = AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);310 311 if (RT_SUCCESS(rc))312 {313 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pszTag);314 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);315 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);316 }317 318 return rc;319 }320 321 /**322 * Destroys an audio test environment.323 *324 * @param pTstEnv Audio test environment to destroy.325 */326 static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)327 {328 if (!pTstEnv)329 return;330 331 PDMAudioHostEnumDelete(&pTstEnv->DevEnum);332 333 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)334 {335 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);336 if (RT_FAILURE(rc2))337 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);338 }339 340 AudioTestSetDestroy(&pTstEnv->Set);341 }342 343 /**344 * Initializes an audio test parameters set.345 *346 * @param pTstParms Test parameters set to initialize.347 */348 static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)349 {350 RT_ZERO(*pTstParms);351 }352 353 /**354 * Destroys an audio test parameters set.355 *356 * @param pTstParms Test parameters set to destroy.357 */358 static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)359 {360 if (!pTstParms)361 return;362 363 return;364 }365 366 /**367 * Shows the logo.368 *369 * @param pStream Output stream to show logo on.370 */371 static void audioTestShowLogo(PRTSTREAM pStream)372 {373 RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) "374 VBOX_VERSION_STRING " - r%s\n"375 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"376 "All rights reserved.\n\n", RTBldCfgRevisionStr());377 }378 379 /**380 * Shows tool usage text.381 */382 static void audioTestUsage(PRTSTREAM pStrm)383 {384 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n",385 RTPathFilename(RTProcExecutablePath()));386 RTStrmPrintf(pStrm,387 "\n"388 "Global Options:\n"389 " -q, --quiet\n"390 " Sets verbosity to zero.\n"391 " -v, --verbose\n"392 " Increase verbosity.\n"393 " -V, --version\n"394 " Displays version.\n"395 " -h, -?, --help\n"396 " Displays help.\n"397 );398 399 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)400 {401 RTStrmPrintf(pStrm,402 "\n"403 "Command '%s':\n"404 " %s\n"405 "Options for '%s':\n",406 g_aCommands[iCmd].pszCommand, g_aCommands[iCmd].pszDesc, g_aCommands[iCmd].pszCommand);407 PCRTGETOPTDEF const paOptions = g_aCommands[iCmd].paOptions;408 for (unsigned i = 0; i < g_aCommands[iCmd].cOptions; i++)409 {410 if (RT_C_IS_PRINT(paOptions[i].iShort))411 RTStrmPrintf(pStrm, " -%c, %s\n", g_aCmdTestOptions[i].iShort, g_aCmdTestOptions[i].pszLong);412 else413 RTStrmPrintf(pStrm, " %s\n", g_aCmdTestOptions[i].pszLong);414 415 const char *pszHelp = NULL;416 if (paOptions == g_aCmdTestOptions)417 {418 switch (g_aCmdTestOptions[i].iShort)419 {420 case 'd':421 pszHelp = "Use the specified audio device";422 break;423 case 'e':424 pszHelp = "Exclude the given test id from the list";425 break;426 case 'a':427 pszHelp = "Exclude all tests from the list (useful to enable single tests later with --include)";428 break;429 case 'i':430 pszHelp = "Include the given test id in the list";431 break;432 }433 }434 /** @todo Add help text for all options. */435 if (pszHelp)436 RTStrmPrintf(pStrm, " %s\n", pszHelp);437 }438 }439 440 }441 329 442 330 /** @name Driver Helper Fakes / Stubs … … 485 373 /** @} */ 486 374 487 488 375 /** 489 376 * Constructs a PDM audio driver instance. 490 377 * 491 378 * @returns VBox status code. 492 * @param pDrvReg PDM driver registration record to use for construction. 493 * @param ppDrvIns Where to return the driver instance structure. 494 * @param ppDrvAudio Where to return the audio driver interface of type IHOSTAUDIO. 495 */ 496 static int audioTestDrvConstruct(PCPDMDRVREG pDrvReg, PPPDMDRVINS ppDrvIns, PPDMIHOSTAUDIO *ppDrvAudio) 379 * @param pDrvStack The stack this is associated with. 380 * @param pDrvReg PDM driver registration record to use for construction. 381 * @param pParentDrvIns The parent driver (if any). 382 * @param ppDrvIns Where to return the driver instance structure. 383 */ 384 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns, 385 PPPDMDRVINS ppDrvIns) 497 386 { 498 387 /* The destruct function must have valid data to work with. */ 499 388 *ppDrvIns = NULL; 500 *ppDrvAudio = NULL;501 389 502 390 /* … … 524 412 RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY); 525 413 526 pDrvIns->u32Version = PDM_DRVINS_VERSION; 527 pDrvIns->iInstance = 0; 528 pDrvIns->pHlpR3 = &s_DrvHlp; 529 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0]; 530 pDrvIns->pReg = pDrvReg; 531 pDrvIns->pCfg = (PCFGMNODE)pDrvReg; 532 //pDrvIns->pUpBase = NULL; 533 //pDrvIns->pDownBase = NULL; 414 pDrvIns->u32Version = PDM_DRVINS_VERSION; 415 pDrvIns->iInstance = 0; 416 pDrvIns->pHlpR3 = &s_DrvHlp; 417 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0]; 418 pDrvIns->pReg = pDrvReg; 419 pDrvIns->pCfg = (PCFGMNODE)pDrvReg; 420 pDrvIns->Internal.s.pStack = pDrvStack; 421 pDrvIns->pUpBase = NULL; 422 pDrvIns->pDownBase = NULL; 423 if (pParentDrvIns) 424 { 425 Assert(pParentDrvIns->pDownBase == NULL); 426 pParentDrvIns->pDownBase = &pDrvIns->IBase; 427 pDrvIns->pUpBase = &pParentDrvIns->IBase; 428 } 534 429 535 430 /* … … 539 434 if (RT_SUCCESS(rc)) 540 435 { 541 PPDMIHOSTAUDIO pDrvAudio = (PPDMIHOSTAUDIO)pDrvIns->IBase.pfnQueryInterface(&pDrvIns->IBase, PDMIHOSTAUDIO_IID); 542 if (pDrvAudio) 436 *ppDrvIns = pDrvIns; 437 return VINF_SUCCESS; 438 } 439 440 RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc); 441 if (pDrvReg->pfnDestruct) 442 pDrvReg->pfnDestruct(pDrvIns); 443 RTMemFree(pDrvIns); 444 return rc; 445 } 446 447 /** 448 * Destructs a PDM audio driver instance. 449 * 450 * @param pDrvIns Driver instance to destruct. 451 */ 452 static void audioTestDrvDestruct(PPDMDRVINS pDrvIns) 453 { 454 if (pDrvIns) 455 { 456 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION); 457 458 if (pDrvIns->pReg->pfnDestruct) 459 pDrvIns->pReg->pfnDestruct(pDrvIns); 460 461 pDrvIns->u32Version = 0; 462 pDrvIns->pReg = NULL; 463 RTMemFree(pDrvIns); 464 } 465 } 466 467 /** 468 * Sends the PDM driver a power off notification. 469 * 470 * @param pDrvIns Driver instance to notify. 471 */ 472 static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns) 473 { 474 if (pDrvIns) 475 { 476 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION); 477 if (pDrvIns->pReg->pfnPowerOff) 478 pDrvIns->pReg->pfnPowerOff(pDrvIns); 479 } 480 } 481 482 /** 483 * Deletes a driver stack. 484 * 485 * This will power off and destroy the drivers. 486 */ 487 static void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack) 488 { 489 /* 490 * Do power off notifications (top to bottom). 491 */ 492 audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns); 493 audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns); 494 495 /* 496 * Drivers are destroyed from bottom to top (closest to the device). 497 */ 498 audioTestDrvDestruct(pDrvStack->pDrvBackendIns); 499 pDrvStack->pDrvBackendIns = NULL; 500 pDrvStack->pIHostAudio = NULL; 501 502 audioTestDrvDestruct(pDrvStack->pDrvAudioIns); 503 pDrvStack->pDrvAudioIns = NULL; 504 pDrvStack->pIAudioConnector = NULL; 505 } 506 507 /** 508 * Initializes a driver stack. 509 * @returns VBox status code. 510 * @param pDrvStack The driver stack to initialize. 511 * @param pDrvReg The backend driver to use. 512 * @param fWithDrvAudio Whether to inlcude DrvAudio in the stack or not. 513 */ 514 static int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio) 515 { 516 int rc; 517 RT_ZERO(*pDrvStack); 518 pDrvStack->pDrvReg = pDrvReg; 519 if (!fWithDrvAudio) 520 rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns); 521 else 522 { 523 rc = VERR_NOT_IMPLEMENTED; 524 } 525 526 /* 527 * Get the IHostAudio interface and check that the host driver is working. 528 */ 529 if (RT_SUCCESS(rc)) 530 { 531 pDrvStack->pIHostAudio 532 = (PPDMIHOSTAUDIO)pDrvStack->pDrvBackendIns->IBase.pfnQueryInterface(&pDrvStack->pDrvBackendIns->IBase, 533 PDMIHOSTAUDIO_IID); 534 if (pDrvStack->pIHostAudio) 543 535 { 544 PDMAUDIOBACKENDSTS enmStatus = pDrv Audio->pfnGetStatus(pDrvAudio, PDMAUDIODIR_OUT);536 PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT); 545 537 if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING) 546 {547 *ppDrvAudio = pDrvAudio;548 *ppDrvIns = pDrvIns;549 538 return VINF_SUCCESS; 550 } 539 551 540 RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus); 552 541 } 553 542 else 554 543 RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName); 544 audioTestDriverStackDelete(pDrvStack); 545 } 546 547 return rc; 548 } 549 550 /** 551 * Creates an output stream. 552 * 553 * @returns VBox status code. 554 * @param pDrvStack The audio driver stack to create it via. 555 * @param pProps The audio properties to use. 556 * @param cMsBufferSize The buffer size in milliseconds. 557 * @param cMsPreBuffer The pre-buffering amount in milliseconds. 558 * @param cMsSchedulingHint The scheduling hint in milliseconds. 559 * @param ppStream Where to return the stream pointer on success. 560 */ 561 static int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps, 562 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, 563 PPDMAUDIOSTREAM *ppStream) 564 { 565 *ppStream = NULL; 566 567 int rc; 568 if (pDrvStack->pIAudioConnector) 569 { 570 rc = VERR_NOT_IMPLEMENTED; 555 571 } 556 572 else 557 RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc); 558 if (pDrvReg->pfnDestruct) 559 pDrvReg->pfnDestruct(pDrvIns); 560 RTMemFree(pDrvIns); 573 { 574 /* 575 * Calculate the stream config. 576 */ 577 PDMAUDIOSTREAMCFG CfgReq; 578 rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps); 579 AssertRC(rc); 580 CfgReq.enmDir = PDMAUDIODIR_OUT; 581 CfgReq.u.enmDst = PDMAUDIOPLAYBACKDST_UNKNOWN; 582 CfgReq.enmLayout = PDMAUDIOSTREAMLAYOUT_INTERLEAVED; 583 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0 584 ? 10 : cMsSchedulingHint; 585 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps, 586 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0 587 ? 300 : cMsBufferSize); 588 if (cMsPreBuffer == UINT32_MAX) 589 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize * 2 / 3; 590 else 591 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer); 592 if (CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16) 593 { 594 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!", 595 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize); 596 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16 597 ? CfgReq.Backend.cFramesBufferSize - 16 : 0; 598 } 599 600 static uint32_t s_idxStream = 0; 601 uint32_t const idxStream = s_idxStream++; 602 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream); 603 604 /* 605 * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM 606 * structure actually is for this backend. 607 */ 608 PDMAUDIOBACKENDCFG BackendCfg; 609 rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg); 610 if (RT_SUCCESS(rc)) 611 { 612 if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM)) 613 { 614 /* 615 * Allocate and initialize the stream. 616 */ 617 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream; 618 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream); 619 if (pStreamAt) 620 { 621 pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC; 622 pStreamAt->Core.enmDir = PDMAUDIODIR_OUT; 623 pStreamAt->Core.cbBackend = cbStream; 624 pStreamAt->Core.Props = CfgReq.Props; 625 RTStrPrintf(pStreamAt->Core.szName, sizeof(pStreamAt->Core.szName), "out-%u", idxStream); 626 627 pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC; 628 pStreamAt->Backend.pStream = &pStreamAt->Core; 629 630 /* 631 * Call the backend to create the stream. 632 */ 633 pStreamAt->Cfg = CfgReq; 634 635 rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend, 636 &CfgReq, &pStreamAt->Cfg); 637 if (RT_SUCCESS(rc)) 638 { 639 pStreamAt->Core.Props = pStreamAt->Cfg.Props; 640 if (g_uVerbosity > 1) 641 { 642 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16]; 643 RTMsgInfo("Created backend stream: %s\n", 644 PDMAudioStrmCfgToString(&pStreamAt->Cfg, szTmp, sizeof(szTmp))); 645 } 646 647 /* Return if stream is ready: */ 648 if (rc == VINF_SUCCESS) 649 { 650 *ppStream = &pStreamAt->Core; 651 return VINF_SUCCESS; 652 } 653 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED) 654 { 655 /* 656 * Do async init right here and now. 657 */ 658 rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend, 659 false /*fDestroyed*/); 660 if (RT_SUCCESS(rc)) 661 { 662 *ppStream = &pStreamAt->Core; 663 return VINF_SUCCESS; 664 } 665 666 RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc); 667 } 668 else 669 { 670 RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc); 671 rc = VERR_IPE_UNEXPECTED_INFO_STATUS; 672 } 673 pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend); 674 } 675 else 676 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc\n", rc); 677 } 678 else 679 { 680 RTTestFailed(g_hTest, "Out of memory!\n"); 681 rc = VERR_NO_MEMORY; 682 } 683 } 684 else 685 { 686 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM)); 687 rc = VERR_OUT_OF_RANGE; 688 } 689 } 690 else 691 RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc); 692 } 561 693 return rc; 562 694 } 563 695 564 696 /** 565 * Destructs a PDM audio driver instance. 697 * Destroys a stream. 698 */ 699 static void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 700 { 701 if (pStream) 702 { 703 if (pDrvStack->pIAudioConnector) 704 { 705 int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream); 706 if (RT_FAILURE(rc)) 707 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc); 708 } 709 else 710 { 711 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 712 int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend); 713 if (RT_SUCCESS(rc)) 714 { 715 pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC; 716 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC; 717 RTMemFree(pStreamAt); 718 } 719 else 720 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc); 721 } 722 } 723 } 724 725 /** 726 * Enables a stream. 727 */ 728 static int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 729 { 730 int rc; 731 if (pDrvStack->pIAudioConnector) 732 { 733 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE); 734 if (RT_FAILURE(rc)) 735 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc); 736 } 737 else 738 { 739 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 740 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_ENABLE); 741 if (RT_FAILURE(rc)) 742 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc); 743 } 744 return rc; 745 } 746 747 /** 748 * Drains an output stream. 749 */ 750 static int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync) 751 { 752 int rc; 753 if (pDrvStack->pIAudioConnector) 754 { 755 /* 756 * Issue the drain request. 757 */ 758 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN); 759 if (RT_SUCCESS(rc) && fSync) 760 { 761 /* 762 * This is a synchronous drain, so wait for the driver to change state to inactive. 763 */ 764 PDMAUDIOSTREAMSTATE enmState; 765 while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream)) 766 >= PDMAUDIOSTREAMSTATE_ENABLED) 767 { 768 RTThreadSleep(2); 769 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream); 770 if (RT_FAILURE(rc)) 771 { 772 RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc); 773 break; 774 } 775 } 776 if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE) 777 { 778 RTTestFailed(g_hTest, "Stream state not INACTIVE after draing: %s", PDMAudioStreamStateGetName(enmState)); 779 rc = VERR_AUDIO_STREAM_NOT_READY; 780 } 781 } 782 else if (RT_FAILURE(rc)) 783 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc); 784 } 785 else 786 { 787 /* 788 * Issue the drain request. 789 */ 790 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 791 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_DRAIN); 792 if (RT_SUCCESS(rc) && fSync) 793 { 794 /* 795 * This is a synchronous drain, so wait for the driver to change state to inactive. 796 */ 797 PDMHOSTAUDIOSTREAMSTATE enmHostState; 798 while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend)) 799 == PDMHOSTAUDIOSTREAMSTATE_DRAINING) 800 { 801 RTThreadSleep(2); 802 uint32_t cbWritten = UINT32_MAX; 803 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, 804 NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten); 805 if (RT_FAILURE(rc)) 806 { 807 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc); 808 break; 809 } 810 if (cbWritten != 0) 811 { 812 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten); 813 rc = VERR_MISSING; 814 break; 815 } 816 } 817 if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY) 818 { 819 RTTestFailed(g_hTest, "Stream state not OKAY after draing: %s", PDMHostAudioStreamStateGetName(enmHostState)); 820 rc = VERR_AUDIO_STREAM_NOT_READY; 821 } 822 } 823 else if (RT_FAILURE(rc)) 824 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc); 825 } 826 return rc; 827 } 828 829 /** 830 * Checks if the stream is okay. 831 * @returns true if okay, false if not. 832 */ 833 static bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 834 { 835 /* 836 * Get the stream status and check if it means is okay or not. 837 */ 838 bool fRc = false; 839 if (pDrvStack->pIAudioConnector) 840 { 841 PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream); 842 switch (enmState) 843 { 844 case PDMAUDIOSTREAMSTATE_NOT_WORKING: 845 case PDMAUDIOSTREAMSTATE_NEED_REINIT: 846 break; 847 case PDMAUDIOSTREAMSTATE_INACTIVE: 848 case PDMAUDIOSTREAMSTATE_ENABLED: 849 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE: 850 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE: 851 fRc = true; 852 break; 853 /* no default */ 854 case PDMAUDIOSTREAMSTATE_INVALID: 855 case PDMAUDIOSTREAMSTATE_END: 856 case PDMAUDIOSTREAMSTATE_32BIT_HACK: 857 break; 858 } 859 } 860 else 861 { 862 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 863 PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, 864 &pStreamAt->Backend); 865 switch (enmHostState) 866 { 867 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING: 868 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING: 869 break; 870 case PDMHOSTAUDIOSTREAMSTATE_OKAY: 871 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: 872 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: 873 fRc = true; 874 break; 875 /* no default */ 876 case PDMHOSTAUDIOSTREAMSTATE_INVALID: 877 case PDMHOSTAUDIOSTREAMSTATE_END: 878 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK: 879 break; 880 } 881 } 882 return fRc; 883 } 884 885 /** 886 * Gets the number of bytes it's currently possible to write to the stream. 887 */ 888 static uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 889 { 890 uint32_t cbWritable; 891 if (pDrvStack->pIAudioConnector) 892 cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream); 893 else 894 { 895 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 896 cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend); 897 } 898 return cbWritable; 899 } 900 901 /** 902 * Tries to play the @a cbBuf bytes of samples in @a pvBuf. 903 */ 904 static int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, 905 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed) 906 { 907 int rc; 908 if (pDrvStack->pIAudioConnector) 909 { 910 rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed); 911 if (RT_FAILURE(rc)) 912 RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc); 913 } 914 else 915 { 916 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 917 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed); 918 if (RT_FAILURE(rc)) 919 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc); 920 } 921 return rc; 922 } 923 924 925 /********************************************************************************************************************************* 926 * WAVE File Reader. * 927 *********************************************************************************************************************************/ 928 /** 929 * Opens a wave-file for reading. 566 930 * 567 931 * @returns VBox status code. 568 * @param pDrvReg PDM driver registration record to destruct. 569 * @param pDrvIns Driver instance to destruct. 570 */ 571 static int audioTestDrvDestruct(PCPDMDRVREG pDrvReg, PPDMDRVINS pDrvIns) 572 { 573 if (!pDrvIns) 574 return VINF_SUCCESS; 575 576 if (pDrvReg->pfnDestruct) 577 pDrvReg->pfnDestruct(pDrvIns); 578 579 pDrvIns->u32Version = 0; 580 RTMemFree(pDrvIns); 581 582 return VINF_SUCCESS; 583 } 932 * @param pszFile The file to open. 933 * @param pWaveFile The open wave file structure to fill in on success. 934 */ 935 static int AudioTestWaveFileOpen(const char *pszFile, PAUDIOTESTWAVEFILE pWaveFile) 936 { 937 RT_ZERO(pWaveFile->Props); 938 pWaveFile->hFile = NIL_RTFILE; 939 int rc = RTFileOpen(&pWaveFile->hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); 940 if (RT_FAILURE(rc)) 941 return rc; 942 uint64_t cbFile = 0; 943 rc = RTFileQuerySize(pWaveFile->hFile, &cbFile); 944 if (RT_SUCCESS(rc)) 945 { 946 union 947 { 948 uint8_t ab[512]; 949 struct 950 { 951 RTRIFFHDR Hdr; 952 RTRIFFWAVEFMTCHUNK Fmt; 953 } Wave; 954 RTRIFFLIST List; 955 RTRIFFWAVEDATACHUNK Data; 956 } uBuf; 957 958 rc = RTFileRead(pWaveFile->hFile, &uBuf.Wave, sizeof(uBuf.Wave), NULL); 959 if (RT_SUCCESS(rc)) 960 { 961 rc = VERR_VFS_UNKNOWN_FORMAT; 962 if ( uBuf.Wave.Hdr.uMagic == RTRIFFHDR_MAGIC 963 && uBuf.Wave.Hdr.uFileType == RTRIFF_FILE_TYPE_WAVE 964 && uBuf.Wave.Fmt.Chunk.uMagic == RTRIFFWAVEFMT_MAGIC 965 && uBuf.Wave.Fmt.Chunk.cbChunk >= sizeof(uBuf.Wave.Fmt.Data)) 966 { 967 if (uBuf.Wave.Hdr.cbFile != cbFile - sizeof(RTRIFFCHUNK)) 968 RTMsgWarning("%s: File size mismatch: %#x, actual %#RX64 (ignored)", 969 pszFile, uBuf.Wave.Hdr.cbFile, cbFile - sizeof(RTRIFFCHUNK)); 970 rc = VERR_VFS_BOGUS_FORMAT; 971 if (uBuf.Wave.Fmt.Data.uFormatTag != RTRIFFWAVEFMT_TAG_PCM) 972 RTMsgError("%s: Unsupported uFormatTag value: %u (expected 1)", pszFile, uBuf.Wave.Fmt.Data.uFormatTag); 973 else if ( uBuf.Wave.Fmt.Data.cBitsPerSample != 8 974 && uBuf.Wave.Fmt.Data.cBitsPerSample != 16 975 && uBuf.Wave.Fmt.Data.cBitsPerSample != 32) 976 RTMsgError("%s: Unsupported cBitsPerSample value: %u", pszFile, uBuf.Wave.Fmt.Data.cBitsPerSample); 977 else if ( uBuf.Wave.Fmt.Data.cChannels < 1 978 || uBuf.Wave.Fmt.Data.cChannels >= 16) 979 RTMsgError("%s: Unsupported cChannels value: %u (expected 1..15)", pszFile, uBuf.Wave.Fmt.Data.cChannels); 980 else if ( uBuf.Wave.Fmt.Data.uHz < 4096 981 || uBuf.Wave.Fmt.Data.uHz > 768000) 982 RTMsgError("%s: Unsupported uHz value: %u (expected 4096..768000)", pszFile, uBuf.Wave.Fmt.Data.uHz); 983 else if (uBuf.Wave.Fmt.Data.cbFrame != uBuf.Wave.Fmt.Data.cChannels * uBuf.Wave.Fmt.Data.cBitsPerSample / 8) 984 RTMsgError("%s: Invalid cbFrame value: %u (expected %u)", pszFile, uBuf.Wave.Fmt.Data.cbFrame, 985 uBuf.Wave.Fmt.Data.cChannels * uBuf.Wave.Fmt.Data.cBitsPerSample / 8); 986 else if (uBuf.Wave.Fmt.Data.cbRate != uBuf.Wave.Fmt.Data.cbFrame * uBuf.Wave.Fmt.Data.uHz) 987 RTMsgError("%s: Invalid cbRate value: %u (expected %u)", pszFile, uBuf.Wave.Fmt.Data.cbRate, 988 uBuf.Wave.Fmt.Data.cbFrame * uBuf.Wave.Fmt.Data.uHz); 989 else 990 { 991 /* 992 * Copy out the data we need from the file format structure. 993 */ 994 PDMAudioPropsInit(&pWaveFile->Props, uBuf.Wave.Fmt.Data.cBitsPerSample / 8, true /*fSigned*/, 995 uBuf.Wave.Fmt.Data.cChannels, uBuf.Wave.Fmt.Data.uHz); 996 pWaveFile->offSamples = sizeof(RTRIFFHDR) + sizeof(RTRIFFCHUNK) + uBuf.Wave.Fmt.Chunk.cbChunk; 997 998 /* 999 * Find the 'data' chunk with the audio samples. 1000 * 1001 * There can be INFO lists both preceeding this and succeeding 1002 * it, containing IART and other things we can ignored. Thus 1003 * we read a list header here rather than just a chunk header, 1004 * since it doesn't matter if we read 4 bytes extra as 1005 * AudioTestWaveFileRead uses RTFileReadAt anyway. 1006 */ 1007 rc = RTFileReadAt(pWaveFile->hFile, pWaveFile->offSamples, &uBuf, sizeof(uBuf.List), NULL); 1008 if (RT_SUCCESS(rc)) 1009 { 1010 /* HACK ALERT: Skip one INFO list and hope we find a data chunk following it: */ 1011 if ( uBuf.List.uMagic == RTRIFFLIST_MAGIC 1012 && uBuf.List.uListType == RTRIFFLIST_TYPE_INFO 1013 && uBuf.List.cbChunk <= (uint32_t)cbFile - pWaveFile->offSamples - sizeof(RTRIFFCHUNK)) 1014 { 1015 pWaveFile->offSamples += sizeof(RTRIFFCHUNK) + uBuf.List.cbChunk; 1016 rc = RTFileReadAt(pWaveFile->hFile, pWaveFile->offSamples, &uBuf, sizeof(uBuf.List), NULL); 1017 } 1018 1019 pWaveFile->offSamples += sizeof(uBuf.Data.Chunk); 1020 pWaveFile->cbSamples = (uint32_t)cbFile - pWaveFile->offSamples; 1021 1022 rc = VERR_VFS_BOGUS_FORMAT; 1023 if ( uBuf.Data.Chunk.uMagic == RTRIFFWAVEDATACHUNK_MAGIC 1024 && uBuf.Data.Chunk.cbChunk <= pWaveFile->cbSamples 1025 && PDMAudioPropsIsSizeAligned(&pWaveFile->Props, uBuf.Data.Chunk.cbChunk)) 1026 { 1027 pWaveFile->cbSamples = uBuf.Data.Chunk.cbChunk; 1028 /* 1029 * We're good! 1030 */ 1031 pWaveFile->offCur = 0; 1032 return VINF_SUCCESS; 1033 } 1034 1035 RTMsgError("%s: Bad data header: uMagic=%#x (expected %#x), cbChunk=%#x (max %#RX64, align %u)", 1036 pszFile, uBuf.Data.Chunk.uMagic, RTRIFFWAVEDATACHUNK_MAGIC, 1037 uBuf.Data.Chunk.cbChunk, pWaveFile->cbSamples, PDMAudioPropsFrameSize(&pWaveFile->Props)); 1038 } 1039 else 1040 RTMsgError("%s: Failed to read data header: %Rrc", pszFile, rc); 1041 } 1042 } 1043 else 1044 RTMsgError("%s: Bad file header: uMagic=%#x (vs. %#x), uFileType=%#x (vs %#x), uFmtMagic=%#x (vs %#x) cbFmtChunk=%#x (min %#x)", 1045 pszFile, uBuf.Wave.Hdr.uMagic, RTRIFFHDR_MAGIC, uBuf.Wave.Hdr.uFileType, RTRIFF_FILE_TYPE_WAVE, 1046 uBuf.Wave.Fmt.Chunk.uMagic, RTRIFFWAVEFMT_MAGIC, 1047 uBuf.Wave.Fmt.Chunk.cbChunk, sizeof(uBuf.Wave.Fmt.Data)); 1048 } 1049 else 1050 RTMsgError("%s: Failed to read file header: %Rrc", pszFile, rc); 1051 } 1052 else 1053 RTMsgError("%s: Failed to query file size: %Rrc", pszFile, rc); 1054 1055 RTFileClose(pWaveFile->hFile); 1056 pWaveFile->hFile = NIL_RTFILE; 1057 return rc; 1058 } 1059 1060 /** 1061 * Closes a wave file. 1062 */ 1063 static void AudioTestWaveFileClose(PAUDIOTESTWAVEFILE pWaveFile) 1064 { 1065 RTFileClose(pWaveFile->hFile); 1066 pWaveFile->hFile = NIL_RTFILE; 1067 } 1068 1069 /** 1070 * Reads samples from a wave file. 1071 * 1072 * @returns VBox status code. See RTVfsFileRead for EOF status handling. 1073 * @param pWaveFile The file to read from. 1074 * @param pvBuf Where to put the samples. 1075 * @param cbBuf How much to read at most. 1076 * @param pcbRead Where to return the actual number of bytes read, 1077 * optional. 1078 */ 1079 static int AudioTestWaveFileRead(PAUDIOTESTWAVEFILE pWaveFile, void *pvBuf, size_t cbBuf, size_t *pcbRead) 1080 { 1081 int rc = RTFileReadAt(pWaveFile->hFile, pWaveFile->offCur, pvBuf, cbBuf, pcbRead); 1082 if (RT_SUCCESS(rc)) 1083 { 1084 if (pcbRead) 1085 { 1086 pWaveFile->offCur += *pcbRead; 1087 if (cbBuf > *pcbRead) 1088 rc = VINF_EOF; 1089 else if (!cbBuf && pWaveFile->offCur == pWaveFile->cbSamples) 1090 rc = VINF_EOF; 1091 } 1092 else 1093 pWaveFile->offCur += cbBuf; 1094 } 1095 return rc; 1096 } 1097 1098 1099 /********************************************************************************************************************************* 1100 * Implementation of Something * 1101 *********************************************************************************************************************************/ 1102 1103 /** 1104 * Initializes an audio test environment. 1105 * 1106 * @param pTstEnv Audio test environment to initialize. 1107 * @param pDrvAudio Audio driver to use. 1108 * @param pszPathOut Output path to use. If NULL, the system's temp directory will be used. 1109 * @param pszPathTemp Temporary path to use. If NULL, the system's temp directory will be used. 1110 * @param pszTag Tag name to use. If NULL, a generated UUID will be used. 1111 */ 1112 static int audioTestEnvInit(PAUDIOTESTENV pTstEnv, PPDMIHOSTAUDIO pDrvAudio, const char *pszTag) 1113 { 1114 pTstEnv->pDrvAudio = pDrvAudio; 1115 PDMAudioHostEnumInit(&pTstEnv->DevEnum); 1116 1117 int rc = VINF_SUCCESS; 1118 1119 char szPathTemp[RTPATH_MAX]; 1120 if ( !strlen(pTstEnv->szPathTemp) 1121 || !strlen(pTstEnv->szPathOut)) 1122 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp)); 1123 1124 if ( RT_SUCCESS(rc) 1125 && !strlen(pTstEnv->szPathTemp)) 1126 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp"); 1127 1128 if ( RT_SUCCESS(rc) 1129 && !strlen(pTstEnv->szPathOut)) 1130 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat"); 1131 1132 if (RT_SUCCESS(rc)) 1133 rc = AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag); 1134 1135 if (RT_SUCCESS(rc)) 1136 { 1137 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pszTag); 1138 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut); 1139 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp); 1140 } 1141 1142 return rc; 1143 } 1144 1145 /** 1146 * Destroys an audio test environment. 1147 * 1148 * @param pTstEnv Audio test environment to destroy. 1149 */ 1150 static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv) 1151 { 1152 if (!pTstEnv) 1153 return; 1154 1155 PDMAudioHostEnumDelete(&pTstEnv->DevEnum); 1156 1157 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++) 1158 { 1159 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]); 1160 if (RT_FAILURE(rc2)) 1161 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2); 1162 } 1163 1164 AudioTestSetDestroy(&pTstEnv->Set); 1165 } 1166 1167 /** 1168 * Initializes an audio test parameters set. 1169 * 1170 * @param pTstParms Test parameters set to initialize. 1171 */ 1172 static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms) 1173 { 1174 RT_ZERO(*pTstParms); 1175 } 1176 1177 /** 1178 * Destroys an audio test parameters set. 1179 * 1180 * @param pTstParms Test parameters set to destroy. 1181 */ 1182 static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms) 1183 { 1184 if (!pTstParms) 1185 return; 1186 1187 return; 1188 } 1189 1190 1191 /********************************************************************************************************************************* 1192 * Some other stuff, you name it. * 1193 *********************************************************************************************************************************/ 584 1194 585 1195 /** … … 1009 1619 } 1010 1620 1621 /** Option help for the 'test' command. */ 1622 static DECLCALLBACK(const char *) audioTestCmdTestHelp(PCRTGETOPTDEF pOpt) 1623 { 1624 switch (pOpt->iShort) 1625 { 1626 case 'd': return "Use the specified audio device"; 1627 case 'e': return "Exclude the given test id from the list"; 1628 case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)"; 1629 case 'i': return "Include the given test id in the list"; 1630 } 1631 return NULL; 1632 } 1633 1011 1634 /** 1012 1635 * Main (entry) function for the testing functionality of VKAT. … … 1037 1660 switch (rc) 1038 1661 { 1039 case 'h':1040 audioTestUsage(g_pStdOut);1041 return RTEXITCODE_SUCCESS;1042 1043 1662 case 'a': 1044 1663 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++) … … 1119 1738 break; 1120 1739 1740 case 'V': 1741 return audioTestVersion(); 1742 case 'h': 1743 return audioTestUsage(g_pStdOut); 1744 1121 1745 default: 1122 1746 return RTGetOptPrintError(rc, &ValueUnion); … … 1129 1753 RTTestBanner(g_hTest); 1130 1754 1131 PPDMIHOSTAUDIO pDrvAudio = NULL; 1132 PPDMDRVINS pDrvIns = NULL; 1133 rc = audioTestDrvConstruct(pDrvReg, &pDrvIns, &pDrvAudio); 1755 AUDIOTESTDRVSTACK DrvStack; 1756 rc = audioTestDriverStackInit(&DrvStack, pDrvReg, false /*fWithDrvAudio*/); 1134 1757 if (RT_SUCCESS(rc)) 1135 1758 { 1136 1759 /* For now all tests have the same test environment. */ 1137 rc = audioTestEnvInit(&TstEnv, pDrvAudio, pszTag); 1760 /** @todo bake the DrvStack into the test env make make it more flexible so 1761 * we can also test with/without DrvAudio (need option above). */ 1762 rc = audioTestEnvInit(&TstEnv, DrvStack.pIHostAudio, pszTag); 1138 1763 if (RT_SUCCESS(rc)) 1139 1764 { … … 1155 1780 audioTestEnvDestroy(&TstEnv); 1156 1781 } 1157 }1158 audioTestDrvDestruct(pDrvReg, pDrvIns);1782 audioTestDriverStackDelete(&DrvStack); 1783 } 1159 1784 1160 1785 audioTestParmsDestroy(&TstCust); … … 1168 1793 return RTTestSummaryAndDestroy(g_hTest); 1169 1794 } 1795 1796 1797 /********************************************************************************************************************************* 1798 * Command: verify * 1799 *********************************************************************************************************************************/ 1170 1800 1171 1801 /** … … 1252 1882 break; 1253 1883 1884 case 'V': 1885 return audioTestVersion(); 1886 case 'h': 1887 return audioTestUsage(g_pStdOut); 1888 1254 1889 default: 1255 1890 return RTGetOptPrintError(rc, &ValueUnion); … … 1273 1908 */ 1274 1909 return RTTestSummaryAndDestroy(g_hTest); 1910 } 1911 1912 1913 /********************************************************************************************************************************* 1914 * Command: play * 1915 *********************************************************************************************************************************/ 1916 /** 1917 * Command line parameters for test mode. 1918 */ 1919 static const RTGETOPTDEF g_aCmdPlayOptions[] = 1920 { 1921 { "--backend", 'b', RTGETOPT_REQ_STRING }, 1922 }; 1923 1924 /** the 'play' command option help. */ 1925 static DECLCALLBACK(const char *) audioTestCmdPlayHelp(PCRTGETOPTDEF pOpt) 1926 { 1927 switch (pOpt->iShort) 1928 { 1929 case 'b': return "The audio backend to use."; 1930 default: return NULL; 1931 } 1932 } 1933 1934 /** 1935 * Worker for audioTestCmdPlayHandler that plays one file. 1936 */ 1937 static RTEXITCODE audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, uint32_t cMsBufferSize, 1938 uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint) 1939 { 1940 /* 1941 * First we must open the file and determin the format. 1942 */ 1943 AUDIOTESTWAVEFILE WaveFile; 1944 int rc = AudioTestWaveFileOpen(pszFile, &WaveFile); 1945 if (RT_FAILURE(rc)) 1946 return RTMsgErrorExitFailure("Failed to open '%s': %Rrc", pszFile, rc); 1947 1948 if (g_uVerbosity > 0) 1949 { 1950 char szTmp[128]; 1951 RTMsgInfo("Opened '%s' for playing\n", pszFile); 1952 RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp))); 1953 RTMsgInfo("Size: %'RU32 bytes / %#RX32 / %'RU32 frames / %'RU64 ns\n", 1954 WaveFile.cbSamples, WaveFile.cbSamples, 1955 PDMAudioPropsBytesToFrames(&WaveFile.Props, WaveFile.cbSamples), 1956 PDMAudioPropsBytesToNano(&WaveFile.Props, WaveFile.cbSamples)); 1957 } 1958 1959 /* 1960 * Construct the driver stack. 1961 */ 1962 RTEXITCODE rcExit = RTEXITCODE_FAILURE; 1963 AUDIOTESTDRVSTACK DrvStack; 1964 rc = audioTestDriverStackInit(&DrvStack, pDrvReg, false /*fWithDrvAudio*/); 1965 if (RT_SUCCESS(rc)) 1966 { 1967 /* 1968 * Open a stream for the output. 1969 */ 1970 PPDMAUDIOSTREAM pStream = NULL; 1971 rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &WaveFile.Props, cMsBufferSize, 1972 cMsPreBuffer, cMsSchedulingHint, &pStream); 1973 if (RT_SUCCESS(rc)) 1974 { 1975 rc = audioTestDriverStackStreamEnable(&DrvStack, pStream); 1976 if (RT_SUCCESS(rc)) 1977 { 1978 uint64_t const nsStarted = RTTimeNanoTS(); 1979 1980 /* 1981 * Transfer data as quickly as we're allowed. 1982 */ 1983 for (;;) 1984 { 1985 /* Read a chunk from the wave file. */ 1986 uint8_t abSamples[16384]; 1987 size_t cbSamples = 0; 1988 rc = AudioTestWaveFileRead(&WaveFile, abSamples, sizeof(abSamples), &cbSamples); 1989 if (RT_SUCCESS(rc) && cbSamples > 0) 1990 { 1991 /* Transfer the data to the audio stream. */ 1992 for (uint32_t offSamples = 0; offSamples < cbSamples;) 1993 { 1994 uint32_t const cbCanWrite = audioTestDriverStackStreamGetWritable(&DrvStack, pStream); 1995 if (cbCanWrite > 0) 1996 { 1997 uint32_t const cbToPlay = RT_MIN(cbCanWrite, (uint32_t)cbSamples - offSamples); 1998 uint32_t cbPlayed = 0; 1999 rc = audioTestDriverStackStreamPlay(&DrvStack, pStream, &abSamples[offSamples], 2000 cbToPlay, &cbPlayed); 2001 if (RT_SUCCESS(rc)) 2002 { 2003 if (cbPlayed) 2004 offSamples += cbPlayed; 2005 else 2006 { 2007 rcExit = RTMsgErrorExitFailure("Played zero out of %#x bytes - %#x bytes reported playable!\n", 2008 cbToPlay, cbCanWrite); 2009 break; 2010 } 2011 } 2012 else 2013 { 2014 rcExit = RTMsgErrorExitFailure("Failed to play %#x bytes: %Rrc\n", cbToPlay, rc); 2015 break; 2016 } 2017 } 2018 else if (audioTestDriverStackStreamIsOkay(&DrvStack, pStream)) 2019 RTThreadSleep(RT_MIN(RT_MAX(1, cMsSchedulingHint), 256)); 2020 else 2021 { 2022 rcExit = RTMsgErrorExitFailure("Stream is not okay!\n"); 2023 break; 2024 } 2025 } 2026 } 2027 else if (RT_SUCCESS(rc) && cbSamples == 0) 2028 { 2029 rcExit = RTEXITCODE_SUCCESS; 2030 break; 2031 } 2032 else 2033 { 2034 rcExit = RTMsgErrorExitFailure("Error reading wav file '%s': %Rrc", pszFile, rc); 2035 break; 2036 } 2037 } 2038 2039 /* 2040 * Drain the stream. 2041 */ 2042 if (rcExit == RTEXITCODE_SUCCESS) 2043 { 2044 if (g_uVerbosity > 0) 2045 RTMsgInfo("%'RU64 ns: Draining...\n", RTTimeNanoTS() - nsStarted); 2046 rc = audioTestDriverStackStreamDrain(&DrvStack, pStream, true /*fSync*/); 2047 if (RT_SUCCESS(rc)) 2048 { 2049 if (g_uVerbosity > 0) 2050 RTMsgInfo("%'RU64 ns: Done\n", RTTimeNanoTS() - nsStarted); 2051 } 2052 else 2053 rcExit = RTMsgErrorExitFailure("Draining failed: %Rrc", rc); 2054 } 2055 } 2056 else 2057 rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc); 2058 audioTestDriverStackStreamDestroy(&DrvStack, pStream); 2059 } 2060 else 2061 rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc); 2062 audioTestDriverStackDelete(&DrvStack); 2063 } 2064 else 2065 rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc); 2066 AudioTestWaveFileClose(&WaveFile); 2067 return rcExit; 2068 } 2069 2070 /** 2071 * The 'play' command handler. 2072 * 2073 * @returns Program exit code. 2074 * @param argc Number of argv arguments. 2075 * @param argv argv arguments. 2076 */ 2077 static DECLCALLBACK(RTEXITCODE) audioTestCmdPlayHandler(int argc, char **argv) 2078 { 2079 /* 2080 * Parse arguments. 2081 */ 2082 /* Option values: */ 2083 PCPDMDRVREG pDrvReg = g_aBackends[0].pDrvReg; 2084 uint32_t cMsBufferSize = UINT32_MAX; 2085 uint32_t cMsPreBuffer = UINT32_MAX; 2086 uint32_t cMsSchedulingHint = UINT32_MAX; 2087 2088 RTGETOPTSTATE GetState; 2089 int rc = RTGetOptInit(&GetState, argc, argv, g_aCmdPlayOptions, RT_ELEMENTS(g_aCmdPlayOptions), 2090 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST); 2091 AssertRCReturn(rc, RTEXITCODE_INIT); 2092 2093 RTGETOPTUNION ValueUnion; 2094 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0) 2095 { 2096 switch (rc) 2097 { 2098 case 'b': 2099 pDrvReg = NULL; 2100 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++) 2101 if ( strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 0 2102 || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0) 2103 { 2104 pDrvReg = g_aBackends[i].pDrvReg; 2105 break; 2106 } 2107 if (pDrvReg == NULL) 2108 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz); 2109 break; 2110 2111 case VINF_GETOPT_NOT_OPTION: 2112 { 2113 RTEXITCODE rcExit = audioTestPlayOne(ValueUnion.psz, pDrvReg, cMsBufferSize, cMsPreBuffer, cMsSchedulingHint); 2114 if (rcExit != RTEXITCODE_SUCCESS) 2115 return rcExit; 2116 break; 2117 } 2118 2119 case 'V': 2120 return audioTestVersion(); 2121 case 'h': 2122 return audioTestUsage(g_pStdOut); 2123 2124 default: 2125 return RTGetOptPrintError(rc, &ValueUnion); 2126 } 2127 } 2128 return RTEXITCODE_SUCCESS; 2129 } 2130 2131 2132 /** 2133 * Commands. 2134 */ 2135 static struct 2136 { 2137 /** The command name. */ 2138 const char *pszCommand; 2139 /** The command handler. */ 2140 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(int argc, char **argv)); 2141 2142 /** Command description. */ 2143 const char *pszDesc; 2144 /** Options array. */ 2145 PCRTGETOPTDEF paOptions; 2146 /** Number of options in the option array. */ 2147 size_t cOptions; 2148 /** Gets help for an option. */ 2149 DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt)); 2150 } const g_aCommands[] = 2151 { 2152 { 2153 "test", audioTestMain, 2154 "Does some kind of testing, I guess...", 2155 g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions), audioTestCmdTestHelp 2156 }, 2157 { 2158 "verify", audioVerifyMain, 2159 "Verfies something, I guess...", 2160 g_aCmdVerifyOptions, RT_ELEMENTS(g_aCmdVerifyOptions), NULL, 2161 }, 2162 { 2163 "play", audioTestCmdPlayHandler, 2164 "Plays one or more wave files.", 2165 g_aCmdPlayOptions, RT_ELEMENTS(g_aCmdPlayOptions), audioTestCmdPlayHelp, 2166 }, 2167 }; 2168 2169 /** 2170 * Shows tool usage text. 2171 */ 2172 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm) 2173 { 2174 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n", 2175 RTPathFilename(RTProcExecutablePath())); 2176 RTStrmPrintf(pStrm, 2177 "\n" 2178 "Global Options:\n" 2179 " -q, --quiet\n" 2180 " Sets verbosity to zero.\n" 2181 " -v, --verbose\n" 2182 " Increase verbosity.\n" 2183 " -V, --version\n" 2184 " Displays version.\n" 2185 " -h, -?, --help\n" 2186 " Displays help.\n" 2187 ); 2188 2189 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++) 2190 { 2191 RTStrmPrintf(pStrm, 2192 "\n" 2193 "Command '%s':\n" 2194 " %s\n" 2195 "Options for '%s':\n", 2196 g_aCommands[iCmd].pszCommand, g_aCommands[iCmd].pszDesc, g_aCommands[iCmd].pszCommand); 2197 PCRTGETOPTDEF const paOptions = g_aCommands[iCmd].paOptions; 2198 for (unsigned i = 0; i < g_aCommands[iCmd].cOptions; i++) 2199 { 2200 if (RT_C_IS_PRINT(paOptions[i].iShort)) 2201 RTStrmPrintf(pStrm, " -%c, %s\n", g_aCmdTestOptions[i].iShort, g_aCmdTestOptions[i].pszLong); 2202 else 2203 RTStrmPrintf(pStrm, " %s\n", g_aCmdTestOptions[i].pszLong); 2204 2205 const char *pszHelp = NULL; 2206 if (g_aCommands[i].pfnOptionHelp) 2207 pszHelp = g_aCommands[i].pfnOptionHelp(&paOptions[i]); 2208 if (pszHelp) 2209 RTStrmPrintf(pStrm, " %s\n", pszHelp); 2210 } 2211 } 2212 return RTEXITCODE_SUCCESS; 2213 } 2214 2215 /** 2216 * Shows tool version. 2217 */ 2218 static RTEXITCODE audioTestVersion(void) 2219 { 2220 RTPrintf("v0.0.1\n"); 2221 return RTEXITCODE_SUCCESS; 2222 } 2223 2224 /** 2225 * Shows the logo. 2226 * 2227 * @param pStream Output stream to show logo on. 2228 */ 2229 static void audioTestShowLogo(PRTSTREAM pStream) 2230 { 2231 RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) Version " VBOX_VERSION_STRING " - r%s\n" 2232 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n" 2233 "All rights reserved.\n\n", RTBldCfgRevisionStr()); 1275 2234 } 1276 2235 … … 1306 2265 1307 2266 case 'V': 1308 RTPrintf("v0.0.1\n"); 1309 return RTEXITCODE_SUCCESS; 2267 return audioTestVersion(); 1310 2268 1311 2269 case 'h': 1312 2270 audioTestShowLogo(g_pStdOut); 1313 audioTestUsage(g_pStdOut); 1314 return RTEXITCODE_SUCCESS; 2271 return audioTestUsage(g_pStdOut); 1315 2272 1316 2273 case VINF_GETOPT_NOT_OPTION:
Note:
See TracChangeset
for help on using the changeset viewer.