Changeset 89401 in vbox for trunk/src/VBox
- Timestamp:
- May 31, 2021 1:17:26 PM (4 years ago)
- Location:
- trunk/src/VBox/ValidationKit/utils/audio
- Files:
-
- 1 added
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/ValidationKit/utils/audio/vkat.cpp
r89399 r89401 72 72 #include "../../../Devices/Audio/AudioTestService.h" 73 73 #include "../../../Devices/Audio/AudioTestServiceClient.h" 74 #include "vkatInternal.h" 74 75 #include "VBoxDD.h" 75 76 … … 153 154 154 155 /** 155 * Audio driver stack.156 *157 * This can be just be backend driver alone or DrvAudio with a backend.158 * @todo add automatic resampling via mixer so we can test more of the audio159 * stack used by the device emulations.160 */161 typedef struct AUDIOTESTDRVSTACK162 {163 /** The device registration record for the backend. */164 PCPDMDRVREG pDrvReg;165 /** The backend driver instance. */166 PPDMDRVINS pDrvBackendIns;167 /** The backend's audio interface. */168 PPDMIHOSTAUDIO pIHostAudio;169 170 /** The DrvAudio instance. */171 PPDMDRVINS pDrvAudioIns;172 /** This is NULL if we don't use DrvAudio. */173 PPDMIAUDIOCONNECTOR pIAudioConnector;174 } AUDIOTESTDRVSTACK;175 /** Pointer to an audio driver stack. */176 typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK;177 178 /**179 * Backend-only stream structure.180 */181 typedef struct AUDIOTESTDRVSTACKSTREAM182 {183 /** The public stream data. */184 PDMAUDIOSTREAM Core;185 /** The acquired config. */186 PDMAUDIOSTREAMCFG Cfg;187 /** The backend data (variable size). */188 PDMAUDIOBACKENDSTREAM Backend;189 } AUDIOTESTDRVSTACKSTREAM;190 /** Pointer to a backend-only stream structure. */191 typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM;192 193 /** Maximum audio streams a test environment can handle. */194 #define AUDIOTESTENV_MAX_STREAMS 8195 196 /**197 156 * Structure for keeping an audio test audio stream. 198 157 */ … … 276 235 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms); 277 236 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream); 278 279 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg,280 PPDMDRVINS pParentDrvIns, PPPDMDRVINS ppDrvIns);281 237 282 238 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm); … … 420 376 421 377 static volatile bool g_fTerminated = false; 422 /** The test handle. */423 static RTTEST g_hTest;424 378 /** The release logger. */ 425 379 static PRTLOGGER g_pRelLogger = NULL; 380 381 382 /** The test handle. */ 383 RTTEST g_hTest; 426 384 /** The current verbosity level. */ 427 staticunsigned g_uVerbosity = 0;385 unsigned g_uVerbosity = 0; 428 386 /** DrvAudio: Enable debug (or not). */ 429 staticbool g_fDrvAudioDebug = 0;387 bool g_fDrvAudioDebug = 0; 430 388 /** DrvAudio: The debug output path. */ 431 static const char *g_pszDrvAudioDebug = NULL; 432 433 434 /********************************************************************************************************************************* 435 * Fake PDM Driver Handling. * 436 *********************************************************************************************************************************/ 437 438 /** @name Driver Fakes/Stubs 439 * 440 * @note The VMM functions defined here will turn into driver helpers before 441 * long, as the drivers aren't supposed to import directly from the VMM in 442 * the future. 443 * 444 * @{ */ 445 446 VMMR3DECL(PCFGMNODE) CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath) 447 { 448 RT_NOREF(pNode, pszPath); 449 return NULL; 450 } 451 452 VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString) 453 { 454 if (pNode != NULL) 455 { 456 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode; 457 if (g_uVerbosity > 2) 458 RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString); 459 460 if ( ( strcmp(pDrvReg->szName, "PulseAudio") == 0 461 || strcmp(pDrvReg->szName, "HostAudioWas") == 0) 462 && strcmp(pszName, "VmName") == 0) 463 return RTStrCopy(pszString, cchString, "vkat"); 464 465 if ( strcmp(pDrvReg->szName, "HostAudioWas") == 0 466 && strcmp(pszName, "VmUuid") == 0) 467 return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e"); 468 } 469 else if (g_uVerbosity > 2) 470 RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString); 471 472 return VERR_CFGM_VALUE_NOT_FOUND; 473 } 474 475 VMMR3DECL(int) CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString) 476 { 477 char szStr[128]; 478 int rc = CFGMR3QueryString(pNode, pszName, szStr, sizeof(szStr)); 479 if (RT_SUCCESS(rc)) 480 *ppszString = RTStrDup(szStr); 481 482 return rc; 483 } 484 485 VMMR3DECL(void) MMR3HeapFree(void *pv) 486 { 487 /* counterpart to CFGMR3QueryStringAlloc */ 488 RTStrFree((char *)pv); 489 } 490 491 VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef) 492 { 493 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode; 494 if (RT_VALID_PTR(pDrvReg)) 495 { 496 const char *pszRet = pszDef; 497 if ( g_pszDrvAudioDebug 498 && strcmp(pDrvReg->szName, "AUDIO") == 0 499 && strcmp(pszName, "DebugPathOut") == 0) 500 pszRet = g_pszDrvAudioDebug; 501 502 int rc = RTStrCopy(pszString, cchString, pszRet); 503 504 if (g_uVerbosity > 2) 505 RTPrintf("debug: CFGMR3QueryStringDef([%s], %s, %p, %#x, %s) -> '%s' + %Rrc\n", 506 pDrvReg->szName, pszName, pszString, cchString, pszDef, pszRet, rc); 507 return rc; 508 } 509 510 if (g_uVerbosity > 2) 511 RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef); 512 return RTStrCopy(pszString, cchString, pszDef); 513 } 514 515 VMMR3DECL(int) CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef) 516 { 517 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode; 518 if (RT_VALID_PTR(pDrvReg)) 519 { 520 *pf = fDef; 521 if ( strcmp(pDrvReg->szName, "AUDIO") == 0 522 && strcmp(pszName, "DebugEnabled") == 0) 523 *pf = g_fDrvAudioDebug; 524 525 if (g_uVerbosity > 2) 526 RTPrintf("debug: CFGMR3QueryBoolDef([%s], %s, %p, %RTbool) -> %RTbool\n", pDrvReg->szName, pszName, pf, fDef, *pf); 527 return VINF_SUCCESS; 528 } 529 *pf = fDef; 530 return VINF_SUCCESS; 531 } 532 533 VMMR3DECL(int) CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8) 534 { 535 RT_NOREF(pNode, pszName, pu8); 536 return VERR_CFGM_VALUE_NOT_FOUND; 537 } 538 539 VMMR3DECL(int) CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32) 540 { 541 RT_NOREF(pNode, pszName, pu32); 542 return VERR_CFGM_VALUE_NOT_FOUND; 543 } 544 545 VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode, 546 const char *pszValidValues, const char *pszValidNodes, 547 const char *pszWho, uint32_t uInstance) 548 { 549 RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance); 550 return VINF_SUCCESS; 551 } 552 553 /** @} */ 554 555 /** @name Driver Helper Fakes 556 * @{ */ 557 558 static DECLCALLBACK(int) audioTestDrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface) 559 { 560 /* DrvAudio must be allowed to attach the backend driver (paranoid 561 backend drivers may call us to check that nothing is attached). */ 562 if (strcmp(pDrvIns->pReg->szName, "AUDIO") == 0) 563 { 564 PAUDIOTESTDRVSTACK pDrvStack = pDrvIns->Internal.s.pStack; 565 AssertReturn(pDrvStack->pDrvBackendIns == NULL, VERR_PDM_DRIVER_ALREADY_ATTACHED); 566 567 if (g_uVerbosity > 1) 568 RTMsgInfo("Attaching backend '%s' to DrvAudio...\n", pDrvStack->pDrvReg->szName); 569 int rc = audioTestDrvConstruct(pDrvStack, pDrvStack->pDrvReg, pDrvIns, &pDrvStack->pDrvBackendIns); 570 if (RT_SUCCESS(rc)) 571 { 572 if (ppBaseInterface) 573 *ppBaseInterface = &pDrvStack->pDrvBackendIns->IBase; 574 } 575 else 576 RTMsgError("Failed to attach backend: %Rrc", rc); 577 return rc; 578 } 579 RT_NOREF(fFlags); 580 return VERR_PDM_NO_ATTACHED_DRIVER; 581 } 582 583 static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, 584 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc, 585 const char *pszName, ...) 586 { 587 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName); 588 } 589 590 static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType, 591 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc, 592 const char *pszName, va_list args) 593 { 594 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args); 595 } 596 597 static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample) 598 { 599 RT_NOREF(pDrvIns, pvSample); 600 return VINF_SUCCESS; 601 } 602 603 static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix) 604 { 605 RT_NOREF(pDrvIns, pszPrefix); 606 return VINF_SUCCESS; 607 } 608 609 /** 610 * Get the driver helpers. 611 */ 612 static const PDMDRVHLPR3 *audioTestFakeGetDrvHlp(void) 613 { 614 /* 615 * Note! No initializer for s_DrvHlp (also why it's not a file global). 616 * We do not want to have to update this code every time PDMDRVHLPR3 617 * grows new entries or are otherwise modified. Only when the 618 * entries used by the audio driver changes do we want to change 619 * our code. 620 */ 621 static PDMDRVHLPR3 s_DrvHlp; 622 if (s_DrvHlp.u32Version != PDM_DRVHLPR3_VERSION) 623 { 624 s_DrvHlp.u32Version = PDM_DRVHLPR3_VERSION; 625 s_DrvHlp.u32TheEnd = PDM_DRVHLPR3_VERSION; 626 s_DrvHlp.pfnAttach = audioTestDrvHlp_Attach; 627 s_DrvHlp.pfnSTAMRegisterF = audioTestDrvHlp_STAMRegisterF; 628 s_DrvHlp.pfnSTAMRegisterV = audioTestDrvHlp_STAMRegisterV; 629 s_DrvHlp.pfnSTAMDeregister = audioTestDrvHlp_STAMDeregister; 630 s_DrvHlp.pfnSTAMDeregisterByPrefix = audioTestDrvHlp_STAMDeregisterByPrefix; 631 } 632 return &s_DrvHlp; 633 } 634 635 /** @} */ 636 637 638 /** 639 * Implementation of PDMIBASE::pfnQueryInterface for a fake device above 640 * DrvAudio. 641 */ 642 static DECLCALLBACK(void *) audioTestFakeDeviceIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID) 643 { 644 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface); 645 RTMsgWarning("audioTestFakeDeviceIBaseQueryInterface: Unknown interface: %s\n", pszIID); 646 return NULL; 647 } 648 649 /** IBase interface for a fake device above DrvAudio. */ 650 static PDMIBASE g_AudioTestFakeDeviceIBase = { audioTestFakeDeviceIBaseQueryInterface }; 651 652 653 static DECLCALLBACK(int) audioTestIHostAudioPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream, 654 uintptr_t uUser, void *pvUser) 655 { 656 RT_NOREF(pInterface, pStream, uUser, pvUser); 657 RTMsgWarning("audioTestIHostAudioPort_DoOnWorkerThread was called\n"); 658 return VERR_NOT_IMPLEMENTED; 659 } 660 661 DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser) 662 { 663 RT_NOREF(pInterface, enmDir, pvUser); 664 RTMsgWarning("audioTestIHostAudioPort_NotifyDeviceChanged was called\n"); 665 } 666 667 static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface, 668 PPDMAUDIOBACKENDSTREAM pStream) 669 { 670 RT_NOREF(pInterface, pStream); 671 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch was called\n"); 672 } 673 674 static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, 675 PPDMAUDIOBACKENDSTREAM pStream, bool fReInit) 676 { 677 RT_NOREF(pInterface, pStream, fReInit); 678 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyDeviceChanged was called\n"); 679 } 680 681 static DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface) 682 { 683 RT_NOREF(pInterface); 684 RTMsgWarning("audioTestIHostAudioPort_NotifyDevicesChanged was called\n"); 685 } 686 687 static PDMIHOSTAUDIOPORT g_AudioTestIHostAudioPort = 688 { 689 audioTestIHostAudioPort_DoOnWorkerThread, 690 audioTestIHostAudioPort_NotifyDeviceChanged, 691 audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch, 692 audioTestIHostAudioPort_StreamNotifyDeviceChanged, 693 audioTestIHostAudioPort_NotifyDevicesChanged, 694 }; 695 696 /** 697 * Implementation of PDMIBASE::pfnQueryInterface for a fake DrvAudio above a 698 * backend. 699 */ 700 static DECLCALLBACK(void *) audioTestFakeDrvAudioIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID) 701 { 702 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface); 703 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &g_AudioTestIHostAudioPort); 704 RTMsgWarning("audioTestFakeDrvAudioIBaseQueryInterface: Unknown interface: %s\n", pszIID); 705 return NULL; 706 } 707 708 /** IBase interface for a fake DrvAudio above a lonesome backend. */ 709 static PDMIBASE g_AudioTestFakeDrvAudioIBase = { audioTestFakeDrvAudioIBaseQueryInterface }; 710 711 712 713 /** 714 * Constructs a PDM audio driver instance. 715 * 716 * @returns VBox status code. 717 * @param pDrvStack The stack this is associated with. 718 * @param pDrvReg PDM driver registration record to use for construction. 719 * @param pParentDrvIns The parent driver (if any). 720 * @param ppDrvIns Where to return the driver instance structure. 721 */ 722 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns, 723 PPPDMDRVINS ppDrvIns) 724 { 725 /* The destruct function must have valid data to work with. */ 726 *ppDrvIns = NULL; 727 728 /* 729 * Check registration structure validation (doesn't need to be too 730 * thorough, PDM check it in detail on every VM startup). 731 */ 732 AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER); 733 RTMsgInfo("Initializing backend '%s' ...\n", pDrvReg->szName); 734 AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER); 735 736 /* 737 * Create the instance data structure. 738 */ 739 PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance])); 740 RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY); 741 742 pDrvIns->u32Version = PDM_DRVINS_VERSION; 743 pDrvIns->iInstance = 0; 744 pDrvIns->pHlpR3 = audioTestFakeGetDrvHlp(); 745 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0]; 746 pDrvIns->pReg = pDrvReg; 747 pDrvIns->pCfg = (PCFGMNODE)pDrvReg; 748 pDrvIns->Internal.s.pStack = pDrvStack; 749 pDrvIns->pUpBase = NULL; 750 pDrvIns->pDownBase = NULL; 751 if (pParentDrvIns) 752 { 753 Assert(pParentDrvIns->pDownBase == NULL); 754 pParentDrvIns->pDownBase = &pDrvIns->IBase; 755 pDrvIns->pUpBase = &pParentDrvIns->IBase; 756 } 757 else if (strcmp(pDrvReg->szName, "AUDIO") == 0) 758 pDrvIns->pUpBase = &g_AudioTestFakeDeviceIBase; 759 else 760 pDrvIns->pUpBase = &g_AudioTestFakeDrvAudioIBase; 761 762 /* 763 * Invoke the constructor. 764 */ 765 int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/); 766 if (RT_SUCCESS(rc)) 767 { 768 *ppDrvIns = pDrvIns; 769 return VINF_SUCCESS; 770 } 771 772 RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc); 773 if (pDrvReg->pfnDestruct) 774 pDrvReg->pfnDestruct(pDrvIns); 775 RTMemFree(pDrvIns); 776 return rc; 777 } 778 779 /** 780 * Destructs a PDM audio driver instance. 781 * 782 * @param pDrvIns Driver instance to destruct. 783 */ 784 static void audioTestDrvDestruct(PPDMDRVINS pDrvIns) 785 { 786 if (pDrvIns) 787 { 788 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION); 789 790 if (pDrvIns->pReg->pfnDestruct) 791 pDrvIns->pReg->pfnDestruct(pDrvIns); 792 793 pDrvIns->u32Version = 0; 794 pDrvIns->pReg = NULL; 795 RTMemFree(pDrvIns); 796 } 797 } 798 799 /** 800 * Sends the PDM driver a power off notification. 801 * 802 * @param pDrvIns Driver instance to notify. 803 */ 804 static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns) 805 { 806 if (pDrvIns) 807 { 808 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION); 809 if (pDrvIns->pReg->pfnPowerOff) 810 pDrvIns->pReg->pfnPowerOff(pDrvIns); 811 } 812 } 813 814 /** 815 * Deletes a driver stack. 816 * 817 * This will power off and destroy the drivers. 818 */ 819 static void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack) 820 { 821 /* 822 * Do power off notifications (top to bottom). 823 */ 824 audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns); 825 audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns); 826 827 /* 828 * Drivers are destroyed from bottom to top (closest to the device). 829 */ 830 audioTestDrvDestruct(pDrvStack->pDrvBackendIns); 831 pDrvStack->pDrvBackendIns = NULL; 832 pDrvStack->pIHostAudio = NULL; 833 834 audioTestDrvDestruct(pDrvStack->pDrvAudioIns); 835 pDrvStack->pDrvAudioIns = NULL; 836 pDrvStack->pIAudioConnector = NULL; 837 } 838 839 /** 840 * Initializes a driver stack. 841 * 842 * @returns VBox status code. 843 * @param pDrvStack The driver stack to initialize. 844 * @param pDrvReg The backend driver to use. 845 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not. 846 */ 847 static int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio) 848 { 849 RT_ZERO(*pDrvStack); 850 pDrvStack->pDrvReg = pDrvReg; 851 852 int rc; 853 if (!fWithDrvAudio) 854 rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns); 855 else 856 { 857 rc = audioTestDrvConstruct(pDrvStack, &g_DrvAUDIO, NULL /*pParentDrvIns*/, &pDrvStack->pDrvAudioIns); 858 if (RT_SUCCESS(rc)) 859 { 860 Assert(pDrvStack->pDrvAudioIns); 861 PPDMIBASE const pIBase = &pDrvStack->pDrvAudioIns->IBase; 862 pDrvStack->pIAudioConnector = (PPDMIAUDIOCONNECTOR)pIBase->pfnQueryInterface(pIBase, PDMIAUDIOCONNECTOR_IID); 863 if (pDrvStack->pIAudioConnector) 864 { 865 /* Both input and output is disabled by default. Fix that: */ 866 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_OUT, true); 867 if (RT_SUCCESS(rc)) 868 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_IN, true); 869 if (RT_FAILURE(rc)) 870 { 871 RTTestFailed(g_hTest, "Failed to enabled input and output: %Rrc", rc); 872 audioTestDriverStackDelete(pDrvStack); 873 } 874 } 875 else 876 { 877 RTTestFailed(g_hTest, "Failed to query PDMIAUDIOCONNECTOR"); 878 audioTestDriverStackDelete(pDrvStack); 879 rc = VERR_PDM_MISSING_INTERFACE; 880 } 881 } 882 } 883 884 /* 885 * Get the IHostAudio interface and check that the host driver is working. 886 */ 887 if (RT_SUCCESS(rc)) 888 { 889 PPDMIBASE const pIBase = &pDrvStack->pDrvBackendIns->IBase; 890 pDrvStack->pIHostAudio = (PPDMIHOSTAUDIO)pIBase->pfnQueryInterface(pIBase, PDMIHOSTAUDIO_IID); 891 if (pDrvStack->pIHostAudio) 892 { 893 PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT); 894 if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING) 895 return VINF_SUCCESS; 896 897 RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus); 898 } 899 else 900 RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName); 901 audioTestDriverStackDelete(pDrvStack); 902 } 903 904 return rc; 905 } 906 907 /** 908 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice. 909 */ 910 static int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId) 911 { 912 int rc; 913 if ( pDrvStack->pIHostAudio 914 && pDrvStack->pIHostAudio->pfnSetDevice) 915 rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId); 916 else if (!pszDevId || *pszDevId) 917 rc = VINF_SUCCESS; 918 else 919 rc = VERR_INVALID_FUNCTION; 920 return rc; 921 } 922 923 /** 924 * Common stream creation code. 925 * 926 * @returns VBox status code. 927 * @param pDrvStack The audio driver stack to create it via. 928 * @param pCfgReq The requested config. 929 * @param ppStream Where to return the stream pointer on success. 930 * @param pCfgAcq Where to return the actual (well, not 931 * necessarily when using DrvAudio, but probably 932 * the same) stream config on success (not used as 933 * input). 934 */ 935 static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAMCFG pCfgReq, 936 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq) 937 { 938 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16]; 939 int rc; 940 *ppStream = NULL; 941 942 if (pDrvStack->pIAudioConnector) 943 { 944 /* 945 * DrvAudio does most of the work here. 946 */ 947 PDMAUDIOSTREAMCFG CfgGst = *pCfgReq; 948 rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF, 949 pCfgReq, &CfgGst, ppStream); 950 if (RT_SUCCESS(rc)) 951 { 952 *pCfgAcq = *pCfgReq; /** @todo PDMIAUDIOCONNECTOR::pfnStreamCreate only does one utterly pointless change to the two configs (enmLayout) from what I can tell... */ 953 pCfgAcq->Props = (*ppStream)->Props; 954 RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp))); 955 return rc; 956 } 957 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc", rc); 958 } 959 else 960 { 961 /* 962 * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM 963 * structure actually is for this backend. 964 */ 965 PDMAUDIOBACKENDCFG BackendCfg; 966 rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg); 967 if (RT_SUCCESS(rc)) 968 { 969 if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM)) 970 { 971 /* 972 * Allocate and initialize the stream. 973 */ 974 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream; 975 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream); 976 if (pStreamAt) 977 { 978 pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC; 979 pStreamAt->Core.enmDir = PDMAUDIODIR_OUT; 980 pStreamAt->Core.cbBackend = cbStream; 981 pStreamAt->Core.Props = pCfgReq->Props; 982 RTStrPrintf(pStreamAt->Core.szName, sizeof(pStreamAt->Core.szName), pCfgReq->szName); 983 984 pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC; 985 pStreamAt->Backend.pStream = &pStreamAt->Core; 986 987 /* 988 * Call the backend to create the stream. 989 */ 990 pStreamAt->Cfg = *pCfgReq; 991 992 rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend, 993 pCfgReq, &pStreamAt->Cfg); 994 if (RT_SUCCESS(rc)) 995 { 996 pStreamAt->Core.Props = pStreamAt->Cfg.Props; 997 if (g_uVerbosity > 1) 998 RTMsgInfo("Created backend stream: %s\n", 999 PDMAudioStrmCfgToString(&pStreamAt->Cfg, szTmp, sizeof(szTmp))); 1000 1001 /* Return if stream is ready: */ 1002 if (rc == VINF_SUCCESS) 1003 { 1004 *ppStream = &pStreamAt->Core; 1005 *pCfgAcq = pStreamAt->Cfg; 1006 return VINF_SUCCESS; 1007 } 1008 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED) 1009 { 1010 /* 1011 * Do async init right here and now. 1012 */ 1013 rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend, 1014 false /*fDestroyed*/); 1015 if (RT_SUCCESS(rc)) 1016 { 1017 *ppStream = &pStreamAt->Core; 1018 *pCfgAcq = pStreamAt->Cfg; 1019 return VINF_SUCCESS; 1020 } 1021 1022 RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc); 1023 } 1024 else 1025 { 1026 RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc); 1027 rc = VERR_IPE_UNEXPECTED_INFO_STATUS; 1028 } 1029 pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/); 1030 } 1031 else 1032 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc\n", rc); 1033 } 1034 else 1035 { 1036 RTTestFailed(g_hTest, "Out of memory!\n"); 1037 rc = VERR_NO_MEMORY; 1038 } 1039 } 1040 else 1041 { 1042 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM)); 1043 rc = VERR_OUT_OF_RANGE; 1044 } 1045 } 1046 else 1047 RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc); 1048 } 1049 return rc; 1050 } 1051 1052 /** 1053 * Creates an output stream. 1054 * 1055 * @returns VBox status code. 1056 * @param pDrvStack The audio driver stack to create it via. 1057 * @param pProps The audio properties to use. 1058 * @param cMsBufferSize The buffer size in milliseconds. 1059 * @param cMsPreBuffer The pre-buffering amount in milliseconds. 1060 * @param cMsSchedulingHint The scheduling hint in milliseconds. 1061 * @param ppStream Where to return the stream pointer on success. 1062 * @param pCfgAcq Where to return the actual (well, not 1063 * necessarily when using DrvAudio, but probably 1064 * the same) stream config on success (not used as 1065 * input). 1066 */ 1067 static int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps, 1068 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, 1069 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq) 1070 { 1071 /* 1072 * Calculate the stream config. 1073 */ 1074 PDMAUDIOSTREAMCFG CfgReq; 1075 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps); 1076 AssertRC(rc); 1077 CfgReq.enmDir = PDMAUDIODIR_OUT; 1078 CfgReq.enmPath = PDMAUDIOPATH_OUT_FRONT; 1079 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0 1080 ? 10 : cMsSchedulingHint; 1081 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0)) 1082 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */ 1083 else 1084 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps, 1085 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0 1086 ? 300 : cMsBufferSize); 1087 if (cMsPreBuffer == UINT32_MAX) 1088 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */ 1089 : CfgReq.Backend.cFramesBufferSize * 2 / 3; 1090 else 1091 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer); 1092 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 1093 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ ) 1094 { 1095 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!", 1096 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize); 1097 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16 1098 ? CfgReq.Backend.cFramesBufferSize - 16 : 0; 1099 } 1100 1101 static uint32_t s_idxStream = 0; 1102 uint32_t const idxStream = s_idxStream++; 1103 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream); 1104 1105 /* 1106 * Call common code to do the actual work. 1107 */ 1108 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq); 1109 } 1110 1111 /** 1112 * Creates an input stream. 1113 * 1114 * @returns VBox status code. 1115 * @param pDrvStack The audio driver stack to create it via. 1116 * @param pProps The audio properties to use. 1117 * @param cMsBufferSize The buffer size in milliseconds. 1118 * @param cMsPreBuffer The pre-buffering amount in milliseconds. 1119 * @param cMsSchedulingHint The scheduling hint in milliseconds. 1120 * @param ppStream Where to return the stream pointer on success. 1121 * @param pCfgAcq Where to return the actual (well, not 1122 * necessarily when using DrvAudio, but probably 1123 * the same) stream config on success (not used as 1124 * input). 1125 */ 1126 static int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps, 1127 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, 1128 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq) 1129 { 1130 /* 1131 * Calculate the stream config. 1132 */ 1133 PDMAUDIOSTREAMCFG CfgReq; 1134 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps); 1135 AssertRC(rc); 1136 CfgReq.enmDir = PDMAUDIODIR_IN; 1137 CfgReq.enmPath = PDMAUDIOPATH_IN_LINE; 1138 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0 1139 ? 10 : cMsSchedulingHint; 1140 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0)) 1141 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */ 1142 else 1143 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps, 1144 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0 1145 ? 300 : cMsBufferSize); 1146 if (cMsPreBuffer == UINT32_MAX) 1147 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */ 1148 : CfgReq.Backend.cFramesBufferSize / 2; 1149 else 1150 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer); 1151 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */ 1152 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ ) 1153 { 1154 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!", 1155 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize); 1156 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16 1157 ? CfgReq.Backend.cFramesBufferSize - 16 : 0; 1158 } 1159 1160 static uint32_t s_idxStream = 0; 1161 uint32_t const idxStream = s_idxStream++; 1162 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream); 1163 1164 /* 1165 * Call common code to do the actual work. 1166 */ 1167 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq); 1168 } 1169 1170 /** 1171 * Destroys a stream. 1172 */ 1173 static void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1174 { 1175 if (pStream) 1176 { 1177 if (pDrvStack->pIAudioConnector) 1178 { 1179 int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/); 1180 if (RT_FAILURE(rc)) 1181 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc); 1182 } 1183 else 1184 { 1185 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1186 int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/); 1187 if (RT_SUCCESS(rc)) 1188 { 1189 pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC; 1190 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC; 1191 RTMemFree(pStreamAt); 1192 } 1193 else 1194 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc); 1195 } 1196 } 1197 } 1198 1199 /** 1200 * Enables a stream. 1201 */ 1202 static int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1203 { 1204 int rc; 1205 if (pDrvStack->pIAudioConnector) 1206 { 1207 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE); 1208 if (RT_FAILURE(rc)) 1209 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc); 1210 } 1211 else 1212 { 1213 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1214 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_ENABLE); 1215 if (RT_FAILURE(rc)) 1216 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc); 1217 } 1218 return rc; 1219 } 1220 1221 /** 1222 * Drains an output stream. 1223 */ 1224 static int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync) 1225 { 1226 int rc; 1227 if (pDrvStack->pIAudioConnector) 1228 { 1229 /* 1230 * Issue the drain request. 1231 */ 1232 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN); 1233 if (RT_SUCCESS(rc) && fSync) 1234 { 1235 /* 1236 * This is a synchronous drain, so wait for the driver to change state to inactive. 1237 */ 1238 PDMAUDIOSTREAMSTATE enmState; 1239 while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream)) 1240 >= PDMAUDIOSTREAMSTATE_ENABLED) 1241 { 1242 RTThreadSleep(2); 1243 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream); 1244 if (RT_FAILURE(rc)) 1245 { 1246 RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc); 1247 break; 1248 } 1249 } 1250 if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE) 1251 { 1252 RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState)); 1253 rc = VERR_AUDIO_STREAM_NOT_READY; 1254 } 1255 } 1256 else if (RT_FAILURE(rc)) 1257 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc); 1258 } 1259 else 1260 { 1261 /* 1262 * Issue the drain request. 1263 */ 1264 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1265 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_DRAIN); 1266 if (RT_SUCCESS(rc) && fSync) 1267 { 1268 /* 1269 * This is a synchronous drain, so wait for the driver to change state to inactive. 1270 */ 1271 PDMHOSTAUDIOSTREAMSTATE enmHostState; 1272 while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend)) 1273 == PDMHOSTAUDIOSTREAMSTATE_DRAINING) 1274 { 1275 RTThreadSleep(2); 1276 uint32_t cbWritten = UINT32_MAX; 1277 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, 1278 NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten); 1279 if (RT_FAILURE(rc)) 1280 { 1281 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc); 1282 break; 1283 } 1284 if (cbWritten != 0) 1285 { 1286 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten); 1287 rc = VERR_MISSING; 1288 break; 1289 } 1290 } 1291 if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY) 1292 { 1293 RTTestFailed(g_hTest, "Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState)); 1294 rc = VERR_AUDIO_STREAM_NOT_READY; 1295 } 1296 } 1297 else if (RT_FAILURE(rc)) 1298 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc); 1299 } 1300 return rc; 1301 } 1302 1303 /** 1304 * Checks if the stream is okay. 1305 * @returns true if okay, false if not. 1306 */ 1307 static bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1308 { 1309 /* 1310 * Get the stream status and check if it means is okay or not. 1311 */ 1312 bool fRc = false; 1313 if (pDrvStack->pIAudioConnector) 1314 { 1315 PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream); 1316 switch (enmState) 1317 { 1318 case PDMAUDIOSTREAMSTATE_NOT_WORKING: 1319 case PDMAUDIOSTREAMSTATE_NEED_REINIT: 1320 break; 1321 case PDMAUDIOSTREAMSTATE_INACTIVE: 1322 case PDMAUDIOSTREAMSTATE_ENABLED: 1323 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE: 1324 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE: 1325 fRc = true; 1326 break; 1327 /* no default */ 1328 case PDMAUDIOSTREAMSTATE_INVALID: 1329 case PDMAUDIOSTREAMSTATE_END: 1330 case PDMAUDIOSTREAMSTATE_32BIT_HACK: 1331 break; 1332 } 1333 } 1334 else 1335 { 1336 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1337 PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, 1338 &pStreamAt->Backend); 1339 switch (enmHostState) 1340 { 1341 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING: 1342 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING: 1343 break; 1344 case PDMHOSTAUDIOSTREAMSTATE_OKAY: 1345 case PDMHOSTAUDIOSTREAMSTATE_DRAINING: 1346 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE: 1347 fRc = true; 1348 break; 1349 /* no default */ 1350 case PDMHOSTAUDIOSTREAMSTATE_INVALID: 1351 case PDMHOSTAUDIOSTREAMSTATE_END: 1352 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK: 1353 break; 1354 } 1355 } 1356 return fRc; 1357 } 1358 1359 /** 1360 * Gets the number of bytes it's currently possible to write to the stream. 1361 */ 1362 static uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1363 { 1364 uint32_t cbWritable; 1365 if (pDrvStack->pIAudioConnector) 1366 cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream); 1367 else 1368 { 1369 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1370 cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend); 1371 } 1372 return cbWritable; 1373 } 1374 1375 /** 1376 * Tries to play the @a cbBuf bytes of samples in @a pvBuf. 1377 */ 1378 static int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, 1379 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed) 1380 { 1381 int rc; 1382 if (pDrvStack->pIAudioConnector) 1383 { 1384 rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed); 1385 if (RT_FAILURE(rc)) 1386 RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc); 1387 } 1388 else 1389 { 1390 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1391 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed); 1392 if (RT_FAILURE(rc)) 1393 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc); 1394 } 1395 return rc; 1396 } 1397 1398 /** 1399 * Tries to capture @a cbBuf bytes of samples in @a pvBuf. 1400 */ 1401 static int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, 1402 void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured) 1403 { 1404 int rc; 1405 if (pDrvStack->pIAudioConnector) 1406 { 1407 rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured); 1408 if (RT_FAILURE(rc)) 1409 RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc); 1410 } 1411 else 1412 { 1413 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream; 1414 rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured); 1415 if (RT_FAILURE(rc)) 1416 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc); 1417 } 1418 return rc; 1419 } 389 const char *g_pszDrvAudioDebug = NULL; 1420 390 1421 391 -
trunk/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp
r89399 r89401 1 1 /* $Id$ */ 2 2 /** @file 3 * Validation Kit Audio Test (VKAT) utility for testing and validating the audio stack.3 * Validation Kit Audio Test (VKAT) - Driver stack code. 4 4 */ 5 5 … … 65 65 #define PDMDRVINSINT_DECLARED 66 66 67 #include <VBox/vmm/pdmaudioinline.h> 68 #include <VBox/vmm/pdmaudiohostenuminline.h> 69 70 #include "../../../Devices/Audio/AudioHlp.h" 71 #include "../../../Devices/Audio/AudioTest.h" 72 #include "../../../Devices/Audio/AudioTestService.h" 73 #include "../../../Devices/Audio/AudioTestServiceClient.h" 74 #include "VBoxDD.h" 75 76 77 /********************************************************************************************************************************* 78 * Defined Constants And Macros * 79 *********************************************************************************************************************************/ 80 /** For use in the option switch to handle common options. */ 81 #define AUDIO_TEST_COMMON_OPTION_CASES(a_ValueUnion) \ 82 case 'q': \ 83 g_uVerbosity = 0; \ 84 if (g_pRelLogger) \ 85 RTLogGroupSettings(g_pRelLogger, "all=0 all.e"); \ 86 break; \ 87 \ 88 case 'v': \ 89 g_uVerbosity++; \ 90 if (g_pRelLogger) \ 91 RTLogGroupSettings(g_pRelLogger, g_uVerbosity == 1 ? "all.e.l" : g_uVerbosity == 2 ? "all.e.l.f" : "all=~0"); \ 92 break; \ 93 \ 94 case 'V': \ 95 return audioTestVersion(); \ 96 \ 97 case 'h': \ 98 return audioTestUsage(g_pStdOut); \ 99 \ 100 case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE: \ 101 g_fDrvAudioDebug = true; \ 102 break; \ 103 \ 104 case AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH: \ 105 g_pszDrvAudioDebug = (a_ValueUnion).psz; \ 106 break 107 108 109 /********************************************************************************************************************************* 110 * Structures and Typedefs * 111 *********************************************************************************************************************************/ 112 /** 113 * Enumeration specifying the current audio test mode. 114 */ 115 typedef enum AUDIOTESTMODE 116 { 117 /** Unknown mode. */ 118 AUDIOTESTMODE_UNKNOWN = 0, 119 /** VKAT is running on the guest side. */ 120 AUDIOTESTMODE_GUEST, 121 /** VKAT is running on the host side. */ 122 AUDIOTESTMODE_HOST 123 } AUDIOTESTMODE; 124 125 struct AUDIOTESTENV; 126 /** Pointer a audio test environment. */ 127 typedef AUDIOTESTENV *PAUDIOTESTENV; 128 129 struct AUDIOTESTDESC; 130 /** Pointer a audio test descriptor. */ 131 typedef AUDIOTESTDESC *PAUDIOTESTDESC; 132 133 /** 134 * Callback to set up the test parameters for a specific test. 135 * 136 * @returns IPRT status code. 137 * @retval VINF_SUCCESS if setting the parameters up succeeded. Any other error code 138 * otherwise indicating the kind of error. 139 * @param pszTest Test name. 140 * @param pTstParmsAcq The audio test parameters to set up. 141 */ 142 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTSETUP,(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)); 143 /** Pointer to an audio test setup callback. */ 144 typedef FNAUDIOTESTSETUP *PFNAUDIOTESTSETUP; 145 146 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTEXEC,(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)); 147 /** Pointer to an audio test exec callback. */ 148 typedef FNAUDIOTESTEXEC *PFNAUDIOTESTEXEC; 149 150 typedef DECLCALLBACKTYPE(int, FNAUDIOTESTDESTROY,(PAUDIOTESTENV pTstEnv, void *pvCtx)); 151 /** Pointer to an audio test destroy callback. */ 152 typedef FNAUDIOTESTDESTROY *PFNAUDIOTESTDESTROY; 153 154 /** 155 * Audio driver stack. 156 * 157 * This can be just be backend driver alone or DrvAudio with a backend. 158 * @todo add automatic resampling via mixer so we can test more of the audio 159 * stack used by the device emulations. 160 */ 161 typedef struct AUDIOTESTDRVSTACK 162 { 163 /** The device registration record for the backend. */ 164 PCPDMDRVREG pDrvReg; 165 /** The backend driver instance. */ 166 PPDMDRVINS pDrvBackendIns; 167 /** The backend's audio interface. */ 168 PPDMIHOSTAUDIO pIHostAudio; 169 170 /** The DrvAudio instance. */ 171 PPDMDRVINS pDrvAudioIns; 172 /** This is NULL if we don't use DrvAudio. */ 173 PPDMIAUDIOCONNECTOR pIAudioConnector; 174 } AUDIOTESTDRVSTACK; 175 /** Pointer to an audio driver stack. */ 176 typedef AUDIOTESTDRVSTACK *PAUDIOTESTDRVSTACK; 177 178 /** 179 * Backend-only stream structure. 180 */ 181 typedef struct AUDIOTESTDRVSTACKSTREAM 182 { 183 /** The public stream data. */ 184 PDMAUDIOSTREAM Core; 185 /** The acquired config. */ 186 PDMAUDIOSTREAMCFG Cfg; 187 /** The backend data (variable size). */ 188 PDMAUDIOBACKENDSTREAM Backend; 189 } AUDIOTESTDRVSTACKSTREAM; 190 /** Pointer to a backend-only stream structure. */ 191 typedef AUDIOTESTDRVSTACKSTREAM *PAUDIOTESTDRVSTACKSTREAM; 192 193 /** Maximum audio streams a test environment can handle. */ 194 #define AUDIOTESTENV_MAX_STREAMS 8 195 196 /** 197 * Structure for keeping an audio test audio stream. 198 */ 199 typedef struct AUDIOTESTSTREAM 200 { 201 /** The PDM stream. */ 202 PPDMAUDIOSTREAM pStream; 203 /** The backend stream. */ 204 PPDMAUDIOBACKENDSTREAM pBackend; 205 /** The stream config. */ 206 PDMAUDIOSTREAMCFG Cfg; 207 } AUDIOTESTSTREAM; 208 /** Pointer to audio test stream. */ 209 typedef AUDIOTESTSTREAM *PAUDIOTESTSTREAM; 210 211 /** 212 * Audio test environment parameters. 213 * Not necessarily bound to a specific test (can be reused). 214 */ 215 typedef struct AUDIOTESTENV 216 { 217 /** Audio testing mode. */ 218 AUDIOTESTMODE enmMode; 219 /** Output path for storing the test environment's final test files. */ 220 char szPathOut[RTPATH_MAX]; 221 /** Temporary path for this test environment. */ 222 char szPathTemp[RTPATH_MAX]; 223 /** The audio test driver stack. */ 224 AUDIOTESTDRVSTACK DrvStack; 225 /** The current (last) audio device enumeration to use. */ 226 PDMAUDIOHOSTENUM DevEnum; 227 /** Audio stream. */ 228 AUDIOTESTSTREAM aStreams[AUDIOTESTENV_MAX_STREAMS]; 229 /** The audio test set to use. */ 230 AUDIOTESTSET Set; 231 union 232 { 233 struct 234 { 235 /** ATS instance to use. */ 236 ATSSERVER Srv; 237 } Guest; 238 struct 239 { 240 ATSCLIENT Client; 241 } Host; 242 } u; 243 } AUDIOTESTENV; 244 245 /** 246 * Audio test descriptor. 247 */ 248 typedef struct AUDIOTESTDESC 249 { 250 /** (Sort of) Descriptive test name. */ 251 const char *pszName; 252 /** Flag whether the test is excluded. */ 253 bool fExcluded; 254 /** The setup callback. */ 255 PFNAUDIOTESTSETUP pfnSetup; 256 /** The exec callback. */ 257 PFNAUDIOTESTEXEC pfnExec; 258 /** The destruction callback. */ 259 PFNAUDIOTESTDESTROY pfnDestroy; 260 } AUDIOTESTDESC; 261 262 /** 263 * Structure for keeping a user context for the test service callbacks. 264 */ 265 typedef struct ATSCALLBACKCTX 266 { 267 PAUDIOTESTENV pTstEnv; 268 } ATSCALLBACKCTX; 269 typedef ATSCALLBACKCTX *PATSCALLBACKCTX; 270 271 272 /********************************************************************************************************************************* 273 * Internal Functions * 274 *********************************************************************************************************************************/ 275 static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms); 276 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms); 277 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream); 278 279 static int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, 280 PPDMDRVINS pParentDrvIns, PPPDMDRVINS ppDrvIns); 281 282 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm); 283 static RTEXITCODE audioTestVersion(void); 284 285 286 /********************************************************************************************************************************* 287 * Global Variables * 288 *********************************************************************************************************************************/ 289 /** 290 * Common long options values. 291 */ 292 enum 293 { 294 AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE = 256, 295 AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH 296 }; 297 298 /** 299 * Long option values for the 'test' command. 300 */ 301 enum 302 { 303 VKAT_TEST_OPT_COUNT = 900, 304 VKAT_TEST_OPT_DEV, 305 VKAT_TEST_OPT_ATS_ADDR, 306 VKAT_TEST_OPT_ATS_PORT, 307 VKAT_TEST_OPT_MODE, 308 VKAT_TEST_OPT_OUTDIR, 309 VKAT_TEST_OPT_PAUSE, 310 VKAT_TEST_OPT_PCM_HZ, 311 VKAT_TEST_OPT_PCM_BIT, 312 VKAT_TEST_OPT_PCM_CHAN, 313 VKAT_TEST_OPT_PCM_SIGNED, 314 VKAT_TEST_OPT_TAG, 315 VKAT_TEST_OPT_TEMPDIR, 316 VKAT_TEST_OPT_VOL 317 }; 318 319 /** 320 * Long option values for the 'verify' command. 321 */ 322 enum 323 { 324 VKAT_VERIFY_OPT_TAG = 900 325 }; 326 327 /** 328 * Long option values for the 'selftest' command. 329 */ 330 enum 331 { 332 VKAT_SELFTEST_OPT_ATS_HOST = 900 333 }; 334 335 /** 336 * Common command line parameters. 337 */ 338 static const RTGETOPTDEF g_aCmdCommonOptions[] = 339 { 340 { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, 341 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, 342 { "--debug-audio", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_ENABLE, RTGETOPT_REQ_NOTHING }, 343 { "--debug-audio-path", AUDIO_TEST_OPT_CMN_DEBUG_AUDIO_PATH, RTGETOPT_REQ_STRING }, 344 }; 345 346 /** 347 * Command line parameters for test mode. 348 */ 349 static const RTGETOPTDEF g_aCmdTestOptions[] = 350 { 351 { "--backend", 'b', RTGETOPT_REQ_STRING }, 352 { "--drvaudio", 'd', RTGETOPT_REQ_NOTHING }, 353 { "--exclude", 'e', RTGETOPT_REQ_UINT32 }, 354 { "--exclude-all", 'a', RTGETOPT_REQ_NOTHING }, 355 { "--mode", VKAT_TEST_OPT_MODE, RTGETOPT_REQ_STRING }, 356 { "--ats-address", VKAT_TEST_OPT_ATS_ADDR, RTGETOPT_REQ_STRING }, 357 { "--ats-port", VKAT_TEST_OPT_ATS_PORT, RTGETOPT_REQ_UINT32 }, 358 { "--include", 'i', RTGETOPT_REQ_UINT32 }, 359 { "--outdir", VKAT_TEST_OPT_OUTDIR, RTGETOPT_REQ_STRING }, 360 { "--count", VKAT_TEST_OPT_COUNT, RTGETOPT_REQ_UINT32 }, 361 { "--device", VKAT_TEST_OPT_DEV, RTGETOPT_REQ_STRING }, 362 { "--pause", VKAT_TEST_OPT_PAUSE, RTGETOPT_REQ_UINT32 }, 363 { "--pcm-bit", VKAT_TEST_OPT_PCM_BIT, RTGETOPT_REQ_UINT8 }, 364 { "--pcm-chan", VKAT_TEST_OPT_PCM_CHAN, RTGETOPT_REQ_UINT8 }, 365 { "--pcm-hz", VKAT_TEST_OPT_PCM_HZ, RTGETOPT_REQ_UINT16 }, 366 { "--pcm-signed", VKAT_TEST_OPT_PCM_SIGNED, RTGETOPT_REQ_BOOL }, 367 { "--tag", VKAT_TEST_OPT_TAG, RTGETOPT_REQ_STRING }, 368 { "--tempdir", VKAT_TEST_OPT_TEMPDIR, RTGETOPT_REQ_STRING }, 369 { "--volume", VKAT_TEST_OPT_VOL, RTGETOPT_REQ_UINT8 } 370 }; 371 372 /** 373 * Command line parameters for verification mode. 374 */ 375 static const RTGETOPTDEF g_aCmdVerifyOptions[] = 376 { 377 { "--tag", VKAT_VERIFY_OPT_TAG, RTGETOPT_REQ_STRING } 378 }; 379 380 /** 381 * Backends. 382 * 383 * @note The first backend in the array is the default one for the platform. 384 */ 385 static struct 386 { 387 /** The driver registration structure. */ 388 PCPDMDRVREG pDrvReg; 389 /** The backend name. 390 * Aliases are implemented by having multiple entries for the same backend. */ 391 const char *pszName; 392 } const g_aBackends[] = 393 { 394 #if defined(VBOX_WITH_AUDIO_ALSA) && defined(RT_OS_LINUX) 395 { &g_DrvHostALSAAudio, "alsa" }, 396 #endif 397 #ifdef VBOX_WITH_AUDIO_PULSE 398 { &g_DrvHostPulseAudio, "pulseaudio" }, 399 { &g_DrvHostPulseAudio, "pulse" }, 400 { &g_DrvHostPulseAudio, "pa" }, 401 #endif 402 #ifdef VBOX_WITH_AUDIO_OSS 403 { &g_DrvHostOSSAudio, "oss" }, 404 #endif 405 #if defined(RT_OS_DARWIN) 406 { &g_DrvHostCoreAudio, "coreaudio" }, 407 { &g_DrvHostCoreAudio, "core" }, 408 { &g_DrvHostCoreAudio, "ca" }, 409 #endif 410 #if defined(RT_OS_WINDOWS) 411 { &g_DrvHostAudioWas, "wasapi" }, 412 { &g_DrvHostAudioWas, "was" }, 413 { &g_DrvHostDSound, "directsound" }, 414 { &g_DrvHostDSound, "dsound" }, 415 { &g_DrvHostDSound, "ds" }, 416 #endif 417 { &g_DrvHostValidationKitAudio, "valkit" } 418 }; 419 AssertCompile(sizeof(g_aBackends) > 0 /* port me */); 420 421 static volatile bool g_fTerminated = false; 422 /** The test handle. */ 423 static RTTEST g_hTest; 424 /** The release logger. */ 425 static PRTLOGGER g_pRelLogger = NULL; 426 /** The current verbosity level. */ 427 static unsigned g_uVerbosity = 0; 428 /** DrvAudio: Enable debug (or not). */ 429 static bool g_fDrvAudioDebug = 0; 430 /** DrvAudio: The debug output path. */ 431 static const char *g_pszDrvAudioDebug = NULL; 67 #include "vkatInternal.h" 68 432 69 433 70 … … 720 357 * @param ppDrvIns Where to return the driver instance structure. 721 358 */ 722 staticint audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,723 359 int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns, 360 PPPDMDRVINS ppDrvIns) 724 361 { 725 362 /* The destruct function must have valid data to work with. */ … … 817 454 * This will power off and destroy the drivers. 818 455 */ 819 staticvoid audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)456 void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack) 820 457 { 821 458 /* … … 845 482 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not. 846 483 */ 847 staticint audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)484 int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio) 848 485 { 849 486 RT_ZERO(*pDrvStack); … … 908 545 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice. 909 546 */ 910 staticint audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)547 int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId) 911 548 { 912 549 int rc; … … 1065 702 * input). 1066 703 */ 1067 staticint audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,1068 1069 704 int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps, 705 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, 706 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq) 1070 707 { 1071 708 /* … … 1124 761 * input). 1125 762 */ 1126 staticint audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,1127 1128 763 int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps, 764 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, 765 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq) 1129 766 { 1130 767 /* … … 1171 808 * Destroys a stream. 1172 809 */ 1173 staticvoid audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)810 void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1174 811 { 1175 812 if (pStream) … … 1200 837 * Enables a stream. 1201 838 */ 1202 staticint audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)839 int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1203 840 { 1204 841 int rc; … … 1222 859 * Drains an output stream. 1223 860 */ 1224 staticint audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)861 int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync) 1225 862 { 1226 863 int rc; … … 1305 942 * @returns true if okay, false if not. 1306 943 */ 1307 staticbool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)944 bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1308 945 { 1309 946 /* … … 1360 997 * Gets the number of bytes it's currently possible to write to the stream. 1361 998 */ 1362 staticuint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)999 uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream) 1363 1000 { 1364 1001 uint32_t cbWritable; … … 1376 1013 * Tries to play the @a cbBuf bytes of samples in @a pvBuf. 1377 1014 */ 1378 staticint audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,1379 1015 int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, 1016 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed) 1380 1017 { 1381 1018 int rc; … … 1399 1036 * Tries to capture @a cbBuf bytes of samples in @a pvBuf. 1400 1037 */ 1401 staticint audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,1402 1038 int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, 1039 void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured) 1403 1040 { 1404 1041 int rc; … … 1419 1056 } 1420 1057 1421 1422 /*********************************************************************************************************************************1423 * ATS Callback Implementations *1424 *********************************************************************************************************************************/1425 1426 /**1427 * Note: Called within server (client serving) thread.1428 */1429 static DECLCALLBACK(int) audioTestSvcTonePlayCallback(void const *pvUser, PPDMAUDIOSTREAMCFG pStreamCfg, PAUDIOTESTTONEPARMS pToneParms)1430 {1431 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;1432 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;1433 1434 AUDIOTESTTONE TstTone;1435 AudioTestToneInitRandom(&TstTone, &pStreamCfg->Props);1436 1437 int rc;1438 1439 const PPDMAUDIOSTREAM pStream = pTstEnv->aStreams[0].pStream; /** @todo Make this dynamic. */1440 1441 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream))1442 {1443 uint32_t cbBuf;1444 uint8_t abBuf[_4K];1445 1446 const uint64_t tsStartMs = RTTimeMilliTS();1447 const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Chose a random scheduling (in ms). */1448 const uint32_t cbPerMs = PDMAudioPropsMilliToBytes(&pStream->Props, cSchedulingMs);1449 1450 do1451 {1452 rc = AudioTestToneGenerate(&TstTone, abBuf, RT_MIN(cbPerMs, sizeof(abBuf)), &cbBuf);1453 if (RT_SUCCESS(rc))1454 {1455 uint32_t cbWritten;1456 rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream, abBuf, cbBuf, &cbWritten);1457 }1458 1459 if (RTTimeMilliTS() - tsStartMs >= pToneParms->msDuration)1460 break;1461 1462 if (RT_FAILURE(rc))1463 break;1464 1465 RTThreadSleep(cSchedulingMs);1466 1467 } while (RT_SUCCESS(rc));1468 }1469 else1470 rc = VERR_AUDIO_STREAM_NOT_READY;1471 1472 return rc;1473 }1474 1475 1476 /*********************************************************************************************************************************1477 * Implementation of audio test environment handling *1478 *********************************************************************************************************************************/1479 1480 /**1481 * Initializes an audio test environment.1482 *1483 * @param pTstEnv Audio test environment to initialize.1484 * @param pDrvReg Audio driver to use.1485 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.1486 * @param pszTag Tag name to use. If NULL, a generated UUID will be used.1487 * @param pszTcpAddr TCP/IP address to connect to.1488 * @param uTcpPort TCP/IP port to connect to.1489 */1490 static int audioTestEnvInit(PAUDIOTESTENV pTstEnv,1491 PCPDMDRVREG pDrvReg, bool fWithDrvAudio, const char *pszTag,1492 const char *pszTcpAddr, uint32_t uTcpPort)1493 {1494 PDMAudioHostEnumInit(&pTstEnv->DevEnum);1495 1496 int rc = audioTestDriverStackInit(&pTstEnv->DrvStack, pDrvReg, fWithDrvAudio);1497 if (RT_FAILURE(rc))1498 return rc;1499 1500 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");1501 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pszTag);1502 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);1503 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);1504 1505 char szPathTemp[RTPATH_MAX];1506 if ( !strlen(pTstEnv->szPathTemp)1507 || !strlen(pTstEnv->szPathOut))1508 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));1509 1510 if ( RT_SUCCESS(rc)1511 && !strlen(pTstEnv->szPathTemp))1512 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");1513 1514 if ( RT_SUCCESS(rc)1515 && !strlen(pTstEnv->szPathOut))1516 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");1517 1518 if (RT_SUCCESS(rc))1519 rc = AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);1520 1521 if (RT_FAILURE(rc))1522 return rc;1523 1524 /** @todo Implement NAT mode like we do for TxS later? */1525 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)1526 {1527 ATSCALLBACKCTX Ctx;1528 Ctx.pTstEnv = pTstEnv;1529 1530 ATSCALLBACKS Callbacks;1531 Callbacks.pfnTonePlay = audioTestSvcTonePlayCallback;1532 Callbacks.pvUser = &Ctx;1533 1534 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Starting ATS ...\n");1535 rc = AudioTestSvcInit(&pTstEnv->u.Guest.Srv, &Callbacks);1536 if (RT_SUCCESS(rc))1537 rc = AudioTestSvcStart(&pTstEnv->u.Guest.Srv);1538 1539 if (RT_FAILURE(rc))1540 {1541 RTTestFailed(g_hTest, "Initializing ATS failed with %Rrc\n", rc);1542 return rc;1543 }1544 1545 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "ATS running\n");1546 1547 while (!g_fTerminated) /** @todo Implement signal handling. */1548 {1549 RTThreadSleep(100);1550 }1551 1552 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Shutting down ATS ...\n");1553 1554 int rc2 = AudioTestSvcShutdown(&pTstEnv->u.Guest.Srv);1555 if (RT_SUCCESS(rc))1556 rc = rc2;1557 }1558 else1559 {1560 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to ATS at %s:%RU32 ...\n", pszTcpAddr, uTcpPort);1561 rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.Client, pszTcpAddr, uTcpPort);1562 if (RT_FAILURE(rc))1563 {1564 RTTestFailed(g_hTest, "Connecting to ATS failed with %Rrc\n", rc);1565 return rc;1566 }1567 }1568 1569 audioTestDriverStackDelete(&pTstEnv->DrvStack);1570 1571 return rc;1572 }1573 1574 /**1575 * Destroys an audio test environment.1576 *1577 * @param pTstEnv Audio test environment to destroy.1578 */1579 static void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)1580 {1581 if (!pTstEnv)1582 return;1583 1584 PDMAudioHostEnumDelete(&pTstEnv->DevEnum);1585 1586 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)1587 {1588 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);1589 if (RT_FAILURE(rc2))1590 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);1591 }1592 1593 AudioTestSetDestroy(&pTstEnv->Set);1594 audioTestDriverStackDelete(&pTstEnv->DrvStack);1595 }1596 1597 /**1598 * Initializes an audio test parameters set.1599 *1600 * @param pTstParms Test parameters set to initialize.1601 */1602 static void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)1603 {1604 RT_ZERO(*pTstParms);1605 }1606 1607 /**1608 * Destroys an audio test parameters set.1609 *1610 * @param pTstParms Test parameters set to destroy.1611 */1612 static void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)1613 {1614 if (!pTstParms)1615 return;1616 1617 return;1618 }1619 1620 1621 /*********************************************************************************************************************************1622 * Device enumeration + handling. *1623 *********************************************************************************************************************************/1624 1625 /**1626 * Enumerates audio devices and optionally searches for a specific device.1627 *1628 * @returns VBox status code.1629 * @param pTstEnv Test env to use for enumeration.1630 * @param pszDev Device name to search for. Can be NULL if the default device shall be used.1631 * @param ppDev Where to return the pointer of the device enumeration of \a pTstEnv when a1632 * specific device was found.1633 */1634 static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)1635 {1636 RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");1637 1638 if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)1639 {1640 RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");1641 return VINF_NOT_SUPPORTED;1642 }1643 1644 Assert(pszDev == NULL || ppDev);1645 1646 if (ppDev)1647 *ppDev = NULL;1648 1649 int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);1650 if (RT_SUCCESS(rc))1651 {1652 PPDMAUDIOHOSTDEV pDev;1653 RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)1654 {1655 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];1656 if (pDev->pszId)1657 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->szName, pDev->pszId);1658 else1659 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->szName);1660 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));1661 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));1662 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);1663 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);1664 1665 if ( pszDev1666 && !RTStrCmp(pDev->szName, pszDev))1667 {1668 *ppDev = pDev;1669 }1670 }1671 }1672 else1673 RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);1674 1675 RTTestSubDone(g_hTest);1676 1677 if ( pszDev1678 && *ppDev == NULL)1679 {1680 RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);1681 return VERR_NOT_FOUND;1682 }1683 1684 return VINF_SUCCESS;1685 }1686 1687 /**1688 * Opens an audio device.1689 *1690 * @returns VBox status code.1691 * @param pDev Audio device to open.1692 */1693 static int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)1694 {1695 int rc = VINF_SUCCESS;1696 1697 RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->szName);1698 1699 /** @todo Detect + open device here. */1700 1701 RTTestSubDone(g_hTest);1702 1703 return rc;1704 }1705 1706 /**1707 * Closes an audio device.1708 *1709 * @returns VBox status code.1710 * @param pDev Audio device to close.1711 */1712 static int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)1713 {1714 int rc = VINF_SUCCESS;1715 1716 RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->szName);1717 1718 /** @todo Close device here. */1719 1720 RTTestSubDone(g_hTest);1721 1722 return rc;1723 }1724 1725 /**1726 * Destroys an audio test stream.1727 *1728 * @returns VBox status code.1729 * @param pTstEnv Test environment the stream to destroy contains.1730 * @param pStream Audio stream to destroy.1731 */1732 static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)1733 {1734 int rc = VINF_SUCCESS;1735 if (pStream && pStream->pStream)1736 {1737 /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */1738 1739 audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);1740 pStream->pStream = NULL;1741 pStream->pBackend = NULL;1742 }1743 1744 return rc;1745 }1746 1747 /**1748 * Creates an audio default input (recording) test stream.1749 * Convenience function.1750 *1751 * @returns VBox status code.1752 * @param pTstEnv Test environment to use for creating the stream.1753 * @param pStream Audio stream to create.1754 * @param pProps PCM properties to use for creation.1755 */1756 static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)1757 {1758 pStream->pBackend = NULL;1759 int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, 300 /*cMsBufferSize*/, 150 /*cMsPreBuffer*/,1760 10 /*cMsSchedulingHint*/, &pStream->pStream, &pStream->Cfg);1761 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)1762 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;1763 return rc;1764 }1765 1766 /**1767 * Records a test tone from a specific audio test stream.1768 *1769 * @returns VBox status code.1770 * @param pTstEnv Test environment to use for running the test.1771 * @param pStream Stream to use for recording the tone.1772 * @param pParms Tone parameters to use.1773 *1774 * @note Blocking function.1775 */1776 static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)1777 {1778 const char *pcszPathOut = pTstEnv->Set.szPathAbs;1779 1780 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);1781 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);1782 1783 /** @todo Use .WAV here? */1784 PAUDIOTESTOBJ pObj;1785 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-rec.pcm", &pObj);1786 AssertRCReturn(rc, rc);1787 1788 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))1789 {1790 uint8_t abBuf[_4K];1791 1792 const uint64_t tsStartMs = RTTimeMilliTS();1793 const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */1794 1795 do1796 {1797 uint32_t cbRead = 0;1798 rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, sizeof(abBuf), &cbRead);1799 if (RT_SUCCESS(rc))1800 rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);1801 1802 if (RT_FAILURE(rc))1803 break;1804 1805 if (RTTimeMilliTS() - tsStartMs >= pParms->msDuration)1806 break;1807 1808 RTThreadSleep(cSchedulingMs);1809 1810 } while (RT_SUCCESS(rc));1811 }1812 else1813 rc = VERR_AUDIO_STREAM_NOT_READY;1814 1815 int rc2 = AudioTestSetObjClose(pObj);1816 if (RT_SUCCESS(rc))1817 rc = rc2;1818 1819 return rc;1820 }1821 1822 /**1823 * Creates an audio default output (playback) test stream.1824 * Convenience function.1825 *1826 * @returns VBox status code.1827 * @param pTstEnv Test environment to use for creating the stream.1828 * @param pStream Audio stream to create.1829 * @param pProps PCM properties to use for creation.1830 */1831 static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)1832 {1833 pStream->pBackend = NULL;1834 int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, 300 /*cMsBufferSize*/, 200 /*cMsPreBuffer*/,1835 10 /*cMsSchedulingHint*/, &pStream->pStream, &pStream->Cfg);1836 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)1837 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;1838 return rc;1839 }1840 1841 /**1842 * Plays a test tone on a specific audio test stream.1843 *1844 * @returns VBox status code.1845 * @param pTstEnv Test environment to use for running the test.1846 * @param pStream Stream to use for playing the tone.1847 * @param pParms Tone parameters to use.1848 *1849 * @note Blocking function.1850 */1851 static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)1852 {1853 AUDIOTESTTONE TstTone;1854 AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);1855 1856 const char *pcszPathOut = pTstEnv->Set.szPathAbs;1857 1858 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);1859 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);1860 1861 /** @todo Use .WAV here? */1862 PAUDIOTESTOBJ pObj;1863 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "tone-play.pcm", &pObj);1864 AssertRCReturn(rc, rc);1865 1866 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))1867 {1868 uint32_t cbBuf;1869 uint8_t abBuf[_4K];1870 1871 const uint64_t tsStartMs = RTTimeMilliTS();1872 const uint16_t cSchedulingMs = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */1873 const uint32_t cbPerMs = PDMAudioPropsMilliToBytes(&pParms->Props, cSchedulingMs);1874 1875 do1876 {1877 rc = AudioTestToneGenerate(&TstTone, abBuf, RT_MIN(cbPerMs, sizeof(abBuf)), &cbBuf);1878 if (RT_SUCCESS(rc))1879 {1880 /* Write stuff to disk before trying to play it. Help analysis later. */1881 rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);1882 if (RT_SUCCESS(rc))1883 {1884 uint32_t cbWritten;1885 rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,1886 abBuf, cbBuf, &cbWritten);1887 }1888 }1889 1890 if (RTTimeMilliTS() - tsStartMs >= pParms->msDuration)1891 break;1892 1893 if (RT_FAILURE(rc))1894 break;1895 1896 RTThreadSleep(cSchedulingMs);1897 1898 } while (RT_SUCCESS(rc));1899 }1900 else1901 rc = VERR_AUDIO_STREAM_NOT_READY;1902 1903 int rc2 = AudioTestSetObjClose(pObj);1904 if (RT_SUCCESS(rc))1905 rc = rc2;1906 1907 return rc;1908 }1909 1910 /**1911 * Overrides audio test base parameters with another set.1912 *1913 * @returns VBox status code.1914 * @param pBaseParms Base parameters to override.1915 * @param pOverrideParms Override parameters to use for overriding the base parameters.1916 *1917 * @note Overriding a parameter depends on its type / default values.1918 */1919 static int audioTestCombineParms(PAUDIOTESTPARMS pBaseParms, PAUDIOTESTPARMS pOverrideParms)1920 {1921 RT_NOREF(pBaseParms, pOverrideParms);1922 1923 /** @todo Implement parameter overriding. */1924 return VERR_NOT_IMPLEMENTED;1925 }1926 1927 1928 /*********************************************************************************************************************************1929 * Test callbacks *1930 *********************************************************************************************************************************/1931 1932 /**1933 * @copydoc FNAUDIOTESTSETUP1934 */1935 static DECLCALLBACK(int) audioTestPlayToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)1936 {1937 RT_NOREF(pTstEnv, pTstDesc, ppvCtx);1938 1939 pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_PLAY;1940 1941 PDMAudioPropsInit(&pTstParmsAcq->Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);1942 1943 pTstParmsAcq->enmDir = PDMAUDIODIR_OUT;1944 #ifdef DEBUG1945 pTstParmsAcq->cIterations = 2;1946 #else1947 pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);1948 #endif1949 pTstParmsAcq->idxCurrent = 0;1950 1951 return VINF_SUCCESS;1952 }1953 1954 /**1955 * @copydoc FNAUDIOTESTEXEC1956 */1957 static DECLCALLBACK(int) audioTestPlayToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)1958 {1959 RT_NOREF(pvCtx);1960 1961 int rc = VINF_SUCCESS;1962 1963 PAUDIOTESTSTREAM pStream = &pTstEnv->aStreams[0];1964 1965 for (uint32_t i = 0; i < pTstParms->cIterations; i++)1966 {1967 AudioTestToneParamsInitRandom(&pTstParms->TestTone, &pTstParms->Props);1968 1969 PAUDIOTESTENTRY pTst;1970 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", pTstParms, &pTst);1971 if (RT_SUCCESS(rc))1972 {1973 rc = audioTestCreateStreamDefaultOut(pTstEnv, pStream, &pTstParms->TestTone.Props);1974 if (RT_SUCCESS(rc))1975 {1976 rc = audioTestPlayTone(pTstEnv, pStream, &pTstParms->TestTone);1977 }1978 1979 int rc2 = audioTestStreamDestroy(pTstEnv, pStream);1980 if (RT_SUCCESS(rc))1981 rc = rc2;1982 1983 if (RT_SUCCESS(rc))1984 {1985 AudioTestSetTestDone(pTst);1986 }1987 else1988 AudioTestSetTestFailed(pTst, rc, "Playing test tone failed");1989 }1990 1991 if (RT_FAILURE(rc))1992 RTTestFailed(g_hTest, "Playing tone failed\n");1993 }1994 1995 return rc;1996 }1997 1998 /**1999 * @copydoc FNAUDIOTESTDESTROY2000 */2001 static DECLCALLBACK(int) audioTestPlayToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)2002 {2003 RT_NOREF(pTstEnv, pvCtx);2004 2005 return VINF_SUCCESS;2006 }2007 2008 /**2009 * @copydoc FNAUDIOTESTSETUP2010 */2011 static DECLCALLBACK(int) audioTestRecordToneSetup(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc, PAUDIOTESTPARMS pTstParmsAcq, void **ppvCtx)2012 {2013 RT_NOREF(pTstEnv, pTstDesc, ppvCtx);2014 2015 pTstParmsAcq->enmType = AUDIOTESTTYPE_TESTTONE_RECORD;2016 2017 RT_ZERO(pTstParmsAcq->TestTone);2018 PDMAudioPropsInit(&pTstParmsAcq->TestTone.Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);2019 2020 pTstParmsAcq->enmDir = PDMAUDIODIR_IN;2021 #ifdef DEBUG2022 pTstParmsAcq->cIterations = 2;2023 #else2024 pTstParmsAcq->cIterations = RTRandU32Ex(1, 10);2025 #endif2026 pTstParmsAcq->idxCurrent = 0;2027 2028 return VINF_SUCCESS;2029 }2030 2031 /**2032 * @copydoc FNAUDIOTESTEXEC2033 */2034 static DECLCALLBACK(int) audioTestRecordToneExec(PAUDIOTESTENV pTstEnv, void *pvCtx, PAUDIOTESTPARMS pTstParms)2035 {2036 RT_NOREF(pvCtx);2037 2038 int rc = VINF_SUCCESS;2039 2040 PAUDIOTESTSTREAM pStream = &pTstEnv->aStreams[0];2041 2042 for (uint32_t i = 0; i < pTstParms->cIterations; i++)2043 {2044 pTstParms->TestTone.msDuration = RTRandU32Ex(50 /* ms */, RT_MS_10SEC); /** @todo Record even longer? */2045 2046 PAUDIOTESTENTRY pTst;2047 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone", pTstParms, &pTst);2048 if (RT_SUCCESS(rc))2049 {2050 /** @todo For now we're (re-)creating the recording stream for each iteration. Change that to be random. */2051 rc = audioTestCreateStreamDefaultIn(pTstEnv, pStream, &pTstParms->TestTone.Props);2052 if (RT_SUCCESS(rc))2053 rc = audioTestRecordTone(pTstEnv, pStream, &pTstParms->TestTone);2054 2055 int rc2 = audioTestStreamDestroy(pTstEnv, pStream);2056 if (RT_SUCCESS(rc))2057 rc = rc2;2058 2059 if (RT_SUCCESS(rc))2060 {2061 AudioTestSetTestDone(pTst);2062 }2063 else2064 AudioTestSetTestFailed(pTst, rc, "Recording test tone failed");2065 }2066 2067 if (RT_FAILURE(rc))2068 RTTestFailed(g_hTest, "Recording tone failed\n");2069 }2070 2071 return rc;2072 }2073 2074 /**2075 * @copydoc FNAUDIOTESTDESTROY2076 */2077 static DECLCALLBACK(int) audioTestRecordToneDestroy(PAUDIOTESTENV pTstEnv, void *pvCtx)2078 {2079 RT_NOREF(pTstEnv, pvCtx);2080 2081 return VINF_SUCCESS;2082 }2083 2084 2085 /*********************************************************************************************************************************2086 * Test execution *2087 *********************************************************************************************************************************/2088 2089 static AUDIOTESTDESC g_aTests[] =2090 {2091 /* pszTest fExcluded pfnSetup */2092 { "PlayTone", false, audioTestPlayToneSetup, audioTestPlayToneExec, audioTestPlayToneDestroy },2093 { "RecordTone", false, audioTestRecordToneSetup, audioTestRecordToneExec, audioTestRecordToneDestroy }2094 };2095 2096 /**2097 * Runs one specific audio test.2098 *2099 * @returns VBox status code.2100 * @param pTstEnv Test environment to use for running the test.2101 * @param pTstDesc Test to run.2102 * @param uSeq Test sequence # in case there are more tests.2103 * @param pOverrideParms Test parameters for overriding the actual test parameters. Optional.2104 */2105 static int audioTestOne(PAUDIOTESTENV pTstEnv, PAUDIOTESTDESC pTstDesc,2106 unsigned uSeq, PAUDIOTESTPARMS pOverrideParms)2107 {2108 RT_NOREF(uSeq);2109 2110 int rc;2111 2112 AUDIOTESTPARMS TstParms;2113 audioTestParmsInit(&TstParms);2114 2115 RTTestSub(g_hTest, pTstDesc->pszName);2116 2117 if (pTstDesc->fExcluded)2118 {2119 RTTestSkipped(g_hTest, "Excluded from list");2120 return VINF_SUCCESS;2121 }2122 2123 void *pvCtx = NULL; /* Test-specific opaque context. Optional and can be NULL. */2124 2125 if (pTstDesc->pfnSetup)2126 {2127 rc = pTstDesc->pfnSetup(pTstEnv, pTstDesc, &TstParms, &pvCtx);2128 if (RT_FAILURE(rc))2129 {2130 RTTestFailed(g_hTest, "Test setup failed\n");2131 return rc;2132 }2133 }2134 2135 audioTestCombineParms(&TstParms, pOverrideParms);2136 2137 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test #%u: %RU32 iterations\n", uSeq, TstParms.cIterations);2138 2139 if (strlen(TstParms.Dev.szName)) /** @todo Refine this check. */2140 rc = audioTestDeviceOpen(&TstParms.Dev);2141 2142 AssertPtr(pTstDesc->pfnExec);2143 rc = pTstDesc->pfnExec(pTstEnv, pvCtx, &TstParms);2144 2145 RTTestSubDone(g_hTest);2146 2147 if (pTstDesc->pfnDestroy)2148 {2149 int rc2 = pTstDesc->pfnDestroy(pTstEnv, pvCtx);2150 if (RT_SUCCESS(rc))2151 rc = rc2;2152 2153 if (RT_FAILURE(rc2))2154 RTTestFailed(g_hTest, "Test destruction failed\n");2155 }2156 2157 rc = audioTestDeviceClose(&TstParms.Dev);2158 2159 audioTestParmsDestroy(&TstParms);2160 2161 return rc;2162 }2163 2164 /**2165 * Runs all specified tests in a row.2166 *2167 * @returns VBox status code.2168 * @param pTstEnv Test environment to use for running all tests.2169 * @param pOverrideParms Test parameters for (some / all) specific test parameters. Optional.2170 */2171 static int audioTestWorker(PAUDIOTESTENV pTstEnv, PAUDIOTESTPARMS pOverrideParms)2172 {2173 int rc = VINF_SUCCESS;2174 2175 unsigned uSeq = 0;2176 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)2177 {2178 int rc2 = audioTestOne(pTstEnv, &g_aTests[i], uSeq, pOverrideParms);2179 if (RT_SUCCESS(rc))2180 rc = rc2;2181 2182 if (!g_aTests[i].fExcluded)2183 uSeq++;2184 }2185 2186 return rc;2187 }2188 2189 /** Option help for the 'test' command. */2190 static DECLCALLBACK(const char *) audioTestCmdTestHelp(PCRTGETOPTDEF pOpt)2191 {2192 switch (pOpt->iShort)2193 {2194 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";2195 case VKAT_TEST_OPT_DEV: return "Use the specified audio device";2196 case 'e': return "Exclude the given test id from the list";2197 case 'a': return "Exclude all tests from the list (useful to enable single tests later with --include)";2198 case 'i': return "Include the given test id in the list";2199 }2200 return NULL;2201 }2202 2203 /**2204 * Main (entry) function for the testing functionality of VKAT.2205 *2206 * @returns Program exit code.2207 * @param pGetState RTGetOpt state.2208 */2209 static DECLCALLBACK(RTEXITCODE) audioTestMain(PRTGETOPTSTATE pGetState)2210 {2211 AUDIOTESTENV TstEnv;2212 RT_ZERO(TstEnv);2213 2214 AUDIOTESTPARMS TstCust;2215 audioTestParmsInit(&TstCust);2216 2217 const char *pszDevice = NULL; /* Custom device to use. Can be NULL if not being used. */2218 const char *pszTag = NULL; /* Custom tag to use. Can be NULL if not being used. */2219 PCPDMDRVREG pDrvReg = g_aBackends[0].pDrvReg;2220 bool fWithDrvAudio = false;2221 uint8_t cPcmSampleBit = 0;2222 uint8_t cPcmChannels = 0;2223 uint32_t uPcmHz = 0;2224 bool fPcmSigned = true;2225 const char *pszTcpAddr = NULL;2226 uint16_t uTcpPort = 0;2227 2228 int rc;2229 RTGETOPTUNION ValueUnion;2230 while ((rc = RTGetOpt(pGetState, &ValueUnion)))2231 {2232 switch (rc)2233 {2234 case 'a':2235 for (unsigned i = 0; i < RT_ELEMENTS(g_aTests); i++)2236 g_aTests[i].fExcluded = true;2237 break;2238 2239 case 'b':2240 pDrvReg = NULL;2241 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)2242 if ( strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 02243 || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)2244 {2245 pDrvReg = g_aBackends[i].pDrvReg;2246 break;2247 }2248 if (pDrvReg == NULL)2249 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);2250 break;2251 2252 case 'd':2253 fWithDrvAudio = true;2254 break;2255 2256 case 'e':2257 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))2258 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --exclude", ValueUnion.u32);2259 g_aTests[ValueUnion.u32].fExcluded = true;2260 break;2261 2262 case VKAT_TEST_OPT_ATS_ADDR:2263 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)2264 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must specify a test mode first!");2265 pszTcpAddr = ValueUnion.psz;2266 break;2267 2268 case VKAT_TEST_OPT_ATS_PORT:2269 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)2270 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Must specify a test mode first!");2271 uTcpPort = ValueUnion.u32;2272 break;2273 2274 case VKAT_TEST_OPT_MODE:2275 if (TstEnv.enmMode != AUDIOTESTMODE_UNKNOWN)2276 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Test mode (guest / host) already specified");2277 TstEnv.enmMode = RTStrICmp(ValueUnion.psz, "guest") == 0 ? AUDIOTESTMODE_GUEST : AUDIOTESTMODE_HOST;2278 break;2279 2280 case 'i':2281 if (ValueUnion.u32 >= RT_ELEMENTS(g_aTests))2282 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid test number %u passed to --include", ValueUnion.u32);2283 g_aTests[ValueUnion.u32].fExcluded = false;2284 break;2285 2286 case VKAT_TEST_OPT_COUNT:2287 return RTMsgErrorExitFailure("Not yet implemented!");2288 2289 case VKAT_TEST_OPT_DEV:2290 pszDevice = ValueUnion.psz;2291 break;2292 2293 case VKAT_TEST_OPT_PAUSE:2294 return RTMsgErrorExitFailure("Not yet implemented!");2295 2296 case VKAT_TEST_OPT_OUTDIR:2297 rc = RTStrCopy(TstEnv.szPathOut, sizeof(TstEnv.szPathOut), ValueUnion.psz);2298 if (RT_FAILURE(rc))2299 return RTMsgErrorExitFailure("Failed to copy out directory: %Rrc", rc);2300 break;2301 2302 case VKAT_TEST_OPT_PCM_BIT:2303 cPcmSampleBit = ValueUnion.u8;2304 break;2305 2306 case VKAT_TEST_OPT_PCM_CHAN:2307 cPcmChannels = ValueUnion.u8;2308 break;2309 2310 case VKAT_TEST_OPT_PCM_HZ:2311 uPcmHz = ValueUnion.u32;2312 break;2313 2314 case VKAT_TEST_OPT_PCM_SIGNED:2315 fPcmSigned = ValueUnion.f;2316 break;2317 2318 case VKAT_TEST_OPT_TAG:2319 pszTag = ValueUnion.psz;2320 break;2321 2322 case VKAT_TEST_OPT_TEMPDIR:2323 rc = RTStrCopy(TstEnv.szPathTemp, sizeof(TstEnv.szPathTemp), ValueUnion.psz);2324 if (RT_FAILURE(rc))2325 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Temp dir invalid, rc=%Rrc", rc);2326 break;2327 2328 case VKAT_TEST_OPT_VOL:2329 TstCust.TestTone.uVolumePercent = ValueUnion.u8;2330 break;2331 2332 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);2333 2334 default:2335 return RTGetOptPrintError(rc, &ValueUnion);2336 }2337 }2338 2339 /*2340 * Start testing.2341 */2342 RTTestBanner(g_hTest);2343 2344 /* Initialize the custom test parameters with sensible defaults if nothing else is given. */2345 PDMAudioPropsInit(&TstCust.TestTone.Props,2346 cPcmSampleBit ? cPcmSampleBit / 8 : 2 /* 16-bit */, fPcmSigned, cPcmChannels ? cPcmChannels : 2,2347 uPcmHz ? uPcmHz : 44100);2348 2349 if (TstEnv.enmMode == AUDIOTESTMODE_UNKNOWN)2350 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No test mode specified!\n");2351 2352 if (TstEnv.enmMode == AUDIOTESTMODE_HOST)2353 {2354 /* Use the default port is none is specified. */2355 if (!uTcpPort)2356 uTcpPort = ATS_DEFAULT_PORT;2357 2358 if (!pszTcpAddr)2359 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--ats-address missing\n");2360 }2361 2362 /* For now all tests have the same test environment. */2363 rc = audioTestEnvInit(&TstEnv, pDrvReg, fWithDrvAudio, pszTag, pszTcpAddr, uTcpPort);2364 if (RT_SUCCESS(rc))2365 {2366 PPDMAUDIOHOSTDEV pDev;2367 rc = audioTestDevicesEnumerateAndCheck(&TstEnv, pszDevice, &pDev);2368 if (RT_SUCCESS(rc))2369 audioTestWorker(&TstEnv, &TstCust);2370 2371 /* Before destroying the test environment, pack up the test set so2372 * that it's ready for transmission. */2373 char szFileOut[RTPATH_MAX];2374 rc = AudioTestSetPack(&TstEnv.Set, TstEnv.szPathOut, szFileOut, sizeof(szFileOut));2375 if (RT_SUCCESS(rc))2376 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", szFileOut);2377 2378 #ifndef DEBUG_andy2379 /* Clean up. */2380 AudioTestSetClose(&TstEnv.Set); /* wipe fails on windows if the manifest file is open*/2381 2382 int rc2 = AudioTestSetWipe(&TstEnv.Set);2383 AssertRC(rc2); /* Annoying, but not test-critical. */2384 #endif2385 audioTestEnvDestroy(&TstEnv);2386 }2387 2388 audioTestParmsDestroy(&TstCust);2389 2390 if (RT_FAILURE(rc)) /* Let us know that something went wrong in case we forgot to mention it. */2391 RTTestFailed(g_hTest, "Tested failed with %Rrc\n", rc);2392 2393 /*2394 * Print summary and exit.2395 */2396 return RTTestSummaryAndDestroy(g_hTest);2397 }2398 2399 2400 /*********************************************************************************************************************************2401 * Command: verify *2402 *********************************************************************************************************************************/2403 2404 static int audioVerifyOpenTestSet(const char *pszPathSet, PAUDIOTESTSET pSet)2405 {2406 int rc;2407 2408 char szPathExtracted[RTPATH_MAX];2409 2410 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Opening test set '%s'\n", pszPathSet);2411 2412 const bool fPacked = AudioTestSetIsPacked(pszPathSet);2413 if (fPacked)2414 {2415 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set is an archive and needs to be unpacked\n");2416 2417 char szPathTemp[RTPATH_MAX];2418 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));2419 if (RT_SUCCESS(rc))2420 {2421 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using temporary directory '%s'\n", szPathTemp);2422 2423 rc = RTPathJoin(szPathExtracted, sizeof(szPathExtracted), szPathTemp, "vkat-XXXX");2424 if (RT_SUCCESS(rc))2425 {2426 rc = RTDirCreateTemp(szPathExtracted, 0755);2427 if (RT_SUCCESS(rc))2428 {2429 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unpacking archive to '%s'\n", szPathExtracted);2430 rc = AudioTestSetUnpack(pszPathSet, szPathExtracted);2431 if (RT_SUCCESS(rc))2432 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Archive successfully unpacked\n");2433 }2434 }2435 }2436 }2437 else2438 rc = VINF_SUCCESS;2439 2440 if (RT_SUCCESS(rc))2441 rc = AudioTestSetOpen(pSet, fPacked ? szPathExtracted : pszPathSet);2442 2443 if (RT_FAILURE(rc))2444 RTTestFailed(g_hTest, "Unable to open / unpack test set archive: %Rrc", rc);2445 2446 return rc;2447 }2448 2449 /**2450 * Verifies one single test set.2451 *2452 * @returns VBox status code.2453 * @param pszPathSetA Absolute path to test set A.2454 * @param pszPathSetB Absolute path to test set B.2455 */2456 static int audioVerifyOne(const char *pszPathSetA, const char *pszPathSetB)2457 {2458 RTTestSubF(g_hTest, "Verifying");2459 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verifying test set '%s' with test set '%s'\n", pszPathSetA, pszPathSetB);2460 2461 AUDIOTESTSET SetA, SetB;2462 int rc = audioVerifyOpenTestSet(pszPathSetA, &SetA);2463 if (RT_SUCCESS(rc))2464 rc = audioVerifyOpenTestSet(pszPathSetB, &SetB);2465 2466 if (RT_SUCCESS(rc))2467 {2468 AUDIOTESTERRORDESC errDesc;2469 rc = AudioTestSetVerify(&SetA, &SetB, &errDesc);2470 if (RT_SUCCESS(rc))2471 {2472 if (AudioTestErrorDescFailed(&errDesc))2473 {2474 /** @todo Use some AudioTestErrorXXX API for enumeration here later. */2475 PAUDIOTESTERRORENTRY pErrEntry;2476 RTListForEach(&errDesc.List, pErrEntry, AUDIOTESTERRORENTRY, Node)2477 RTTestFailed(g_hTest, pErrEntry->szDesc);2478 }2479 else2480 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Verification successful\n");2481 2482 AudioTestErrorDescDestroy(&errDesc);2483 }2484 else2485 RTTestFailed(g_hTest, "Verification failed with %Rrc", rc);2486 }2487 2488 AudioTestSetClose(&SetA);2489 AudioTestSetClose(&SetB);2490 2491 RTTestSubDone(g_hTest);2492 2493 return rc;2494 }2495 2496 /**2497 * Main (entry) function for the verification functionality of VKAT.2498 *2499 * @returns Program exit code.2500 * @param pGetState RTGetOpt state.2501 */2502 static DECLCALLBACK(RTEXITCODE) audioVerifyMain(PRTGETOPTSTATE pGetState)2503 {2504 /*2505 * Parse options and process arguments.2506 */2507 char *pszSetA = NULL;2508 char *pszSetB = NULL;2509 unsigned iTestSet = 0;2510 2511 int rc;2512 RTGETOPTUNION ValueUnion;2513 while ((rc = RTGetOpt(pGetState, &ValueUnion)))2514 {2515 switch (rc)2516 {2517 case VKAT_VERIFY_OPT_TAG:2518 break;2519 2520 case VINF_GETOPT_NOT_OPTION:2521 {2522 char **ppszSet = iTestSet == 0 ? &pszSetA : &pszSetB;2523 2524 if (iTestSet == 0)2525 RTTestBanner(g_hTest);2526 2527 *ppszSet = RTStrDup(ValueUnion.psz);2528 AssertPtrReturn(*ppszSet, RTEXITCODE_FAILURE);2529 2530 iTestSet++;2531 break;2532 }2533 2534 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);2535 2536 default:2537 return RTGetOptPrintError(rc, &ValueUnion);2538 }2539 }2540 2541 if (!iTestSet)2542 return RTMsgErrorExitFailure("At least one test set must be specified");2543 2544 if (iTestSet > 2)2545 return RTMsgErrorExitFailure("Only two test sets can be verified at one time");2546 2547 /*2548 * If only test set A is given, default to the current directory2549 * for test set B.2550 */2551 if (iTestSet == 1)2552 {2553 char szDirCur[RTPATH_MAX];2554 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));2555 if (RT_SUCCESS(rc))2556 {2557 Assert(pszSetB == NULL);2558 pszSetB = RTStrDup(szDirCur);2559 AssertPtrReturn(pszSetB, RTEXITCODE_FAILURE);2560 }2561 else2562 RTTestFailed(g_hTest, "Failed to retrieve current directory: %Rrc", rc);2563 }2564 2565 if (RT_SUCCESS(rc))2566 rc = audioVerifyOne(pszSetA, pszSetB);2567 2568 RTStrFree(pszSetA);2569 RTStrFree(pszSetB);2570 2571 /*2572 * Print summary and exit.2573 */2574 return RTTestSummaryAndDestroy(g_hTest);2575 }2576 2577 2578 /*********************************************************************************************************************************2579 * Command: play *2580 *********************************************************************************************************************************/2581 2582 /**2583 * Worker for audioTestCmdPlayHandler that plays one file.2584 */2585 static RTEXITCODE audioTestPlayOne(const char *pszFile, PCPDMDRVREG pDrvReg, const char *pszDevId, uint32_t cMsBufferSize,2586 uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint, bool fWithDrvAudio)2587 {2588 /*2589 * First we must open the file and determin the format.2590 */2591 RTERRINFOSTATIC ErrInfo;2592 AUDIOTESTWAVEFILE WaveFile;2593 int rc = AudioTestWaveFileOpen(pszFile, &WaveFile, RTErrInfoInitStatic(&ErrInfo));2594 if (RT_FAILURE(rc))2595 return RTMsgErrorExitFailure("Failed to open '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);2596 2597 if (g_uVerbosity > 0)2598 {2599 char szTmp[128];2600 RTMsgInfo("Opened '%s' for playing\n", pszFile);2601 RTMsgInfo("Format: %s\n", PDMAudioPropsToString(&WaveFile.Props, szTmp, sizeof(szTmp)));2602 RTMsgInfo("Size: %'RU32 bytes / %#RX32 / %'RU32 frames / %'RU64 ns\n",2603 WaveFile.cbSamples, WaveFile.cbSamples,2604 PDMAudioPropsBytesToFrames(&WaveFile.Props, WaveFile.cbSamples),2605 PDMAudioPropsBytesToNano(&WaveFile.Props, WaveFile.cbSamples));2606 }2607 2608 /*2609 * Construct the driver stack.2610 */2611 RTEXITCODE rcExit = RTEXITCODE_FAILURE;2612 AUDIOTESTDRVSTACK DrvStack;2613 rc = audioTestDriverStackInit(&DrvStack, pDrvReg, fWithDrvAudio);2614 if (RT_SUCCESS(rc))2615 {2616 /*2617 * Set the output device if one is specified.2618 */2619 rc = audioTestDriverStackSetDevice(&DrvStack, PDMAUDIODIR_OUT, pszDevId);2620 if (RT_SUCCESS(rc))2621 {2622 /*2623 * Open a stream for the output.2624 */2625 PDMAUDIOSTREAMCFG CfgAcq;2626 PPDMAUDIOSTREAM pStream = NULL;2627 rc = audioTestDriverStackStreamCreateOutput(&DrvStack, &WaveFile.Props, cMsBufferSize,2628 cMsPreBuffer, cMsSchedulingHint, &pStream, &CfgAcq);2629 if (RT_SUCCESS(rc))2630 {2631 rc = audioTestDriverStackStreamEnable(&DrvStack, pStream);2632 if (RT_SUCCESS(rc))2633 {2634 uint64_t const nsStarted = RTTimeNanoTS();2635 2636 /*2637 * Transfer data as quickly as we're allowed.2638 */2639 for (;;)2640 {2641 /* Read a chunk from the wave file. */2642 uint8_t abSamples[16384];2643 size_t cbSamples = 0;2644 rc = AudioTestWaveFileRead(&WaveFile, abSamples, sizeof(abSamples), &cbSamples);2645 if (RT_SUCCESS(rc) && cbSamples > 0)2646 {2647 /* Transfer the data to the audio stream. */2648 for (uint32_t offSamples = 0; offSamples < cbSamples;)2649 {2650 uint32_t const cbCanWrite = audioTestDriverStackStreamGetWritable(&DrvStack, pStream);2651 if (cbCanWrite > 0)2652 {2653 uint32_t const cbToPlay = RT_MIN(cbCanWrite, (uint32_t)cbSamples - offSamples);2654 uint32_t cbPlayed = 0;2655 rc = audioTestDriverStackStreamPlay(&DrvStack, pStream, &abSamples[offSamples],2656 cbToPlay, &cbPlayed);2657 if (RT_SUCCESS(rc))2658 {2659 if (cbPlayed)2660 offSamples += cbPlayed;2661 else2662 {2663 rcExit = RTMsgErrorExitFailure("Played zero out of %#x bytes - %#x bytes reported playable!\n",2664 cbToPlay, cbCanWrite);2665 break;2666 }2667 }2668 else2669 {2670 rcExit = RTMsgErrorExitFailure("Failed to play %#x bytes: %Rrc\n", cbToPlay, rc);2671 break;2672 }2673 }2674 else if (audioTestDriverStackStreamIsOkay(&DrvStack, pStream))2675 RTThreadSleep(RT_MIN(RT_MAX(1, CfgAcq.Device.cMsSchedulingHint), 256));2676 else2677 {2678 rcExit = RTMsgErrorExitFailure("Stream is not okay!\n");2679 break;2680 }2681 }2682 }2683 else if (RT_SUCCESS(rc) && cbSamples == 0)2684 {2685 rcExit = RTEXITCODE_SUCCESS;2686 break;2687 }2688 else2689 {2690 rcExit = RTMsgErrorExitFailure("Error reading wav file '%s': %Rrc", pszFile, rc);2691 break;2692 }2693 }2694 2695 /*2696 * Drain the stream.2697 */2698 if (rcExit == RTEXITCODE_SUCCESS)2699 {2700 if (g_uVerbosity > 0)2701 RTMsgInfo("%'RU64 ns: Draining...\n", RTTimeNanoTS() - nsStarted);2702 rc = audioTestDriverStackStreamDrain(&DrvStack, pStream, true /*fSync*/);2703 if (RT_SUCCESS(rc))2704 {2705 if (g_uVerbosity > 0)2706 RTMsgInfo("%'RU64 ns: Done\n", RTTimeNanoTS() - nsStarted);2707 }2708 else2709 rcExit = RTMsgErrorExitFailure("Draining failed: %Rrc", rc);2710 }2711 }2712 else2713 rcExit = RTMsgErrorExitFailure("Enabling the output stream failed: %Rrc", rc);2714 audioTestDriverStackStreamDestroy(&DrvStack, pStream);2715 }2716 else2717 rcExit = RTMsgErrorExitFailure("Creating output stream failed: %Rrc", rc);2718 }2719 else2720 rcExit = RTMsgErrorExitFailure("Failed to set output device to '%s': %Rrc", pszDevId, rc);2721 audioTestDriverStackDelete(&DrvStack);2722 }2723 else2724 rcExit = RTMsgErrorExitFailure("Driver stack construction failed: %Rrc", rc);2725 AudioTestWaveFileClose(&WaveFile);2726 return rcExit;2727 }2728 2729 /**2730 * Command line parameters for test mode.2731 */2732 static const RTGETOPTDEF g_aCmdPlayOptions[] =2733 {2734 { "--backend", 'b', RTGETOPT_REQ_STRING },2735 { "--output-device", 'o', RTGETOPT_REQ_STRING },2736 { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },2737 };2738 2739 /** the 'play' command option help. */2740 static DECLCALLBACK(const char *) audioTestCmdPlayHelp(PCRTGETOPTDEF pOpt)2741 {2742 switch (pOpt->iShort)2743 {2744 case 'b': return "The audio backend to use.";2745 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";2746 case 'o': return "The ID of the output device to use.";2747 default: return NULL;2748 }2749 }2750 2751 /**2752 * The 'play' command handler.2753 *2754 * @returns Program exit code.2755 * @param pGetState RTGetOpt state.2756 */2757 static DECLCALLBACK(RTEXITCODE) audioTestCmdPlayHandler(PRTGETOPTSTATE pGetState)2758 {2759 /* Option values: */2760 PCPDMDRVREG pDrvReg = g_aBackends[0].pDrvReg;2761 uint32_t cMsBufferSize = UINT32_MAX;2762 uint32_t cMsPreBuffer = UINT32_MAX;2763 uint32_t cMsSchedulingHint = UINT32_MAX;2764 const char *pszDevId = NULL;2765 bool fWithDrvAudio = false;2766 2767 /* Argument processing loop: */2768 int rc;2769 RTGETOPTUNION ValueUnion;2770 while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)2771 {2772 switch (rc)2773 {2774 case 'b':2775 pDrvReg = NULL;2776 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)2777 if ( strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 02778 || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)2779 {2780 pDrvReg = g_aBackends[i].pDrvReg;2781 break;2782 }2783 if (pDrvReg == NULL)2784 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);2785 break;2786 2787 case 'd':2788 fWithDrvAudio = true;2789 break;2790 2791 case 'o':2792 pszDevId = ValueUnion.psz;2793 break;2794 2795 case VINF_GETOPT_NOT_OPTION:2796 {2797 RTEXITCODE rcExit = audioTestPlayOne(ValueUnion.psz, pDrvReg, pszDevId, cMsBufferSize, cMsPreBuffer,2798 cMsSchedulingHint, fWithDrvAudio);2799 if (rcExit != RTEXITCODE_SUCCESS)2800 return rcExit;2801 break;2802 }2803 2804 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);2805 2806 default:2807 return RTGetOptPrintError(rc, &ValueUnion);2808 }2809 }2810 return RTEXITCODE_SUCCESS;2811 }2812 2813 /**2814 * Command line parameters for self-test mode.2815 */2816 static const RTGETOPTDEF g_aCmdSelftestOptions[] =2817 {2818 { "--ats-host", VKAT_SELFTEST_OPT_ATS_HOST, RTGETOPT_REQ_STRING },2819 { "--backend", 'b', RTGETOPT_REQ_STRING },2820 { "--with-drv-audio", 'd', RTGETOPT_REQ_NOTHING },2821 };2822 2823 /** the 'selftest' command option help. */2824 static DECLCALLBACK(const char *) audioTestCmdSelftestHelp(PCRTGETOPTDEF pOpt)2825 {2826 switch (pOpt->iShort)2827 {2828 case 'b': return "The audio backend to use.";2829 case 'd': return "Go via DrvAudio instead of directly interfacing with the backend.";2830 default: return NULL;2831 }2832 }2833 2834 /**2835 * Tests the Audio Test Service (ATS).2836 *2837 * @returns VBox status code.2838 * @param pDrvReg Backend driver to use.2839 * @param pszAdr Address of ATS server to connect to.2840 * If NULL, an own (local) ATS server will be created.2841 */2842 static int audioTestDoSelftestAts(PCPDMDRVREG pDrvReg, const char *pszAdr)2843 {2844 AUDIOTESTENV TstEnv;2845 int rc = audioTestDriverStackInit(&TstEnv.DrvStack, pDrvReg, true /* fWithDrvAudio */);2846 if (RT_SUCCESS(rc))2847 {2848 /** @todo Make stream parameters configurable. */2849 PDMAUDIOPCMPROPS Props;2850 PDMAudioPropsInit(&Props, 16 /* bit */ / 8, true /* fSigned */, 2 /* Channels */, 44100 /* Hz */);2851 2852 PDMAUDIOSTREAMCFG CfgAcq;2853 PPDMAUDIOSTREAM pStream = NULL;2854 rc = audioTestDriverStackStreamCreateOutput(&TstEnv.DrvStack, &Props,2855 UINT32_MAX /* cMsBufferSize */,2856 UINT32_MAX /* cMsPreBuffer */,2857 UINT32_MAX /* cMsSchedulingHint */, &pStream, &CfgAcq);2858 if (RT_SUCCESS(rc))2859 {2860 rc = audioTestDriverStackStreamEnable(&TstEnv.DrvStack, pStream);2861 if (RT_SUCCESS(rc))2862 {2863 ATSCALLBACKCTX Ctx;2864 Ctx.pTstEnv = &TstEnv;2865 2866 ATSCALLBACKS Callbacks;2867 Callbacks.pfnTonePlay = audioTestSvcTonePlayCallback;2868 Callbacks.pvUser = &Ctx;2869 2870 /* Start an own ATS instance if no address to connect was specified. */2871 ATSSERVER Srv;2872 if (pszAdr == NULL)2873 {2874 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting ATS ...\n");2875 2876 rc = AudioTestSvcInit(&Srv, &Callbacks);2877 if (RT_SUCCESS(rc))2878 {2879 rc = AudioTestSvcStart(&Srv);2880 if (RT_SUCCESS(rc))2881 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "ATS running\n");2882 }2883 }2884 else2885 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting to ATS at '%s' ...\n", pszAdr);2886 2887 if (RT_SUCCESS(rc))2888 {2889 ATSCLIENT Conn;2890 rc = AudioTestSvcClientConnect(&Conn, NULL, 0);2891 if (RT_SUCCESS(rc))2892 {2893 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to ATS, testing ...\n");2894 2895 /* Do the bare minimum here to get a test tone out. */2896 AUDIOTESTTONEPARMS ToneParms;2897 RT_ZERO(ToneParms);2898 ToneParms.msDuration = RTRandU32Ex(250, 1000 * 5);2899 memcpy(&ToneParms.Props, &CfgAcq.Props, sizeof(PDMAUDIOPCMPROPS));2900 2901 rc = AudioTestSvcClientTonePlay(&Conn, &CfgAcq, &ToneParms);2902 2903 int rc2 = AudioTestSvcClientClose(&Conn);2904 if (RT_SUCCESS(rc))2905 rc = rc2;2906 }2907 else2908 RTTestFailed(g_hTest, "Connecting to ATS failed, rc=%Rrc\n", rc);2909 2910 int rc2 = AudioTestSvcShutdown(&Srv);2911 if (RT_SUCCESS(rc))2912 rc = rc2;2913 }2914 2915 if (pszAdr == NULL)2916 {2917 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Shutting down ATS ...\n");2918 2919 int rc2 = AudioTestSvcDestroy(&Srv);2920 if (RT_SUCCESS(rc))2921 rc = rc2;2922 }2923 }2924 }2925 }2926 2927 if (RT_FAILURE(rc))2928 RTTestFailed(g_hTest, "Testing ATS failed with %Rrc\n", rc);2929 2930 return rc;2931 }2932 2933 /**2934 * Main function for performing the self-tests.2935 *2936 * @returns VBox status code.2937 * @param pDrvReg Backend driver to use.2938 * @param pszAtsAdr Address of ATS server to connect to.2939 * If NULL, an own (local) ATS server will be created.2940 */2941 static int audioTestDoSelftest(PCPDMDRVREG pDrvReg, const char *pszAtsAdr)2942 {2943 int rc = audioTestDoSelftestAts(pDrvReg, pszAtsAdr);2944 if (RT_FAILURE(rc))2945 RTTestFailed(g_hTest, "Self-test failed with: %Rrc", rc);2946 2947 return rc;2948 }2949 2950 /**2951 * The 'selftest' command handler.2952 *2953 * @returns Program exit code.2954 * @param pGetState RTGetOpt state.2955 */2956 static DECLCALLBACK(RTEXITCODE) audioTestCmdSelftestHandler(PRTGETOPTSTATE pGetState)2957 {2958 /* Option values: */2959 PCPDMDRVREG pDrvReg = g_aBackends[0].pDrvReg;2960 bool fWithDrvAudio = false;2961 char *pszAtsAddr = NULL;2962 2963 /* Argument processing loop: */2964 int rc;2965 RTGETOPTUNION ValueUnion;2966 while ((rc = RTGetOpt(pGetState, &ValueUnion)) != 0)2967 {2968 switch (rc)2969 {2970 case VKAT_SELFTEST_OPT_ATS_HOST:2971 {2972 pszAtsAddr = RTStrDup(ValueUnion.psz);2973 break;2974 }2975 2976 case 'b':2977 {2978 pDrvReg = NULL;2979 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aBackends); i++)2980 if ( strcmp(ValueUnion.psz, g_aBackends[i].pszName) == 02981 || strcmp(ValueUnion.psz, g_aBackends[i].pDrvReg->szName) == 0)2982 {2983 pDrvReg = g_aBackends[i].pDrvReg;2984 break;2985 }2986 if (pDrvReg == NULL)2987 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown backend: '%s'", ValueUnion.psz);2988 break;2989 }2990 2991 case 'd':2992 {2993 fWithDrvAudio = true;2994 break;2995 }2996 2997 case VINF_GETOPT_NOT_OPTION:2998 {2999 break;3000 }3001 3002 AUDIO_TEST_COMMON_OPTION_CASES(ValueUnion);3003 3004 default:3005 return RTGetOptPrintError(rc, &ValueUnion);3006 }3007 }3008 3009 audioTestDoSelftest(pDrvReg, pszAtsAddr);3010 3011 RTStrFree(pszAtsAddr);3012 3013 /*3014 * Print summary and exit.3015 */3016 return RTTestSummaryAndDestroy(g_hTest);3017 }3018 3019 3020 /**3021 * Commands.3022 */3023 static struct3024 {3025 /** The command name. */3026 const char *pszCommand;3027 /** The command handler. */3028 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(PRTGETOPTSTATE pGetState));3029 3030 /** Command description. */3031 const char *pszDesc;3032 /** Options array. */3033 PCRTGETOPTDEF paOptions;3034 /** Number of options in the option array. */3035 size_t cOptions;3036 /** Gets help for an option. */3037 DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt));3038 } const g_aCommands[] =3039 {3040 {3041 "test", audioTestMain,3042 "Runs audio tests and creates an audio test set.",3043 g_aCmdTestOptions, RT_ELEMENTS(g_aCmdTestOptions), audioTestCmdTestHelp3044 },3045 {3046 "verify", audioVerifyMain,3047 "Verifies a formerly created audio test set.",3048 g_aCmdVerifyOptions, RT_ELEMENTS(g_aCmdVerifyOptions), NULL,3049 },3050 {3051 "play", audioTestCmdPlayHandler,3052 "Plays one or more wave files.",3053 g_aCmdPlayOptions, RT_ELEMENTS(g_aCmdPlayOptions), audioTestCmdPlayHelp,3054 },3055 {3056 "selftest", audioTestCmdSelftestHandler,3057 "Performs self-tests.",3058 g_aCmdSelftestOptions, RT_ELEMENTS(g_aCmdSelftestOptions), audioTestCmdSelftestHelp,3059 }3060 };3061 3062 /**3063 * Shows tool usage text.3064 */3065 static RTEXITCODE audioTestUsage(PRTSTREAM pStrm)3066 {3067 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n",3068 RTPathFilename(RTProcExecutablePath()));3069 RTStrmPrintf(pStrm,3070 "\n"3071 "Global Options:\n"3072 " --debug-audio\n"3073 " Enables DrvAudio debugging.\n"3074 " --debug-audio-path=<path>\n"3075 " Tells DrvAudio where to put its debug output (wav-files).\n"3076 " -q, --quiet\n"3077 " Sets verbosity to zero.\n"3078 " -v, --verbose\n"3079 " Increase verbosity.\n"3080 " -V, --version\n"3081 " Displays version.\n"3082 " -h, -?, --help\n"3083 " Displays help.\n"3084 );3085 3086 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)3087 {3088 RTStrmPrintf(pStrm,3089 "\n"3090 "Command '%s':\n"3091 " %s\n"3092 "Options for '%s':\n",3093 g_aCommands[iCmd].pszCommand, g_aCommands[iCmd].pszDesc, g_aCommands[iCmd].pszCommand);3094 PCRTGETOPTDEF const paOptions = g_aCommands[iCmd].paOptions;3095 for (unsigned i = 0; i < g_aCommands[iCmd].cOptions; i++)3096 {3097 if (RT_C_IS_PRINT(paOptions[i].iShort))3098 RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);3099 else3100 RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);3101 3102 const char *pszHelp = NULL;3103 if (g_aCommands[iCmd].pfnOptionHelp)3104 pszHelp = g_aCommands[iCmd].pfnOptionHelp(&paOptions[i]);3105 if (pszHelp)3106 RTStrmPrintf(pStrm, " %s\n", pszHelp);3107 }3108 }3109 return RTEXITCODE_SUCCESS;3110 }3111 3112 /**3113 * Shows tool version.3114 */3115 static RTEXITCODE audioTestVersion(void)3116 {3117 RTPrintf("v0.0.1\n");3118 return RTEXITCODE_SUCCESS;3119 }3120 3121 /**3122 * Shows the logo.3123 *3124 * @param pStream Output stream to show logo on.3125 */3126 static void audioTestShowLogo(PRTSTREAM pStream)3127 {3128 RTStrmPrintf(pStream, VBOX_PRODUCT " VKAT (Validation Kit Audio Test) Version " VBOX_VERSION_STRING " - r%s\n"3129 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"3130 "All rights reserved.\n\n", RTBldCfgRevisionStr());3131 }3132 3133 int main(int argc, char **argv)3134 {3135 /*3136 * Init IPRT and globals.3137 */3138 RTEXITCODE rcExit = RTTestInitAndCreate("AudioTest", &g_hTest);3139 if (rcExit != RTEXITCODE_SUCCESS)3140 return rcExit;3141 3142 #ifdef RT_OS_WINDOWS3143 HRESULT hrc = CoInitializeEx(NULL /*pReserved*/, COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE);3144 if (FAILED(hrc))3145 RTMsgWarning("CoInitializeEx failed: %#x", hrc);3146 #endif3147 3148 /*3149 * Configure release logging to go to stderr.3150 */3151 static const char * const g_apszLogGroups[] = VBOX_LOGGROUP_NAMES;3152 int rc = RTLogCreate(&g_pRelLogger, RTLOGFLAGS_PREFIX_THREAD, "all.e.l", "VKAT_RELEASE_LOG",3153 RT_ELEMENTS(g_apszLogGroups), g_apszLogGroups, RTLOGDEST_STDERR, "vkat-release.log");3154 if (RT_SUCCESS(rc))3155 RTLogRelSetDefaultInstance(g_pRelLogger);3156 else3157 RTMsgWarning("Failed to create release logger: %Rrc", rc);3158 3159 /*3160 * Process common options.3161 */3162 RTGETOPTSTATE GetState;3163 rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions,3164 RT_ELEMENTS(g_aCmdCommonOptions), 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);3165 AssertRCReturn(rc, RTEXITCODE_INIT);3166 3167 RTGETOPTUNION ValueUnion;3168 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)3169 {3170 switch (rc)3171 {3172 case 'q':3173 g_uVerbosity = 0;3174 if (g_pRelLogger)3175 RTLogGroupSettings(g_pRelLogger, "all=0 all.e");3176 break;3177 3178 case 'v':3179 g_uVerbosity++;3180 if (g_pRelLogger)3181 RTLogGroupSettings(g_pRelLogger, g_uVerbosity == 1 ? "all.e.l" : g_uVerbosity == 2 ? "all.e.l.f" : "all=~0");3182 break;3183 3184 case 'V':3185 return audioTestVersion();3186 3187 case 'h':3188 audioTestShowLogo(g_pStdOut);3189 return audioTestUsage(g_pStdOut);3190 3191 case VINF_GETOPT_NOT_OPTION:3192 {3193 for (uintptr_t i = 0; i < RT_ELEMENTS(g_aCommands); i++)3194 if (strcmp(ValueUnion.psz, g_aCommands[i].pszCommand) == 0)3195 {3196 size_t const cCombinedOptions = g_aCommands[i].cOptions + RT_ELEMENTS(g_aCmdCommonOptions);3197 PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));3198 if (paCombinedOptions)3199 {3200 memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));3201 memcpy(&paCombinedOptions[RT_ELEMENTS(g_aCmdCommonOptions)],3202 g_aCommands[i].paOptions, g_aCommands[i].cOptions * sizeof(RTGETOPTDEF));3203 3204 rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,3205 GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);3206 if (RT_SUCCESS(rc))3207 {3208 3209 rcExit = g_aCommands[i].pfnHandler(&GetState);3210 RTMemFree(paCombinedOptions);3211 return rcExit;3212 }3213 return RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);3214 }3215 return RTMsgErrorExitFailure("Out of memory!");3216 }3217 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);3218 audioTestUsage(g_pStdErr);3219 return RTEXITCODE_SYNTAX;3220 }3221 3222 default:3223 return RTGetOptPrintError(rc, &ValueUnion);3224 }3225 }3226 3227 RTMsgError("No command specified!\n");3228 audioTestUsage(g_pStdErr);3229 return RTEXITCODE_SYNTAX;3230 }3231
Note:
See TracChangeset
for help on using the changeset viewer.