Changeset 63711 in vbox for trunk/src/VBox
- Timestamp:
- Sep 5, 2016 12:04:01 PM (8 years ago)
- Location:
- trunk/src/VBox
- Files:
-
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DevIchAc97.cpp
r63690 r63711 375 375 /** Number of active (running) SDn streams. */ 376 376 uint8_t cStreamsActive; 377 #ifndef VBOX_WITH_AUDIO_ CALLBACKS377 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 378 378 /** The timer for pumping data thru the attached LUN drivers. */ 379 379 PTMTIMERR3 pTimer; … … 424 424 #ifndef VBOX_DEVICE_STRUCT_TESTCASE 425 425 426 #if 0 /* unused */ 426 427 static void ichac97DestroyIn(PAC97STATE pThis, PDMAUDIORECSOURCE enmRecSource); 427 428 static void ichac97DestroyOut(PAC97STATE pThis); 429 #endif 428 430 DECLINLINE(PAC97STREAM) ichac97GetStreamFromID(PAC97STATE pThis, uint32_t uID); 429 431 static int ichac97StreamInit(PAC97STATE pThis, PAC97STREAM pStream, uint8_t u8Strm); 430 432 static DECLCALLBACK(void) ichac97Reset(PPDMDEVINS pDevIns); 431 #ifndef VBOX_WITH_AUDIO_ CALLBACKS433 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 432 434 static void ichac97TimerMaybeStart(PAC97STATE pThis); 433 435 static void ichac97TimerMaybeStop(PAC97STATE pThis); … … 560 562 pThis->cStreamsActive--; 561 563 562 #ifndef VBOX_WITH_AUDIO_ CALLBACKS564 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 563 565 ichac97TimerMaybeStop(pThis); 564 566 #endif … … 567 569 { 568 570 pThis->cStreamsActive++; 569 #ifndef VBOX_WITH_AUDIO_ CALLBACKS571 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 570 572 ichac97TimerMaybeStart(pThis); 571 573 #endif … … 621 623 } 622 624 625 #if 0 /* unused */ 623 626 static void ichac97StreamsDestroy(PAC97STATE pThis) 624 627 { … … 633 636 ichac97StreamDestroy(&pThis->StreamOut); 634 637 } 638 #endif 635 639 636 640 static int ichac97StreamsInit(PAC97STATE pThis) … … 672 676 } 673 677 678 #if 0 /* unused */ 674 679 static void ichac97DestroyIn(PAC97STATE pThis, PDMAUDIORECSOURCE enmRecSource) 675 680 { … … 728 733 } 729 734 } 735 #endif 730 736 731 737 static int ichac97CreateIn(PAC97STATE pThis, … … 1344 1350 } 1345 1351 1346 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1347 1352 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 1348 1353 static void ichac97TimerMaybeStart(PAC97STATE pThis) 1349 1354 { … … 1449 1454 STAM_PROFILE_STOP(&pThis->StatTimer, a); 1450 1455 } 1451 1452 #endif /* !VBOX_WITH_AUDIO_CALLBACKS */ 1456 #endif /* !VBOX_WITH_AUDIO_AC97_CALLBACKS */ 1453 1457 1454 1458 static int ichac97TransferAudio(PAC97STATE pThis, PAC97STREAM pStream, uint32_t cbToProcess, uint32_t *pcbProcessed) … … 2581 2585 N_("AC'97 configuration error: Querying \"Codec\" as string failed")); 2582 2586 2583 #ifndef VBOX_WITH_AUDIO_ CALLBACKS2587 #ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 2584 2588 uint16_t uTimerHz; 2585 2589 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, AC97_TIMER_HZ /* Default value, if not set. */); … … 2714 2718 2715 2719 if (RT_SUCCESS(rc)) 2716 {2717 2720 ichac97StreamsInit(pThis); 2718 2721 2719 PAC97DRIVER pDrv; 2720 RTListForEach(&pThis->lstDrv, pDrv, AC97DRIVER, Node) 2721 { 2722 /* 2723 * Only primary drivers are critical for the VM to run. Everything else 2724 * might not worth showing an own error message box in the GUI. 2725 */ 2726 if (!(pDrv->Flags & PDMAUDIODRVFLAGS_PRIMARY)) 2727 continue; 2728 2729 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector; 2730 AssertPtr(pCon); 2731 2732 bool fValidLineIn = AudioMixerStreamIsValid(pDrv->LineIn.pMixStrm); 2733 bool fValidMicIn = AudioMixerStreamIsValid(pDrv->MicIn.pMixStrm); 2734 bool fValidOut = AudioMixerStreamIsValid(pDrv->Out.pMixStrm); 2735 2736 if ( !fValidLineIn 2737 && !fValidMicIn 2738 && !fValidOut) 2739 { 2740 LogRel(("AC97: Falling back to NULL backend (no sound audible)\n")); 2741 2742 /* Destroy the streams before re-attaching the NULL driver. */ 2743 ichac97StreamsDestroy(pThis); 2744 2745 ichac97Reset(pDevIns); 2746 ichac97Reattach(pThis, pDrv, pDrv->uLUN, "NullAudio"); 2747 2748 ichac97StreamsInit(pThis); 2749 2750 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding", 2751 N_("No audio devices could be opened. Selecting the NULL audio backend " 2752 "with the consequence that no sound is audible")); 2753 } 2754 else 2755 { 2756 bool fWarn = false; 2757 2758 PDMAUDIOBACKENDCFG backendCfg; 2759 int rc2 = pCon->pfnGetConfig(pCon, &backendCfg); 2760 if (RT_SUCCESS(rc2)) 2761 { 2762 if (backendCfg.cSources) 2763 { 2764 /* If the audio backend supports two or more input streams at once, 2765 * warn if one of our two inputs (microphone-in and line-in) failed to initialize. */ 2766 if (backendCfg.cMaxStreamsIn >= 2) 2767 fWarn = !fValidLineIn || !fValidMicIn; 2768 /* If the audio backend only supports one input stream at once (e.g. pure ALSA, and 2769 * *not* ALSA via PulseAudio plugin!), only warn if both of our inputs failed to initialize. 2770 * One of the two simply is not in use then. */ 2771 else if (backendCfg.cMaxStreamsIn == 1) 2772 fWarn = !fValidLineIn && !fValidMicIn; 2773 /* Don't warn if our backend is not able of supporting any input streams at all. */ 2774 } 2775 2776 if ( !fWarn 2777 && backendCfg.cSinks) 2778 { 2779 fWarn = !fValidOut; 2780 } 2781 } 2782 else 2783 { 2784 LogRel(("AC97: Unable to retrieve audio backend configuration for LUN #%RU8, rc=%Rrc\n", pDrv->uLUN, rc2)); 2785 fWarn = true; 2786 } 2787 2788 if (fWarn) 2789 { 2790 char szMissingStreams[255] = ""; 2791 size_t len = 0; 2792 if (!fValidLineIn) 2793 { 2794 LogRel(("AC97: WARNING: Unable to open PCM line input for LUN #%RU8!\n", pDrv->uLUN)); 2795 len = RTStrPrintf(szMissingStreams, sizeof(szMissingStreams), "PCM Input"); 2796 } 2797 if (!fValidMicIn) 2798 { 2799 LogRel(("AC97: WARNING: Unable to open PCM microphone input for LUN #%RU8!\n", pDrv->uLUN)); 2800 len += RTStrPrintf(szMissingStreams + len, 2801 sizeof(szMissingStreams) - len, len ? ", PCM Microphone" : "PCM Microphone"); 2802 } 2803 if (!fValidOut) 2804 { 2805 LogRel(("AC97: WARNING: Unable to open PCM output for LUN #%RU8!\n", pDrv->uLUN)); 2806 len += RTStrPrintf(szMissingStreams + len, 2807 sizeof(szMissingStreams) - len, len ? ", PCM Output" : "PCM Output"); 2808 } 2809 2810 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding", 2811 N_("Some AC'97 audio streams (%s) could not be opened. Guest applications generating audio " 2812 "output or depending on audio input may hang. Make sure your host audio device " 2813 "is working properly. Check the logfile for error messages of the audio " 2814 "subsystem"), szMissingStreams); 2815 } 2816 } 2817 } 2818 } 2819 2820 # ifndef VBOX_WITH_AUDIO_CALLBACKS 2722 # ifndef VBOX_WITH_AUDIO_AC97_CALLBACKS 2821 2723 if (RT_SUCCESS(rc)) 2822 2724 { … … 2835 2737 } 2836 2738 } 2837 # else 2739 # else /* !VBOX_WITH_AUDIO_AC97_CALLBACKS */ 2838 2740 if (RT_SUCCESS(rc)) 2839 2741 { … … 2865 2767 } 2866 2768 } 2867 # endif 2769 # endif /* VBOX_WITH_AUDIO_AC97_CALLBACKS */ 2868 2770 2869 2771 # ifdef VBOX_WITH_STATISTICS -
trunk/src/VBox/Devices/Audio/DevIchHda.cpp
r63690 r63711 820 820 /** Number of active (running) SDn streams. */ 821 821 uint8_t cStreamsActive; 822 #ifndef VBOX_WITH_AUDIO_ CALLBACKS822 #ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 823 823 /** The timer for pumping data thru the attached LUN drivers. */ 824 824 PTMTIMERR3 pTimer; … … 833 833 #endif 834 834 #ifdef VBOX_WITH_STATISTICS 835 # ifndef VBOX_WITH_AUDIO_ CALLBACKS835 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 836 836 STAMPROFILE StatTimer; 837 837 # endif … … 868 868 typedef HDASTATE *PHDASTATE; 869 869 870 #ifdef VBOX_WITH_AUDIO_ CALLBACKS870 #ifdef VBOX_WITH_AUDIO_HDA_CALLBACKS 871 871 typedef struct HDACALLBACKCTX 872 872 { … … 976 976 * Timer routines. 977 977 */ 978 #if !defined(VBOX_WITH_AUDIO_ CALLBACKS) && defined(IN_RING3)978 #if !defined(VBOX_WITH_AUDIO_HDA_CALLBACKS) && defined(IN_RING3) 979 979 static void hdaTimerMaybeStart(PHDASTATE pThis); 980 980 static void hdaTimerMaybeStop(PHDASTATE pThis); … … 1828 1828 pThis->cStreamsActive--; 1829 1829 1830 # ifndef VBOX_WITH_AUDIO_ CALLBACKS1830 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 1831 1831 hdaTimerMaybeStop(pThis); 1832 1832 # endif … … 1835 1835 { 1836 1836 pThis->cStreamsActive++; 1837 # ifndef VBOX_WITH_AUDIO_ CALLBACKS1837 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 1838 1838 hdaTimerMaybeStart(pThis); 1839 1839 # endif … … 4133 4133 } 4134 4134 4135 #ifndef VBOX_WITH_AUDIO_CALLBACKS 4136 4135 #ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 4137 4136 static void hdaTimerMaybeStart(PHDASTATE pThis) 4138 4137 { … … 4270 4269 } 4271 4270 4272 #else /* VBOX_WITH_AUDIO_ CALLBACKS */4273 4274 static DECLCALLBACK(int) hdaCallbackInput(PDMAUDIOC ALLBACKTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)4271 #else /* VBOX_WITH_AUDIO_HDA_CALLBACKS */ 4272 4273 static DECLCALLBACK(int) hdaCallbackInput(PDMAUDIOCBTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser) 4275 4274 { 4276 4275 Assert(enmType == PDMAUDIOCALLBACKTYPE_INPUT); … … 4283 4282 AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER); 4284 4283 4285 PPDMAUDIOC ALLBACKDATAIN pData = (PPDMAUDIOCALLBACKDATAIN)pvUser;4286 AssertReturn(cbUser == sizeof(PDMAUDIOC ALLBACKDATAIN), VERR_INVALID_PARAMETER);4284 PPDMAUDIOCBDATA_DATA_INPUT pData = (PPDMAUDIOCBDATA_DATA_INPUT)pvUser; 4285 AssertReturn(cbUser == sizeof(PDMAUDIOCBDATA_DATA_INPUT), VERR_INVALID_PARAMETER); 4287 4286 4288 4287 return hdaTransfer(pCtx->pThis, PI_INDEX, UINT32_MAX, &pData->cbOutRead); 4289 4288 } 4290 4289 4291 static DECLCALLBACK(int) hdaCallbackOutput(PDMAUDIOC ALLBACKTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser)4290 static DECLCALLBACK(int) hdaCallbackOutput(PDMAUDIOCBTYPE enmType, void *pvCtx, size_t cbCtx, void *pvUser, size_t cbUser) 4292 4291 { 4293 4292 Assert(enmType == PDMAUDIOCALLBACKTYPE_OUTPUT); … … 4300 4299 AssertReturn(cbCtx == sizeof(HDACALLBACKCTX), VERR_INVALID_PARAMETER); 4301 4300 4302 PPDMAUDIOC ALLBACKDATAOUT pData = (PPDMAUDIOCALLBACKDATAOUT)pvUser;4303 AssertReturn(cbUser == sizeof(PDMAUDIOC ALLBACKDATAOUT), VERR_INVALID_PARAMETER);4301 PPDMAUDIOCBDATA_DATA_OUTPUT pData = (PPDMAUDIOCBDATA_DATA_OUTPUT)pvUser; 4302 AssertReturn(cbUser == sizeof(PDMAUDIOCBDATA_DATA_OUTPUT), VERR_INVALID_PARAMETER); 4304 4303 4305 4304 PHDASTATE pThis = pCtx->pThis; … … 4318 4317 } 4319 4318 } 4320 #endif /* VBOX_WITH_AUDIO_ CALLBACKS */4319 #endif /* VBOX_WITH_AUDIO_HDA_CALLBACKS */ 4321 4320 4322 4321 static int hdaTransfer(PHDASTATE pThis, PHDASTREAM pStream, uint32_t cbToProcess, uint32_t *pcbProcessed) … … 5489 5488 LogFlowFuncEnter(); 5490 5489 5491 # ifndef VBOX_WITH_AUDIO_ CALLBACKS5490 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 5492 5491 /* 5493 5492 * Stop the timer, if any. … … 5572 5571 HDA_REG(pThis, STATESTS) = 0x1; 5573 5572 5574 # ifndef VBOX_WITH_AUDIO_ CALLBACKS5573 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 5575 5574 hdaTimerMaybeStart(pThis); 5576 5575 # endif … … 5830 5829 return PDMDEV_SET_ERROR(pDevIns, rc, 5831 5830 N_("HDA configuration error: failed to read R0Enabled as boolean")); 5832 #ifndef VBOX_WITH_AUDIO_ CALLBACKS5831 #ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 5833 5832 uint16_t uTimerHz; 5834 5833 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 200 /* Hz */); … … 6059 6058 AssertRC(rc); 6060 6059 } 6061 6062 /*6063 * Initialize the driver chain.6064 */6065 PHDADRIVER pDrv;6066 RTListForEach(&pThis->lstDrv, pDrv, HDADRIVER, Node)6067 {6068 /*6069 * Only primary drivers are critical for the VM to run. Everything else6070 * might not worth showing an own error message box in the GUI.6071 */6072 if (!(pDrv->Flags & PDMAUDIODRVFLAGS_PRIMARY))6073 continue;6074 6075 PPDMIAUDIOCONNECTOR pCon = pDrv->pConnector;6076 AssertPtr(pCon);6077 6078 bool fValidLineIn = AudioMixerStreamIsValid(pDrv->LineIn.pMixStrm);6079 #ifdef VBOX_WITH_HDA_MIC_IN6080 bool fValidMicIn = AudioMixerStreamIsValid(pDrv->MicIn.pMixStrm);6081 #endif6082 bool fValidOut = AudioMixerStreamIsValid(pDrv->Front.pMixStrm);6083 #ifdef VBOX_WITH_HDA_51_SURROUND6084 /** @todo Anything to do here? */6085 #endif6086 6087 if ( !fValidLineIn6088 #ifdef VBOX_WITH_HDA_MIC_IN6089 && !fValidMicIn6090 #endif6091 && !fValidOut)6092 {6093 LogRel(("HDA: Falling back to NULL backend (no sound audible)\n"));6094 6095 hdaReset(pDevIns);6096 hdaReattach(pThis, pDrv, pDrv->uLUN, "NullAudio");6097 6098 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",6099 N_("No audio devices could be opened. Selecting the NULL audio backend "6100 "with the consequence that no sound is audible"));6101 }6102 else6103 {6104 bool fWarn = false;6105 6106 PDMAUDIOBACKENDCFG backendCfg;6107 int rc2 = pCon->pfnGetConfig(pCon, &backendCfg);6108 if (RT_SUCCESS(rc2))6109 {6110 if (backendCfg.cSources)6111 {6112 #ifdef VBOX_WITH_HDA_MIC_IN6113 /* If the audio backend supports two or more input streams at once,6114 * warn if one of our two inputs (microphone-in and line-in) failed to initialize. */6115 if (backendCfg.cMaxStreamsIn >= 2)6116 fWarn = !fValidLineIn || !fValidMicIn;6117 /* If the audio backend only supports one input stream at once (e.g. pure ALSA, and6118 * *not* ALSA via PulseAudio plugin!), only warn if both of our inputs failed to initialize.6119 * One of the two simply is not in use then. */6120 else if (backendCfg.cMaxStreamsIn == 1)6121 fWarn = !fValidLineIn && !fValidMicIn;6122 /* Don't warn if our backend is not able of supporting any input streams at all. */6123 #else6124 /* We only have line-in as input source. */6125 fWarn = !fValidLineIn;6126 #endif6127 }6128 6129 if ( !fWarn6130 && backendCfg.cSinks)6131 {6132 fWarn = !fValidOut;6133 }6134 }6135 else6136 {6137 LogRel(("HDA: Unable to retrieve audio backend configuration for LUN #%RU8, rc=%Rrc\n", pDrv->uLUN, rc2));6138 fWarn = true;6139 }6140 6141 if (fWarn)6142 {6143 char szMissingStreams[255];6144 size_t len = 0;6145 if (!fValidLineIn)6146 {6147 LogRel(("HDA: WARNING: Unable to open PCM line input for LUN #%RU8!\n", pDrv->uLUN));6148 len = RTStrPrintf(szMissingStreams, sizeof(szMissingStreams), "PCM Input");6149 }6150 #ifdef VBOX_WITH_HDA_MIC_IN6151 if (!fValidMicIn)6152 {6153 LogRel(("HDA: WARNING: Unable to open PCM microphone input for LUN #%RU8!\n", pDrv->uLUN));6154 len += RTStrPrintf(szMissingStreams + len,6155 sizeof(szMissingStreams) - len, len ? ", PCM Microphone" : "PCM Microphone");6156 }6157 #endif6158 if (!fValidOut)6159 {6160 LogRel(("HDA: WARNING: Unable to open PCM output for LUN #%RU8!\n", pDrv->uLUN));6161 len += RTStrPrintf(szMissingStreams + len,6162 sizeof(szMissingStreams) - len, len ? ", PCM Output" : "PCM Output");6163 }6164 6165 PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "HostAudioNotResponding",6166 N_("Some HDA audio streams (%s) could not be opened. Guest applications generating audio "6167 "output or depending on audio input may hang. Make sure your host audio device "6168 "is working properly. Check the logfile for error messages of the audio "6169 "subsystem"), szMissingStreams);6170 }6171 }6172 }6173 6060 } 6174 6061 … … 6254 6141 } 6255 6142 6256 # ifndef VBOX_WITH_AUDIO_ CALLBACKS6143 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 6257 6144 if (RT_SUCCESS(rc)) 6258 6145 { … … 6309 6196 * Register statistics. 6310 6197 */ 6311 # ifndef VBOX_WITH_AUDIO_ CALLBACKS6198 # ifndef VBOX_WITH_AUDIO_HDA_CALLBACKS 6312 6199 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "/Devices/HDA/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling hdaTimer."); 6313 6200 # endif -
trunk/src/VBox/Devices/Audio/DevSB16.cpp
r63478 r63711 182 182 /** Number of active (running) SDn streams. */ 183 183 uint8_t cStreamsActive; 184 #ifndef VBOX_WITH_AUDIO_ CALLBACKS184 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 185 185 /** The timer for pumping data thru the attached LUN drivers. */ 186 186 PTMTIMERR3 pTimerIO; … … 205 205 static int sb16OpenOut(PSB16STATE pThis, PPDMAUDIOSTREAMCFG pCfg); 206 206 static void sb16CloseOut(PSB16STATE pThis); 207 #ifndef VBOX_WITH_AUDIO_ CALLBACKS207 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 208 208 static void sb16TimerMaybeStart(PSB16STATE pThis); 209 209 static void sb16TimerMaybeStop(PSB16STATE pThis); … … 448 448 if (hold) 449 449 { 450 #ifndef VBOX_WITH_AUDIO_ CALLBACKS450 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 451 451 pThis->cStreamsActive++; 452 452 sb16TimerMaybeStart(pThis); … … 454 454 PDMDevHlpDMASchedule(pThis->pDevInsR3); 455 455 } 456 #ifndef VBOX_WITH_AUDIO_ CALLBACKS456 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 457 457 else 458 458 { … … 1756 1756 } 1757 1757 1758 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1759 1758 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 1760 1759 static void sb16TimerMaybeStart(PSB16STATE pThis) 1761 1760 { … … 1891 1890 } 1892 1891 } 1893 1894 #endif /* !VBOX_WITH_AUDIO_CALLBACKS */ 1892 #endif /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */ 1895 1893 1896 1894 static void sb16Save(PSSMHANDLE pSSM, PSB16STATE pThis) … … 2334 2332 N_("SB16 configuration error: Failed to get the \"Version\" value")); 2335 2333 2336 #ifndef VBOX_WITH_AUDIO_ CALLBACKS2334 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 2337 2335 uint16_t uTimerHz; 2338 2336 rc = CFGMR3QueryU16Def(pCfg, "TimerHz", &uTimerHz, 25 /* Hz */); … … 2450 2448 } 2451 2449 2452 #ifndef VBOX_WITH_AUDIO_ CALLBACKS2450 #ifndef VBOX_WITH_AUDIO_SB16_CALLBACKS 2453 2451 if (RT_SUCCESS(rc)) 2454 2452 { … … 2466 2464 AssertMsgFailedReturn(("Error creating I/O timer, rc=%Rrc\n", rc), rc); 2467 2465 } 2468 #else 2466 #else /* !VBOX_WITH_AUDIO_SB16_CALLBACKS */ 2469 2467 if (RT_SUCCESS(rc)) 2470 2468 { … … 2498 2496 } 2499 2497 } 2500 #endif 2498 #endif /* VBOX_WITH_AUDIO_SB16_CALLBACKS */ 2501 2499 2502 2500 return VINF_SUCCESS; -
trunk/src/VBox/Devices/Audio/DrvAudio.cpp
r63684 r63711 44 44 static int drvAudioStreamControlInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd); 45 45 static int drvAudioStreamControlInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd); 46 static int drvAudioStreamDestroyInBackendInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream); 46 static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 47 static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream); 47 48 static int drvAudioStreamUninitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream); 48 49 static int drvAudioStreamInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgHost, PPDMAUDIOSTREAMCFG pCfgGuest); 49 50 static int drvAudioStreamIterateInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream); 50 51 static int drvAudioStreamReInitInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pStream); 52 static int drvAudioStreamLinkToInternal(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAM pPair); 51 53 52 54 #ifndef VBOX_AUDIO_TESTCASE … … 525 527 */ 526 528 527 /* Make the acquired host configuration the requested host configuration initially,528 * in case the backend does not report back an acquired configuration. */529 PDMAUDIOSTREAMCFG CfgHostAcq;530 memcpy(&CfgHostAcq, pCfgHost, sizeof(PDMAUDIOSTREAMCFG));531 532 529 #ifdef DEBUG 533 530 LogFunc(("[%s] Requested host format:\n", pStream->szName)); … … 541 538 #endif 542 539 543 int rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream,544 pCfgHost /* pCfgReq */, &CfgHostAcq /* pCfgAcq */);540 PDMAUDIOSTREAMCFG CfgHostAcq; 541 int rc = drvAudioStreamCreateInternalBackend(pThis,pHstStream, pCfgHost, &CfgHostAcq); 545 542 if (RT_FAILURE(rc)) 546 {547 LogRel2(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pStream->szName, rc));548 543 return rc; 549 }550 551 /* Only set the host's stream to initialized if we were able create the stream552 * in the host backend. This is necessary for trying to re-initialize the stream553 * at some later point in time. */554 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED;555 544 556 545 #ifdef DEBUG … … 599 588 AssertRC(rc2); 600 589 601 #ifdef VBOX_WITH_STATISTICS602 char szStatName[255];603 #endif604 605 590 if (pCfgGuest->enmDir == PDMAUDIODIR_IN) 606 591 { … … 608 593 rc2 = AudioMixBufLinkTo(&pHstStream->MixBuf, &pGstStream->MixBuf); 609 594 AssertRC(rc2); 610 611 #ifdef VBOX_WITH_STATISTICS612 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);613 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed,614 szStatName, STAMUNIT_BYTES, "Elapsed bytes read.");615 616 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);617 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead,618 szStatName, STAMUNIT_BYTES, "Total bytes read.");619 620 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesCaptured", pHstStream->szName);621 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->In.StatSamplesCaptured,622 szStatName, STAMUNIT_COUNT, "Total samples captured.");623 #endif624 595 } 625 596 else … … 628 599 rc2 = AudioMixBufLinkTo(&pGstStream->MixBuf, &pHstStream->MixBuf); 629 600 AssertRC(rc2); 630 631 #ifdef VBOX_WITH_STATISTICS632 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStream->szName);633 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed,634 szStatName, STAMUNIT_BYTES, "Elapsed bytes written.");635 636 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStream->szName);637 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten,638 szStatName, STAMUNIT_BYTES, "Total bytes written.");639 640 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesPlayed", pHstStream->szName);641 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStream->Out.StatSamplesPlayed,642 szStatName, STAMUNIT_COUNT, "Total samples played.");643 #endif644 601 } 645 602 … … 655 612 656 613 /** 614 * Schedules a re-initialization of all current audio streams. 615 * The actual re-initialization will happen at some later point in time. 616 * 617 * @returns IPRT status code. 618 * @param pThis Pointer to driver instance. 619 */ 620 static int drvAudioScheduleReInit(PDRVAUDIO pThis) 621 { 622 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 623 624 LogFunc(("\n")); 625 626 PPDMAUDIOSTREAM pHstStream; 627 RTListForEach(&pThis->lstHstStreams, pHstStream, PDMAUDIOSTREAM, Node) 628 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT; 629 630 return VINF_SUCCESS; 631 } 632 633 /** 657 634 * Re-initializes an audio stream with its existing host and guest stream configuration. 635 * This might be the case if the backend told us we need to re-initialize because something 636 * on the host side has changed. 658 637 * 659 638 * @returns IPRT status code. … … 672 651 AssertPtr(pGstStream); 673 652 674 int rc; 675 676 if (/* Stream initialized? */ 677 (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED) 678 /* Not in pending re-init before? */ 679 && !(pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT)) 680 { 681 /* Disable first. */ 682 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_DISABLE); 683 if (RT_FAILURE(rc)) 684 { 685 LogFunc(("[%s] Error disabling stream, rc=%Rrc\n", pStream->szName, rc)); 686 return rc; 687 } 688 689 /* Give the backend the chance to clean up the old context. */ 690 rc = pThis->pHostDrvAudio->pfnStreamDestroy(pThis->pHostDrvAudio, pHstStream); 691 if (RT_FAILURE(rc)) 692 { 693 LogFunc(("[%s] Error destroying stream in backend, rc=%Rrc\n", pStream->szName, rc)); 694 return rc; 695 } 696 697 /* Set the pending re-init bit. */ 698 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT; 699 } 700 701 LogFlowFunc(("[%s] Host status is 0x%x\n", pStream->szName, pHstStream->fStatus)); 702 703 /* Try to re-initialize the stream. */ 704 rc = drvAudioStreamInitInternal(pThis, pStream, &pHstStream->Cfg, &pGstStream->Cfg); 653 /* 654 * Gather current stream status. 655 */ 656 bool fIsEnabled = pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED; /* Stream is enabled? */ 657 658 /* 659 * Destroy and re-create stream on backend side. 660 */ 661 int rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_DISABLE); 705 662 if (RT_SUCCESS(rc)) 706 663 { 707 /* Try to restore the previous stream status, if possible. */ 708 PDMAUDIOSTREAMCMD enmCmdRestore = PDMAUDIOSTREAMCMD_UNKNOWN; 709 710 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_ENABLED) /* Stream was running before? */ 711 { 712 LogFunc(("[%s] Re-enabling host stream ...\n", pStream->szName)); 713 enmCmdRestore = PDMAUDIOSTREAMCMD_ENABLE; 714 } 715 716 if (enmCmdRestore != PDMAUDIOSTREAMCMD_UNKNOWN) 717 rc = pThis->pHostDrvAudio->pfnStreamControl(pThis->pHostDrvAudio, pHstStream, PDMAUDIOSTREAMCMD_ENABLE); 718 719 /* Re-initialization successful, remove bit again. */ 720 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT; 721 } 722 723 LogFunc(("[%s] Reinitialization returned %Rrc\n", pStream->szName, rc)); 664 rc = drvAudioStreamDestroyInternalBackend(pThis, pHstStream); 665 if (RT_SUCCESS(rc)) 666 { 667 rc = drvAudioStreamCreateInternalBackend(pThis, pHstStream, &pHstStream->Cfg, NULL /* pCfgAcq */); 668 /** @todo Validate (re-)acquired configuration with pHstStream->Cfg? */ 669 } 670 } 671 672 /* 673 * Restore previous stream state. 674 */ 675 if (RT_SUCCESS(rc)) 676 { 677 if (fIsEnabled) 678 rc = drvAudioStreamControlInternalBackend(pThis, pHstStream, PDMAUDIOSTREAMCMD_ENABLE); 679 } 680 681 if (RT_FAILURE(rc)) 682 LogRel2(("Audio: Re-initializing stream '%s' failed with %Rrc\n", pStream->szName, rc)); 683 684 LogFunc(("[%s] Returning %Rrc\n", pStream->szName, rc)); 724 685 return rc; 725 686 } … … 985 946 986 947 /** 948 * Links an audio stream to another audio stream (pair). 949 * 950 * @returns IPRT status code. 951 * @param pStream Stream to handle linking for. 952 * @param pPair Stream to link pStream to. Specify NULL to unlink pStream from a former linked stream. 953 */ 954 static int drvAudioStreamLinkToInternal(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAM pPair) 955 { 956 if (pPair) /* Link. */ 957 { 958 pStream->pPair = pPair; 959 pPair->pPair = pStream; 960 961 LogRel2(("Linked audio stream '%s' to '%s'\n", pStream->szName, pPair->szName)); 962 } 963 else /* Unlink. */ 964 { 965 if (pStream->pPair) 966 { 967 LogRel2(("Unlinked pair '%s' from stream '%s'\n", pStream->pPair->szName, pStream->szName)); 968 969 AssertMsg(pStream->pPair->pPair == pStream, 970 ("Pair '%s' is not linked to '%s' (linked to '%s')\n", 971 pStream->pPair->szName, pStream->szName, pStream->pPair->pPair ? pStream->pPair->pPair->szName : "<NONE>")); 972 973 pStream->pPair->pPair = NULL; /* Also make sure to unlink the pair from pStream */ 974 pStream->pPair = NULL; 975 } 976 } 977 978 return VINF_SUCCESS; 979 } 980 981 /** 987 982 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnStreamPlay} 988 983 */ … … 1012 1007 rc = VERR_NOT_AVAILABLE; 1013 1008 break; 1014 }1015 1016 /* Backend output (temporarily) disabled / unavailable? */1017 if ( pThis->pHostDrvAudio->pfnGetStatus1018 && pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_OUT) != PDMAUDIOBACKENDSTS_RUNNING)1019 {1020 /* Pull the new config from the backend and check again. */1021 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);1022 AssertRC(rc);1023 1024 if ( !pThis->BackendCfg.cSinks1025 || !pThis->BackendCfg.cMaxStreamsOut)1026 {1027 rc = VERR_NOT_AVAILABLE;1028 break;1029 }1030 1009 } 1031 1010 … … 1044 1023 rc = VERR_NOT_AVAILABLE); 1045 1024 1025 /* Is the stream scheduled for re-initialization? Do so now. */ 1026 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT) 1027 { 1028 rc = drvAudioStreamReInitInternal(pThis, pStream); 1029 if (RT_FAILURE(rc)) 1030 break; 1031 1032 /* On success, remove pending re-init flag. */ 1033 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT; 1034 } 1035 1046 1036 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus); 1047 1037 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream); 1048 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))1049 {1050 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));1051 rc = drvAudioStreamReInitInternal(pThis, pStream);1052 if (RT_FAILURE(rc))1053 {1054 LogFunc(("[%s] Failed to re-initialize backend, rc=%Rrc\n", pHstStream->szName, rc));1055 break;1056 }1057 }1058 1038 1059 1039 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf); … … 1139 1119 do 1140 1120 { 1141 /* Backend input (temporarily) disabled / unavailable? */1142 if (pThis->pHostDrvAudio->pfnGetStatus(pThis->pHostDrvAudio, PDMAUDIODIR_IN) != PDMAUDIOBACKENDSTS_RUNNING)1143 {1144 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg);1145 AssertRC(rc);1146 1147 if ( !pThis->BackendCfg.cSources1148 || !pThis->BackendCfg.cMaxStreamsIn)1149 {1150 rc = VERR_NOT_AVAILABLE;1151 break;1152 }1153 }1154 1155 1121 PPDMAUDIOSTREAM pHstStream = drvAudioGetHostStream(pStream); 1156 1122 AssertPtr(pHstStream); … … 1167 1133 rc = VERR_NOT_AVAILABLE); 1168 1134 1135 /* Is the stream scheduled for re-initialization? Do so now. */ 1136 if (pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT) 1137 { 1138 rc = drvAudioStreamReInitInternal(pThis, pStream); 1139 if (RT_FAILURE(rc)) 1140 break; 1141 1142 /* On success, remove pending re-init flag. */ 1143 pHstStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_PENDING_REINIT; 1144 } 1145 1146 AssertPtr(pThis->pHostDrvAudio->pfnStreamGetStatus); 1169 1147 PDMAUDIOSTRMSTS strmSts = pThis->pHostDrvAudio->pfnStreamGetStatus(pThis->pHostDrvAudio, pHstStream); 1170 if (!(strmSts & PDMAUDIOSTRMSTS_FLAG_INITIALIZED))1171 {1172 LogFunc(("[%s] Backend not initialized (anymore), re-initializing ...\n", pHstStream->szName));1173 rc = drvAudioStreamReInitInternal(pThis, pStream);1174 break;1175 }1176 1148 1177 1149 uint32_t cSamplesLive = AudioMixBufLive(&pGstStream->MixBuf); … … 1217 1189 } 1218 1190 1219 #ifdef VBOX_WITH_AUDIO_ CALLBACKS1191 #ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS 1220 1192 /** 1221 1193 * Duplicates an audio callback. … … 1226 1198 static PPDMAUDIOCALLBACK drvAudioCallbackDuplicate(PPDMAUDIOCALLBACK pCB) 1227 1199 { 1228 AssertPtrReturn(pCB, VERR_INVALID_POINTER);1200 AssertPtrReturn(pCB, NULL); 1229 1201 1230 1202 PPDMAUDIOCALLBACK pCBCopy = (PPDMAUDIOCALLBACK)RTMemDup((void *)pCB, sizeof(PDMAUDIOCALLBACK)); … … 1293 1265 switch (pCB->enmType) 1294 1266 { 1295 case PDMAUDIOC ALLBACKTYPE_INPUT:1267 case PDMAUDIOCBTYPE_DATA_INPUT: 1296 1268 RTListAppend(&pThis->lstCBIn, &pCB->Node); 1297 1269 break; 1298 1270 1299 case PDMAUDIOC ALLBACKTYPE_OUTPUT:1271 case PDMAUDIOCBTYPE_DATA_OUTPUT: 1300 1272 RTListAppend(&pThis->lstCBOut, &pCB->Node); 1301 1273 break; … … 1319 1291 * @interface_method_impl{PDMIAUDIOCONNECTOR,pfnCallback} 1320 1292 */ 1321 static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOC ALLBACKTYPE enmType,1293 static DECLCALLBACK(int) drvAudioCallback(PPDMIAUDIOCONNECTOR pInterface, PDMAUDIOCBTYPE enmType, 1322 1294 void *pvUser, size_t cbUser) 1323 1295 { … … 1331 1303 switch (enmType) 1332 1304 { 1333 case PDMAUDIOC ALLBACKTYPE_INPUT:1305 case PDMAUDIOCBTYPE_DATA_INPUT: 1334 1306 pListAnchor = &pThis->lstCBIn; 1335 1307 break; 1336 1308 1337 case PDMAUDIOC ALLBACKTYPE_OUTPUT:1309 case PDMAUDIOCBTYPE_DATA_OUTPUT: 1338 1310 pListAnchor = &pThis->lstCBOut; 1339 1311 break; … … 1350 1322 { 1351 1323 Assert(pCB->enmType == enmType); 1352 pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser); 1353 } 1354 } 1355 1356 return VINF_SUCCESS; 1357 } 1358 #endif 1324 int rc2 = pCB->pfnCallback(enmType, pCB->pvCtx, pCB->cbCtx, pvUser, cbUser); 1325 if (RT_FAILURE(rc2)) 1326 LogFunc(("Failed with %Rrc\n", rc2)); 1327 } 1328 1329 return VINF_SUCCESS; 1330 } 1331 1332 return VERR_NOT_SUPPORTED; 1333 } 1334 #endif /* VBOX_WITH_AUDIO_DEVICE_CALLBACKS */ 1335 1336 #ifdef VBOX_WITH_AUDIO_CALLBACKS 1337 /** 1338 * Backend callback implementation. 1339 * 1340 * Important: No calls back to the backend within this function, as the backend 1341 * might hold any locks / critical sections while executing this callback. 1342 * Will result in some ugly deadlocks then. 1343 * 1344 * @copydoc FNPDMHOSTAUDIOCALLBACK 1345 */ 1346 static DECLCALLBACK(int) drvAudioBackendCallback(PPDMDRVINS pDrvIns, 1347 PDMAUDIOCBTYPE enmType, void *pvUser, size_t cbUser) 1348 { 1349 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER); 1350 RT_NOREF(pvUser, cbUser); 1351 /* pvUser and cbUser are optional. */ 1352 1353 /* Get the upper driver (PDMIAUDIOCONNECTOR). */ 1354 AssertPtr(pDrvIns->pUpBase); 1355 PPDMIAUDIOCONNECTOR pInterface = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR); 1356 AssertPtr(pInterface); 1357 PDRVAUDIO pThis = PDMIAUDIOCONNECTOR_2_DRVAUDIO(pInterface); 1358 1359 int rc = RTCritSectEnter(&pThis->CritSect); 1360 AssertRCReturn(rc, rc); 1361 1362 LogFunc(("pThis=%p, enmType=%RU32, pvUser=%p, cbUser=%zu\n", pThis, enmType, pvUser, cbUser)); 1363 1364 switch (enmType) 1365 { 1366 case PDMAUDIOCBTYPE_DEVICES_CHANGED: 1367 LogRel(("Audio: Host audio device configuration has changed\n")); 1368 rc = drvAudioScheduleReInit(pThis); 1369 break; 1370 1371 default: 1372 AssertMsgFailed(("Not supported\n")); 1373 break; 1374 } 1375 1376 int rc2 = RTCritSectLeave(&pThis->CritSect); 1377 if (RT_SUCCESS(rc)) 1378 rc = rc2; 1379 1380 LogFlowFunc(("Returning %Rrc\n", rc)); 1381 return rc; 1382 } 1383 #endif /* VBOX_WITH_AUDIO_CALLBACKS */ 1359 1384 1360 1385 /** … … 1387 1412 } 1388 1413 1389 /* Get the configuration data from backend. */ 1414 /* 1415 * Get the backend configuration. 1416 */ 1390 1417 rc = pThis->pHostDrvAudio->pfnGetConfig(pThis->pHostDrvAudio, &pThis->BackendCfg); 1391 1418 if (RT_FAILURE(rc)) … … 1395 1422 } 1396 1423 1397 pThis->cStreamsFreeIn = 0; 1398 pThis->cStreamsFreeOut = 0; 1399 1400 if (pThis->BackendCfg.cSinks) 1401 { 1402 Assert(pThis->BackendCfg.cbStreamOut); 1403 1404 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut; 1405 } 1406 1407 if (pThis->BackendCfg.cSources) 1408 { 1409 Assert(pThis->BackendCfg.cbStreamIn); 1410 1411 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn; 1412 } 1424 pThis->cStreamsFreeIn = pThis->BackendCfg.cMaxStreamsIn; 1425 pThis->cStreamsFreeOut = pThis->BackendCfg.cMaxStreamsOut; 1413 1426 1414 1427 LogFlowFunc(("cStreamsFreeIn=%RU8, cStreamsFreeOut=%RU8\n", pThis->cStreamsFreeIn, pThis->cStreamsFreeOut)); … … 1418 1431 RT_MIN(64, pThis->cStreamsFreeIn), RT_MIN(64, pThis->cStreamsFreeOut))); 1419 1432 1433 /* 1434 * If the backend supports it, do a device enumeration. 1435 */ 1436 bool fLog = true; 1437 1438 if (pThis->pHostDrvAudio->pfnGetDevices) 1439 { 1440 PDMAUDIODEVICEENUM DevEnum; 1441 int rc2 = pThis->pHostDrvAudio->pfnGetDevices(pThis->pHostDrvAudio, &DevEnum); 1442 if (RT_SUCCESS(rc2)) 1443 { 1444 if (fLog) 1445 LogRel(("Audio: Found %RU16 devices\n", DevEnum.cDevices)); 1446 1447 PPDMAUDIODEVICE pDev; 1448 RTListForEach(&DevEnum.lstDevices, pDev, PDMAUDIODEVICE, Node) 1449 { 1450 if (fLog) 1451 { 1452 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags); 1453 1454 LogRel(("Audio: Device '%s':\n", pDev->szName)); 1455 LogRel(("Audio: \tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage))); 1456 LogRel(("Audio: \tFlags = %s\n", pszFlags ? pszFlags : "<NONE>")); 1457 LogRel(("Audio: \tInput channels = %RU8\n", pDev->cMaxInputChannels)); 1458 LogRel(("Audio: \tOutput channels = %RU8\n", pDev->cMaxOutputChannels)); 1459 1460 if (pszFlags) 1461 RTStrFree(pszFlags); 1462 } 1463 } 1464 1465 DrvAudioHlpDeviceEnumFree(&DevEnum); 1466 } 1467 else 1468 { 1469 LogRel2(("Audio: Device enumeration failed with %Rrc\n", rc2)); 1470 /* Not fatal. */ 1471 } 1472 } 1473 1474 #ifdef VBOX_WITH_AUDIO_CALLBACKS 1475 /* 1476 * If the backend supports it, offer a callback to this connector. 1477 */ 1478 if (pThis->pHostDrvAudio->pfnSetCallback) 1479 { 1480 int rc2 = pThis->pHostDrvAudio->pfnSetCallback(pThis->pHostDrvAudio, drvAudioBackendCallback); 1481 if (RT_FAILURE(rc2)) 1482 LogRel(("Audio: Error registering backend callback, rc=%Rrc\n", rc2)); 1483 /* Not fatal. */ 1484 } 1485 #endif 1486 1420 1487 LogFlowFuncLeave(); 1421 1488 return VINF_SUCCESS; … … 1461 1528 1462 1529 int rc = RTCritSectInit(&pThis->CritSect); 1530 AssertRCReturn(rc, rc); 1463 1531 1464 1532 /** @todo Add audio driver options. */ … … 1467 1535 * If everything went well, initialize the lower driver. 1468 1536 */ 1469 if (RT_SUCCESS(rc)) 1470 rc = drvAudioHostInit(pThis, pCfgHandle); 1537 rc = drvAudioHostInit(pThis, pCfgHandle); 1471 1538 1472 1539 LogFlowFuncLeaveRC(rc); … … 1670 1737 strlen(pCfgHost->szName) ? pCfgHost->szName : "<Untitled>"); 1671 1738 1672 pHstStrm->pPair = pGstStrm; 1739 rc = drvAudioStreamLinkToInternal(pHstStrm, pGstStrm); 1740 AssertRCBreak(rc); 1673 1741 1674 1742 /* … … 1679 1747 1680 1748 pGstStrm->fStatus = pHstStrm->fStatus; /* Reflect the host stream's status. */ 1681 pGstStrm->pPair = pHstStrm; 1749 1750 rc = drvAudioStreamLinkToInternal(pGstStrm, pHstStrm); 1751 AssertRCBreak(rc); 1682 1752 1683 1753 /* … … 1691 1761 } 1692 1762 1763 #ifdef VBOX_WITH_STATISTICS 1764 char szStatName[255]; 1765 1766 if (pCfgGuest->enmDir == PDMAUDIODIR_IN) 1767 { 1768 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName); 1769 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesElapsed, 1770 szStatName, STAMUNIT_BYTES, "Elapsed bytes read."); 1771 1772 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStrm->szName); 1773 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->In.StatBytesTotalRead, 1774 szStatName, STAMUNIT_BYTES, "Total bytes read."); 1775 1776 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesCaptured", pHstStrm->szName); 1777 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->In.StatSamplesCaptured, 1778 szStatName, STAMUNIT_COUNT, "Total samples captured."); 1779 } 1780 else if (pCfgGuest->enmDir == PDMAUDIODIR_OUT) 1781 { 1782 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesElapsed", pGstStrm->szName); 1783 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesElapsed, 1784 szStatName, STAMUNIT_BYTES, "Elapsed bytes written."); 1785 1786 RTStrPrintf(szStatName, sizeof(szStatName), "Guest/%s/BytesRead", pGstStrm->szName); 1787 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pGstStrm->Out.StatBytesTotalWritten, 1788 szStatName, STAMUNIT_BYTES, "Total bytes written."); 1789 1790 RTStrPrintf(szStatName, sizeof(szStatName), "Host/%s/SamplesPlayed", pHstStrm->szName); 1791 PDMDrvHlpSTAMRegCounterEx(pThis->pDrvIns, &pHstStrm->Out.StatSamplesPlayed, 1792 szStatName, STAMUNIT_COUNT, "Total samples played."); 1793 } 1794 #endif 1795 1693 1796 } while (0); 1694 1797 … … 1982 2085 if (RT_SUCCESS(rc)) 1983 2086 { 2087 #ifdef VBOX_WITH_STATISTICS 2088 if (pHstStream->enmDir == PDMAUDIODIR_IN) 2089 { 2090 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->In.StatSamplesCaptured); 2091 } 2092 else if (pHstStream->enmDir == PDMAUDIODIR_OUT) 2093 { 2094 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pHstStream->Out.StatSamplesPlayed); 2095 } 2096 #endif 1984 2097 RTListNodeRemove(&pHstStream->Node); 1985 2098 … … 1997 2110 if (RT_SUCCESS(rc)) 1998 2111 { 2112 #ifdef VBOX_WITH_STATISTICS 2113 if (pGstStream->enmDir == PDMAUDIODIR_IN) 2114 { 2115 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesElapsed); 2116 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->In.StatBytesTotalRead); 2117 } 2118 else if (pGstStream->enmDir == PDMAUDIODIR_OUT) 2119 { 2120 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesElapsed); 2121 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pGstStream->Out.StatBytesTotalWritten); 2122 } 2123 #endif 1999 2124 RTListNodeRemove(&pGstStream->Node); 2000 2125 … … 2028 2153 2029 2154 /** 2155 * Creates an audio stream on the backend side. 2156 * 2157 * @returns IPRT status code. 2158 * @param pThis Pointer to driver instance. 2159 * @param pHstStream (Host) audio stream to use for creating the stream on the backend side. 2160 * @param pCfgReq Requested audio stream configuration to use for stream creation. 2161 * @param pCfgAcq Acquired audio stream configuration returned by the backend. Optional, can be NULL. 2162 */ 2163 static int drvAudioStreamCreateInternalBackend(PDRVAUDIO pThis, 2164 PPDMAUDIOSTREAM pHstStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2165 { 2166 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 2167 AssertPtrReturn(pHstStream, VERR_INVALID_POINTER); 2168 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 2169 /* pCfgAcq is optional. */ 2170 2171 AssertMsg(pHstStream->enmCtx == PDMAUDIOSTREAMCTX_HOST, 2172 ("Stream '%s' is not a host stream and therefore has no backend\n", pHstStream->szName)); 2173 2174 AssertMsg((pHstStream->fStatus & PDMAUDIOSTRMSTS_FLAG_INITIALIZED) == 0, 2175 ("Stream '%s' already initialized in backend\n", pHstStream->szName)); 2176 2177 PDMAUDIOSTREAMCFG CfgAcq; 2178 2179 /* Make the acquired host configuration the requested host configuration initially, 2180 * in case the backend does not report back an acquired configuration. */ 2181 memcpy(&CfgAcq, pCfgReq, sizeof(PDMAUDIOSTREAMCFG)); 2182 2183 int rc = pThis->pHostDrvAudio->pfnStreamCreate(pThis->pHostDrvAudio, pHstStream, pCfgReq, &CfgAcq); 2184 if (RT_FAILURE(rc)) 2185 { 2186 LogRel2(("Audio: Creating stream '%s' in backend failed with %Rrc\n", pHstStream->szName, rc)); 2187 return rc; 2188 } 2189 2190 /* Validate acquired configuration. */ 2191 if (!DrvAudioHlpStreamCfgIsValid(&CfgAcq)) 2192 { 2193 LogRel2(("Audio: Creating stream '%s' has an invalid configuration, skipping\n", pHstStream->szName)); 2194 return VERR_INVALID_PARAMETER; 2195 } 2196 2197 /* Only set the host's stream to initialized if we were able create the stream 2198 * in the host backend. This is necessary for trying to re-initialize the stream 2199 * at some later point in time. */ 2200 pHstStream->fStatus |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED; 2201 2202 if (pCfgAcq) 2203 memcpy(pCfgAcq, &CfgAcq, sizeof(PDMAUDIOSTREAMCFG)); 2204 2205 return VINF_SUCCESS; 2206 } 2207 2208 /** 2030 2209 * Calls the backend to give it the chance to destroy its part of the audio stream. 2031 2210 * … … 2034 2213 * @param pHstStream Host audio stream to call the backend destruction for. 2035 2214 */ 2036 static int drvAudioStreamDestroyIn BackendInternal(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream)2215 static int drvAudioStreamDestroyInternalBackend(PDRVAUDIO pThis, PPDMAUDIOSTREAM pHstStream) 2037 2216 { 2038 2217 AssertPtrReturn(pThis, VERR_INVALID_POINTER); … … 2087 2266 pStream->fStatus &= ~PDMAUDIOSTRMSTS_FLAG_INITIALIZED; 2088 2267 } 2089 2090 if (pStream->enmDir == PDMAUDIODIR_IN)2091 {2092 #ifdef VBOX_WITH_STATISTICS2093 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesElapsed);2094 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatBytesTotalRead);2095 #endif2096 }2097 else if (pStream->enmDir == PDMAUDIODIR_OUT)2098 {2099 #ifdef VBOX_WITH_STATISTICS2100 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesElapsed);2101 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatBytesTotalWritten);2102 #endif2103 }2104 2268 } 2105 2269 else if (pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST) 2106 2270 { 2107 rc = drvAudioStreamDestroyInBackendInternal(pThis, pStream); 2108 2109 if (pStream->enmDir == PDMAUDIODIR_IN) 2110 { 2111 #ifdef VBOX_WITH_STATISTICS 2112 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->In.StatSamplesCaptured); 2113 #endif 2114 } 2115 else if (pStream->enmDir == PDMAUDIODIR_OUT) 2116 { 2117 #ifdef VBOX_WITH_STATISTICS 2118 PDMDrvHlpSTAMDeregister(pThis->pDrvIns, &pStream->Out.StatSamplesPlayed); 2119 #endif 2120 } 2271 rc = drvAudioStreamDestroyInternalBackend(pThis, pStream); 2121 2272 } 2122 2273 else … … 2126 2277 { 2127 2278 /* Make sure that the pair (if any) knows that we're not valid anymore. */ 2128 i f (pStream->pPair)2129 pStream->pPair->pPair = NULL;2279 int rc2 = drvAudioStreamLinkToInternal(pStream, NULL); 2280 AssertRC(rc2); 2130 2281 2131 2282 /* Reset status. */ 2132 2283 pStream->fStatus = PDMAUDIOSTRMSTS_FLAG_NONE; 2133 2134 /* Clear name. */2135 pStream->szName[0] = '\0';2136 2284 2137 2285 /* Destroy mixing buffer. */ … … 2177 2325 PPDMAUDIOSTREAM pStream; 2178 2326 RTListForEach(&pThis->lstHstStreams, pStream, PDMAUDIOSTREAM, Node) 2179 drvAudioStreamDestroyIn BackendInternal(pThis, pStream);2327 drvAudioStreamDestroyInternalBackend(pThis, pStream); 2180 2328 2181 2329 /* … … 2205 2353 RTListInit(&pThis->lstHstStreams); 2206 2354 RTListInit(&pThis->lstGstStreams); 2207 #ifdef VBOX_WITH_AUDIO_ CALLBACKS2355 #ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS 2208 2356 RTListInit(&pThis->lstCBIn); 2209 2357 RTListInit(&pThis->lstCBOut); … … 2233 2381 pThis->IAudioConnector.pfnStreamPlay = drvAudioStreamPlay; 2234 2382 pThis->IAudioConnector.pfnStreamCapture = drvAudioStreamCapture; 2235 #ifdef VBOX_WITH_AUDIO_ CALLBACKS2383 #ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS 2236 2384 pThis->IAudioConnector.pfnRegisterCallbacks = drvAudioRegisterCallbacks; 2237 2385 pThis->IAudioConnector.pfnCallback = drvAudioCallback; … … 2341 2489 Assert(RTListIsEmpty(&pThis->lstGstStreams)); 2342 2490 2343 #ifdef VBOX_WITH_AUDIO_ CALLBACKS2491 #ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS 2344 2492 /* 2345 * Destroy callbacks, if any.2493 * Destroy device callbacks, if any. 2346 2494 */ 2347 2495 PPDMAUDIOCALLBACK pCB, pCBNext; -
trunk/src/VBox/Devices/Audio/DrvAudio.h
r63362 r63711 120 120 /** Audio configuration settings retrieved from the backend. */ 121 121 PDMAUDIOBACKENDCFG BackendCfg; 122 #ifdef VBOX_WITH_AUDIO_ CALLBACKS122 #ifdef VBOX_WITH_AUDIO_DEVICE_CALLBACKS 123 123 /** @todo Use a map with primary key set to the callback type? */ 124 124 RTLISTANCHOR lstCBIn; … … 154 154 int DrvAudioHlpGetFileName(char *pszFile, size_t cchFile, const char *pszPath, const char *pszName, PDMAUDIOFILETYPE enmType); 155 155 156 PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData); 157 void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev); 158 159 int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm); 160 void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm); 161 int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev); 162 PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmDir); 163 void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, PPDMAUDIODEVICEENUM pDevEnm); 164 165 const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir); 166 char *DrvAudioHlpAudDevFlagsToStrA(PDMAUDIODEVFLAG fFlags); 167 156 168 int DrvAudioHlpWAVFileOpen(PPDMAUDIOFILE pFile, const char *pszFile, uint32_t fOpen, PPDMAUDIOPCMPROPS pProps, PDMAUDIOFILEFLAGS fFlags); 157 169 int DrvAudioHlpWAVFileClose(PPDMAUDIOFILE pFile); -
trunk/src/VBox/Devices/Audio/DrvAudioCommon.cpp
r63362 r63711 50 50 #include <iprt/uuid.h> 51 51 52 #define LOG_GROUP LOG_GROUP_DRV_AUDIO 53 #include <VBox/log.h> 54 55 #include <VBox/err.h> 52 56 #include <VBox/vmm/pdmdev.h> 53 57 #include <VBox/vmm/pdm.h> 54 #include <VBox/err.h>55 58 #include <VBox/vmm/mm.h> 56 59 … … 200 203 } 201 204 } 205 } 206 207 /** 208 * Allocates an audio device. 209 * 210 * @returns Newly allocated audio device, or NULL if failed. 211 * @param cbData How much additional data (in bytes) should be allocated to provide 212 * a (backend) specific area to store additional data. 213 * Optional, can be 0. 214 */ 215 PPDMAUDIODEVICE DrvAudioHlpDeviceAlloc(size_t cbData) 216 { 217 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)RTMemAllocZ(sizeof(PDMAUDIODEVICE)); 218 if (!pDev) 219 return NULL; 220 221 if (cbData) 222 { 223 pDev->pvData = RTMemAlloc(cbData); 224 if (!pDev->pvData) 225 { 226 RTMemFree(pDev); 227 return NULL; 228 } 229 } 230 231 pDev->cbData = cbData; 232 233 pDev->cMaxInputChannels = 0; 234 pDev->cMaxOutputChannels = 0; 235 236 return pDev; 237 } 238 239 /** 240 * Frees an audio device. 241 * 242 * @param pDev Device to free. 243 */ 244 void DrvAudioHlpDeviceFree(PPDMAUDIODEVICE pDev) 245 { 246 if (!pDev) 247 return; 248 249 Assert(pDev->cRefCount == 0); 250 251 if (pDev->pvData) 252 { 253 Assert(pDev->cbData); 254 255 RTMemFree(pDev->pvData); 256 } 257 258 RTMemFree(pDev); 259 } 260 261 /** 262 * Initializes an audio device enumeration structure. 263 * 264 * @returns IPRT status code. 265 * @param pDevEnm Device enumeration to initialize. 266 */ 267 int DrvAudioHlpDeviceEnumInit(PPDMAUDIODEVICEENUM pDevEnm) 268 { 269 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER); 270 271 RTListInit(&pDevEnm->lstDevices); 272 pDevEnm->cDevices = 0; 273 274 return VINF_SUCCESS; 275 } 276 277 /** 278 * Frees audio device enumeration data. 279 * 280 * @param pDevEnm Device enumeration to destroy. 281 */ 282 void DrvAudioHlpDeviceEnumFree(PPDMAUDIODEVICEENUM pDevEnm) 283 { 284 if (!pDevEnm) 285 return; 286 287 PPDMAUDIODEVICE pDev, pDevNext; 288 RTListForEachSafe(&pDevEnm->lstDevices, pDev, pDevNext, PDMAUDIODEVICE, Node) 289 { 290 RTListNodeRemove(&pDev->Node); 291 292 DrvAudioHlpDeviceFree(pDev); 293 294 pDevEnm->cDevices--; 295 } 296 297 /* Sanity. */ 298 Assert(RTListIsEmpty(&pDevEnm->lstDevices)); 299 Assert(pDevEnm->cDevices == 0); 300 } 301 302 /** 303 * Adds an audio device to a device enumeration. 304 * 305 * @return IPRT status code. 306 * @param pDevEnm Device enumeration to add device to. 307 * @param pDev Device to add. 308 */ 309 int DrvAudioHlpDeviceEnumAdd(PPDMAUDIODEVICEENUM pDevEnm, PPDMAUDIODEVICE pDev) 310 { 311 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER); 312 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 313 314 RTListAppend(&pDevEnm->lstDevices, &pDev->Node); 315 pDevEnm->cDevices++; 316 317 return VINF_SUCCESS; 318 } 319 320 /** 321 * Returns the default device of a given device enumeration. 322 * This assumes that only one default device per usage is set. 323 * 324 * @returns Default device if found, or NULL if none found. 325 * @param pDevEnm Device enumeration to get default device for. 326 * @param enmUsage Usage to get default device for. 327 */ 328 PPDMAUDIODEVICE DrvAudioHlpDeviceEnumGetDefaultDevice(PPDMAUDIODEVICEENUM pDevEnm, PDMAUDIODIR enmUsage) 329 { 330 AssertPtrReturn(pDevEnm, NULL); 331 332 PPDMAUDIODEVICE pDev; 333 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node) 334 { 335 if (enmUsage != PDMAUDIODIR_ANY) 336 { 337 if (enmUsage != pDev->enmUsage) /* Wrong usage? Skip. */ 338 continue; 339 } 340 341 if (pDev->fFlags & PDMAUDIODEV_FLAGS_DEFAULT) 342 return pDev; 343 } 344 345 return NULL; 346 } 347 348 /** 349 * Logs an audio device enumeration. 350 * 351 * @param pszDesc Logging description. 352 * @param pDevEnm Device enumeration to log. 353 */ 354 void DrvAudioHlpDeviceEnumPrint(const char *pszDesc, PPDMAUDIODEVICEENUM pDevEnm) 355 { 356 AssertPtrReturnVoid(pszDesc); 357 AssertPtrReturnVoid(pDevEnm); 358 359 LogFunc(("%s: %RU16 devices\n", pszDesc, pDevEnm->cDevices)); 360 361 PPDMAUDIODEVICE pDev; 362 RTListForEach(&pDevEnm->lstDevices, pDev, PDMAUDIODEVICE, Node) 363 { 364 char *pszFlags = DrvAudioHlpAudDevFlagsToStrA(pDev->fFlags); 365 366 LogFunc(("Device '%s':\n", pDev->szName)); 367 LogFunc(("\tUsage = %s\n", DrvAudioHlpAudDirToStr(pDev->enmUsage))); 368 LogFunc(("\tFlags = %s\n", pszFlags ? pszFlags : "<NONE>")); 369 LogFunc(("\tInput channels = %RU8\n", pDev->cMaxInputChannels)); 370 LogFunc(("\tOutput channels = %RU8\n", pDev->cMaxOutputChannels)); 371 LogFunc(("\tData = %p (%zu bytes)\n", pDev->pvData, pDev->cbData)); 372 373 if (pszFlags) 374 RTStrFree(pszFlags); 375 } 376 } 377 378 /** 379 * Converts an audio direction to a string. 380 * 381 * @returns Stringified audio direction, or "Unknown", if not found. 382 * @param enmDir Audio direction to convert. 383 */ 384 const char *DrvAudioHlpAudDirToStr(PDMAUDIODIR enmDir) 385 { 386 switch (enmDir) 387 { 388 case PDMAUDIODIR_UNKNOWN: return "Unknown"; 389 case PDMAUDIODIR_IN: return "Input"; 390 case PDMAUDIODIR_OUT: return "Output"; 391 case PDMAUDIODIR_ANY: return "Duplex"; 392 default: break; 393 } 394 395 AssertMsgFailed(("Invalid audio direction %ld\n", enmDir)); 396 return "Unknown"; 397 } 398 399 /** 400 * Converts an audio device flags to a string. 401 * 402 * @returns Stringified audio flags. Must be free'd with RTStrFree(). 403 * NULL if no flags set. 404 * @param fFlags Audio flags to convert. 405 */ 406 char *DrvAudioHlpAudDevFlagsToStrA(PDMAUDIODEVFLAG fFlags) 407 { 408 409 #define APPEND_FLAG_TO_STR(_aFlag) \ 410 if (fFlags & PDMAUDIODEV_FLAGS_##_aFlag) \ 411 { \ 412 if (pszFlags) \ 413 { \ 414 rc2 = RTStrAAppend(&pszFlags, " "); \ 415 if (RT_FAILURE(rc2)) \ 416 break; \ 417 } \ 418 \ 419 rc2 = RTStrAAppend(&pszFlags, #_aFlag); \ 420 if (RT_FAILURE(rc2)) \ 421 break; \ 422 } \ 423 424 char *pszFlags = NULL; 425 int rc2; 426 427 do 428 { 429 APPEND_FLAG_TO_STR(DEFAULT); 430 APPEND_FLAG_TO_STR(HOTPLUG); 431 APPEND_FLAG_TO_STR(BUGGY); 432 APPEND_FLAG_TO_STR(IGNORE); 433 434 } while (0); 435 436 if ( RT_FAILURE(rc2) 437 && pszFlags) 438 { 439 RTStrFree(pszFlags); 440 pszFlags = NULL; 441 } 442 443 #undef APPEND_FLAG_TO_STR 444 445 return pszFlags; 202 446 } 203 447 … … 346 590 return PDMAUDIOFMT_S32; 347 591 348 AssertMsgFailed(("Invalid audio format \"%s\"\n", pszFmt));592 AssertMsgFailed(("Invalid audio format '%s'\n", pszFmt)); 349 593 return PDMAUDIOFMT_INVALID; 350 594 } -
trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp
r63534 r63711 1533 1533 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAMOUT); 1534 1534 1535 pBackendCfg->cSources = 0;1536 pBackendCfg->cSinks = 0;1537 1538 1535 /* Enumerate sound devices. */ 1539 1536 char **pszHints; … … 1558 1555 if (pszIOID) 1559 1556 { 1557 #if 0 1560 1558 if (!RTStrICmp("input", pszIOID)) 1561 pBackendCfg->cSources++; 1559 1562 1560 else if (!RTStrICmp("output", pszIOID)) 1563 pBackendCfg->cSinks++; 1561 #endif 1564 1562 } 1565 1563 else /* NULL means bidirectional, input + output. */ 1566 1564 { 1567 pBackendCfg->cSources++;1568 pBackendCfg->cSinks++;1569 1565 } 1570 1566 … … 1584 1580 pszHintCur++; 1585 1581 } 1586 1587 LogRel2(("ALSA: Found %RU8 host playback devices\n", pBackendCfg->cSinks));1588 LogRel2(("ALSA: Found %RU8 host capturing devices\n", pBackendCfg->cSources));1589 1582 1590 1583 snd_device_name_free_hint((void **)pszHints); -
trunk/src/VBox/Devices/Audio/DrvHostCoreAudio.cpp
r63683 r63711 16 16 */ 17 17 18 19 18 /********************************************************************************************************************************* 20 19 * Header Files * … … 22 21 #define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO 23 22 #include <VBox/log.h> 24 #include <VBox/vmm/pdmaudioifs.h>25 23 26 24 #include "DrvAudio.h" … … 51 49 #endif 52 50 51 /* Enables utilizing the Core Audio converter unit for converting 52 * input / output from/to our requested formats. That might be more 53 * performant than using our own routines later down the road. */ 54 /** @todo Needs more investigation and testing first before enabling. */ 55 //# define VBOX_WITH_AUDIO_CA_CONVERTER 56 57 #ifdef DEBUG_andy 58 # undef DEBUG_DUMP_PCM_DATA_PATH 59 # define DEBUG_DUMP_PCM_DATA_PATH "/Users/anloeffl/Documents/" 60 # undef VBOX_WITH_AUDIO_CA_CONVERTER 61 #endif 62 53 63 /** @todo 54 64 * - Maybe make sure the threads are immediately stopped if playing/recording stops. … … 64 74 */ 65 75 76 /* Prototypes needed for COREAUDIODEVICE. */ 77 struct DRVHOSTCOREAUDIO; 78 typedef struct DRVHOSTCOREAUDIO *PDRVHOSTCOREAUDIO; 79 80 /** 81 * Structure for holding Core Audio-specific device data. 82 * This data then lives in the pvData part of the PDMAUDIODEVICE struct. 83 */ 84 typedef struct COREAUDIODEVICEDATA 85 { 86 /** Pointer to driver instance this device is bound to. */ 87 PDRVHOSTCOREAUDIO pDrv; 88 /** The audio device ID of the currently used device. */ 89 AudioDeviceID deviceID; 90 /** List of attached (native) Core Audio streams attached to this device. */ 91 RTLISTANCHOR lstStreams; 92 } COREAUDIODEVICEDATA, *PCOREAUDIODEVICEDATA; 93 94 /** 95 * Host Coreaudio driver instance data. 96 * @implements PDMIAUDIOCONNECTOR 97 */ 98 typedef struct DRVHOSTCOREAUDIO 99 { 100 /** Pointer to the driver instance structure. */ 101 PPDMDRVINS pDrvIns; 102 /** Pointer to host audio interface. */ 103 PDMIHOSTAUDIO IHostAudio; 104 /** Critical section to serialize access. */ 105 RTCRITSECT CritSect; 106 /** Current (last reported) device enumeration. */ 107 PDMAUDIODEVICEENUM Devices; 108 /** Pointer to the currently used input device in the device enumeration. 109 * Can be NULL if none assigned. */ 110 PPDMAUDIODEVICE pDefaultDevIn; 111 /** Pointer to the currently used output device in the device enumeration. 112 * Can be NULL if none assigned. */ 113 PPDMAUDIODEVICE pDefaultDevOut; 114 #ifdef VBOX_WITH_AUDIO_CALLBACKS 115 /** Callback function to the upper driver. 116 * Can be NULL if not being used / registered. */ 117 PFNPDMHOSTAUDIOCALLBACK pfnCallback; 118 #endif 119 } DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO; 120 121 /** Converts a pointer to DRVHOSTCOREAUDIO::IHostAudio to a PDRVHOSTCOREAUDIO. */ 122 #define PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio) 123 124 /** 125 * Structure for holding a Core Audio unit 126 * and its data. 127 */ 128 typedef struct COREAUDIOUNIT 129 { 130 /** Pointer to the device this audio unit is bound to. 131 * Can be NULL if not bound to a device (anymore). */ 132 PPDMAUDIODEVICE pDevice; 133 /** The actual audio unit object. */ 134 AudioUnit audioUnit; 135 /** Stream description for using with VBox: 136 * - When using this audio unit for input (capturing), this format states 137 * the unit's output format. 138 * - When using this audio unit for output (playback), this format states 139 * the unit's input format. */ 140 AudioStreamBasicDescription streamFmt; 141 } COREAUDIOUNIT, *PCOREAUDIOUNIT; 66 142 67 143 /******************************************************************************* … … 77 153 char pszSampleRate[32]; 78 154 LogRel2(("CoreAudio: %s description:\n", pszDesc)); 79 LogRel2(("CoreAudio: 155 LogRel2(("CoreAudio:\tFormat ID: %RU32 (%c%c%c%c)\n", pASBD->mFormatID, 80 156 RT_BYTE4(pASBD->mFormatID), RT_BYTE3(pASBD->mFormatID), 81 157 RT_BYTE2(pASBD->mFormatID), RT_BYTE1(pASBD->mFormatID))); 82 LogRel2(("CoreAudio: 158 LogRel2(("CoreAudio:\tFlags: %RU32", pASBD->mFormatFlags)); 83 159 if (pASBD->mFormatFlags & kAudioFormatFlagIsFloat) 84 160 LogRel2((" Float")); … … 99 175 LogRel2(("\n")); 100 176 snprintf(pszSampleRate, 32, "%.2f", (float)pASBD->mSampleRate); /** @todo r=andy Use RTStrPrint*. */ 101 LogRel2(("CoreAudio: 102 LogRel2(("CoreAudio: 103 LogRel2(("CoreAudio: 104 LogRel2(("CoreAudio: 105 LogRel2(("CoreAudio: 106 LogRel2(("CoreAudio: 177 LogRel2(("CoreAudio:\tSampleRate : %s\n", pszSampleRate)); 178 LogRel2(("CoreAudio:\tChannelsPerFrame: %RU32\n", pASBD->mChannelsPerFrame)); 179 LogRel2(("CoreAudio:\tFramesPerPacket : %RU32\n", pASBD->mFramesPerPacket)); 180 LogRel2(("CoreAudio:\tBitsPerChannel : %RU32\n", pASBD->mBitsPerChannel)); 181 LogRel2(("CoreAudio:\tBytesPerFrame : %RU32\n", pASBD->mBytesPerFrame)); 182 LogRel2(("CoreAudio:\tBytesPerPacket : %RU32\n", pASBD->mBytesPerPacket)); 107 183 } 108 184 … … 139 215 } 140 216 217 static int coreAudioASBDToStreamCfg(AudioStreamBasicDescription *pASBD, PPDMAUDIOSTREAMCFG pCfg) 218 { 219 AssertPtrReturn(pASBD, VERR_INVALID_PARAMETER); 220 AssertPtrReturn(pCfg, VERR_INVALID_PARAMETER); 221 222 pCfg->cChannels = pASBD->mChannelsPerFrame; 223 pCfg->uHz = (uint32_t)pASBD->mSampleRate; 224 pCfg->enmEndianness = PDMAUDIOENDIANNESS_LITTLE; 225 226 int rc = VINF_SUCCESS; 227 228 if (pASBD->mFormatFlags & kAudioFormatFlagIsSignedInteger) 229 { 230 switch (pASBD->mBitsPerChannel) 231 { 232 case 8: pCfg->enmFormat = PDMAUDIOFMT_S8; break; 233 case 16: pCfg->enmFormat = PDMAUDIOFMT_S16; break; 234 case 32: pCfg->enmFormat = PDMAUDIOFMT_S32; break; 235 default: rc = VERR_NOT_SUPPORTED; break; 236 } 237 } 238 else 239 { 240 switch (pASBD->mBitsPerChannel) 241 { 242 case 8: pCfg->enmFormat = PDMAUDIOFMT_U8; break; 243 case 16: pCfg->enmFormat = PDMAUDIOFMT_U16; break; 244 case 32: pCfg->enmFormat = PDMAUDIOFMT_U32; break; 245 default: rc = VERR_NOT_SUPPORTED; break; 246 } 247 } 248 249 AssertRC(rc); 250 return rc; 251 } 252 141 253 static OSStatus coreAudioSetFrameBufferSize(AudioDeviceID deviceID, bool fInput, UInt32 cReqSize, UInt32 *pcActSize) 142 254 { … … 212 324 } 213 325 214 DECL_FORCE_INLINE(bool) coreAudioIsRunning(AudioDeviceID deviceID) 215 { 216 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsRunning, kAudioObjectPropertyScopeGlobal, 217 kAudioObjectPropertyElementMaster }; 218 UInt32 uFlag = 0; 219 UInt32 uSize = sizeof(uFlag); 220 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uFlag); 221 if (err != kAudioHardwareNoError) 222 LogRel(("CoreAudio: Could not determine whether the device is running (%RI32)\n", err)); 223 224 return (uFlag >= 1); 225 } 226 326 #if 0 /* unused */ 227 327 static int coreAudioCFStringToCString(const CFStringRef pCFString, char **ppszString) 228 328 { … … 239 339 } 240 340 241 #if 0 /* unused */242 341 static AudioDeviceID coreAudioDeviceUIDtoID(const char* pszUID) 243 342 { … … 276 375 * Defined Constants And Macros * 277 376 *********************************************************************************************************************************/ 377 278 378 /** @name Initialization status indicator used for the recreation of the AudioUnits. 279 * @{ */ 280 #define CA_STATUS_UNINIT UINT32_C(0) /**< The device is uninitialized */ 281 #define CA_STATUS_IN_INIT UINT32_C(1) /**< The device is currently initializing */ 282 #define CA_STATUS_INIT UINT32_C(2) /**< The device is initialized */ 283 #define CA_STATUS_IN_UNINIT UINT32_C(3) /**< The device is currently uninitializing */ 284 #define CA_STATUS_REINIT UINT32_C(4) /**< The device has to be reinitialized */ 285 /** @} */ 286 287 288 /********************************************************************************************************************************* 289 * Global Variables * 290 *********************************************************************************************************************************/ 291 /* Error code which indicates "End of data" */ 292 static const OSStatus g_caConverterEOFDErr = 0x656F6664; /* 'eofd' */ 293 294 295 /********************************************************************************************************************************* 296 * Structures and Typedefs * 297 *********************************************************************************************************************************/ 379 * 380 * Global structures section 381 * 382 ******************************************************************************/ 383 384 /** 385 * Enumeration for a Core Audio stream status. 386 */ 387 typedef enum COREAUDIOSTATUS 388 { 389 /** The device is uninitialized. */ 390 COREAUDIOSTATUS_UNINIT = 0, 391 /** The device is currently initializing. */ 392 COREAUDIOSTATUS_IN_INIT, 393 /** The device is initialized. */ 394 COREAUDIOSTATUS_INIT, 395 /** The device is currently uninitializing. */ 396 COREAUDIOSTATUS_IN_UNINIT, 397 /* The device has to be reinitialized */ 398 COREAUDIOSTATUS_REINIT, 399 /** The usual 32-bit hack. */ 400 COREAUDIOSTATUS_32BIT_HACK = 0x7fffffff 401 } COREAUDIOSTATUS, *PCOREAUDIOSTATUS; 402 403 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 404 /* Error code which indicates "End of data" */ 405 static const OSStatus caConverterEOFDErr = 0x656F6664; /* 'eofd' */ 406 #endif 407 298 408 /* Prototypes needed for COREAUDIOSTREAMCBCTX. */ 299 struct COREAUDIOSTREAMIN; 300 typedef struct COREAUDIOSTREAMIN *PCOREAUDIOSTREAMIN; 301 struct COREAUDIOSTREAMOUT; 302 typedef struct COREAUDIOSTREAMOUT *PCOREAUDIOSTREAMOUT; 303 304 /** 305 * Host Coreaudio driver instance data. 306 * @implements PDMIAUDIOCONNECTOR 307 */ 308 typedef struct DRVHOSTCOREAUDIO 309 { 310 /** Pointer to the driver instance structure. */ 311 PPDMDRVINS pDrvIns; 312 /** Pointer to host audio interface. */ 313 PDMIHOSTAUDIO IHostAudio; 314 } DRVHOSTCOREAUDIO, *PDRVHOSTCOREAUDIO; 315 316 /** Converts a pointer to DRVHOSTCOREAUDIO::IHostAudio to a PDRVHOSTCOREAUDIO. */ 317 #define PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTCOREAUDIO, IHostAudio) 318 319 /** 320 * Simple structure for maintaining a stream's callback context. 321 ** @todo Remove this as soon as we have unified input/output streams in this backend. 322 */ 323 typedef struct COREAUDIOSTREAMCBCTX 324 { 325 /** Pointer to driver instance. */ 326 PDRVHOSTCOREAUDIO pThis; 409 struct COREAUDIOSTREAM; 410 typedef struct COREAUDIOSTREAM *PCOREAUDIOSTREAM; 411 412 /** 413 * Structure for keeping a conversion callback context. 414 * This is needed when using an audio converter during input/output processing. 415 */ 416 typedef struct COREAUDIOCONVCBCTX 417 { 418 /** Pointer to the stream this context is bound to. */ 419 PCOREAUDIOSTREAM pStream; 420 /** Source stream description. */ 421 AudioStreamBasicDescription asbdSrc; 422 /** Destination stream description. */ 423 AudioStreamBasicDescription asbdDst; 424 /** Pointer to native buffer list used for rendering the source audio data into. */ 425 AudioBufferList *pBufLstSrc; 426 /** Total packet conversion count. */ 427 UInt32 uPacketCnt; 428 /** Current packet conversion index. */ 429 UInt32 uPacketIdx; 430 /** Error count, for limiting the logging. */ 431 UInt32 cErrors; 432 } COREAUDIOCONVCBCTX, *PCOREAUDIOCONVCBCTX; 433 434 /** 435 * Structure for keeping the input stream specifics. 436 */ 437 typedef struct COREAUDIOSTREAMIN 438 { 439 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 440 /** The audio converter if necessary. NULL if no converter is being used. */ 441 AudioConverterRef ConverterRef; 442 /** Callback context for the audio converter. */ 443 COREAUDIOCONVCBCTX convCbCtx; 444 #endif 445 /** The ratio between the device & the stream sample rate. */ 446 Float64 sampleRatio; 447 } COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN; 448 449 /** 450 * Structure for keeping the output stream specifics. 451 */ 452 typedef struct COREAUDIOSTREAMOUT 453 { 454 /** Nothing here yet. */ 455 } COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT; 456 457 /** 458 * Structure for maintaining a Core Audio stream. 459 */ 460 typedef struct COREAUDIOSTREAM 461 { 462 /** Host input stream. 463 * Note: Always must come first in this structure! */ 464 PDMAUDIOSTREAM Stream; 465 /** Note: This *always* must come first! */ 466 union 467 { 468 COREAUDIOSTREAMIN In; 469 COREAUDIOSTREAMOUT Out; 470 }; 471 /** List node for the device's stream list. */ 472 RTLISTNODE Node; 473 /** Pointer to driver instance this stream is bound to. */ 474 PDRVHOSTCOREAUDIO pDrv; 327 475 /** The stream's direction. */ 328 476 PDMAUDIODIR enmDir; 329 union 330 { 331 /** Pointer to self, if it's an input stream. */ 332 PCOREAUDIOSTREAMIN pIn; 333 /** Pointer to self, if it's an output stream. */ 334 PCOREAUDIOSTREAMOUT pOut; 335 }; 336 } COREAUDIOSTREAMCBCTX, *PCOREAUDIOSTREAMCBCTX; 337 338 /** 339 * Structure for keeping a conversion callback context. 340 * This is needed when using an audio converter during input/output processing. 341 */ 342 typedef struct COREAUDIOCONVCBCTX 343 { 344 /** Pointer to stream context this converter callback context 345 * is bound to. */ 346 /** @todo Remove this as soon as we have unified input/output streams in this backend. */ 347 COREAUDIOSTREAMCBCTX pStream; 348 /** Source stream description. */ 349 AudioStreamBasicDescription asbdSrc; 350 /** Destination stream description. */ 351 AudioStreamBasicDescription asbdDst; 352 /** Native buffer list used for rendering the source audio data into. */ 353 AudioBufferList bufLstSrc; 354 /** Total packet conversion count. */ 355 UInt32 uPacketCnt; 356 /** Current packet conversion index. */ 357 UInt32 uPacketIdx; 358 } COREAUDIOCONVCBCTX, *PCOREAUDIOCONVCBCTX; 359 360 /** @todo Unify COREAUDIOSTREAMOUT / COREAUDIOSTREAMIN. */ 361 typedef struct COREAUDIOSTREAMOUT 362 { 363 /** Host output stream. 364 * Note: Always must come first in this structure! */ 365 PDMAUDIOSTREAM Stream; 366 /** Stream description which is default on the device. */ 367 AudioStreamBasicDescription deviceFormat; 368 /** Stream description which is selected for using with VBox. */ 369 AudioStreamBasicDescription streamFormat; 370 /** The audio device ID of the currently used device. */ 371 AudioDeviceID deviceID; 372 /** The AudioUnit being used. */ 373 AudioUnit audioUnit; 374 /** A ring buffer for transferring data to the playback thread. */ 375 PRTCIRCBUF pCircBuf; 477 /** The audio unit for this stream. */ 478 COREAUDIOUNIT Unit; 376 479 /** Initialization status tracker. Used when some of the device parameters 377 480 * or the device itself is changed during the runtime. */ 378 volatile uint32_t status; 379 /** Flag whether the "default device changed" listener was registered. */ 380 bool fDefDevChgListReg; 381 /** Flag whether the "device state changed" listener was registered. */ 382 bool fDevStateChgListReg; 383 /** Callback context for this stream for handing this stream in to 384 * a CoreAudio callback. 385 ** @todo Remove this as soon as we have unified input/output streams in this backend. */ 386 COREAUDIOSTREAMCBCTX cbCtx; 387 } COREAUDIOSTREAMOUT, *PCOREAUDIOSTREAMOUT; 388 389 typedef struct COREAUDIOSTREAMIN 390 { 391 /** Host input stream. 392 * Note: Always must come first in this structure! */ 393 PDMAUDIOSTREAM Stream; 394 /** Stream description which is default on the device. */ 395 AudioStreamBasicDescription deviceFormat; 396 /** Stream description which is selected for using with VBox. */ 397 AudioStreamBasicDescription streamFormat; 398 /** The audio device ID of the currently used device. */ 399 AudioDeviceID deviceID; 400 /** The AudioUnit used. */ 401 AudioUnit audioUnit; 402 /** A ring buffer for transferring data from the capturing thread. */ 403 PRTCIRCBUF pCircBuf; 404 /** The audio converter if necessary. NULL if no converter is being used. */ 405 AudioConverterRef pConverter; 406 /** Callback context for the audio converter. */ 407 COREAUDIOCONVCBCTX convCbCtx; 408 /** The ratio between the device & the stream sample rate. */ 409 Float64 sampleRatio; 410 /** Initialization status tracker. Used when some of the device parameters 411 * or the device itself is changed during the runtime. */ 412 volatile uint32_t status; 413 /** Flag whether the "default device changed" listener was registered. */ 414 bool fDefDevChgListReg; 415 /** Flag whether the "device state changed" listener was registered. */ 416 bool fDevStateChgListReg; 417 /** Callback context for this stream for handing this stream in to 418 * a CoreAudio callback. 419 ** @todo Remove this as soon as we have unified input/output streams in this backend. */ 420 COREAUDIOSTREAMCBCTX cbCtx; 421 } COREAUDIOSTREAMIN, *PCOREAUDIOSTREAMIN; 422 423 424 /********************************************************************************************************************************* 425 * Internal Functions * 426 *********************************************************************************************************************************/ 427 static OSStatus coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, UInt32 cAddresses, const AudioObjectPropertyAddress properties[], void *pvUser); 481 volatile uint32_t status; 482 /** An internal ring buffer for transferring data from/to the rendering callbacks. */ 483 PRTCIRCBUF pCircBuf; 484 } COREAUDIOSTREAM, *PCOREAUDIOSTREAM; 485 486 static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev); 487 static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev); 488 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream); 489 490 static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 491 static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); 492 493 static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd); 494 495 static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev); 496 static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev); 497 static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, PDRVHOSTCOREAUDIO pDrv); 498 499 static OSStatus coreAudioDevPropChgCb(AudioObjectID propertyID, UInt32 nAddresses, const AudioObjectPropertyAddress properties[], void *pvUser); 428 500 static OSStatus coreAudioPlaybackCb(void *pvUser, AudioUnitRenderActionFlags *pActionFlags, const AudioTimeStamp *pAudioTS, UInt32 uBusID, UInt32 cFrames, AudioBufferList* pBufData); 429 501 430 502 503 /** 504 * Returns whether an assigned audio unit to a given stream is running or not. 505 * 506 * @return True if audio unit is running, false if not. 507 * @param pStream Audio stream to check. 508 */ 509 static bool coreAudioUnitIsRunning(PCOREAUDIOSTREAM pStream) 510 { 511 AssertPtrReturn(pStream, false); 512 513 UInt32 uFlag = 0; 514 UInt32 uSize = sizeof(uFlag); 515 OSStatus err = AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, 516 0, &uFlag, &uSize); 517 if (err != kAudioHardwareNoError) 518 LogRel(("CoreAudio: Could not determine whether the audio unit is running (%RI32)\n", err)); 519 520 Log3Func(("%s -> %RU32\n", pStream->enmDir == PDMAUDIODIR_IN ? "Input" : "Output", uFlag)); 521 522 return (uFlag >= 1); 523 } 524 525 526 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 431 527 /** 432 528 * Initializes a conversion callback context. … … 434 530 * @return IPRT status code. 435 531 * @param pConvCbCtx Conversion callback context to initialize. 532 * @param pStream Pointer to stream to use. 436 533 * @param pASBDSrc Input (source) stream description to use. 437 534 * @param pASBDDst Output (destination) stream description to use. 438 535 */ 439 static int coreAudioInitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx, 536 static int coreAudioInitConvCbCtx(PCOREAUDIOCONVCBCTX pConvCbCtx, PCOREAUDIOSTREAM pStream, 440 537 AudioStreamBasicDescription *pASBDSrc, AudioStreamBasicDescription *pASBDDst) 441 538 { 442 539 AssertPtrReturn(pConvCbCtx, VERR_INVALID_POINTER); 540 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 443 541 AssertPtrReturn(pASBDSrc, VERR_INVALID_POINTER); 444 542 AssertPtrReturn(pASBDDst, VERR_INVALID_POINTER); 445 543 544 #ifdef DEBUG 545 coreAudioPrintASBD("CbCtx: Src", pASBDSrc); 546 coreAudioPrintASBD("CbCtx: Dst", pASBDDst); 547 #endif 548 549 pConvCbCtx->pStream = pStream; 550 446 551 memcpy(&pConvCbCtx->asbdSrc, pASBDSrc, sizeof(AudioStreamBasicDescription)); 447 552 memcpy(&pConvCbCtx->asbdDst, pASBDDst, sizeof(AudioStreamBasicDescription)); 448 553 449 /* Create the AudioBufferList structure with one buffer. */ 450 pConvCbCtx->bufLstSrc.mNumberBuffers = 1; 451 452 /* Initialize the conversion buffer. */ 453 pConvCbCtx->bufLstSrc.mBuffers[0].mNumberChannels = pConvCbCtx->asbdSrc.mChannelsPerFrame; 454 pConvCbCtx->bufLstSrc.mBuffers[0].mDataByteSize = 0; 455 pConvCbCtx->bufLstSrc.mBuffers[0].mData = NULL; 554 pConvCbCtx->pBufLstSrc = NULL; 555 pConvCbCtx->cErrors = 0; 456 556 457 557 return VINF_SUCCESS; 458 558 } 559 459 560 460 561 /** … … 468 569 AssertPtrReturnVoid(pConvCbCtx); 469 570 571 pConvCbCtx->pStream = NULL; 572 470 573 RT_ZERO(pConvCbCtx->asbdSrc); 471 574 RT_ZERO(pConvCbCtx->asbdDst); 472 575 473 pConvCbCtx->bufLstSrc.mNumberBuffers = 0; 474 } 475 476 /** 477 * Does a (Re-)enumeration of the host's playback + recording devices. 576 pConvCbCtx->pBufLstSrc = NULL; 577 pConvCbCtx->cErrors = 0; 578 } 579 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 580 581 582 /** 583 * Does a (re-)enumeration of the host's playback + recording devices. 478 584 * 479 585 * @return IPRT status code. 480 586 * @param pThis Host audio driver instance. 481 * @param pCfg Where to store the enumeration results. 482 * @param fEnum Enumeration flags. 483 */ 484 static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, bool fIn, uint32_t fEnum) 485 { 486 RT_NOREF(fEnum); 487 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 488 /* pCfg is optional. */ 587 * @param enmUsage Which devices to enumerate. 588 * @param pDevEnm Where to store the enumerated devices. 589 */ 590 static int coreAudioDevicesEnumerate(PDRVHOSTCOREAUDIO pThis, PDMAUDIODIR enmUsage, PPDMAUDIODEVICEENUM pDevEnm) 591 { 592 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 593 AssertPtrReturn(pDevEnm, VERR_INVALID_POINTER); 489 594 490 595 int rc = VINF_SUCCESS; 491 596 492 uint8_t cDevs = 0;493 494 597 do 495 598 { 599 AudioDeviceID defaultDeviceID = kAudioDeviceUnknown; 600 601 /* Fetch the default audio device currently in use. */ 602 AudioObjectPropertyAddress propAdrDefaultDev = { enmUsage == PDMAUDIODIR_IN 603 ? kAudioHardwarePropertyDefaultInputDevice 604 : kAudioHardwarePropertyDefaultOutputDevice, 605 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 606 UInt32 uSize = sizeof(defaultDeviceID); 607 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdrDefaultDev, 0, NULL, &uSize, &defaultDeviceID); 608 if (err != noErr) 609 { 610 LogRel(("CoreAudio: Unable to determine default %s device (%RI32)\n", 611 enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback", err)); 612 return VERR_NOT_FOUND; 613 } 614 615 if (defaultDeviceID == kAudioDeviceUnknown) 616 { 617 LogFunc(("No default %s device found\n", enmUsage == PDMAUDIODIR_IN ? "capturing" : "playback")); 618 /* Keep going. */ 619 } 620 496 621 AudioObjectPropertyAddress propAdrDevList = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, 497 622 kAudioObjectPropertyElementMaster }; 498 UInt32 uSize = 0; 499 OSStatuserr = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize);623 624 err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propAdrDevList, 0, NULL, &uSize); 500 625 if (err != kAudioHardwareNoError) 501 626 break; … … 509 634 break; 510 635 511 UInt32 cDevices = uSize / sizeof (AudioDeviceID); 512 for (UInt32 i = 0; i < cDevices; i++) 513 { 514 AudioDeviceID curDevID = pDevIDs[i]; 636 rc = DrvAudioHlpDeviceEnumInit(pDevEnm); 637 if (RT_FAILURE(rc)) 638 break; 639 640 UInt16 cDevices = uSize / sizeof(AudioDeviceID); 641 642 PPDMAUDIODEVICE pDev = NULL; 643 for (UInt16 i = 0; i < cDevices; i++) 644 { 645 if (pDev) /* Some (skipped) device to clean up first? */ 646 DrvAudioHlpDeviceFree(pDev); 647 648 pDev = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA)); 649 if (!pDev) 650 { 651 rc = VERR_NO_MEMORY; 652 break; 653 } 654 655 /* Set usage. */ 656 pDev->enmUsage = enmUsage; 657 658 /* Init backend-specific device data. */ 659 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData; 660 AssertPtr(pDevData); 661 coreAudioDeviceDataInit(pDevData, pDevIDs[i], pThis); 515 662 516 663 /* Check if the device is valid. */ 664 AudioDeviceID curDevID = pDevData->deviceID; 665 517 666 AudioObjectPropertyAddress propAddrCfg = { kAudioDevicePropertyStreamConfiguration, 518 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 667 enmUsage == PDMAUDIODIR_IN 668 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 519 669 kAudioObjectPropertyElementMaster }; 520 670 … … 527 677 continue; 528 678 529 bool fIsValid = false;530 531 679 err = AudioObjectGetPropertyData(curDevID, &propAddrCfg, 0, NULL, &uSize, pBufList); 532 680 if (err == noErr) … … 534 682 for (UInt32 a = 0; a < pBufList->mNumberBuffers; a++) 535 683 { 536 fIsValid = pBufList->mBuffers[a].mNumberChannels > 0; 537 if (fIsValid) 538 break; 684 if (enmUsage == PDMAUDIODIR_IN) 685 pDev->cMaxInputChannels += pBufList->mBuffers[a].mNumberChannels; 686 else if (enmUsage == PDMAUDIODIR_OUT) 687 pDev->cMaxOutputChannels += pBufList->mBuffers[a].mNumberChannels; 539 688 } 540 689 } … … 546 695 } 547 696 548 if (!fIsValid) 697 /* Check if the device is valid, e.g. has any input/output channels according to its usage. */ 698 if ( enmUsage == PDMAUDIODIR_IN 699 && !pDev->cMaxInputChannels) 700 { 549 701 continue; 702 } 703 else if ( enmUsage == PDMAUDIODIR_OUT 704 && !pDev->cMaxOutputChannels) 705 { 706 continue; 707 } 550 708 551 709 /* Resolve the device's name. */ 552 710 AudioObjectPropertyAddress propAddrName = { kAudioObjectPropertyName, 553 fIn ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 711 enmUsage == PDMAUDIODIR_IN 712 ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 554 713 kAudioObjectPropertyElementMaster }; 555 714 uSize = sizeof(CFStringRef); … … 567 726 && CFStringGetCString(pcfstrName, pszName, uMax, kCFStringEncodingUTF8)) 568 727 { 569 LogRel2(("CoreAudio: Found %s device '%s'\n", fIn ? "recording" : "playback", pszName)); 570 cDevs++; 728 RTStrPrintf(pDev->szName, sizeof(pDev->szName), "%s", pszName); 571 729 } 572 730 … … 579 737 580 738 CFRelease(pcfstrName); 739 740 /* Adjust flags. */ 741 if (curDevID == defaultDeviceID) /* Is the device the default device? */ 742 pDev->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT; 743 744 /* Add the device to the enumeration. */ 745 rc = DrvAudioHlpDeviceEnumAdd(pDevEnm, pDev); 746 if (RT_FAILURE(rc)) 747 break; 748 749 /* NULL device pointer because it's now part of the device enumeration. */ 750 pDev = NULL; 751 } 752 753 if (RT_FAILURE(rc)) 754 { 755 DrvAudioHlpDeviceFree(pDev); 756 pDev = NULL; 581 757 } 582 758 583 759 } while (0); 584 760 585 if (fIn) 586 LogRel2(("CoreAudio: Found %RU8 recording device(s)\n", cDevs)); 761 if (RT_SUCCESS(rc)) 762 { 763 #ifdef DEBUG 764 LogFunc(("Devices for pDevEnm=%p, enmUsage=%RU32:\n", pDevEnm, enmUsage)); 765 DrvAudioHlpDeviceEnumPrint("Core Audio", pDevEnm); 766 #endif 767 } 587 768 else 588 LogRel2(("CoreAudio: Found %RU8 playback device(s)\n", cDevs)); 589 590 if (pCfg) 591 { 592 if (fIn) 593 pCfg->cSources = cDevs; 594 else 595 pCfg->cSinks = cDevs; 596 } 769 DrvAudioHlpDeviceEnumFree(pDevEnm); 597 770 598 771 LogFlowFuncLeaveRC(rc); … … 600 773 } 601 774 602 /** 603 * Updates this host driver's internal status, according to the global, overall input/output 604 * state and all connected (native) audio streams. 775 776 /** 777 * Checks if an audio device with a specific device ID is in the given device 778 * enumeration or not. 605 779 * 780 * @retval true if the node is the last element in the list. 781 * @retval false otherwise. 782 * 783 * @param pEnmSrc Device enumeration to search device ID in. 784 * @param deviceID Device ID to search. 785 */ 786 bool coreAudioDevicesHasDevice(PPDMAUDIODEVICEENUM pEnmSrc, AudioDeviceID deviceID) 787 { 788 PPDMAUDIODEVICE pDevSrc; 789 RTListForEach(&pEnmSrc->lstDevices, pDevSrc, PDMAUDIODEVICE, Node) 790 { 791 PCOREAUDIODEVICEDATA pDevSrcData = (PCOREAUDIODEVICEDATA)pDevSrc->pvData; 792 AssertPtr(pDevSrcData); 793 794 if (pDevSrcData->deviceID == deviceID) 795 return true; 796 } 797 798 return false; 799 } 800 801 802 /** 803 * Enumerates all host devices and builds a final device enumeration list, consisting 804 * of (duplex) input and output devices. 805 * 806 * @return IPRT status code. 606 807 * @param pThis Host audio driver instance. 607 * @param pCfg Where to store the backend configuration. Optional. 608 * @param fEnum Enumeration flags. 609 */ 610 int coreAudioUpdateStatusInternalEx(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum) 611 { 612 RT_NOREF(fEnum); 613 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 614 /* pCfg is optional. */ 615 616 PDMAUDIOBACKENDCFG Cfg; 617 RT_ZERO(Cfg); 618 619 Cfg.cbStreamOut = sizeof(COREAUDIOSTREAMOUT); 620 Cfg.cbStreamIn = sizeof(COREAUDIOSTREAMIN); 621 Cfg.cMaxStreamsIn = UINT32_MAX; 622 Cfg.cMaxStreamsOut = UINT32_MAX; 623 624 int rc = coreAudioDevicesEnumerate(pThis, &Cfg, false /* fIn */, 0 /* fEnum */); 625 AssertRC(rc); 626 rc = coreAudioDevicesEnumerate(pThis, &Cfg, true /* fIn */, 0 /* fEnum */); 627 AssertRC(rc); 628 629 if (pCfg) 630 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG)); 808 * @param pEnmDst Where to store the device enumeration list. 809 */ 810 int coreAudioDevicesEnumerateAll(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICEENUM pEnmDst) 811 { 812 PDMAUDIODEVICEENUM devEnmIn; 813 int rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_IN, &devEnmIn); 814 if (RT_SUCCESS(rc)) 815 { 816 PDMAUDIODEVICEENUM devEnmOut; 817 rc = coreAudioDevicesEnumerate(pThis, PDMAUDIODIR_OUT, &devEnmOut); 818 if (RT_SUCCESS(rc)) 819 { 820 /* 821 * Build up the final device enumeration, based on the input and output device lists 822 * just enumerated. 823 * 824 * Also make sure to handle duplex devices, that is, devices which act as input and output 825 * at the same time. 826 */ 827 828 rc = DrvAudioHlpDeviceEnumInit(pEnmDst); 829 if (RT_SUCCESS(rc)) 830 { 831 PPDMAUDIODEVICE pDevSrcIn; 832 RTListForEach(&devEnmIn.lstDevices, pDevSrcIn, PDMAUDIODEVICE, Node) 833 { 834 PCOREAUDIODEVICEDATA pDevSrcInData = (PCOREAUDIODEVICEDATA)pDevSrcIn->pvData; 835 AssertPtr(pDevSrcInData); 836 837 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA)); 838 if (!pDevDst) 839 { 840 rc = VERR_NO_MEMORY; 841 break; 842 } 843 844 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData; 845 AssertPtr(pDevDstData); 846 coreAudioDeviceDataInit(pDevDstData, pDevSrcInData->deviceID, pThis); 847 848 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcIn->szName); 849 850 pDevDst->enmUsage = PDMAUDIODIR_IN; /* Input device by default (simplex). */ 851 pDevDst->cMaxInputChannels = pDevSrcIn->cMaxInputChannels; 852 853 /* Handle flags. */ 854 if (pDevSrcIn->fFlags & PDMAUDIODEV_FLAGS_DEFAULT) 855 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT; 856 /** @todo Handle hot plugging? */ 857 858 /* 859 * Now search through the list of all found output devices and check if we found 860 * an output device with the same device ID as the currently handled input device. 861 * 862 * If found, this means we have to treat that device as a duplex device then. 863 */ 864 PPDMAUDIODEVICE pDevSrcOut; 865 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node) 866 { 867 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData; 868 AssertPtr(pDevSrcOutData); 869 870 if (pDevSrcInData->deviceID == pDevSrcOutData->deviceID) 871 { 872 pDevDst->enmUsage = PDMAUDIODIR_ANY; 873 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels; 874 break; 875 } 876 } 877 878 if (RT_SUCCESS(rc)) 879 { 880 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst); 881 } 882 else 883 { 884 DrvAudioHlpDeviceFree(pDevDst); 885 pDevDst = NULL; 886 } 887 } 888 889 if (RT_SUCCESS(rc)) 890 { 891 /* 892 * As a last step, add all remaining output devices which have not been handled in the loop above, 893 * that is, all output devices which operate in simplex mode. 894 */ 895 PPDMAUDIODEVICE pDevSrcOut; 896 RTListForEach(&devEnmOut.lstDevices, pDevSrcOut, PDMAUDIODEVICE, Node) 897 { 898 PCOREAUDIODEVICEDATA pDevSrcOutData = (PCOREAUDIODEVICEDATA)pDevSrcOut->pvData; 899 AssertPtr(pDevSrcOutData); 900 901 if (coreAudioDevicesHasDevice(pEnmDst, pDevSrcOutData->deviceID)) 902 continue; /* Already in our list, skip. */ 903 904 PPDMAUDIODEVICE pDevDst = DrvAudioHlpDeviceAlloc(sizeof(COREAUDIODEVICEDATA)); 905 if (!pDevDst) 906 { 907 rc = VERR_NO_MEMORY; 908 break; 909 } 910 911 PCOREAUDIODEVICEDATA pDevDstData = (PCOREAUDIODEVICEDATA)pDevDst->pvData; 912 AssertPtr(pDevDstData); 913 coreAudioDeviceDataInit(pDevDstData, pDevSrcOutData->deviceID, pThis); 914 915 RTStrCopy(pDevDst->szName, sizeof(pDevDst->szName), pDevSrcOut->szName); 916 917 pDevDst->enmUsage = PDMAUDIODIR_OUT; 918 pDevDst->cMaxOutputChannels = pDevSrcOut->cMaxOutputChannels; 919 920 pDevDstData->deviceID = pDevSrcOutData->deviceID; 921 922 /* Handle flags. */ 923 if (pDevSrcOut->fFlags & PDMAUDIODEV_FLAGS_DEFAULT) 924 pDevDst->fFlags |= PDMAUDIODEV_FLAGS_DEFAULT; 925 /** @todo Handle hot plugging? */ 926 927 rc = DrvAudioHlpDeviceEnumAdd(pEnmDst, pDevDst); 928 if (RT_FAILURE(rc)) 929 { 930 DrvAudioHlpDeviceFree(pDevDst); 931 break; 932 } 933 } 934 } 935 936 if (RT_FAILURE(rc)) 937 DrvAudioHlpDeviceEnumFree(pEnmDst); 938 } 939 940 DrvAudioHlpDeviceEnumFree(&devEnmOut); 941 } 942 943 DrvAudioHlpDeviceEnumFree(&devEnmIn); 944 } 945 946 #ifdef DEBUG 947 if (RT_SUCCESS(rc)) 948 DrvAudioHlpDeviceEnumPrint("Core Audio (Final)", pEnmDst); 949 #endif 631 950 632 951 LogFlowFuncLeaveRC(rc); … … 635 954 636 955 637 /** 638 * Implements the OS X callback AudioObjectPropertyListenerProc. 639 */ 640 static OSStatus drvHostCoreAudioDeviceStateChanged(AudioObjectID propertyID, 641 UInt32 cAddresses, 642 const AudioObjectPropertyAddress paProperties[], 643 void *pvUser) 644 { 645 RT_NOREF(propertyID, cAddresses, paProperties) 646 LogFlowFunc(("propertyID=%u cAddresses=%u pvUser=%p\n", propertyID, cAddresses, pvUser)); 647 648 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser; 649 AssertPtr(pCbCtx); 956 #if 0 957 static int coreAudioDeviceInit(PPDMAUDIODEVICE pDev, PDRVHOSTCOREAUDIO pDrv, PDMAUDIODIR enmUsage, AudioDeviceID deviceID) 958 { 959 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 960 961 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 962 AssertPtrReturn(pData, VERR_INVALID_POINTER); 963 964 /* Init common parameters. */ 965 pDev->enmUsage = enmUsage; 966 967 /* Init Core Audio-specifics. */ 968 pData->deviceID = deviceID; 969 pData->pDrv = pDrv; 970 971 RTListInit(&pData->lstStreams); 972 973 return VINF_SUCCESS; 974 } 975 #endif 976 977 978 /** 979 * Initializes a Core Audio-specific device data structure. 980 * 981 * @returns IPRT status code. 982 * @param pDevData Device data structure to initialize. 983 * @param deviceID Core Audio device ID to assign this structure to. 984 * @param pDrv Driver instance to use. 985 */ 986 static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, PDRVHOSTCOREAUDIO pDrv) 987 { 988 AssertPtrReturnVoid(pDevData); 989 AssertPtrReturnVoid(pDrv); 990 991 pDevData->deviceID = deviceID; 992 pDevData->pDrv = pDrv; 993 994 RTListInit(&pDevData->lstStreams); 995 } 996 997 998 /** 999 * Propagates an audio device status to all its connected Core Audio streams. 1000 * 1001 * @return IPRT status code. 1002 * @param pDev Audio device to propagate status for. 1003 * @param enmSts Status to propagate. 1004 */ 1005 static int coreAudioDevicePropagateStatus(PPDMAUDIODEVICE pDev, COREAUDIOSTATUS enmSts) 1006 { 1007 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 1008 1009 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pDev->pvData; 1010 AssertPtrReturn(pDevData, VERR_INVALID_POINTER); 1011 1012 /* Sanity. */ 1013 AssertPtr(pDevData->pDrv); 1014 1015 LogFlowFunc(("pDev=%p, pDevData=%p, enmSts=%RU32\n", pDev, pDevData, enmSts)); 1016 1017 PCOREAUDIOSTREAM pCAStream; 1018 RTListForEach(&pDevData->lstStreams, pCAStream, COREAUDIOSTREAM, Node) 1019 { 1020 LogFlowFunc(("pCAStream=%p\n", pCAStream)); 1021 1022 /* We move the reinitialization to the next output event. 1023 * This make sure this thread isn't blocked and the 1024 * reinitialization is done when necessary only. */ 1025 ASMAtomicXchgU32(&pCAStream->status, enmSts); 1026 } 1027 1028 return VINF_SUCCESS; 1029 } 1030 1031 1032 static DECLCALLBACK(OSStatus) coreAudioDeviceStateChangedCb(AudioObjectID propertyID, 1033 UInt32 nAddresses, 1034 const AudioObjectPropertyAddress properties[], 1035 void *pvUser) 1036 { 1037 RT_NOREF(propertyID, nAddresses, properties); 1038 1039 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 1040 1041 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser; 1042 AssertPtr(pDev); 1043 1044 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 1045 AssertPtrReturn(pData, VERR_INVALID_POINTER); 1046 1047 PDRVHOSTCOREAUDIO pThis = pData->pDrv; 1048 AssertPtr(pThis); 1049 1050 int rc2 = RTCritSectEnter(&pThis->CritSect); 1051 AssertRC(rc2); 650 1052 651 1053 UInt32 uAlive = 1; … … 655 1057 kAudioObjectPropertyElementMaster }; 656 1058 657 AudioDeviceID deviceID = pCbCtx->enmDir == PDMAUDIODIR_IN 658 ? pCbCtx->pIn->deviceID : pCbCtx->pOut->deviceID; 1059 AudioDeviceID deviceID = pData->deviceID; 659 1060 660 1061 OSStatus err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &uAlive); … … 669 1070 if (fIsDead) 670 1071 { 671 switch (pCbCtx->enmDir) 672 { 673 case PDMAUDIODIR_IN: 674 { 675 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn; 676 677 /* We move the reinitialization to the next output event. 678 * This make sure this thread isn't blocked and the 679 * reinitialization is done when necessary only. */ 680 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT); 681 682 LogRel(("CoreAudio: Recording device stopped functioning\n")); 1072 LogRel2(("CoreAudio: Device '%s' stopped functioning\n", pDev->szName)); 1073 1074 /* Mark device as dead. */ 1075 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_UNINIT); 1076 AssertRC(rc2); 1077 } 1078 1079 rc2 = RTCritSectLeave(&pThis->CritSect); 1080 AssertRC(rc2); 1081 1082 return noErr; 1083 } 1084 1085 /* Callback for getting notified when the default recording/playback device has been changed. */ 1086 static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChangedCb(AudioObjectID propertyID, 1087 UInt32 nAddresses, 1088 const AudioObjectPropertyAddress properties[], 1089 void *pvUser) 1090 { 1091 RT_NOREF(propertyID, nAddresses); 1092 1093 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 1094 1095 PDRVHOSTCOREAUDIO pThis = (PDRVHOSTCOREAUDIO)pvUser; 1096 AssertPtr(pThis); 1097 1098 int rc2 = RTCritSectEnter(&pThis->CritSect); 1099 AssertRC(rc2); 1100 1101 OSStatus err = noErr; 1102 1103 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++) 1104 { 1105 PPDMAUDIODEVICE pDev = NULL; 1106 1107 /* 1108 * Check if the default input / output device has been changed. 1109 */ 1110 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress]; 1111 switch (pProperty->mSelector) 1112 { 1113 case kAudioHardwarePropertyDefaultInputDevice: 1114 LogFlowFunc(("kAudioHardwarePropertyDefaultInputDevice\n")); 1115 pDev = pThis->pDefaultDevIn; 683 1116 break; 684 } 685 686 case PDMAUDIODIR_OUT: 687 { 688 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut; 689 690 /* We move the reinitialization to the next output event. 691 * This make sure this thread isn't blocked and the 692 * reinitialization is done when necessary only. */ 693 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT); 694 695 LogRel(("CoreAudio: Playback device stopped functioning\n")); 1117 1118 case kAudioHardwarePropertyDefaultOutputDevice: 1119 LogFlowFunc(("kAudioHardwarePropertyDefaultOutputDevice\n")); 1120 pDev = pThis->pDefaultDevOut; 696 1121 break; 697 }698 1122 699 1123 default: 700 AssertMsgFailed(("Not implemented\n"));1124 /* Skip others. */ 701 1125 break; 702 1126 } 703 } 704 705 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */); 1127 1128 LogFlowFunc(("pDev=%p\n", pDev)); 1129 1130 if (pDev) 1131 { 1132 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 1133 AssertPtr(pData); 1134 1135 /* This listener is called on every change of the hardware 1136 * device. So check if the default device has really changed. */ 1137 UInt32 uSize = sizeof(AudioDeviceID); 1138 UInt32 uResp = 0; 1139 1140 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp); 1141 if (err == noErr) 1142 { 1143 if (pData->deviceID != uResp) /* Has the device ID changed? */ 1144 { 1145 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT); 1146 AssertRC(rc2); 1147 } 1148 } 1149 } 1150 } 1151 1152 #ifdef VBOX_WITH_AUDIO_CALLBACKS 1153 if (pThis->pfnCallback) 1154 /* Ignore rc */ pThis->pfnCallback(pThis->pDrvIns, PDMAUDIOCBTYPE_DEVICES_CHANGED, NULL, 0); 1155 #endif 1156 1157 rc2 = RTCritSectLeave(&pThis->CritSect); 706 1158 AssertRC(rc2); 707 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */);708 AssertRC(rc2);709 1159 710 1160 return noErr; 711 1161 } 712 1162 713 714 /** 715 * Implements the OS X callback AudioObjectPropertyListenerProc for getting 716 * notified when the default recording/playback device has been changed. 717 */ 718 static OSStatus coreAudioDefaultDeviceChanged(AudioObjectID propertyID, 719 UInt32 cAddresses, 720 const AudioObjectPropertyAddress properties[], 721 void *pvUser) 722 { 723 RT_NOREF(propertyID); 724 LogFlowFunc(("propertyID=%u cAddresses=%u pvUser=%p\n", propertyID, cAddresses, pvUser)); 725 726 PCOREAUDIOSTREAMCBCTX pCbCtx = (PCOREAUDIOSTREAMCBCTX)pvUser; 727 AssertPtr(pCbCtx); 728 729 OSStatus err = noErr; 730 for (UInt32 idxAddress = 0; idxAddress < cAddresses; idxAddress++) 731 { 732 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress]; 733 734 switch (pProperty->mSelector) 735 { 736 case kAudioHardwarePropertyDefaultInputDevice: 737 { 738 PCOREAUDIOSTREAMIN pStreamIn = pCbCtx->pIn; 739 AssertPtr(pStreamIn); 740 741 /* This listener is called on every change of the hardware 742 * device. So check if the default device has really changed. */ 743 UInt32 uSize = sizeof(pStreamIn->deviceID); 744 UInt32 uResp; 745 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp); 746 747 if (err == noErr) 748 { 749 if (pStreamIn->deviceID != uResp) 750 { 751 LogRel(("CoreAudio: Default device for recording has changed\n")); 752 753 /* We move the reinitialization to the next input event. 754 * This make sure this thread isn't blocked and the 755 * reinitialization is done when necessary only. */ 756 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT); 757 } 758 } 759 break; 760 } 761 762 case kAudioHardwarePropertyDefaultOutputDevice: 763 { 764 PCOREAUDIOSTREAMOUT pStreamOut = pCbCtx->pOut; 765 AssertPtr(pStreamOut); 766 767 /* This listener is called on every change of the hardware 768 * device. So check if the default device has really changed. */ 769 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, 770 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 771 772 UInt32 uSize = sizeof(pStreamOut->deviceID); 773 UInt32 uResp; 774 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &uResp); 775 776 if (err == noErr) 777 { 778 if (pStreamOut->deviceID != uResp) 779 { 780 LogRel(("CoreAudio: Default device for playback has changed\n")); 781 782 /* We move the reinitialization to the next input event. 783 * This make sure this thread isn't blocked and the 784 * reinitialization is done when necessary only. */ 785 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_REINIT); 786 } 787 } 788 break; 789 } 790 791 default: 792 break; 793 } 794 } 795 796 int rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, false /* fIn */, 0 /* fEnum */); 797 AssertRC(rc2); 798 rc2 = coreAudioDevicesEnumerate(pCbCtx->pThis, NULL /* pCfg */, true /* fIn */, 0 /* fEnum */); 799 AssertRC(rc2); 800 801 /** @todo Implement callback notification here to let the audio connector / device emulation 802 * know that something has changed. */ 803 804 return noErr; 805 } 806 807 808 /** 809 * Implements the OS X callback AudioObjectPropertyListenerProc for getting 810 * notified when some of the properties of an audio device has changed. 811 */ 812 static OSStatus coreAudioRecordingAudioDevicePropertyChanged(AudioObjectID propertyID, 813 UInt32 cAddresses, 814 const AudioObjectPropertyAddress paProperties[], 815 void *pvUser) 816 { 817 RT_NOREF(cAddresses, paProperties); 818 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser; 819 820 switch (propertyID) 821 { 822 #ifdef DEBUG 823 case kAudioDeviceProcessorOverload: 824 { 825 LogFunc(("Processor overload detected!\n")); 826 break; 827 } 828 #endif /* DEBUG */ 829 case kAudioDevicePropertyNominalSampleRate: 830 { 831 LogRel(("CoreAudio: Recording sample rate changed\n")); 832 833 /* We move the reinitialization to the next input event. 834 * This make sure this thread isn't blocked and the 835 * reinitialization is done when necessary only. */ 836 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_REINIT); 837 break; 838 } 839 840 default: 841 break; 842 } 843 844 return noErr; 845 } 846 847 /** 848 * Implements the OS X callback AudioConverterComplexInputDataProc for 849 * converting audio input data from one format to another. 850 */ 851 static OSStatus coreAudioConverterCallback(AudioConverterRef inAudioConverter, 852 UInt32 *ioNumberDataPackets, 853 AudioBufferList *ioData, 854 AudioStreamPacketDescription **ppASPD, 855 void *pvUser) 856 { 857 RT_NOREF(inAudioConverter, ppASPD); 858 AssertPtrReturn(ioNumberDataPackets, g_caConverterEOFDErr); 859 AssertPtrReturn(ioData, g_caConverterEOFDErr); 1163 /** 1164 * Re-initializes a Core Audio stream with a specific audio device and stream configuration. 1165 * 1166 * @return IPRT status code. 1167 * @param pThis Driver instance. 1168 * @param pCAStream Audio stream to re-initialize. 1169 * @param pDev Audio device to use for re-initialization. 1170 * @param pCfg Stream configuration to use for re-initialization. 1171 */ 1172 static int coreAudioStreamReinitEx(PDRVHOSTCOREAUDIO pThis, 1173 PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev, PPDMAUDIOSTREAMCFG pCfg) 1174 { 1175 int rc = coreAudioStreamUninit(pCAStream); 1176 if (RT_SUCCESS(rc)) 1177 { 1178 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 1179 if (RT_SUCCESS(rc)) 1180 { 1181 if (pCAStream->enmDir == PDMAUDIODIR_IN) 1182 rc = coreAudioStreamInitIn (pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */); 1183 else 1184 rc = coreAudioStreamInitOut(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */); 1185 1186 if (RT_SUCCESS(rc)) 1187 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE); 1188 1189 if (RT_FAILURE(rc)) 1190 { 1191 int rc2 = coreAudioStreamUninit(pCAStream); 1192 AssertRC(rc2); 1193 } 1194 } 1195 } 1196 1197 if (RT_FAILURE(rc)) 1198 LogRel(("CoreAudio: Unable to re-init stream: %Rrc\n", rc)); 1199 1200 return rc; 1201 } 1202 1203 /** 1204 * Re-initializes a Core Audio stream with a specific audio device. 1205 * 1206 * @return IPRT status code. 1207 * @param pThis Driver instance. 1208 * @param pCAStream Audio stream to re-initialize. 1209 * @param pDev Audio device to use for re-initialization. 1210 */ 1211 static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PPDMAUDIODEVICE pDev) 1212 { 1213 int rc = coreAudioStreamUninit(pCAStream); 1214 if (RT_SUCCESS(rc)) 1215 { 1216 /* Use the acquired stream configuration from the former initialization to 1217 * re-initialize the stream. */ 1218 PDMAUDIOSTREAMCFG CfgAcq; 1219 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, &CfgAcq); 1220 if (RT_SUCCESS(rc)) 1221 rc = coreAudioStreamReinitEx(pThis, pCAStream, pDev, &CfgAcq); 1222 } 1223 1224 return rc; 1225 } 1226 1227 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 1228 /* Callback to convert audio input data from one format to another. */ 1229 static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef inAudioConverter, 1230 UInt32 *ioNumberDataPackets, 1231 AudioBufferList *ioData, 1232 AudioStreamPacketDescription **ppASPD, 1233 void *pvUser) 1234 { 1235 RT_NOREF(inAudioConverter); 1236 1237 AssertPtrReturn(ioNumberDataPackets, caConverterEOFDErr); 1238 AssertPtrReturn(ioData, caConverterEOFDErr); 860 1239 861 1240 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser; … … 867 1246 ioData->mBuffers[0].mData = NULL; 868 1247 869 /** @todo Check converter ID? */ 870 871 /** @todo Handled non-interleaved data by going through the full buffer list, 872 * not only through the first buffer like we do now. */ 873 874 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets)); 875 876 if (pConvCbCtx->uPacketIdx + *ioNumberDataPackets > pConvCbCtx->uPacketCnt) 877 { 878 Log3Func(("Limiting ioNumberDataPackets to %RU32\n", pConvCbCtx->uPacketCnt - pConvCbCtx->uPacketIdx)); 879 *ioNumberDataPackets = pConvCbCtx->uPacketCnt - pConvCbCtx->uPacketIdx; 880 } 881 882 if (*ioNumberDataPackets) 883 { 884 Assert(pConvCbCtx->bufLstSrc.mNumberBuffers == 1); /* Only one buffer for the source supported atm. */ 885 886 size_t cbOff = pConvCbCtx->uPacketIdx * pConvCbCtx->asbdSrc.mBytesPerPacket; 887 888 size_t cbAvail = RT_MIN(*ioNumberDataPackets * pConvCbCtx->asbdSrc.mBytesPerPacket, 889 pConvCbCtx->bufLstSrc.mBuffers[0].mDataByteSize - cbOff); 890 891 void *pvAvail = (uint8_t *)pConvCbCtx->bufLstSrc.mBuffers[0].mData + cbOff; 892 893 Log3Func(("cbOff=%zu, cbAvail=%zu\n", cbOff, cbAvail)); 894 895 /* Set input data for the converter to use. 896 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */ 897 ioData->mNumberBuffers = 1; 898 899 ioData->mBuffers[0].mNumberChannels = pConvCbCtx->bufLstSrc.mBuffers[0].mNumberChannels; 900 ioData->mBuffers[0].mDataByteSize = cbAvail; 901 ioData->mBuffers[0].mData = pvAvail; 1248 if (ppASPD) 1249 { 1250 Log3Func(("Handling packet description not implemented\n")); 1251 } 1252 else 1253 { 1254 /** @todo Check converter ID? */ 1255 1256 /** @todo Handled non-interleaved data by going through the full buffer list, 1257 * not only through the first buffer like we do now. */ 1258 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets)); 1259 1260 UInt32 cNumberDataPackets = *ioNumberDataPackets; 1261 Assert(pConvCbCtx->uPacketIdx + cNumberDataPackets <= pConvCbCtx->uPacketCnt); 1262 1263 if (cNumberDataPackets) 1264 { 1265 AssertPtr(pConvCbCtx->pBufLstSrc); 1266 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers == 1); /* Only one buffer for the source supported atm. */ 1267 1268 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc; 1269 AudioBuffer *pSrcBuf = &pConvCbCtx->pBufLstSrc->mBuffers[0]; 1270 1271 size_t cbOff = pConvCbCtx->uPacketIdx * pSrcASBD->mBytesPerPacket; 1272 1273 cNumberDataPackets = RT_MIN((pSrcBuf->mDataByteSize - cbOff) / pSrcASBD->mBytesPerPacket, 1274 cNumberDataPackets); 1275 1276 void *pvAvail = (uint8_t *)pSrcBuf->mData + cbOff; 1277 size_t cbAvail = RT_MIN(pSrcBuf->mDataByteSize - cbOff, cNumberDataPackets * pSrcASBD->mBytesPerPacket); 1278 1279 Log3Func(("cNumberDataPackets=%RU32, cbOff=%zu, cbAvail=%zu\n", cNumberDataPackets, cbOff, cbAvail)); 1280 1281 /* Set input data for the converter to use. 1282 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */ 1283 ioData->mNumberBuffers = 1; 1284 1285 ioData->mBuffers[0].mNumberChannels = pSrcBuf->mNumberChannels; 1286 ioData->mBuffers[0].mDataByteSize = cbAvail; 1287 ioData->mBuffers[0].mData = pvAvail; 902 1288 903 1289 #ifdef DEBUG_DUMP_PCM_DATA 904 RTFILE fh;905 int rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-converter-cb-input.pcm",906 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);907 if (RT_SUCCESS(rc))908 {909 RTFileWrite(fh, pvAvail, cbAvail, NULL);910 RTFileClose(fh);911 }912 else913 AssertFailed();1290 RTFILE fh; 1291 int rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-converter-cb-input.pcm", 1292 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1293 if (RT_SUCCESS(rc)) 1294 { 1295 RTFileWrite(fh, pvAvail, cbAvail, NULL); 1296 RTFileClose(fh); 1297 } 1298 else 1299 AssertFailed(); 914 1300 #endif 915 916 pConvCbCtx->uPacketIdx += *ioNumberDataPackets; 917 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt); 1301 pConvCbCtx->uPacketIdx += cNumberDataPackets; 1302 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt); 1303 1304 *ioNumberDataPackets = cNumberDataPackets; 1305 } 918 1306 } 919 1307 … … 923 1311 return noErr; 924 1312 } 925 926 /** 927 * Implements the OS X callback AURenderCallback in order to feed the audio 928 * input buffer. 929 */ 930 static OSStatus coreAudioRecordingCb(void *pvUser, 931 AudioUnitRenderActionFlags *pActionFlags, 932 const AudioTimeStamp *pAudioTS, 933 UInt32 uBusID, 934 UInt32 cFrames, 935 AudioBufferList *pBufData) 936 { 937 RT_NOREF(pBufData); 1313 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 1314 1315 /* Callback to feed audio input buffer. */ 1316 static DECLCALLBACK(OSStatus) coreAudioCaptureCb(void *pvUser, 1317 AudioUnitRenderActionFlags *pActionFlags, 1318 const AudioTimeStamp *pAudioTS, 1319 UInt32 uBusID, 1320 UInt32 cFrames, 1321 AudioBufferList *pBufData) 1322 { 1323 RT_NOREF(uBusID, pBufData); 938 1324 939 1325 /* If nothing is pending return immediately. */ … … 941 1327 return noErr; 942 1328 943 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pvUser; 944 945 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT) 1329 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser; 1330 1331 /* Sanity. */ 1332 AssertPtr(pStream); 1333 AssertPtr(pStream->pDrv); 1334 Assert (pStream->enmDir == PDMAUDIODIR_IN); 1335 AssertPtr(pStream->Unit.pDevice); 1336 1337 if (ASMAtomicReadU32(&pStream->status) != COREAUDIOSTATUS_INIT) 946 1338 return noErr; 947 948 PCOREAUDIOCONVCBCTX pConvCbCtx = &pStreamIn->convCbCtx;949 1339 950 1340 OSStatus err = noErr; 951 1341 int rc = VINF_SUCCESS; 952 1342 953 Assert(pConvCbCtx->bufLstSrc.mNumberBuffers == 1); 954 AudioBuffer *pSrcBuf = &pConvCbCtx->bufLstSrc.mBuffers[0]; 955 956 pConvCbCtx->uPacketCnt = cFrames / pConvCbCtx->asbdSrc.mFramesPerPacket; 957 pConvCbCtx->uPacketIdx = 0; 958 959 AudioConverterReset(pStreamIn->pConverter); 960 961 Log3Func(("cFrames=%RU32 (%RU32 frames per packet) -> %RU32 packets\n", 962 cFrames, pConvCbCtx->asbdSrc.mFramesPerPacket, pConvCbCtx->uPacketCnt)); 1343 AudioBufferList srcBufLst; 1344 RT_ZERO(srcBufLst); 963 1345 964 1346 do 965 1347 { 1348 #ifdef DEBUG 1349 AudioStreamBasicDescription asbdIn; 1350 UInt32 uSize = sizeof(asbdIn); 1351 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1352 1, &asbdIn, &uSize); 1353 coreAudioPrintASBD("DevIn", &asbdIn); 1354 1355 AudioStreamBasicDescription asbdOut; 1356 uSize = sizeof(asbdOut); 1357 AudioUnitGetProperty(pStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1358 1, &asbdOut, &uSize); 1359 coreAudioPrintASBD("DevOut", &asbdOut); 1360 #endif 1361 1362 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 966 1363 /* Are we using a converter? */ 967 if (pStreamIn->pConverter) 968 { 969 pSrcBuf->mNumberChannels = pConvCbCtx->asbdSrc.mChannelsPerFrame; 970 pSrcBuf->mDataByteSize = pConvCbCtx->asbdSrc.mBytesPerFrame * cFrames; 971 pSrcBuf->mData = RTMemAllocZ(pSrcBuf->mDataByteSize); 972 if (!pSrcBuf->mData) 1364 if (pStreamIn->ConverterRef) 1365 { 1366 PCOREAUDIOCONVCBCTX pConvCbCtx = &pStreamIn->convCbCtx; 1367 1368 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc; 1369 AudioStreamBasicDescription *pDstASBD = &pConvCbCtx->asbdDst; 1370 # ifdef DEBUG 1371 coreAudioPrintASBD("Src", pSrcASBD); 1372 coreAudioPrintASBD("Dst", pDstASBD); 1373 # endif 1374 /* Initialize source list buffer. */ 1375 srcBufLst.mNumberBuffers = 1; 1376 1377 /* Initialize the first buffer. */ 1378 srcBufLst.mBuffers[0].mNumberChannels = pSrcASBD->mChannelsPerFrame; 1379 srcBufLst.mBuffers[0].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames; 1380 srcBufLst.mBuffers[0].mData = RTMemAllocZ(srcBufLst.mBuffers[0].mDataByteSize); 1381 if (!srcBufLst.mBuffers[0].mData) 973 1382 { 974 1383 rc = VERR_NO_MEMORY; … … 976 1385 } 977 1386 1387 #if 0 1388 /* Initialize the second buffer. */ 1389 srcBufLst.mBuffers[1].mNumberChannels = 1; //pSrcASBD->mChannelsPerFrame; 1390 srcBufLst.mBuffers[1].mDataByteSize = pSrcASBD->mBytesPerFrame * cFrames; 1391 srcBufLst.mBuffers[1].mData = RTMemAllocZ(srcBufLst.mBuffers[1].mDataByteSize); 1392 if (!srcBufLst.mBuffers[1].mData) 1393 { 1394 rc = VERR_NO_MEMORY; 1395 break; 1396 } 1397 #endif 1398 1399 /* Set the buffer list for our callback context. */ 1400 pConvCbCtx->pBufLstSrc = &srcBufLst; 1401 1402 /* Sanity. */ 1403 AssertPtr(pConvCbCtx->pBufLstSrc); 1404 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers >= 1); 1405 1406 /* Get the first buffer. */ 1407 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0]; 1408 1409 Log3Func(("pSrcBuf->mDataByteSize1=%RU32\n", pSrcBuf->mDataByteSize)); 1410 978 1411 /* First, render the source data as usual. */ 979 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pConvCbCtx->bufLstSrc); 1412 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, 1413 &srcBufLst); 980 1414 if (err != noErr) 981 1415 { 982 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32)\n", err)); 1416 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */ 1417 { 1418 LogRel2(("CoreAudio: Failed rendering converted audio input data (%RI32:%c%c%c%c)\n", err, 1419 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err))); 1420 pConvCbCtx->cErrors++; 1421 } 1422 1423 rc = VERR_IO_GEN_FAILURE; 1424 break; 1425 } 1426 1427 /* Note: pSrcBuf->mDataByteSize can have changed after calling AudioUnitRender above! */ 1428 Log3Func(("pSrcBuf->mDataByteSize2=%RU32\n", pSrcBuf->mDataByteSize)); 1429 1430 # ifdef DEBUG_DUMP_PCM_DATA 1431 RTFILE fh; 1432 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-src.pcm", 1433 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1434 if (RT_SUCCESS(rc)) 1435 { 1436 RTFileWrite(fh, pSrcBuf->mData, pSrcBuf->mDataByteSize, NULL); 1437 RTFileClose(fh); 1438 } 1439 else 1440 AssertFailed(); 1441 # endif 1442 AudioBufferList dstBufLst; 1443 RT_ZERO(dstBufLst); 1444 1445 dstBufLst.mNumberBuffers = 1; /* We only use one buffer at once. */ 1446 1447 AudioBuffer *pDstBuf = &dstBufLst.mBuffers[0]; 1448 1449 UInt32 cbDst = pDstASBD->mBytesPerFrame * cFrames; 1450 void *pvDst = RTMemAlloc(cbDst); 1451 if (!pvDst) 1452 { 1453 rc = VERR_NO_MEMORY; 1454 break; 1455 } 1456 1457 pDstBuf->mDataByteSize = cbDst; 1458 pDstBuf->mData = pvDst; 1459 1460 AudioConverterReset(pStreamIn->ConverterRef); 1461 1462 Log3Func(("cbSrcBufSize=%RU32 (BPF=%RU32), cbDstBufSize=%RU32 (BPF=%RU32)\n", 1463 pSrcBuf->mDataByteSize, pSrcASBD->mBytesPerFrame, 1464 pDstBuf->mDataByteSize, pDstASBD->mBytesPerFrame)); 1465 1466 if (pSrcASBD->mSampleRate == pDstASBD->mSampleRate) 1467 { 1468 err = AudioConverterConvertBuffer(pStreamIn->ConverterRef, 1469 #if 0 1470 cbT1, pvT1, &cbT2, pvT2); 1471 #else 1472 pSrcBuf->mDataByteSize, pSrcBuf->mData /* Input */, 1473 &cbDst, pvDst /* Output */); 1474 #endif 1475 if (err != noErr) 1476 { 1477 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */ 1478 { 1479 LogRel2(("CoreAudio: Failed to convert audio input data (%RI32:%c%c%c%c)\n", err, 1480 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err))); 1481 pConvCbCtx->cErrors++; 1482 } 1483 1484 rc = VERR_IO_GEN_FAILURE; 1485 break; 1486 } 1487 1488 # ifdef DEBUG_DUMP_PCM_DATA 1489 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-conv-dst.pcm", 1490 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1491 if (RT_SUCCESS(rc)) 1492 { 1493 RTFileWrite(fh, pvDst, cbDst, NULL); 1494 RTFileClose(fh); 1495 } 1496 else 1497 AssertFailed(); 1498 # endif 1499 } 1500 else /* Invoke FillComplexBuffer because the sample rate is different. */ 1501 { 1502 pConvCbCtx->uPacketCnt = pSrcBuf->mDataByteSize / pSrcASBD->mBytesPerPacket; 1503 pConvCbCtx->uPacketIdx = 0; 1504 1505 Log3Func(("cFrames=%RU32 (%RU32 dest frames per packet) -> %RU32 input frames\n", 1506 cFrames, pDstASBD->mFramesPerPacket, pConvCbCtx->uPacketCnt)); 1507 1508 UInt32 cPacketsToWrite = pDstBuf->mDataByteSize / pDstASBD->mBytesPerPacket; 1509 Assert(cPacketsToWrite); 1510 1511 UInt32 cPacketsWritten = 0; 1512 1513 Log3Func(("cPacketsToWrite=%RU32\n", cPacketsToWrite)); 1514 1515 while (cPacketsToWrite) 1516 { 1517 UInt32 cPacketsIO = cPacketsToWrite; 1518 1519 Log3Func(("cPacketsIO=%RU32 (In)\n", cPacketsIO)); 1520 1521 err = AudioConverterFillComplexBuffer(pStreamIn->ConverterRef, 1522 coreAudioConverterCb, pConvCbCtx /* pvData */, 1523 &cPacketsIO, &dstBufLst, NULL); 1524 if (err != noErr) 1525 { 1526 if (pConvCbCtx->cErrors < 32) /** @todo Make this configurable. */ 1527 { 1528 LogRel2(("CoreAudio: Failed to convert complex audio data (%RI32:%c%c%c%c)\n", err, 1529 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err))); 1530 pConvCbCtx->cErrors++; 1531 } 1532 1533 rc = VERR_IO_GEN_FAILURE; 1534 break; 1535 } 1536 1537 Log3Func(("cPacketsIO=%RU32 (Out)\n", cPacketsIO)); 1538 1539 cPacketsWritten = cPacketsIO; 1540 1541 Assert(cPacketsToWrite >= cPacketsWritten); 1542 cPacketsToWrite -= cPacketsWritten; 1543 1544 size_t cbPacketsWritten = cPacketsWritten * pDstASBD->mBytesPerPacket; 1545 Log3Func(("%RU32 packets written (%zu bytes), err=%RI32\n", cPacketsWritten, cbPacketsWritten, err)); 1546 1547 if (!cPacketsWritten) 1548 break; 1549 1550 # ifdef DEBUG_DUMP_PCM_DATA 1551 rc = RTFileOpen(&fh, DEBUG_DUMP_PCM_DATA_PATH "ca-recording-cb-complex-dst.pcm", 1552 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1553 if (RT_SUCCESS(rc)) 1554 { 1555 RTFileWrite(fh, pvDst, cbDst, NULL); 1556 RTFileClose(fh); 1557 } 1558 else 1559 AssertFailed(); 1560 # endif 1561 size_t cbFree = RTCircBufFree(pStreamIn->pCircBuf); 1562 if (cbFree < cbDst) 1563 { 1564 LogRel2(("CoreAudio: Recording is lagging behind (%zu bytes available but only %zu bytes free)\n", 1565 cbDst, cbFree)); 1566 break; 1567 } 1568 1569 size_t cbDstChunk; 1570 void *puDst; 1571 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbDst, (void **)&puDst, &cbDstChunk); 1572 1573 if (cbDstChunk) 1574 memcpy(puDst, pvDst, cbDstChunk); 1575 1576 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbDstChunk); 1577 } 1578 } 1579 1580 if (pvDst) 1581 { 1582 RTMemFree(pvDst); 1583 pvDst = NULL; 1584 } 1585 } 1586 else /* No converter being used. */ 1587 { 1588 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 1589 1590 AudioStreamBasicDescription *pStreamFmt = &pStream->Unit.streamFmt; 1591 1592 AssertBreakStmt(pStreamFmt->mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER); 1593 AssertBreakStmt(pStreamFmt->mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER); 1594 1595 srcBufLst.mNumberBuffers = 1; 1596 1597 AudioBuffer *pSrcBuf = &srcBufLst.mBuffers[0]; 1598 1599 pSrcBuf->mNumberChannels = pStreamFmt->mChannelsPerFrame; 1600 pSrcBuf->mDataByteSize = pStreamFmt->mBytesPerFrame * cFrames; 1601 pSrcBuf->mData = RTMemAlloc(pSrcBuf->mDataByteSize); 1602 if (!pSrcBuf->mData) 1603 { 1604 rc = VERR_NO_MEMORY; 1605 break; 1606 } 1607 1608 err = AudioUnitRender(pStream->Unit.audioUnit, pActionFlags, pAudioTS, 1 /* Input bus */, cFrames, &srcBufLst); 1609 if (err != noErr) 1610 { 1611 LogRel2(("CoreAudio: Failed rendering non-converted audio input data (%RI32)\n", err)); 983 1612 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */ 984 1613 break; 985 1614 } 986 987 Log3Func(("cbSrcBufSize=%RU32\n", pSrcBuf->mDataByteSize));988 1615 989 1616 #ifdef DEBUG_DUMP_PCM_DATA … … 999 1626 AssertFailed(); 1000 1627 #endif 1001 AudioBufferList dstBufList; 1002 1003 dstBufList.mNumberBuffers = 1; /* We only use one buffer at once. */ 1004 1005 AudioBuffer *pDstBuf = &dstBufList.mBuffers[0]; 1006 pDstBuf->mDataByteSize = pConvCbCtx->asbdDst.mBytesPerFrame * cFrames; 1007 pDstBuf->mData = RTMemAllocZ(pDstBuf->mDataByteSize); 1008 if (!pDstBuf->mData) 1009 { 1010 rc = VERR_NO_MEMORY; 1011 break; 1012 } 1013 1014 UInt32 cPacketsToWriteAndWritten = pConvCbCtx->uPacketCnt; 1015 Assert(cPacketsToWriteAndWritten); 1016 1017 Log3Func(("cPacketsToWrite=%RU32\n", cPacketsToWriteAndWritten)); 1018 1019 do 1020 { 1021 err = AudioConverterFillComplexBuffer(pStreamIn->pConverter, 1022 coreAudioConverterCallback, pConvCbCtx /* pvData */, 1023 &cPacketsToWriteAndWritten, &dstBufList, NULL); 1024 1025 Log3Func(("cPacketsWritten=%RU32 (%zu bytes), err=%RI32\n", 1026 cPacketsToWriteAndWritten, cPacketsToWriteAndWritten * pConvCbCtx->asbdDst.mBytesPerPacket, err)); 1027 1028 if (err != noErr) 1029 { 1030 LogFlowFunc(("Failed to convert audio data (%RI32:%c%c%c%c)\n", err, 1031 RT_BYTE4(err), RT_BYTE3(err), RT_BYTE2(err), RT_BYTE1(err))); 1032 rc = VERR_IO_GEN_FAILURE; 1628 PRTCIRCBUF pCircBuf = pStream->pCircBuf; 1629 1630 const uint32_t cbDataSize = pSrcBuf->mDataByteSize; 1631 const size_t cbBufFree = RTCircBufFree(pCircBuf); 1632 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree); 1633 1634 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail)); 1635 1636 /* Iterate as long as data is available. */ 1637 uint8_t *puDst = NULL; 1638 uint32_t cbWrittenTotal = 0; 1639 while (cbAvail) 1640 { 1641 /* Try to acquire the necessary space from the ring buffer. */ 1642 size_t cbToWrite = 0; 1643 RTCircBufAcquireWriteBlock(pCircBuf, cbAvail, (void **)&puDst, &cbToWrite); 1644 if (!cbToWrite) 1033 1645 break; 1034 } 1035 1036 if (cPacketsToWriteAndWritten == 0) 1037 break; 1038 1039 size_t cbDst = cPacketsToWriteAndWritten * pConvCbCtx->asbdDst.mBytesPerPacket; 1646 1647 /* Copy the data from the Core Audio buffer to the ring buffer. */ 1648 memcpy(puDst, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite); 1040 1649 1041 1650 #ifdef DEBUG_DUMP_PCM_DATA … … 1044 1653 if (RT_SUCCESS(rc)) 1045 1654 { 1046 RTFileWrite(fh, pDstBuf->mData, cbDst, NULL);1655 RTFileWrite(fh, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite, NULL); 1047 1656 RTFileClose(fh); 1048 1657 } … … 1050 1659 AssertFailed(); 1051 1660 #endif 1052 size_t cbFree = RTCircBufFree(pStreamIn->pCircBuf);1053 if (cbFree < cbDst)1054 {1055 LogRel2(("CoreAudio: Recording is lagging behind (%zu bytes available but only %zu bytes free)\n",1056 cbDst, cbFree));1057 break;1058 }1059 1060 size_t cbDstChunk;1061 void *puDst;1062 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbDst, (void **)&puDst, &cbDstChunk);1063 1064 if (cbDstChunk)1065 memcpy(puDst, pDstBuf->mData, cbDstChunk);1066 1067 RTCircBufReleaseWriteBlock(pStreamIn->pCircBuf, cbDstChunk);1068 1069 } while (1);1070 1071 if (pDstBuf->mData)1072 {1073 RTMemFree(pDstBuf->mData);1074 pDstBuf->mData = NULL;1075 }1076 }1077 else /* No converter being used. */1078 {1079 AssertBreakStmt(pStreamIn->streamFormat.mChannelsPerFrame >= 1, rc = VERR_INVALID_PARAMETER);1080 AssertBreakStmt(pStreamIn->streamFormat.mBytesPerFrame >= 1, rc = VERR_INVALID_PARAMETER);1081 1082 AssertBreakStmt(pSrcBuf->mNumberChannels, rc = VERR_INVALID_PARAMETER);1083 1084 pSrcBuf->mNumberChannels = pStreamIn->streamFormat.mChannelsPerFrame;1085 pSrcBuf->mDataByteSize = pStreamIn->streamFormat.mBytesPerFrame * cFrames;1086 pSrcBuf->mData = RTMemAlloc(pSrcBuf->mDataByteSize);1087 if (!pSrcBuf->mData)1088 {1089 rc = VERR_NO_MEMORY;1090 break;1091 }1092 1093 err = AudioUnitRender(pStreamIn->audioUnit, pActionFlags, pAudioTS, uBusID, cFrames, &pConvCbCtx->bufLstSrc);1094 if (err != noErr)1095 {1096 LogRel2(("CoreAudio: Failed rendering non-coverted audio input data (%RI32)\n", err));1097 rc = VERR_IO_GEN_FAILURE; /** @todo Improve this. */1098 break;1099 }1100 1101 const uint32_t cbDataSize = pSrcBuf->mDataByteSize;1102 const size_t cbBufFree = RTCircBufFree(pStreamIn->pCircBuf);1103 size_t cbAvail = RT_MIN(cbDataSize, cbBufFree);1104 1105 Log3Func(("cbDataSize=%RU32, cbBufFree=%zu, cbAvail=%zu\n", cbDataSize, cbBufFree, cbAvail));1106 1107 /* Iterate as long as data is available. */1108 uint8_t *puDst = NULL;1109 uint32_t cbWrittenTotal = 0;1110 while (cbAvail)1111 {1112 /* Try to acquire the necessary space from the ring buffer. */1113 size_t cbToWrite = 0;1114 RTCircBufAcquireWriteBlock(pStreamIn->pCircBuf, cbAvail, (void **)&puDst, &cbToWrite);1115 if (!cbToWrite)1116 break;1117 1118 /* Copy the data from the Core Audio buffer to the ring buffer. */1119 memcpy(puDst, (uint8_t *)pSrcBuf->mData + cbWrittenTotal, cbToWrite);1120 1121 1661 /* Release the ring buffer, so the main thread could start reading this data. */ 1122 RTCircBufReleaseWriteBlock(p StreamIn->pCircBuf, cbToWrite);1662 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite); 1123 1663 1124 1664 cbWrittenTotal += cbToWrite; … … 1129 1669 1130 1670 Log3Func(("cbWrittenTotal=%RU32, cbLeft=%zu\n", cbWrittenTotal, cbAvail)); 1131 } 1671 1672 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 1673 } 1674 #endif 1132 1675 1133 1676 } while (0); 1134 1677 1135 if (pSrcBuf->mData) 1136 { 1137 RTMemFree(pSrcBuf->mData); 1138 pSrcBuf->mData = NULL; 1678 for (UInt32 i = 0; i < srcBufLst.mNumberBuffers; i++) 1679 { 1680 if (srcBufLst.mBuffers[i].mData) 1681 { 1682 RTMemFree(srcBufLst.mBuffers[i].mData); 1683 srcBufLst.mBuffers[i].mData = NULL; 1684 } 1139 1685 } 1140 1686 … … 1142 1688 } 1143 1689 1144 #define CA_BREAK_STMT(stmt) if (true) \ 1145 { \ 1146 stmt; \ 1147 break; \ 1148 } else do { } while (0) 1690 1691 /** 1692 * Initializes a Core Audio stream. 1693 * 1694 * @return IPRT status code. 1695 * @param pThis Driver instance. 1696 * @param pCAStream Stream to initialize. 1697 * @param pDev Audio device to use for this stream. 1698 */ 1699 static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev) 1700 { 1701 Assert(pCAStream->Unit.pDevice == NULL); /* Make sure no device is assigned yet. */ 1702 1703 pCAStream->Unit.pDevice = pDev; 1704 pCAStream->pDrv = pThis; 1705 1706 return VINF_SUCCESS; 1707 } 1708 1709 # define CA_BREAK_STMT(stmt) \ 1710 stmt; \ 1711 break; 1149 1712 1150 1713 /** @todo Eventually split up this function, as this already is huge! */ 1151 static int coreAudioInitIn(PDRVHOSTCOREAUDIO pThis, PPDMAUDIOSTREAM pStream, 1152 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1153 { 1154 RT_NOREF(pThis); 1155 1714 static int coreAudioStreamInitIn(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1715 { 1156 1716 int rc = VINF_SUCCESS; 1157 1717 1158 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;1159 1718 UInt32 cSamples = 0; 1160 1719 1161 1720 OSStatus err = noErr; 1162 UInt32 uSize = 0; 1163 1164 AudioDeviceID deviceID = pStreamIn->deviceID; 1165 if (deviceID == kAudioDeviceUnknown) 1166 { 1167 /* Fetch the default audio recording device currently in use. */ 1168 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, 1169 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 1170 uSize = sizeof(deviceID); 1171 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &deviceID); 1172 if (err != noErr) 1173 { 1174 LogFlowFunc(("CoreAudio: Unable to determine default recording device (%RI32)\n", err)); 1175 return VERR_AUDIO_NO_FREE_INPUT_STREAMS; 1176 } 1177 } 1178 1179 if (deviceID == kAudioDeviceUnknown) 1180 { 1181 LogFlowFunc(("No default recording device found\n")); 1182 return VERR_AUDIO_NO_FREE_INPUT_STREAMS; 1183 } 1721 1722 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice; 1723 AssertPtr(pDev); 1724 1725 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 1726 AssertPtr(pData); 1727 1728 AudioDeviceID deviceID = pData->deviceID; 1729 Assert(deviceID != kAudioDeviceUnknown); 1184 1730 1185 1731 do 1186 1732 { 1187 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_INIT); 1188 1189 /* Assign device ID. */ 1190 pStreamIn->deviceID = deviceID; 1191 1192 /* 1193 * Try to get the name of the recording device and log it. It's not fatal if it fails. 1194 */ 1195 CFStringRef strTemp; 1196 1197 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, 1198 kAudioObjectPropertyElementMaster }; 1199 uSize = sizeof(CFStringRef); 1200 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp); 1201 if (err == noErr) 1202 { 1203 char *pszDevName = NULL; 1204 err = coreAudioCFStringToCString(strTemp, &pszDevName); 1205 if (err == noErr) 1206 { 1207 CFRelease(strTemp); 1208 1209 /* Get the device' UUID. */ 1210 propAdr.mSelector = kAudioDevicePropertyDeviceUID; 1211 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &strTemp); 1212 if (err == noErr) 1213 { 1214 char *pszUID = NULL; 1215 1216 err = coreAudioCFStringToCString(strTemp, &pszUID); 1217 if (err == noErr) 1218 { 1219 CFRelease(strTemp); 1220 LogRel(("CoreAudio: Using recording device: %s (UID: %s)\n", pszDevName, pszUID)); 1221 1222 RTMemFree(pszUID); 1223 } 1224 } 1225 1226 RTMemFree(pszDevName); 1227 } 1228 } 1229 else 1230 { 1231 /* This is not fatal, can happen for some Macs. */ 1232 LogRel2(("CoreAudio: Unable to determine recording device name (%RI32)\n", err)); 1233 } 1733 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_IN_INIT); 1234 1734 1235 1735 /* Get the default frames buffer size, so that we can setup our internal buffers. */ 1236 1736 UInt32 cFrames; 1237 uSize = sizeof(cFrames); 1737 UInt32 uSize = sizeof(cFrames); 1738 1739 AudioObjectPropertyAddress propAdr; 1238 1740 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize; 1239 1741 propAdr.mScope = kAudioDevicePropertyScopeInput; 1240 err = AudioObjectGetPropertyData(pStreamIn->deviceID, &propAdr, 0, NULL, &uSize, &cFrames); 1742 propAdr.mElement = kAudioObjectPropertyElementMaster; 1743 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames); 1241 1744 if (err != noErr) 1242 1745 { … … 1248 1751 1249 1752 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */ 1250 err = coreAudioSetFrameBufferSize( pStreamIn->deviceID, true /* fInput */, cFrames, &cFrames);1753 err = coreAudioSetFrameBufferSize(deviceID, true /* fInput */, cFrames, &cFrames); 1251 1754 if (err != noErr) 1252 1755 { … … 1273 1776 1274 1777 /* Open the default HAL output component. */ 1275 err = AudioComponentInstanceNew(cp, &p StreamIn->audioUnit);1778 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit); 1276 1779 if (err != noErr) 1277 1780 { … … 1282 1785 /* Switch the I/O mode for input to on. */ 1283 1786 UInt32 uFlag = 1; 1284 err = AudioUnitSetProperty(p StreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input,1787 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1285 1788 1, &uFlag, sizeof(uFlag)); 1286 1789 if (err != noErr) … … 1292 1795 /* Switch the I/O mode for output to off. This is important, as this is a pure input stream. */ 1293 1796 uFlag = 0; 1294 err = AudioUnitSetProperty(p StreamIn->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,1797 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 1295 1798 0, &uFlag, sizeof(uFlag)); 1296 1799 if (err != noErr) … … 1301 1804 1302 1805 /* Set the default audio recording device as the device for the new AudioUnit. */ 1303 err = AudioUnitSetProperty(p StreamIn->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,1304 0, & pStreamIn->deviceID, sizeof(pStreamIn->deviceID));1806 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 1807 0, &deviceID, sizeof(deviceID)); 1305 1808 if (err != noErr) 1306 1809 { 1307 LogRel(("CoreAudio: Failed to set current device (%RI32)\n", err));1810 LogRel(("CoreAudio: Failed to set current input device (%RI32)\n", err)); 1308 1811 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1309 1812 } … … 1315 1818 AURenderCallbackStruct cb; 1316 1819 RT_ZERO(cb); 1317 cb.inputProc = coreAudio RecordingCb;1318 cb.inputProcRefCon = p StreamIn;1319 1320 err = AudioUnitSetProperty(p StreamIn->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global,1820 cb.inputProc = coreAudioCaptureCb; 1821 cb.inputProcRefCon = pCAStream; 1822 1823 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 1321 1824 0, &cb, sizeof(cb)); 1322 1825 if (err != noErr) … … 1326 1829 } 1327 1830 1328 /* Fetch the current stream format of the device. */ 1329 RT_ZERO(pStreamIn->deviceFormat); 1330 uSize = sizeof(pStreamIn->deviceFormat); 1331 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1332 1, &pStreamIn->deviceFormat, &uSize); 1831 /* Create the recording device's out format based on our required audio settings. */ 1832 AudioStreamBasicDescription reqFmt; 1833 rc = coreAudioStreamCfgToASBD(pCfgReq, &reqFmt); 1834 if (RT_FAILURE(rc)) 1835 { 1836 LogRel(("CoreAudio: Failed to convert requested input format to native format (%Rrc)\n", rc)); 1837 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1838 } 1839 1840 coreAudioPrintASBD("Requested stream input format", &reqFmt); 1841 1842 /* Fetch the input format of the recording device. */ 1843 AudioStreamBasicDescription devInFmt; 1844 RT_ZERO(devInFmt); 1845 uSize = sizeof(devInFmt); 1846 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1847 1, &devInFmt, &uSize); 1333 1848 if (err != noErr) 1334 1849 { 1335 LogRel(("CoreAudio: Failed to get deviceformat (%RI32)\n", err));1850 LogRel(("CoreAudio: Failed to get input device input format (%RI32)\n", err)); 1336 1851 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1337 1852 } 1338 1853 1339 /* Create an AudioStreamBasicDescription based on our required audio settings. */ 1340 RT_ZERO(pStreamIn->streamFormat); 1341 coreAudioStreamCfgToASBD(pCfgReq, &pStreamIn->streamFormat); 1342 1343 coreAudioPrintASBD("Recording device", &pStreamIn->deviceFormat); 1344 coreAudioPrintASBD("Recording stream", &pStreamIn->streamFormat); 1345 1346 /* If the frequency of the device is different from the requested one we 1347 * need a converter. The same count if the number of channels is different. */ 1348 if ( pStreamIn->deviceFormat.mSampleRate != pStreamIn->streamFormat.mSampleRate 1349 || pStreamIn->deviceFormat.mChannelsPerFrame != pStreamIn->streamFormat.mChannelsPerFrame) 1854 coreAudioPrintASBD("Input device in (initial)", &devInFmt); 1855 1856 /* Fetch the output format of the recording device. */ 1857 AudioStreamBasicDescription devOutFmt; 1858 RT_ZERO(devOutFmt); 1859 uSize = sizeof(devOutFmt); 1860 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1861 1, &devOutFmt, &uSize); 1862 if (err != noErr) 1863 { 1864 LogRel(("CoreAudio: Failed to get input device output format (%RI32)\n", err)); 1865 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1866 } 1867 1868 coreAudioPrintASBD("Input device out (initial)", &devOutFmt); 1869 1870 /* Set the output format for the input device so that it matches the initial input format. 1871 * This apparently is needed for some picky / buggy USB headsets. */ 1872 1873 /* 1874 * The only thing we tweak here is the actual format flags: A lot of USB headsets tend 1875 * to have float PCM data, which we can't handle (yet). 1876 * 1877 * So set this as signed integers in a packed, non-iterleaved format. 1878 */ 1879 devInFmt.mFormatFlags = kAudioFormatFlagIsSignedInteger 1880 | kAudioFormatFlagIsPacked; 1881 1882 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1883 1, &devInFmt, sizeof(devInFmt)); 1884 if (err != noErr) 1885 { 1886 LogRel(("CoreAudio: Failed to set new output format for input device (%RI32)\n", err)); 1887 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1888 } 1889 1890 /* 1891 * Also set the frame buffer size of the device on our AudioUnit. This 1892 * should make sure that the frames count which we receive in the render 1893 * thread is as we like. 1894 */ 1895 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1896 1, &cFrames, sizeof(cFrames)); 1897 if (err != noErr) 1898 { 1899 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err)); 1900 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1901 } 1902 1903 /* 1904 * Initialize the new AudioUnit. 1905 */ 1906 err = AudioUnitInitialize(pCAStream->Unit.audioUnit); 1907 if (err != noErr) 1908 { 1909 LogRel(("CoreAudio: Failed to initialize input audio device (%RI32)\n", err)); 1910 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1911 } 1912 1913 /* Get final input format afer initialization. */ 1914 uSize = sizeof(devInFmt); 1915 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1916 1, &devInFmt, &uSize); 1917 if (err != noErr) 1918 { 1919 LogRel(("CoreAudio: Failed to re-getting input device input format (%RI32)\n", err)); 1920 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1921 } 1922 1923 /* Print the device/stream formats again after the audio unit has been initialized. 1924 * Note that the formats could have changed now. */ 1925 coreAudioPrintASBD("Input device in (after initialization)", &devInFmt); 1926 1927 /* Get final output format afer initialization. */ 1928 uSize = sizeof(devOutFmt); 1929 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1930 1, &devOutFmt, &uSize); 1931 if (err != noErr) 1932 { 1933 LogRel(("CoreAudio: Failed to re-getting input device output format (%RI32)\n", err)); 1934 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1935 } 1936 1937 /* Print the device/stream formats again after the audio unit has been initialized. 1938 * Note that the formats could have changed now. */ 1939 coreAudioPrintASBD("Input device out (after initialization)", &devOutFmt); 1940 1941 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 1942 1943 /* If the frequency of the device' output format different from the requested one we 1944 * need a converter. The same counts if the number of channels or the bits per channel are different. */ 1945 if ( devOutFmt.mChannelsPerFrame != reqFmt.mChannelsPerFrame 1946 || devOutFmt.mSampleRate != reqFmt.mSampleRate) 1350 1947 { 1351 1948 LogRel2(("CoreAudio: Input converter is active\n")); 1352 1949 1353 err = AudioConverterNew(& pStreamIn->deviceFormat, &pStreamIn->streamFormat, &pStreamIn->pConverter);1950 err = AudioConverterNew(&devOutFmt /* Input */, &reqFmt /* Output */, &pCAStream->In.ConverterRef); 1354 1951 if (RT_UNLIKELY(err != noErr)) 1355 1952 { … … 1358 1955 } 1359 1956 1360 if ( pStreamIn->deviceFormat.mChannelsPerFrame== 1 /* Mono */1361 && pStreamIn->streamFormat.mChannelsPerFrame== 2 /* Stereo */)1957 if ( devOutFmt.mChannelsPerFrame == 1 /* Mono */ 1958 && reqFmt.mChannelsPerFrame == 2 /* Stereo */) 1362 1959 { 1363 1960 LogRel2(("CoreAudio: Mono to stereo conversion active\n")); … … 1372 1969 const SInt32 channelMap[2] = {0, 0}; /* Channel map for mono -> stereo. */ 1373 1970 1374 err = AudioConverterSetProperty(p StreamIn->pConverter, kAudioConverterChannelMap, sizeof(channelMap), channelMap);1971 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterChannelMap, sizeof(channelMap), channelMap); 1375 1972 if (err != noErr) 1376 1973 { … … 1382 1979 /* Set sample rate converter quality to maximum. */ 1383 1980 uFlag = kAudioConverterQuality_Max; 1384 err = AudioConverterSetProperty(p StreamIn->pConverter, kAudioConverterSampleRateConverterQuality,1981 err = AudioConverterSetProperty(pCAStream->In.ConverterRef, kAudioConverterSampleRateConverterQuality, 1385 1982 sizeof(uFlag), &uFlag); 1386 1983 if (err != noErr) … … 1389 1986 uSize = sizeof(UInt32); 1390 1987 UInt32 maxOutputSize; 1391 err = AudioConverterGetProperty(pStreamIn-> pConverter, kAudioConverterPropertyMaximumOutputPacketSize,1988 err = AudioConverterGetProperty(pStreamIn->ConverterRef, kAudioConverterPropertyMaximumOutputPacketSize, 1392 1989 &uSize, &maxOutputSize); 1393 1990 if (RT_UNLIKELY(err != noErr)) … … 1398 1995 1399 1996 LogFunc(("Maximum converter packet output size is: %RI32\n", maxOutputSize)); 1400 1401 /* Set the input (source) format, that is, the format the device is recording data with. */ 1402 err = AudioUnitSetProperty(pStreamIn->audioUnit, 1403 kAudioUnitProperty_StreamFormat, 1404 kAudioUnitScope_Input, 1405 1, 1406 &pStreamIn->deviceFormat, 1407 sizeof(pStreamIn->deviceFormat)); 1408 if (RT_UNLIKELY(err != noErr)) 1409 { 1410 LogRel(("CoreAudio: Failed to set device input format (%RI32)\n", err)); 1411 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1412 } 1413 1414 /* Set the output (target) format, that is, the format we created the input stream with. */ 1415 err = AudioUnitSetProperty(pStreamIn->audioUnit, 1416 kAudioUnitProperty_StreamFormat, 1417 kAudioUnitScope_Output, 1418 1, 1419 &pStreamIn->deviceFormat, 1420 sizeof(pStreamIn->deviceFormat)); 1421 if (RT_UNLIKELY(err != noErr)) 1422 { 1423 LogRel(("CoreAudio: Failed to set stream output format (%RI32)\n", err)); 1424 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1425 } 1997 } 1998 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 1999 2000 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 2001 if (pCAStream->In.ConverterRef) 2002 { 2003 /* Save the requested format as our stream format. */ 2004 memcpy(&pCAStream->Unit.streamFmt, &reqFmt, sizeof(AudioStreamBasicDescription)); 1426 2005 } 1427 2006 else 1428 2007 { 1429 /* Set the new output format description for the input stream. */ 1430 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1431 1, &pStreamIn->streamFormat, sizeof(pStreamIn->streamFormat)); 1432 if (err != noErr) 1433 { 1434 LogRel(("CoreAudio: Failed to set output format for input stream (%RI32)\n", err)); 1435 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1436 } 1437 } 1438 1439 /* 1440 * Also set the frame buffer size off the device on our AudioUnit. This 1441 * should make sure that the frames count which we receive in the render 1442 * thread is as we like. 1443 */ 1444 err = AudioUnitSetProperty(pStreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1445 1, &cFrames, sizeof(cFrames)); 1446 if (err != noErr) 1447 { 1448 LogRel(("CoreAudio: Failed to set maximum frame buffer size for input stream (%RI32)\n", err)); 1449 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1450 } 1451 1452 /* Finally initialize the new AudioUnit. */ 1453 err = AudioUnitInitialize(pStreamIn->audioUnit); 1454 if (err != noErr) 1455 { 1456 LogRel(("CoreAudio: Failed to initialize audio unit for input stream (%RI32)\n", err)); 1457 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1458 } 1459 1460 uSize = sizeof(pStreamIn->deviceFormat); 1461 err = AudioUnitGetProperty(pStreamIn->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1462 1, &pStreamIn->deviceFormat, &uSize); 1463 if (err != noErr) 1464 { 1465 LogRel(("CoreAudio: Failed to get recording device format (%RI32)\n", err)); 1466 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1467 } 1468 2008 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 2009 2010 /* Save the final output format as our stream format. */ 2011 memcpy(&pCAStream->Unit.streamFmt, &devOutFmt, sizeof(AudioStreamBasicDescription)); 2012 2013 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 2014 } 2015 #endif 1469 2016 /* 1470 2017 * There are buggy devices (e.g. my Bluetooth headset) which doesn't honor … … 1473 2020 */ 1474 2021 uSize = sizeof(cFrames); 1475 err = AudioUnitGetProperty(p StreamIn->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2022 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1476 2023 0, &cFrames, &uSize); 1477 2024 if (err != noErr) … … 1481 2028 } 1482 2029 1483 /* Destroy any former internal ring buffer. */1484 if (pStreamIn->pCircBuf)1485 {1486 RTCircBufDestroy(pStreamIn->pCircBuf);1487 pStreamIn->pCircBuf = NULL;1488 }1489 1490 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx);1491 1492 2030 /* Calculate the ratio between the device and the stream sample rate. */ 1493 p StreamIn->sampleRatio = pStreamIn->streamFormat.mSampleRate / pStreamIn->deviceFormat.mSampleRate;2031 pCAStream->In.sampleRatio = devOutFmt.mSampleRate / devInFmt.mSampleRate; 1494 2032 1495 2033 /* … … 1501 2039 */ 1502 2040 cSamples = RT_MAX(cFrames, 1503 (cFrames * pStreamIn->deviceFormat.mBytesPerFrame * pStreamIn->sampleRatio)1504 / pStreamIn->streamFormat.mBytesPerFrame)1505 * pStreamIn->streamFormat.mChannelsPerFrame;2041 (cFrames * reqFmt.mBytesPerFrame * pCAStream->In.sampleRatio) 2042 / reqFmt.mBytesPerFrame) 2043 * reqFmt.mChannelsPerFrame; 1506 2044 if (!cSamples) 1507 2045 { … … 1510 2048 } 1511 2049 1512 PDMAUDIOPCMPROPS PCMProps; 1513 int rc2 = DrvAudioHlpStreamCfgToProps(pCfgAcq, &PCMProps); 1514 AssertRC(rc2); 1515 1516 rc = RTCircBufCreate(&pStreamIn->pCircBuf, cSamples << PCMProps.cShift); 2050 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmIn->Props.cShift*/); /** @todo FIX THIS !!! */ 1517 2051 if (RT_FAILURE(rc)) 1518 2052 break; 1519 2053 2054 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 1520 2055 /* Init the converter callback context. */ 1521 rc = coreAudioInitConvCbCtx(&pStreamIn->convCbCtx, 1522 &pStreamIn->deviceFormat /* Source */, &pStreamIn->streamFormat /* Dest */); 1523 1524 if (RT_SUCCESS(rc)) 1525 { 1526 #ifdef DEBUG 1527 propAdr.mSelector = kAudioDeviceProcessorOverload; 1528 propAdr.mScope = kAudioUnitScope_Global; 1529 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr, 1530 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn); 1531 if (RT_UNLIKELY(err != noErr)) 1532 LogRel2(("CoreAudio: Failed to add the processor overload listener for input stream (%RI32)\n", err)); 1533 #endif /* DEBUG */ 1534 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate; 1535 propAdr.mScope = kAudioUnitScope_Global; 1536 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr, 1537 coreAudioRecordingAudioDevicePropertyChanged, (void *)pStreamIn); 1538 /* Not fatal. */ 1539 if (RT_UNLIKELY(err != noErr)) 1540 LogRel2(("CoreAudio: Failed to register sample rate changed listener for input stream (%RI32)\n", err)); 1541 } 2056 2057 /* As source, use the input device' output format, 2058 * as destination, use the initially requested format. */ 2059 rc = coreAudioInitConvCbCtx(&pCAStream->In.convCbCtx, pCAStream, 2060 &devOutFmt /* Source */, &reqFmt /* Dest */); 2061 #endif 1542 2062 1543 2063 } while (0); … … 1545 2065 if (RT_SUCCESS(rc)) 1546 2066 { 1547 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_INIT); 1548 1549 LogFunc(("cSamples=%RU32\n", cSamples)); 1550 1551 if (pCfgAcq) 1552 pCfgAcq->cSampleBufferSize = cSamples; 2067 pCAStream->enmDir = PDMAUDIODIR_IN; 2068 2069 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq); 2070 AssertRC(rc); 2071 2072 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_INIT); 2073 2074 pCfgAcq->cSampleBufferSize = cSamples; 1553 2075 } 1554 2076 else 1555 2077 { 1556 AudioUnitUninitialize(pStreamIn->audioUnit); 1557 1558 if (pStreamIn->pCircBuf) 1559 { 1560 RTCircBufDestroy(pStreamIn->pCircBuf); 1561 pStreamIn->pCircBuf = NULL; 1562 } 1563 1564 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx); 1565 1566 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT); 1567 } 1568 1569 LogFunc(("rc=%Rrc\n", rc)); 2078 int rc2 = coreAudioStreamUninit(pCAStream); 2079 AssertRC(rc2); 2080 2081 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_UNINIT); 2082 } 2083 2084 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc)); 1570 2085 return rc; 1571 2086 } 1572 2087 1573 2088 /** @todo Eventually split up this function, as this already is huge! */ 1574 static int coreAudioInitOut(PDRVHOSTCOREAUDIO pThis, 1575 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1576 { 1577 RT_NOREF(pThis); 1578 2089 static int coreAudioStreamInitOut(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2090 { 1579 2091 int rc = VINF_SUCCESS; 1580 1581 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;1582 2092 UInt32 cSamples = 0; 1583 2093 1584 2094 OSStatus err = noErr; 1585 UInt32 uSize = 0; 1586 1587 AudioDeviceID deviceID = pStreamOut->deviceID; 1588 if (deviceID == kAudioDeviceUnknown) 1589 { 1590 /* Fetch the default audio recording device currently in use. */ 1591 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, 1592 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; 1593 uSize = sizeof(deviceID); 1594 err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propAdr, 0, NULL, &uSize, &deviceID); 1595 if (err != noErr) 1596 { 1597 LogRel(("CoreAudio: Unable to determine default playback device (%RI32)\n", err)); 1598 return VERR_NOT_FOUND; 1599 } 1600 } 1601 1602 if (deviceID == kAudioDeviceUnknown) 1603 { 1604 LogFlowFunc(("No default playback device found\n")); 1605 return VERR_NOT_FOUND; 1606 } 2095 2096 PPDMAUDIODEVICE pDev = pCAStream->Unit.pDevice; 2097 AssertPtr(pDev); 2098 2099 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 2100 AssertPtr(pData); 2101 2102 AudioDeviceID deviceID = pData->deviceID; 2103 Assert(deviceID != kAudioDeviceUnknown); 1607 2104 1608 2105 do 1609 2106 { 1610 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_INIT); 1611 1612 /* Assign device ID. */ 1613 pStreamOut->deviceID = deviceID; 1614 1615 /* 1616 * Try to get the name of the playback device and log it. It's not fatal if it fails. 1617 */ 1618 CFStringRef strTemp; 1619 1620 AudioObjectPropertyAddress propAdr = { kAudioObjectPropertyName, kAudioObjectPropertyScopeGlobal, 1621 kAudioObjectPropertyElementMaster }; 1622 uSize = sizeof(CFStringRef); 1623 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp); 1624 if (err == noErr) 1625 { 1626 char *pszDevName = NULL; 1627 err = coreAudioCFStringToCString(strTemp, &pszDevName); 1628 if (err == noErr) 1629 { 1630 CFRelease(strTemp); 1631 1632 /* Get the device' UUID. */ 1633 propAdr.mSelector = kAudioDevicePropertyDeviceUID; 1634 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &strTemp); 1635 if (err == noErr) 1636 { 1637 char *pszUID = NULL; 1638 err = coreAudioCFStringToCString(strTemp, &pszUID); 1639 if (err == noErr) 1640 { 1641 CFRelease(strTemp); 1642 LogRel(("CoreAudio: Using playback device: %s (UID: %s)\n", pszDevName, pszUID)); 1643 1644 RTMemFree(pszUID); 1645 } 1646 } 1647 1648 RTMemFree(pszDevName); 1649 } 1650 } 1651 else 1652 LogRel(("CoreAudio: Unable to determine playback device name (%RI32)\n", err)); 2107 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_IN_INIT); 1653 2108 1654 2109 /* Get the default frames buffer size, so that we can setup our internal buffers. */ 1655 2110 UInt32 cFrames; 1656 uSize = sizeof(cFrames); 2111 UInt32 uSize = sizeof(cFrames); 2112 2113 AudioObjectPropertyAddress propAdr; 1657 2114 propAdr.mSelector = kAudioDevicePropertyBufferFrameSize; 1658 2115 propAdr.mScope = kAudioDevicePropertyScopeInput; 1659 err = AudioObjectGetPropertyData(pStreamOut->deviceID, &propAdr, 0, NULL, &uSize, &cFrames); 2116 propAdr.mElement = kAudioObjectPropertyElementMaster; 2117 err = AudioObjectGetPropertyData(deviceID, &propAdr, 0, NULL, &uSize, &cFrames); 1660 2118 if (err != noErr) 1661 2119 { … … 1665 2123 1666 2124 /* Set the frame buffer size and honor any minimum/maximum restrictions on the device. */ 1667 err = coreAudioSetFrameBufferSize( pStreamOut->deviceID, false /* fInput */, cFrames, &cFrames);2125 err = coreAudioSetFrameBufferSize(deviceID, false /* fInput */, cFrames, &cFrames); 1668 2126 if (err != noErr) 1669 2127 { … … 1688 2146 1689 2147 /* Open the default HAL output component. */ 1690 err = AudioComponentInstanceNew(cp, &p StreamOut->audioUnit);2148 err = AudioComponentInstanceNew(cp, &pCAStream->Unit.audioUnit); 1691 2149 if (err != noErr) 1692 2150 { … … 1697 2155 /* Switch the I/O mode for output to on. */ 1698 2156 UInt32 uFlag = 1; 1699 err = AudioUnitSetProperty(p StreamOut->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output,2157 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 1700 2158 0, &uFlag, sizeof(uFlag)); 1701 2159 if (err != noErr) … … 1706 2164 1707 2165 /* Set the default audio playback device as the device for the new AudioUnit. */ 1708 err = AudioUnitSetProperty(p StreamOut->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,1709 0, & pStreamOut->deviceID, sizeof(pStreamOut->deviceID));2166 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 2167 0, &deviceID, sizeof(deviceID)); 1710 2168 if (err != noErr) 1711 2169 { … … 1720 2178 AURenderCallbackStruct cb; 1721 2179 RT_ZERO(cb); 1722 cb.inputProc = coreAudioPlaybackCb; /* pvUser */1723 cb.inputProcRefCon = p StreamOut;1724 1725 err = AudioUnitSetProperty(p StreamOut->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,2180 cb.inputProc = coreAudioPlaybackCb; 2181 cb.inputProcRefCon = pCAStream; /* pvUser */ 2182 2183 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 1726 2184 0, &cb, sizeof(cb)); 1727 2185 if (err != noErr) 1728 2186 { 1729 LogRel(("CoreAudio: Failed to register outputcallback (%RI32)\n", err));2187 LogRel(("CoreAudio: Failed to register playback callback (%RI32)\n", err)); 1730 2188 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1731 2189 } 1732 2190 1733 /* Fetch the current stream format of the device. */ 1734 uSize = sizeof(pStreamOut->deviceFormat); 1735 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1736 0, &pStreamOut->deviceFormat, &uSize); 2191 AudioStreamBasicDescription reqFmt; 2192 coreAudioStreamCfgToASBD(pCfgReq, &reqFmt); 2193 coreAudioPrintASBD("Requested stream output format", &reqFmt); 2194 2195 /* Fetch the initial input format of the device. */ 2196 AudioStreamBasicDescription devInFmt; 2197 uSize = sizeof(devInFmt); 2198 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 2199 0, &devInFmt, &uSize); 1737 2200 if (err != noErr) 1738 2201 { 1739 LogRel(("CoreAudio: Failed to get device format(%RI32)\n", err));2202 LogRel(("CoreAudio: Failed to get input format of playback device (%RI32)\n", err)); 1740 2203 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1741 2204 } 1742 2205 1743 /* Create an AudioStreamBasicDescription based on our required audio settings. */ 1744 coreAudioStreamCfgToASBD(pCfgReq, &pStreamOut->streamFormat); 1745 1746 coreAudioPrintASBD("Playback device", &pStreamOut->deviceFormat); 1747 coreAudioPrintASBD("Playback format", &pStreamOut->streamFormat); 1748 1749 /* Set the new output format description for the stream. */ 1750 err = AudioUnitSetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1751 0, &pStreamOut->streamFormat, sizeof(pStreamOut->streamFormat)); 2206 coreAudioPrintASBD("Output device in (initial)", &devInFmt); 2207 2208 /* Fetch the initial output format of the device. */ 2209 AudioStreamBasicDescription devOutFmt; 2210 uSize = sizeof(devOutFmt); 2211 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 2212 0, &devOutFmt, &uSize); 1752 2213 if (err != noErr) 1753 2214 { 2215 LogRel(("CoreAudio: Failed to get output format of playback device (%RI32)\n", err)); 2216 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 2217 } 2218 2219 coreAudioPrintASBD("Output device out (initial)", &devOutFmt); 2220 2221 /* Set the new input format for the output device. */ 2222 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 2223 0, &reqFmt, sizeof(reqFmt)); 2224 if (err != noErr) 2225 { 1754 2226 LogRel(("CoreAudio: Failed to set stream format for output stream (%RI32)\n", err)); 1755 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED);1756 }1757 1758 uSize = sizeof(pStreamOut->deviceFormat);1759 err = AudioUnitGetProperty(pStreamOut->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,1760 0, &pStreamOut->deviceFormat, &uSize);1761 if (err != noErr)1762 {1763 LogRel(("CoreAudio: Failed to retrieve device format for output stream (%RI32)\n", err));1764 2227 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1765 2228 } … … 1770 2233 * thread is as we like. 1771 2234 */ 1772 err = AudioUnitSetProperty(p StreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2235 err = AudioUnitSetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1773 2236 0, &cFrames, sizeof(cFrames)); 1774 2237 if (err != noErr) … … 1778 2241 } 1779 2242 1780 /* Finally initialize the new AudioUnit. */ 1781 err = AudioUnitInitialize(pStreamOut->audioUnit); 2243 /* 2244 * Initialize the new AudioUnit. 2245 */ 2246 err = AudioUnitInitialize(pCAStream->Unit.audioUnit); 1782 2247 if (err != noErr) 1783 2248 { … … 1785 2250 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1786 2251 } 2252 2253 /* Fetch the final output format of the device after the audio unit has been initialized. */ 2254 uSize = sizeof(devInFmt); 2255 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 2256 0, &devInFmt, &uSize); 2257 if (err != noErr) 2258 { 2259 LogRel(("CoreAudio: Failed re-getting input format of output device (%RI32)\n", err)); 2260 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 2261 } 2262 2263 coreAudioPrintASBD("Output device in (after initialization)", &devInFmt); 2264 2265 /* Save this final output format as our stream format. */ 2266 memcpy(&pCAStream->Unit.streamFmt, &devInFmt, sizeof(AudioStreamBasicDescription)); 1787 2267 1788 2268 /* … … 1792 2272 */ 1793 2273 uSize = sizeof(cFrames); 1794 err = AudioUnitGetProperty(p StreamOut->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,2274 err = AudioUnitGetProperty(pCAStream->Unit.audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1795 2275 0, &cFrames, &uSize); 1796 2276 if (err != noErr) 1797 2277 { 1798 2278 LogRel(("CoreAudio: Failed to get maximum frame buffer size from output audio device (%RI32)\n", err)); 1799 1800 AudioUnitUninitialize(pStreamOut->audioUnit);1801 2279 CA_BREAK_STMT(rc = VERR_AUDIO_BACKEND_INIT_FAILED); 1802 2280 } … … 1809 2287 * samples count. 1810 2288 */ 1811 cSamples = cFrames * pStreamOut->streamFormat.mChannelsPerFrame;2289 cSamples = cFrames * reqFmt.mChannelsPerFrame; 1812 2290 if (!cSamples) 1813 2291 { … … 1816 2294 } 1817 2295 1818 /* Destroy any former internal ring buffer. */ 1819 if (pStreamOut->pCircBuf) 1820 { 1821 RTCircBufDestroy(pStreamOut->pCircBuf); 1822 pStreamOut->pCircBuf = NULL; 1823 } 1824 1825 PDMAUDIOPCMPROPS PCMProps; 1826 int rc2 = DrvAudioHlpStreamCfgToProps(pCfgAcq, &PCMProps); 2296 /* Create the internal ring buffer. */ 2297 rc = RTCircBufCreate(&pCAStream->pCircBuf, cSamples << 1 /*pHstStrmOut->Props.cShift*/); /** @todo FIX THIS !!! */ 2298 2299 } while (0); 2300 2301 if (RT_SUCCESS(rc)) 2302 { 2303 pCAStream->enmDir = PDMAUDIODIR_OUT; 2304 2305 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, pCfgAcq); 2306 AssertRC(rc); 2307 2308 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_INIT); 2309 2310 pCfgAcq->cSampleBufferSize = cSamples; 2311 } 2312 else 2313 { 2314 int rc2 = coreAudioStreamUninit(pCAStream); 1827 2315 AssertRC(rc2); 1828 2316 1829 rc = RTCircBufCreate(&pStreamOut->pCircBuf, cSamples << PCMProps.cShift); 1830 if (RT_FAILURE(rc)) 1831 break; 1832 2317 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_UNINIT); 2318 } 2319 2320 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc)); 2321 return rc; 2322 } 2323 2324 2325 /** 2326 * Uninitializes a Core Audio stream. 2327 * 2328 * @return IPRT status code. 2329 * @param pCAStream Stream to uninitialize. 2330 */ 2331 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream) 2332 { 2333 OSStatus err = noErr; 2334 2335 if (pCAStream->Unit.audioUnit) 2336 { 2337 err = AudioUnitUninitialize(pCAStream->Unit.audioUnit); 2338 if (err == noErr) 2339 { 2340 err = AudioComponentInstanceDispose(pCAStream->Unit.audioUnit); 2341 if (err == noErr) 2342 pCAStream->Unit.audioUnit = NULL; 2343 } 2344 } 2345 2346 if (err == noErr) 2347 { 2348 if (pCAStream->pCircBuf) 2349 { 2350 RTCircBufDestroy(pCAStream->pCircBuf); 2351 pCAStream->pCircBuf = NULL; 2352 } 2353 2354 pCAStream->status = COREAUDIOSTATUS_UNINIT; 2355 2356 pCAStream->enmDir = PDMAUDIODIR_UNKNOWN; 2357 pCAStream->pDrv = NULL; 2358 2359 pCAStream->Unit.pDevice = NULL; 2360 RT_ZERO(pCAStream->Unit.streamFmt); 2361 2362 if (pCAStream->enmDir == PDMAUDIODIR_IN) 2363 { 2364 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 2365 if (pCAStream->In.ConverterRef) 2366 { 2367 AudioConverterDispose(pCAStream->In.ConverterRef); 2368 pCAStream->In.ConverterRef = NULL; 2369 } 2370 2371 drvHostCoreAudioUninitConvCbCtx(&pCAStream->In.convCbCtx); 2372 #endif 2373 pCAStream->In.sampleRatio = 1; 2374 } 2375 else if (pCAStream->enmDir == PDMAUDIODIR_OUT) 2376 { 2377 2378 } 2379 } 2380 else 2381 LogRel(("CoreAudio: Failed to uninit stream (%RI32)\n", err)); 2382 2383 return err == noErr ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge! */ 2384 } 2385 2386 2387 /** 2388 * Registers callbacks for a specific Core Audio device. 2389 * 2390 * @return IPRT status code. 2391 * @param pThis Host audio driver instance. 2392 * @param pDev Audio device to use for the registered callbacks. 2393 */ 2394 static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev) 2395 { 2396 RT_NOREF(pThis); 2397 2398 AudioDeviceID deviceID = kAudioDeviceUnknown; 2399 2400 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 2401 if (pData) 2402 deviceID = pData->deviceID; 2403 2404 if (deviceID != kAudioDeviceUnknown) 2405 { 1833 2406 /* 1834 * Register callbacks.2407 * Register device callbacks. 1835 2408 */ 1836 #ifdef DEBUG 2409 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, 2410 kAudioObjectPropertyElementMaster }; 2411 OSStatus err = AudioObjectAddPropertyListener(deviceID, &propAdr, 2412 coreAudioDeviceStateChangedCb, pDev /* pvUser */); 2413 if ( err != noErr 2414 && err != kAudioHardwareIllegalOperationError) 2415 { 2416 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err)); 2417 } 2418 1837 2419 propAdr.mSelector = kAudioDeviceProcessorOverload; 1838 2420 propAdr.mScope = kAudioUnitScope_Global; 1839 err = AudioObjectAddPropertyListener( pStreamOut->deviceID, &propAdr,1840 coreAudio PlaybackAudioDevicePropertyChanged, (void *)pStreamOut);2421 err = AudioObjectAddPropertyListener(deviceID, &propAdr, 2422 coreAudioDevPropChgCb, pDev /* pvUser */); 1841 2423 if (err != noErr) 1842 LogRel(("CoreAudio: Failed to register processor overload listener for output stream (%RI32)\n", err)); 1843 #endif /* DEBUG */ 2424 LogRel(("CoreAudio: Failed to register processor overload listener (%RI32)\n", err)); 1844 2425 1845 2426 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate; 1846 2427 propAdr.mScope = kAudioUnitScope_Global; 1847 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr, 1848 coreAudioPlaybackAudioDevicePropertyChanged, (void *)pStreamOut); 1849 /* Not fatal. */ 2428 err = AudioObjectAddPropertyListener(deviceID, &propAdr, 2429 coreAudioDevPropChgCb, pDev /* pvUser */); 1850 2430 if (err != noErr) 1851 LogRel(("CoreAudio: Failed to register sample rate changed listener for output stream (%RI32)\n", err)); 1852 1853 } while (0); 1854 1855 if (RT_SUCCESS(rc)) 1856 { 1857 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_INIT); 1858 1859 LogFunc(("cSamples=%RU32\n", cSamples)); 1860 1861 if (pCfgAcq) 1862 pCfgAcq->cSampleBufferSize = cSamples; 1863 } 1864 else 1865 { 1866 AudioUnitUninitialize(pStreamOut->audioUnit); 1867 1868 if (pStreamOut->pCircBuf) 1869 { 1870 RTCircBufDestroy(pStreamOut->pCircBuf); 1871 pStreamOut->pCircBuf = NULL; 1872 } 1873 1874 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT); 1875 } 1876 1877 LogFunc(("cSamples=%RU32, rc=%Rrc\n", cSamples, rc)); 1878 return rc; 1879 } 1880 1881 1882 /** 1883 * Implements the OS X callback AudioObjectPropertyListenerProc for getting 1884 * notified when some of the properties of an audio device has changed. 1885 */ 1886 static OSStatus coreAudioPlaybackAudioDevicePropertyChanged(AudioObjectID propertyID, 1887 UInt32 cAddresses, 1888 const AudioObjectPropertyAddress paProperties[], 1889 void *pvUser) 1890 { 1891 RT_NOREF(propertyID, cAddresses, paProperties, pvUser) 2431 LogRel(("CoreAudio: Failed to register sample rate changed listener (%RI32)\n", err)); 2432 } 2433 2434 return VINF_SUCCESS; 2435 } 2436 2437 /** 2438 * Unregisters all formerly registered callbacks of a Core Audio device again. 2439 * 2440 * @return IPRT status code. 2441 * @param pThis Host audio driver instance. 2442 * @param pDev Audio device to use for the registered callbacks. 2443 */ 2444 static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PPDMAUDIODEVICE pDev) 2445 { 2446 RT_NOREF(pThis); 2447 2448 AudioDeviceID deviceID = kAudioDeviceUnknown; 2449 2450 if (pDev) 2451 { 2452 PCOREAUDIODEVICEDATA pData = (PCOREAUDIODEVICEDATA)pDev->pvData; 2453 if (pData) 2454 deviceID = pData->deviceID; 2455 } 2456 2457 if (deviceID != kAudioDeviceUnknown) 2458 { 2459 /* 2460 * Unregister per-device callbacks. 2461 */ 2462 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal, 2463 kAudioObjectPropertyElementMaster }; 2464 OSStatus err = AudioObjectRemovePropertyListener(deviceID, &propAdr, 2465 coreAudioDevPropChgCb, pDev /* pvUser */); 2466 if ( err != noErr 2467 && err != kAudioHardwareBadObjectError) 2468 { 2469 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err)); 2470 } 2471 2472 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate; 2473 err = AudioObjectRemovePropertyListener(deviceID, &propAdr, 2474 coreAudioDevPropChgCb, pDev /* pvUser */); 2475 if ( err != noErr 2476 && err != kAudioHardwareBadObjectError) 2477 { 2478 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err)); 2479 } 2480 2481 propAdr.mSelector = kAudioDevicePropertyDeviceIsAlive; 2482 err = AudioObjectRemovePropertyListener(deviceID, &propAdr, 2483 coreAudioDeviceStateChangedCb, pDev /* pvUser */); 2484 if ( err != noErr 2485 && err != kAudioHardwareBadObjectError) 2486 { 2487 LogRel(("CoreAudio: Failed to remove the device alive listener (%RI32)\n", err)); 2488 } 2489 } 2490 2491 return VINF_SUCCESS; 2492 } 2493 2494 /* Callback for getting notified when some of the properties of an audio device have changed. */ 2495 static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID, 2496 UInt32 cAddresses, 2497 const AudioObjectPropertyAddress properties[], 2498 void *pvUser) 2499 { 2500 RT_NOREF(cAddresses, properties); 2501 2502 PPDMAUDIODEVICE pDev = (PPDMAUDIODEVICE)pvUser; 2503 AssertPtr(pDev); 2504 2505 LogFlowFunc(("propertyID=%u, nAddresses=%u, pDev=%p\n", propertyID, cAddresses, pDev)); 1892 2506 1893 2507 switch (propertyID) 1894 2508 { 1895 2509 #ifdef DEBUG 2510 case kAudioDeviceProcessorOverload: 2511 { 2512 LogFunc(("Processor overload detected!\n")); 2513 break; 2514 } 1896 2515 #endif /* DEBUG */ 2516 case kAudioDevicePropertyNominalSampleRate: 2517 { 2518 int rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT); 2519 AssertRC(rc2); 2520 break; 2521 } 2522 1897 2523 default: 2524 /* Just skip. */ 1898 2525 break; 1899 2526 } … … 1902 2529 } 1903 2530 1904 1905 /** 1906 * Implements the OS X callback AURenderCallback in order to feed the audio 1907 * output buffer. 1908 */ 1909 static OSStatus coreAudioPlaybackCb(void *pvUser, 1910 AudioUnitRenderActionFlags *pActionFlags, 1911 const AudioTimeStamp *pAudioTS, 1912 UInt32 uBusID, 1913 UInt32 cFrames, 1914 AudioBufferList *pBufData) 2531 /* Callback to feed audio output buffer. */ 2532 static DECLCALLBACK(OSStatus) coreAudioPlaybackCb(void *pvUser, 2533 AudioUnitRenderActionFlags *pActionFlags, 2534 const AudioTimeStamp *pAudioTS, 2535 UInt32 uBusID, 2536 UInt32 cFrames, 2537 AudioBufferList *pBufData) 1915 2538 { 1916 2539 RT_NOREF(pActionFlags, pAudioTS, uBusID, cFrames); 1917 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pvUser; 1918 1919 if (ASMAtomicReadU32(&pStreamOut->status) != CA_STATUS_INIT) 2540 2541 PCOREAUDIOSTREAM pStream = (PCOREAUDIOSTREAM)pvUser; 2542 2543 /* Sanity. */ 2544 AssertPtr(pStream); 2545 AssertPtr(pStream->pDrv); 2546 Assert (pStream->enmDir == PDMAUDIODIR_OUT); 2547 AssertPtr(pStream->Unit.pDevice); 2548 2549 if (ASMAtomicReadU32(&pStream->status) != COREAUDIOSTATUS_INIT) 1920 2550 { 1921 2551 pBufData->mBuffers[0].mDataByteSize = 0; … … 1924 2554 1925 2555 /* How much space is used in the ring buffer? */ 1926 size_t cbToRead = RT_MIN(RTCircBufUsed(pStream Out->pCircBuf), pBufData->mBuffers[0].mDataByteSize);2556 size_t cbToRead = RT_MIN(RTCircBufUsed(pStream->pCircBuf), pBufData->mBuffers[0].mDataByteSize); 1927 2557 if (!cbToRead) 1928 2558 { … … 1938 2568 { 1939 2569 /* Try to acquire the necessary block from the ring buffer. */ 1940 RTCircBufAcquireReadBlock(pStream Out->pCircBuf, cbLeft, (void **)&pbSrc, &cbToRead);2570 RTCircBufAcquireReadBlock(pStream->pCircBuf, cbLeft, (void **)&pbSrc, &cbToRead); 1941 2571 1942 2572 /* Break if nothing is used anymore. */ … … 1948 2578 1949 2579 /* Release the read buffer, so it could be used for new data. */ 1950 RTCircBufReleaseReadBlock(pStream Out->pCircBuf, cbToRead);2580 RTCircBufReleaseReadBlock(pStream->pCircBuf, cbToRead); 1951 2581 1952 2582 /* Move offset. */ … … 1976 2606 1977 2607 /** 1978 * @interface_method_impl{PDMIHOSTAUDIO, pfnInit}1979 *1980 * @todo Please put me next to the shutdown function, because then it would be1981 * clear why I'm empty. While at it, it would be nice if you also1982 * reordered my PDMIHOSTAUDIO sibilings according to the interface1983 * (doesn't matter if it's reversed (like all other PDM drivers and1984 * devices) or not).1985 */1986 static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface)1987 {1988 RT_NOREF(pInterface);1989 1990 LogFlowFuncEnter();1991 1992 return VINF_SUCCESS;1993 }1994 1995 /**1996 2608 * @interface_method_impl{PDMIHOSTAUDIO, pfnStreamCapture} 1997 2609 */ … … 2005 2617 /* pcbRead is optional. */ 2006 2618 2007 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream; 2008 2009 /* unused, make you wonder why... 2010 size_t csReads = 0; 2011 char *pcSrc; 2012 PPDMAUDIOSAMPLE psDst; */ 2013 2014 if (ASMAtomicReadU32(&pStreamIn->status) != CA_STATUS_INIT) 2619 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2620 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2621 2622 /* Check if the audio device should be reinitialized. If so do it. */ 2623 if (ASMAtomicReadU32(&pCAStream->status) == COREAUDIOSTATUS_REINIT) 2624 { 2625 /* For now re just re-initialize with the current input device. */ 2626 if (pThis->pDefaultDevIn) 2627 { 2628 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevIn); 2629 if (RT_FAILURE(rc2)) 2630 return VERR_NOT_AVAILABLE; 2631 } 2632 else 2633 return VERR_NOT_AVAILABLE; 2634 } 2635 2636 if (ASMAtomicReadU32(&pCAStream->status) != COREAUDIOSTATUS_INIT) 2015 2637 { 2016 2638 if (pcbRead) … … 2025 2647 { 2026 2648 size_t cbMixBuf = AudioMixBufSizeBytes(&pStream->MixBuf); 2027 size_t cbToWrite = RT_MIN(cbMixBuf, RTCircBufUsed(p StreamIn->pCircBuf));2649 size_t cbToWrite = RT_MIN(cbMixBuf, RTCircBufUsed(pCAStream->pCircBuf)); 2028 2650 2029 2651 uint32_t cWritten, cbWritten; … … 2031 2653 size_t cbToRead; 2032 2654 2033 Log3Func(("cbMixBuf=%zu, cbToWrite=%zu/%zu\n", cbMixBuf, cbToWrite, RTCircBufSize(p StreamIn->pCircBuf)));2655 Log3Func(("cbMixBuf=%zu, cbToWrite=%zu/%zu\n", cbMixBuf, cbToWrite, RTCircBufSize(pCAStream->pCircBuf))); 2034 2656 2035 2657 while (cbToWrite) 2036 2658 { 2037 2659 /* Try to acquire the necessary block from the ring buffer. */ 2038 RTCircBufAcquireReadBlock(p StreamIn->pCircBuf, cbToWrite, (void **)&puBuf, &cbToRead);2660 RTCircBufAcquireReadBlock(pCAStream->pCircBuf, cbToWrite, (void **)&puBuf, &cbToRead); 2039 2661 if (!cbToRead) 2040 2662 { 2041 RTCircBufReleaseReadBlock(p StreamIn->pCircBuf, cbToRead);2663 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbToRead); 2042 2664 break; 2043 2665 } … … 2055 2677 AssertFailed(); 2056 2678 #endif 2057 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf , cbToRead, &cWritten);2679 rc = AudioMixBufWriteCirc(&pStream->MixBuf, puBuf + cbWrittenTotal, cbToRead, &cWritten); 2058 2680 2059 2681 /* Release the read buffer, so it could be used for new data. */ 2060 RTCircBufReleaseReadBlock(p StreamIn->pCircBuf, cbToRead);2682 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbToRead); 2061 2683 2062 2684 if ( RT_FAILURE(rc) 2063 2685 || !cWritten) 2064 2686 { 2065 RTCircBufReleaseReadBlock(p StreamIn->pCircBuf, cbToRead);2687 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbToRead); 2066 2688 break; 2067 2689 } 2068 2690 2069 2691 cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten); 2070 2071 /* Release the read buffer, so it could be used for new data. */2072 RTCircBufReleaseReadBlock(pStreamIn->pCircBuf, cbWritten);2073 2692 2074 2693 Assert(cbToWrite >= cbWritten); … … 2107 2726 */ 2108 2727 static DECLCALLBACK(int) drvHostCoreAudioStreamPlay(PPDMIHOSTAUDIO pInterface, 2109 PPDMAUDIOSTREAM pStream, 2110 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2111 { 2112 RT_NOREF(pvBuf, cbBuf); /** @todo r=bird: this looks weird at first glance... */ 2113 2114 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2115 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2116 /* pcbWritten is optional. */ 2117 2118 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream; 2119 2120 int rc = VINF_SUCCESS; 2728 PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, 2729 uint32_t *pcbWritten) 2730 { 2731 RT_NOREF(pvBuf, cbBuf); 2732 2733 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2734 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2735 2736 /* Check if the audio device should be reinitialized. If so do it. */ 2737 if (ASMAtomicReadU32(&pCAStream->status) == COREAUDIOSTATUS_REINIT) 2738 { 2739 if (pThis->pDefaultDevOut) 2740 { 2741 /* For now re just re-initialize with the current output device. */ 2742 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevOut); 2743 if (RT_FAILURE(rc2)) 2744 return VERR_NOT_AVAILABLE; 2745 } 2746 else 2747 return VERR_NOT_AVAILABLE; 2748 } 2121 2749 2122 2750 uint32_t cLive = AudioMixBufLive(&pStream->MixBuf); … … 2132 2760 uint32_t cbReadTotal = 0; 2133 2761 2134 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(p StreamOut->pCircBuf));2762 size_t cbToRead = RT_MIN(cbLive, RTCircBufFree(pCAStream->pCircBuf)); 2135 2763 Log3Func(("cbToRead=%zu\n", cbToRead)); 2764 2765 int rc = VINF_SUCCESS; 2136 2766 2137 2767 while (cbToRead) … … 2142 2772 2143 2773 /* Try to acquire the necessary space from the ring buffer. */ 2144 RTCircBufAcquireWriteBlock(p StreamOut->pCircBuf, cbToRead, (void **)&puBuf, &cbCopy);2774 RTCircBufAcquireWriteBlock(pCAStream->pCircBuf, cbToRead, (void **)&puBuf, &cbCopy); 2145 2775 if (!cbCopy) 2146 2776 { 2147 RTCircBufReleaseWriteBlock(p StreamOut->pCircBuf, cbCopy);2777 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbCopy); 2148 2778 break; 2149 2779 } … … 2151 2781 Assert(cbCopy <= cbToRead); 2152 2782 2153 rc = AudioMixBufReadCirc(&pStream->MixBuf, 2154 puBuf, cbCopy, &cRead); 2155 2783 rc = AudioMixBufReadCirc(&pStream->MixBuf, puBuf, cbCopy, &cRead); 2156 2784 if ( RT_FAILURE(rc) 2157 2785 || !cRead) 2158 2786 { 2159 RTCircBufReleaseWriteBlock(p StreamOut->pCircBuf, 0);2787 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, 0); 2160 2788 break; 2161 2789 } … … 2164 2792 2165 2793 /* Release the ring buffer, so the read thread could start reading this data. */ 2166 RTCircBufReleaseWriteBlock(p StreamOut->pCircBuf, cbRead);2794 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbRead); 2167 2795 2168 2796 Assert(cbToRead >= cbRead); … … 2186 2814 } 2187 2815 2188 static int coreAudioControlStreamOut(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd) 2189 { 2190 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream; 2816 static DECLCALLBACK(int) coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, 2817 PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd) 2818 { 2819 RT_NOREF(pThis); 2191 2820 2192 2821 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd)); 2193 2822 2194 uint32_t uStatus = ASMAtomicReadU32(&p StreamOut->status);2195 if (!( uStatus == C A_STATUS_INIT2196 || uStatus == C A_STATUS_REINIT))2823 uint32_t uStatus = ASMAtomicReadU32(&pCAStream->status); 2824 if (!( uStatus == COREAUDIOSTATUS_INIT 2825 || uStatus == COREAUDIOSTATUS_REINIT)) 2197 2826 { 2198 2827 return VINF_SUCCESS; … … 2208 2837 { 2209 2838 /* Only start the device if it is actually stopped */ 2210 if (!coreAudio IsRunning(pStreamOut->deviceID))2211 { 2212 err = AudioUnitReset(p StreamOut->audioUnit, kAudioUnitScope_Input, 0);2839 if (!coreAudioUnitIsRunning(pCAStream)) 2840 { 2841 err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0); 2213 2842 if (err != noErr) 2214 2843 { … … 2216 2845 /* Keep going. */ 2217 2846 } 2218 RTCircBufReset(pStreamOut->pCircBuf); 2219 2220 err = AudioOutputUnitStart(pStreamOut->audioUnit); 2847 2848 RTCircBufReset(pCAStream->pCircBuf); 2849 2850 err = AudioOutputUnitStart(pCAStream->Unit.audioUnit); 2221 2851 if (err != noErr) 2222 2852 { … … 2232 2862 { 2233 2863 /* Only stop the device if it is actually running */ 2234 if (coreAudio IsRunning(pStreamOut->deviceID))2235 { 2236 err = AudioOutputUnitStop(p StreamOut->audioUnit);2864 if (coreAudioUnitIsRunning(pCAStream)) 2865 { 2866 err = AudioOutputUnitStop(pCAStream->Unit.audioUnit); 2237 2867 if (err != noErr) 2238 2868 { … … 2242 2872 } 2243 2873 2244 err = AudioUnitReset(p StreamOut->audioUnit, kAudioUnitScope_Input, 0);2874 err = AudioUnitReset(pCAStream->Unit.audioUnit, kAudioUnitScope_Input, 0); 2245 2875 if (err != noErr) 2246 2876 { … … 2261 2891 } 2262 2892 2263 static int coreAudioControlStreamIn(PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)2264 {2265 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;2266 2267 LogFlowFunc(("enmStreamCmd=%RU32\n", enmStreamCmd));2268 2269 uint32_t uStatus = ASMAtomicReadU32(&pStreamIn->status);2270 if (!( uStatus == CA_STATUS_INIT2271 || uStatus == CA_STATUS_REINIT))2272 {2273 return VINF_SUCCESS;2274 }2275 2276 int rc = VINF_SUCCESS;2277 2278 OSStatus err;2279 switch (enmStreamCmd)2280 {2281 case PDMAUDIOSTREAMCMD_ENABLE:2282 case PDMAUDIOSTREAMCMD_RESUME:2283 {2284 /* Only start the device if it is actually stopped */2285 if (!coreAudioIsRunning(pStreamIn->deviceID))2286 {2287 RTCircBufReset(pStreamIn->pCircBuf);2288 err = AudioOutputUnitStart(pStreamIn->audioUnit);2289 if (err != noErr)2290 {2291 LogRel(("CoreAudio: Failed to start recording (%RI32)\n", err));2292 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */2293 break;2294 }2295 }2296 2297 break;2298 }2299 2300 case PDMAUDIOSTREAMCMD_DISABLE:2301 case PDMAUDIOSTREAMCMD_PAUSE:2302 {2303 /* Only stop the device if it is actually running */2304 if (coreAudioIsRunning(pStreamIn->deviceID))2305 {2306 err = AudioOutputUnitStop(pStreamIn->audioUnit);2307 if (err != noErr)2308 {2309 LogRel(("CoreAudio: Failed to stop recording (%RI32)\n", err));2310 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */2311 break;2312 }2313 2314 err = AudioUnitReset(pStreamIn->audioUnit, kAudioUnitScope_Input, 0);2315 if (err != noErr)2316 {2317 LogRel(("CoreAudio: Failed to reset AudioUnit (%RI32)\n", err));2318 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */2319 break;2320 }2321 }2322 break;2323 }2324 2325 default:2326 rc = VERR_NOT_SUPPORTED;2327 break;2328 }2329 2330 LogFlowFuncLeaveRC(rc);2331 return rc;2332 }2333 2334 static int coreAudioDestroyStreamIn(PPDMAUDIOSTREAM pStream)2335 {2336 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;2337 2338 LogFlowFuncEnter();2339 2340 uint32_t status = ASMAtomicReadU32(&pStreamIn->status);2341 if (!( status == CA_STATUS_INIT2342 || status == CA_STATUS_REINIT))2343 {2344 return VINF_SUCCESS;2345 }2346 2347 OSStatus err = noErr;2348 2349 int rc = coreAudioControlStreamIn(&pStreamIn->Stream, PDMAUDIOSTREAMCMD_DISABLE);2350 if (RT_SUCCESS(rc))2351 {2352 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_IN_UNINIT);2353 2354 /*2355 * Unregister recording device callbacks.2356 */2357 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,2358 kAudioObjectPropertyElementMaster };2359 #ifdef DEBUG2360 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,2361 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);2362 if ( err != noErr2363 && err != kAudioHardwareBadObjectError)2364 {2365 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err));2366 }2367 #endif /* DEBUG */2368 2369 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;2370 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr,2371 coreAudioRecordingAudioDevicePropertyChanged, &pStreamIn->cbCtx);2372 if ( err != noErr2373 && err != kAudioHardwareBadObjectError)2374 {2375 LogRel(("CoreAudio: Failed to remove the recording sample rate changed listener (%RI32)\n", err));2376 }2377 2378 if (pStreamIn->fDefDevChgListReg)2379 {2380 propAdr.mSelector = kAudioHardwarePropertyDefaultInputDevice;2381 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,2382 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);2383 if ( err != noErr2384 && err != kAudioHardwareBadObjectError)2385 {2386 LogRel(("CoreAudio: Failed to remove the default recording device changed listener (%RI32)\n", err));2387 }2388 2389 pStreamIn->fDefDevChgListReg = false;2390 }2391 2392 if (pStreamIn->fDevStateChgListReg)2393 {2394 Assert(pStreamIn->deviceID != kAudioDeviceUnknown);2395 2396 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,2397 kAudioObjectPropertyElementMaster };2398 err = AudioObjectRemovePropertyListener(pStreamIn->deviceID, &propAdr2,2399 drvHostCoreAudioDeviceStateChanged, &pStreamIn->cbCtx);2400 if ( err != noErr2401 && err != kAudioHardwareBadObjectError)2402 {2403 LogRel(("CoreAudio: Failed to remove the recording device state changed listener (%RI32)\n", err));2404 }2405 2406 pStreamIn->fDevStateChgListReg = false;2407 }2408 2409 if (pStreamIn->pConverter)2410 {2411 AudioConverterDispose(pStreamIn->pConverter);2412 pStreamIn->pConverter = NULL;2413 }2414 2415 err = AudioUnitUninitialize(pStreamIn->audioUnit);2416 if (err == noErr)2417 err = AudioComponentInstanceDispose(pStreamIn->audioUnit);2418 2419 if ( err != noErr2420 && err != kAudioHardwareBadObjectError)2421 {2422 LogRel(("CoreAudio: Failed to uninit the recording device (%RI32)\n", err));2423 }2424 2425 pStreamIn->deviceID = kAudioDeviceUnknown;2426 pStreamIn->audioUnit = NULL;2427 pStreamIn->sampleRatio = 1;2428 2429 coreAudioUninitConvCbCtx(&pStreamIn->convCbCtx);2430 2431 if (pStreamIn->pCircBuf)2432 {2433 RTCircBufDestroy(pStreamIn->pCircBuf);2434 pStreamIn->pCircBuf = NULL;2435 }2436 2437 ASMAtomicXchgU32(&pStreamIn->status, CA_STATUS_UNINIT);2438 }2439 else2440 {2441 LogRel(("CoreAudio: Failed to stop recording on uninit (%RI32)\n", err));2442 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */2443 }2444 2445 LogFlowFuncLeaveRC(rc);2446 return rc;2447 }2448 2449 static int coreAudioDestroyStreamOut(PPDMAUDIOSTREAM pStream)2450 {2451 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;2452 2453 LogFlowFuncEnter();2454 2455 uint32_t status = ASMAtomicReadU32(&pStreamOut->status);2456 if (!( status == CA_STATUS_INIT2457 || status == CA_STATUS_REINIT))2458 {2459 return VINF_SUCCESS;2460 }2461 2462 int rc = coreAudioControlStreamOut(&pStreamOut->Stream, PDMAUDIOSTREAMCMD_DISABLE);2463 if (RT_SUCCESS(rc))2464 {2465 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_IN_UNINIT);2466 2467 OSStatus err;2468 2469 /*2470 * Unregister playback device callbacks.2471 */2472 AudioObjectPropertyAddress propAdr = { kAudioDeviceProcessorOverload, kAudioObjectPropertyScopeGlobal,2473 kAudioObjectPropertyElementMaster };2474 #ifdef DEBUG2475 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,2476 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);2477 if ( err != noErr2478 && err != kAudioHardwareBadObjectError)2479 {2480 LogRel(("CoreAudio: Failed to remove the playback processor overload listener (%RI32)\n", err));2481 }2482 #endif /* DEBUG */2483 2484 propAdr.mSelector = kAudioDevicePropertyNominalSampleRate;2485 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr,2486 coreAudioPlaybackAudioDevicePropertyChanged, &pStreamOut->cbCtx);2487 if ( err != noErr2488 && err != kAudioHardwareBadObjectError)2489 {2490 LogRel(("CoreAudio: Failed to remove the playback sample rate changed listener (%RI32)\n", err));2491 }2492 2493 if (pStreamOut->fDefDevChgListReg)2494 {2495 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;2496 propAdr.mScope = kAudioObjectPropertyScopeGlobal;2497 propAdr.mElement = kAudioObjectPropertyElementMaster;2498 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr,2499 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);2500 if ( err != noErr2501 && err != kAudioHardwareBadObjectError)2502 {2503 LogRel(("CoreAudio: Failed to remove the default playback device changed listener (%RI32)\n", err));2504 }2505 2506 pStreamOut->fDefDevChgListReg = false;2507 }2508 2509 if (pStreamOut->fDevStateChgListReg)2510 {2511 Assert(pStreamOut->deviceID != kAudioDeviceUnknown);2512 2513 AudioObjectPropertyAddress propAdr2 = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,2514 kAudioObjectPropertyElementMaster };2515 err = AudioObjectRemovePropertyListener(pStreamOut->deviceID, &propAdr2,2516 drvHostCoreAudioDeviceStateChanged, &pStreamOut->cbCtx);2517 if ( err != noErr2518 && err != kAudioHardwareBadObjectError)2519 {2520 LogRel(("CoreAudio: Failed to remove the playback device state changed listener (%RI32)\n", err));2521 }2522 2523 pStreamOut->fDevStateChgListReg = false;2524 }2525 2526 err = AudioUnitUninitialize(pStreamOut->audioUnit);2527 if (err == noErr)2528 err = AudioComponentInstanceDispose(pStreamOut->audioUnit);2529 2530 if ( err != noErr2531 && err != kAudioHardwareBadObjectError)2532 {2533 LogRel(("CoreAudio: Failed to uninit the playback device (%RI32)\n", err));2534 }2535 2536 pStreamOut->deviceID = kAudioDeviceUnknown;2537 pStreamOut->audioUnit = NULL;2538 if (pStreamOut->pCircBuf)2539 {2540 RTCircBufDestroy(pStreamOut->pCircBuf);2541 pStreamOut->pCircBuf = NULL;2542 }2543 2544 ASMAtomicXchgU32(&pStreamOut->status, CA_STATUS_UNINIT);2545 }2546 else2547 LogRel(("CoreAudio: Failed to stop playback on uninit, rc=%Rrc\n", rc));2548 2549 LogFlowFuncLeaveRC(rc);2550 return rc;2551 }2552 2553 static int coreAudioCreateStreamIn(PDRVHOSTCOREAUDIO pThis,2554 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)2555 {2556 AssertPtrReturn(pThis, VERR_INVALID_POINTER);2557 AssertPtrReturn(pStream, VERR_INVALID_POINTER);2558 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);2559 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);2560 2561 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;2562 2563 LogFlowFunc(("enmRecSource=%RU32\n", pCfgReq->DestSource.Source));2564 2565 pStreamIn->deviceID = kAudioDeviceUnknown;2566 pStreamIn->audioUnit = NULL;2567 pStreamIn->pConverter = NULL;2568 pStreamIn->sampleRatio = 1;2569 pStreamIn->pCircBuf = NULL;2570 pStreamIn->status = CA_STATUS_UNINIT;2571 pStreamIn->fDefDevChgListReg = false;2572 pStreamIn->fDevStateChgListReg = false;2573 2574 /* Set callback context. */2575 pStreamIn->cbCtx.pThis = pThis;2576 pStreamIn->cbCtx.enmDir = PDMAUDIODIR_IN;2577 pStreamIn->cbCtx.pIn = pStreamIn;2578 2579 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */2580 2581 #if 02582 /* Try to find the audio device set by the user */2583 if (DeviceUID.pszInputDeviceUID)2584 {2585 pStreamIn->deviceID = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszInputDeviceUID);2586 /* Not fatal */2587 if (pStreamIn->deviceID == kAudioDeviceUnknown)2588 LogRel(("CoreAudio: Unable to find recording device %s. Falling back to the default audio device. \n", DeviceUID.pszInputDeviceUID));2589 else2590 fDeviceByUser = true;2591 }2592 #endif2593 int rc = coreAudioInitIn(pThis, &pStreamIn->Stream, pCfgReq, pCfgAcq);2594 if (RT_SUCCESS(rc))2595 {2596 OSStatus err;2597 2598 /* When the devices isn't forced by the user, we want default device change notifications. */2599 if (!fDeviceByUser)2600 {2601 if (!pStreamIn->fDefDevChgListReg)2602 {2603 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,2604 kAudioObjectPropertyElementMaster };2605 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,2606 coreAudioDefaultDeviceChanged, &pStreamIn->cbCtx);2607 if ( err == noErr2608 || err == kAudioHardwareIllegalOperationError)2609 {2610 pStreamIn->fDefDevChgListReg = true;2611 }2612 else2613 LogRel(("CoreAudio: Failed to add the default recording device changed listener (%RI32)\n", err));2614 }2615 }2616 2617 if ( !pStreamIn->fDevStateChgListReg2618 && (pStreamIn->deviceID != kAudioDeviceUnknown))2619 {2620 /* Register callback for being notified if the device stops being alive. */2621 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,2622 kAudioObjectPropertyElementMaster };2623 err = AudioObjectAddPropertyListener(pStreamIn->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,2624 &pStreamIn->cbCtx);2625 if (err == noErr)2626 {2627 pStreamIn->fDevStateChgListReg = true;2628 }2629 else2630 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err));2631 }2632 }2633 2634 LogFlowFuncLeaveRC(rc);2635 return rc;2636 }2637 2638 static int coreAudioCreateStreamOut(PDRVHOSTCOREAUDIO pThis,2639 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)2640 {2641 AssertPtrReturn(pThis, VERR_INVALID_POINTER);2642 AssertPtrReturn(pStream, VERR_INVALID_POINTER);2643 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);2644 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);2645 2646 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;2647 2648 LogFlowFuncEnter();2649 2650 pStreamOut->deviceID = kAudioDeviceUnknown;2651 pStreamOut->audioUnit = NULL;2652 pStreamOut->pCircBuf = NULL;2653 pStreamOut->status = CA_STATUS_UNINIT;2654 pStreamOut->fDefDevChgListReg = false;2655 pStreamOut->fDevStateChgListReg = false;2656 2657 /* Set callback context. */2658 pStreamOut->cbCtx.pThis = pThis;2659 pStreamOut->cbCtx.enmDir = PDMAUDIODIR_OUT;2660 pStreamOut->cbCtx.pOut = pStreamOut;2661 2662 bool fDeviceByUser = false; /* Do we use a device which was set by the user? */2663 2664 #if 02665 /* Try to find the audio device set by the user. Use2666 * export VBOX_COREAUDIO_OUTPUT_DEVICE_UID=AppleHDAEngineOutput:02667 * to set it. */2668 if (DeviceUID.pszOutputDeviceUID)2669 {2670 pStreamOut->audioDeviceId = drvHostCoreAudioDeviceUIDtoID(DeviceUID.pszOutputDeviceUID);2671 /* Not fatal */2672 if (pStreamOut->audioDeviceId == kAudioDeviceUnknown)2673 LogRel(("CoreAudio: Unable to find playback device %s. Falling back to the default audio device. \n", DeviceUID.pszOutputDeviceUID));2674 else2675 fDeviceByUser = true;2676 }2677 #endif2678 int rc = coreAudioInitOut(pThis, pStream, pCfgReq, pCfgAcq);2679 if (RT_SUCCESS(rc))2680 {2681 OSStatus err;2682 2683 /* When the devices isn't forced by the user, we want default device change notifications. */2684 if (!fDeviceByUser)2685 {2686 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,2687 kAudioObjectPropertyElementMaster };2688 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr,2689 coreAudioDefaultDeviceChanged, &pStreamOut->cbCtx);2690 if (err == noErr)2691 {2692 pStreamOut->fDefDevChgListReg = true;2693 }2694 else2695 LogRel(("CoreAudio: Failed to add the default playback device changed listener (%RI32)\n", err));2696 }2697 2698 if ( !pStreamOut->fDevStateChgListReg2699 && (pStreamOut->deviceID != kAudioDeviceUnknown))2700 {2701 /* Register callback for being notified if the device stops being alive. */2702 AudioObjectPropertyAddress propAdr = { kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,2703 kAudioObjectPropertyElementMaster };2704 err = AudioObjectAddPropertyListener(pStreamOut->deviceID, &propAdr, drvHostCoreAudioDeviceStateChanged,2705 (void *)&pStreamOut->cbCtx);2706 if (err == noErr)2707 {2708 pStreamOut->fDevStateChgListReg = true;2709 }2710 else2711 LogRel(("CoreAudio: Failed to add the playback device state changed listener (%RI32)\n", err));2712 }2713 }2714 2715 LogFlowFuncLeaveRC(rc);2716 return rc;2717 }2718 2719 2893 2720 2894 /** … … 2726 2900 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 2727 2901 2902 RT_BZERO(pBackendCfg, sizeof(PDMAUDIOBACKENDCFG)); 2903 2904 pBackendCfg->cbStreamIn = sizeof(COREAUDIOSTREAM); 2905 pBackendCfg->cbStreamOut = sizeof(COREAUDIOSTREAM); 2906 2907 pBackendCfg->cMaxStreamsIn = UINT32_MAX; 2908 pBackendCfg->cMaxStreamsOut = UINT32_MAX; 2909 2910 LogFlowFunc(("Returning %Rrc\n", VINF_SUCCESS)); 2911 return VINF_SUCCESS; 2912 } 2913 2914 2915 /** 2916 * @interface_method_impl{PDMIHOSTAUDIO, pfnGetDevices} 2917 */ 2918 static DECLCALLBACK(int) drvHostCoreAudioGetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIODEVICEENUM pDeviceEnum) 2919 { 2920 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2921 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER); 2922 2728 2923 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2729 2924 2730 return coreAudioUpdateStatusInternalEx(pThis, pBackendCfg, 0 /* fEnum */); 2731 } 2925 return coreAudioDevicesEnumerateAll(pThis, pDeviceEnum); 2926 } 2927 2928 2929 #ifdef VBOX_WITH_AUDIO_CALLBACKS 2930 /** 2931 * @interface_method_impl{PDMIHOSTAUDIO, pfnSetCallback} 2932 */ 2933 static DECLCALLBACK(int) drvHostCoreAudioSetCallback(PPDMIHOSTAUDIO pInterface, PFNPDMHOSTAUDIOCALLBACK pfnCallback) 2934 { 2935 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2936 /* pfnCallback will be handled below. */ 2937 2938 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2939 2940 int rc = RTCritSectEnter(&pThis->CritSect); 2941 if (RT_SUCCESS(rc)) 2942 { 2943 LogFunc(("pfnCallback=%p\n", pfnCallback)); 2944 2945 if (pfnCallback) /* Register. */ 2946 { 2947 Assert(pThis->pfnCallback == NULL); 2948 pThis->pfnCallback = pfnCallback; 2949 } 2950 else /* Unregister. */ 2951 { 2952 if (pThis->pfnCallback) 2953 pThis->pfnCallback = NULL; 2954 } 2955 2956 int rc2 = RTCritSectLeave(&pThis->CritSect); 2957 AssertRC(rc2); 2958 } 2959 2960 return rc; 2961 } 2962 #endif 2732 2963 2733 2964 … … 2748 2979 */ 2749 2980 static DECLCALLBACK(int) drvHostCoreAudioStreamCreate(PPDMIHOSTAUDIO pInterface, 2750 PPDMAUDIOSTREAM pStream, 2751 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2981 PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2752 2982 { 2753 2983 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); … … 2758 2988 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2759 2989 2990 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST); 2991 2992 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2993 2760 2994 int rc; 2761 if (pCfgReq->enmDir == PDMAUDIODIR_IN) 2762 rc = coreAudioCreateStreamIn(pThis, pStream, pCfgReq, pCfgAcq); 2763 else 2764 rc = coreAudioCreateStreamOut(pThis, pStream, pCfgReq, pCfgAcq); 2995 2996 /* Input or output device? */ 2997 bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN; 2998 2999 /* For now, just use the default device available. */ 3000 PPDMAUDIODEVICE pDev = fIn ? pThis->pDefaultDevIn : pThis->pDefaultDevOut; 3001 3002 /* Init the Core Audio stream. */ 3003 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 3004 if (RT_SUCCESS(rc)) 3005 { 3006 if (pDev) /* (Default) device available? */ 3007 { 3008 /* Sanity. */ 3009 AssertPtr(pDev->pvData); 3010 Assert(pDev->cbData); 3011 3012 if (RT_SUCCESS(rc)) 3013 { 3014 if (fIn) 3015 rc = coreAudioStreamInitIn (pCAStream, pCfgReq, pCfgAcq); 3016 else 3017 rc = coreAudioStreamInitOut(pCAStream, pCfgReq, pCfgAcq); 3018 3019 if (RT_FAILURE(rc)) 3020 { 3021 int rc2 = coreAudioStreamUninit(pCAStream); 3022 AssertRC(rc2); 3023 } 3024 } 3025 } 3026 else 3027 rc = VERR_NOT_AVAILABLE; 3028 } 2765 3029 2766 3030 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc)); … … 2777 3041 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2778 3042 2779 int rc; 2780 if (pStream->enmDir == PDMAUDIODIR_IN) 2781 rc = coreAudioDestroyStreamIn(pStream); 2782 else 2783 rc = coreAudioDestroyStreamOut(pStream); 2784 3043 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 3044 3045 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST); 3046 3047 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 3048 3049 uint32_t status = ASMAtomicReadU32(&pCAStream->status); 3050 if (!( status == COREAUDIOSTATUS_INIT 3051 || status == COREAUDIOSTATUS_REINIT)) 3052 { 3053 return VINF_SUCCESS; 3054 } 3055 3056 int rc = coreAudioStreamControl(pThis, pCAStream, PDMAUDIOSTREAMCMD_DISABLE); 3057 if (RT_SUCCESS(rc)) 3058 { 3059 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_IN_UNINIT); 3060 3061 rc = coreAudioStreamUninit(pCAStream); 3062 if (RT_SUCCESS(rc)) 3063 ASMAtomicXchgU32(&pCAStream->status, COREAUDIOSTATUS_UNINIT); 3064 } 3065 3066 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc)); 2785 3067 return rc; 2786 3068 } … … 2796 3078 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2797 3079 3080 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 3081 2798 3082 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST); 2799 3083 2800 int rc; 2801 if (pStream->enmDir == PDMAUDIODIR_IN) 2802 rc = coreAudioControlStreamIn(pStream, enmStreamCmd); 2803 else 2804 rc = coreAudioControlStreamOut(pStream, enmStreamCmd); 2805 2806 return rc; 3084 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 3085 3086 return coreAudioStreamControl(pThis, pCAStream, enmStreamCmd); 2807 3087 } 2808 3088 … … 2820 3100 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_NONE; 2821 3101 3102 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 3103 3104 if (ASMAtomicReadU32(&pCAStream->status) == COREAUDIOSTATUS_INIT) 3105 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED; 3106 2822 3107 if (pStream->enmDir == PDMAUDIODIR_IN) 2823 3108 { 2824 PCOREAUDIOSTREAMIN pStreamIn = (PCOREAUDIOSTREAMIN)pStream;2825 2826 if (ASMAtomicReadU32(&pStreamIn->status) == CA_STATUS_INIT)2827 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;2828 2829 3109 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED) 2830 3110 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_READABLE; … … 2832 3112 else if (pStream->enmDir == PDMAUDIODIR_OUT) 2833 3113 { 2834 PCOREAUDIOSTREAMOUT pStreamOut = (PCOREAUDIOSTREAMOUT)pStream;2835 2836 if (ASMAtomicReadU32(&pStreamOut->status) == CA_STATUS_INIT)2837 strmSts |= PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED;2838 2839 3114 if (strmSts & PDMAUDIOSTRMSTS_FLAG_ENABLED) 2840 3115 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE; … … 2861 3136 2862 3137 /** 3138 * @interface_method_impl{PDMIHOSTAUDIO, pfnInit} 3139 */ 3140 static DECLCALLBACK(int) drvHostCoreAudioInit(PPDMIHOSTAUDIO pInterface) 3141 { 3142 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 3143 3144 int rc = DrvAudioHlpDeviceEnumInit(&pThis->Devices); 3145 if (RT_SUCCESS(rc)) 3146 { 3147 /* Enumerate all devices internally. */ 3148 rc = coreAudioDevicesEnumerateAll(pThis, &pThis->Devices); 3149 if (RT_SUCCESS(rc)) 3150 { 3151 if (pThis->pDefaultDevIn) 3152 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevIn); 3153 3154 pThis->pDefaultDevIn = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_IN); 3155 if (pThis->pDefaultDevIn) 3156 { 3157 #ifdef DEBUG 3158 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevIn; 3159 AssertPtr(pDevData); 3160 LogFunc(("defaultDevIn ID=%RU32\n", pDevData->deviceID)); 3161 #endif 3162 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevIn); 3163 } 3164 else 3165 LogRel2(("CoreAudio: No initial default capturing device found!\n")); 3166 3167 if (pThis->pDefaultDevOut) 3168 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevOut); 3169 3170 pThis->pDefaultDevOut = DrvAudioHlpDeviceEnumGetDefaultDevice(&pThis->Devices, PDMAUDIODIR_OUT); 3171 if (pThis->pDefaultDevOut) 3172 { 3173 #ifdef DEBUG 3174 PCOREAUDIODEVICEDATA pDevData = (PCOREAUDIODEVICEDATA)pThis->pDefaultDevOut; 3175 AssertPtr(pDevData); 3176 3177 LogFunc(("defaultDevOut ID=%RU32\n", pDevData->deviceID)); 3178 #endif 3179 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevOut); 3180 } 3181 else 3182 LogRel2(("CoreAudio: No initial default playback device found!\n")); 3183 } 3184 } 3185 3186 if (RT_SUCCESS(rc)) 3187 { 3188 /* Register system callbacks. */ 3189 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, 3190 kAudioObjectPropertyElementMaster }; 3191 3192 OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr, 3193 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */); 3194 if ( err != noErr 3195 && err != kAudioHardwareIllegalOperationError) 3196 { 3197 LogRel(("CoreAudio: Failed to add the input default device changed listener (%RI32)\n", err)); 3198 } 3199 3200 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; 3201 err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propAdr, 3202 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */); 3203 if ( err != noErr 3204 && err != kAudioHardwareIllegalOperationError) 3205 { 3206 LogRel(("CoreAudio: Failed to add the output default device changed listener (%RI32)\n", err)); 3207 } 3208 } 3209 3210 LogFlowFunc(("Returning %Rrc\n", rc)); 3211 return rc; 3212 } 3213 3214 3215 /** 2863 3216 * @interface_method_impl{PDMIHOSTAUDIO, pfnShutdown} 2864 3217 */ 2865 3218 static DECLCALLBACK(void) drvHostCoreAudioShutdown(PPDMIHOSTAUDIO pInterface) 2866 3219 { 2867 RT_NOREF(pInterface); 3220 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 3221 3222 /* 3223 * Unregister system callbacks. 3224 */ 3225 AudioObjectPropertyAddress propAdr = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, 3226 kAudioObjectPropertyElementMaster }; 3227 3228 OSStatus err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr, 3229 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */); 3230 if ( err != noErr 3231 && err != kAudioHardwareBadObjectError) 3232 { 3233 LogRel(("CoreAudio: Failed to remove the default input device changed listener (%RI32)\n", err)); 3234 } 3235 3236 propAdr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; 3237 err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propAdr, 3238 coreAudioDefaultDeviceChangedCb, pThis /* pvUser */); 3239 if ( err != noErr 3240 && err != kAudioHardwareBadObjectError) 3241 { 3242 LogRel(("CoreAudio: Failed to remove the default output device changed listener (%RI32)\n", err)); 3243 } 3244 3245 LogFlowFuncEnter(); 2868 3246 } 2869 3247 … … 2874 3252 static DECLCALLBACK(void *) drvHostCoreAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID) 2875 3253 { 2876 PPDMDRVINS 2877 PDRVHOSTCOREAUDIO 2878 2879 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);3254 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); 3255 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO); 3256 3257 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); 2880 3258 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio); 2881 3259 … … 2883 3261 } 2884 3262 3263 2885 3264 /** 2886 3265 * @callback_method_impl{FNPDMDRVCONSTRUCT, 2887 * Construct a DirectSoundAudio driver instance.}3266 * Construct a Core Audio driver instance.} 2888 3267 */ 2889 3268 static DECLCALLBACK(int) drvHostCoreAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) … … 2903 3282 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostCoreAudio); 2904 3283 2905 return VINF_SUCCESS; 2906 } 3284 /* This backend supports device enumeration. */ 3285 pThis->IHostAudio.pfnGetDevices = drvHostCoreAudioGetDevices; 3286 3287 #ifdef VBOX_WITH_AUDIO_CALLBACKS 3288 /* This backend supports host audio callbacks. */ 3289 pThis->IHostAudio.pfnSetCallback = drvHostCoreAudioSetCallback; 3290 pThis->pfnCallback = NULL; 3291 #endif 3292 3293 int rc = RTCritSectInit(&pThis->CritSect); 3294 3295 LogFlowFuncLeaveRC(rc); 3296 return rc; 3297 } 3298 3299 3300 /** 3301 * @callback_method_impl{FNPDMDRVDESTRUCT} 3302 */ 3303 static DECLCALLBACK(void) drvHostCoreAudioDestruct(PPDMDRVINS pDrvIns) 3304 { 3305 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); 3306 PDRVHOSTCOREAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTCOREAUDIO); 3307 3308 int rc2 = RTCritSectDelete(&pThis->CritSect); 3309 AssertRC(rc2); 3310 3311 LogFlowFuncLeaveRC(rc2); 3312 } 3313 2907 3314 2908 3315 /** … … 2922 3329 "Core Audio host driver", 2923 3330 /* fFlags */ 2924 3331 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, 2925 3332 /* fClass. */ 2926 3333 PDM_DRVREG_CLASS_AUDIO, … … 2932 3339 drvHostCoreAudioConstruct, 2933 3340 /* pfnDestruct */ 2934 NULL,3341 drvHostCoreAudioDestruct, 2935 3342 /* pfnRelocate */ 2936 3343 NULL, -
trunk/src/VBox/Devices/Audio/DrvHostDSound.cpp
r63534 r63711 1424 1424 #endif 1425 1425 1426 Cfg.cSources = cbCtx.cDevIn;1427 Cfg.cSinks = cbCtx.cDevOut;1428 1426 Cfg.cMaxStreamsIn = UINT32_MAX; 1429 1427 Cfg.cMaxStreamsOut = UINT32_MAX; … … 2012 2010 && cbFree) 2013 2011 { 2014 PDMAUDIOC ALLBACKDATAOUT Out;2012 PDMAUDIOCBDATA_DATA_OUTPUT Out; 2015 2013 Out.cbInFree = cbFree; 2016 2014 Out.cbOutWritten = 0; -
trunk/src/VBox/Devices/Audio/DrvHostDebugAudio.cpp
r63629 r63711 82 82 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM); 83 83 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM); 84 85 /* The NULL backend has exactly one input source and one output sink. */86 pBackendCfg->cSources = 1;87 pBackendCfg->cSinks = 1;88 84 89 85 pBackendCfg->cMaxStreamsOut = 1; /* Output */ -
trunk/src/VBox/Devices/Audio/DrvHostNullAudio.cpp
r63549 r63711 106 106 pBackendCfg->cbStreamOut = sizeof(NULLAUDIOSTREAMOUT); 107 107 pBackendCfg->cbStreamIn = sizeof(NULLAUDIOSTREAMIN); 108 109 /* The NULL backend has exactly one input source and one output sink. */110 pBackendCfg->cSources = 1;111 pBackendCfg->cSinks = 1;112 108 113 109 pBackendCfg->cMaxStreamsOut = 1; /* Output */ -
trunk/src/VBox/Devices/Audio/DrvHostOSSAudio.cpp
r63534 r63711 589 589 pBackendCfg->cbStreamOut = sizeof(OSSAUDIOSTREAMOUT); 590 590 591 pBackendCfg->cSources = 0;592 pBackendCfg->cSinks = 0;593 594 591 int hFile = open("/dev/dsp", O_WRONLY | O_NONBLOCK, 0); 595 592 if (hFile == -1) … … 623 620 cDev = ossInfo.numaudios; 624 621 625 pBackendCfg->cSources = cDev;626 pBackendCfg->cSinks = cDev;627 628 622 pBackendCfg->cMaxStreamsIn = UINT32_MAX; 629 623 pBackendCfg->cMaxStreamsOut = UINT32_MAX; … … 634 628 /* Since we cannot query anything, assume that we have at least 635 629 * one input and one output if we found "/dev/dsp" or "/dev/mixer". */ 636 pBackendCfg->cSources = 1;637 pBackendCfg->cSinks = 1;638 630 639 631 pBackendCfg->cMaxStreamsIn = UINT32_MAX; -
trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp
r63534 r63711 1106 1106 if (RT_SUCCESS(rc)) 1107 1107 { 1108 Cfg.cSinks = cbCtx.cDevOut;1109 Cfg.cSources = cbCtx.cDevIn;1110 1111 1108 if (fLog) 1112 1109 { -
trunk/src/VBox/Devices/Makefile.kmk
r63689 r63711 536 536 endif 537 537 538 # Enable generic callback support. 539 VBoxDD_DEFS += VBOX_WITH_AUDIO_CALLBACKS 540 541 # Not yet enabled: Callbacks for the device emulation to let the backends 542 # tell the emulation when and how to process data. 543 if 0 544 VBOX_DEFS += VBOX_WITH_AUDIO_DEVICE_CALLBACKS 545 VBOX_DEFS += VBOX_WITH_AUDIO_SB16_CALLBACKS 546 VBOX_DEFS += VBOX_WITH_AUDIO_AC97_CALLBACKS 547 VBOX_DEFS += VBOX_WITH_AUDIO_HDA_CALLBACKS 548 endif 549 538 550 VBoxDD_SOURCES += \ 539 551 Audio/DevIchAc97.cpp \ -
trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp
r63534 r63711 363 363 pBackendCfg->cMaxStreamsIn = UINT32_MAX; 364 364 pBackendCfg->cMaxStreamsOut = UINT32_MAX; 365 pBackendCfg->cSources = 1;366 pBackendCfg->cSinks = 1;367 365 368 366 return VINF_SUCCESS; … … 629 627 */ 630 628 void *pvUser; 631 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); 629 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */ 632 630 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc); 633 631 … … 639 637 */ 640 638 pvUser = NULL; 641 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); 639 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */ 642 640 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc); 643 641
Note:
See TracChangeset
for help on using the changeset viewer.