Changeset 89008 in vbox
- Timestamp:
- May 12, 2021 12:37:56 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 144347
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Devices/Audio/DrvHostAudioCoreAudio.cpp
r88959 r89008 425 425 * Internal Functions * 426 426 *********************************************************************************************************************************/ 427 static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev);428 427 #ifndef VBOX_WITH_AUDIO_CALLBACKS 429 428 static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PCOREAUDIODEVICEDATA pDev); … … 432 431 433 432 static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd); 434 435 static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv);436 437 static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID, UInt32 nAddresses,438 const AudioObjectPropertyAddress properties[], void *pvUser);439 433 440 434 static int coreAudioStreamInitQueue(PCOREAUDIOSTREAM pCAStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq); … … 501 495 502 496 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 497 498 499 /** 500 * Propagates an audio device status to all its connected Core Audio streams. 501 * 502 * @return IPRT status code. 503 * @param pDev Audio device to propagate status for. 504 * @param enmSts Status to propagate. 505 */ 506 static int coreAudioDevicePropagateStatus(PCOREAUDIODEVICEDATA pDev, COREAUDIOSTATUS enmSts) 507 { 508 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 509 510 511 /* Sanity. */ 512 AssertPtr(pDev->pDrv); 513 514 LogFlowFunc(("pDev=%p enmSts=%RU32\n", pDev, enmSts)); 515 516 PCOREAUDIOSTREAM pCAStream; 517 RTListForEach(&pDev->lstStreams, pCAStream, COREAUDIOSTREAM, Node) 518 { 519 LogFlowFunc(("pCAStream=%p\n", pCAStream)); 520 521 /* We move the reinitialization to the next output event. 522 * This make sure this thread isn't blocked and the 523 * reinitialization is done when necessary only. */ 524 ASMAtomicXchgU32(&pCAStream->enmStatus, enmSts); 525 } 526 527 return VINF_SUCCESS; 528 } 529 530 531 static DECLCALLBACK(OSStatus) coreAudioDeviceStateChangedCb(AudioObjectID propertyID, 532 UInt32 nAddresses, 533 const AudioObjectPropertyAddress properties[], 534 void *pvUser) 535 { 536 RT_NOREF(propertyID, nAddresses, properties); 537 538 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 539 540 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pvUser; 541 AssertPtr(pDev); 542 543 PDRVHOSTCOREAUDIO pThis = pDev->pDrv; 544 AssertPtr(pThis); 545 546 int rc2 = RTCritSectEnter(&pThis->CritSect); 547 AssertRC(rc2); 548 549 UInt32 uAlive = 1; 550 UInt32 uSize = sizeof(UInt32); 551 552 AudioObjectPropertyAddress PropAddr = 553 { 554 kAudioDevicePropertyDeviceIsAlive, 555 kAudioObjectPropertyScopeGlobal, 556 kAudioObjectPropertyElementMaster 557 }; 558 559 AudioDeviceID deviceID = pDev->deviceID; 560 561 OSStatus err = AudioObjectGetPropertyData(deviceID, &PropAddr, 0, NULL, &uSize, &uAlive); 562 563 bool fIsDead = false; 564 565 if (err == kAudioHardwareBadDeviceError) 566 fIsDead = true; /* Unplugged. */ 567 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive))) 568 fIsDead = true; /* Something else happened. */ 569 570 if (fIsDead) 571 { 572 LogRel2(("CoreAudio: Device '%s' stopped functioning\n", pDev->Core.szName)); 573 574 /* Mark device as dead. */ 575 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_UNINIT); 576 AssertRC(rc2); 577 } 578 579 rc2 = RTCritSectLeave(&pThis->CritSect); 580 AssertRC(rc2); 581 582 return noErr; 583 } 584 585 /* Callback for getting notified when the default recording/playback device has been changed. */ 586 /** @todo r=bird: Why DECLCALLBACK? */ 587 static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChangedCb(AudioObjectID propertyID, 588 UInt32 nAddresses, 589 const AudioObjectPropertyAddress properties[], 590 void *pvUser) 591 { 592 RT_NOREF(propertyID, nAddresses); 593 594 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 595 596 PDRVHOSTCOREAUDIO pThis = (PDRVHOSTCOREAUDIO)pvUser; 597 AssertPtr(pThis); 598 599 int rc2 = RTCritSectEnter(&pThis->CritSect); 600 AssertRC(rc2); 601 602 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++) 603 { 604 PCOREAUDIODEVICEDATA pDev = NULL; 605 606 /* 607 * Check if the default input / output device has been changed. 608 */ 609 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress]; 610 switch (pProperty->mSelector) 611 { 612 case kAudioHardwarePropertyDefaultInputDevice: 613 LogFlowFunc(("kAudioHardwarePropertyDefaultInputDevice\n")); 614 pDev = pThis->pDefaultDevIn; 615 break; 616 617 case kAudioHardwarePropertyDefaultOutputDevice: 618 LogFlowFunc(("kAudioHardwarePropertyDefaultOutputDevice\n")); 619 pDev = pThis->pDefaultDevOut; 620 break; 621 622 default: 623 /* Skip others. */ 624 break; 625 } 626 627 LogFlowFunc(("pDev=%p\n", pDev)); 628 629 #ifndef VBOX_WITH_AUDIO_CALLBACKS 630 if (pDev) 631 { 632 /* This listener is called on every change of the hardware 633 * device. So check if the default device has really changed. */ 634 UInt32 uSize = sizeof(AudioDeviceID); 635 UInt32 uResp = 0; 636 637 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp); 638 if (err == noErr) 639 { 640 if (pDev->deviceID != uResp) /* Has the device ID changed? */ 641 { 642 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT); 643 AssertRC(rc2); 644 } 645 } 646 } 647 #endif /* VBOX_WITH_AUDIO_CALLBACKS */ 648 } 649 650 /* Make sure to leave the critical section before notify higher drivers/devices. */ 651 rc2 = RTCritSectLeave(&pThis->CritSect); 652 AssertRC(rc2); 653 654 #ifdef VBOX_WITH_AUDIO_CALLBACKS 655 /* Notify the driver/device above us about possible changes in devices. */ 656 if (pThis->pIHostAudioPort) 657 pThis->pIHostAudioPort->pfnNotifyDevicesChanged(pThis->pIHostAudioPort); 658 #endif 659 660 return noErr; 661 } 662 663 #ifndef VBOX_WITH_AUDIO_CALLBACKS 664 665 /** 666 * Re-initializes a Core Audio stream with a specific audio device and stream configuration. 667 * 668 * @return IPRT status code. 669 * @param pThis Driver instance. 670 * @param pCAStream Audio stream to re-initialize. 671 * @param pDev Audio device to use for re-initialization. 672 * @param pCfg Stream configuration to use for re-initialization. 673 */ 674 static int coreAudioStreamReinitEx(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, 675 PCOREAUDIODEVICEDATA pDev, PPDMAUDIOSTREAMCFG pCfg) 676 { 677 LogFunc(("pCAStream=%p\n", pCAStream)); 678 679 int rc = coreAudioStreamUninit(pCAStream); 680 if (RT_SUCCESS(rc)) 681 { 682 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 683 if (RT_SUCCESS(rc)) 684 { 685 rc = coreAudioStreamInitQueue(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */); 686 if (RT_SUCCESS(rc)) 687 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE); 688 689 if (RT_FAILURE(rc)) 690 { 691 int rc2 = coreAudioStreamUninit(pCAStream); 692 AssertRC(rc2); 693 } 694 } 695 } 696 697 if (RT_FAILURE(rc)) 698 LogRel(("CoreAudio: Unable to re-init stream: %Rrc\n", rc)); 699 700 return rc; 701 } 702 703 /** 704 * Re-initializes a Core Audio stream with a specific audio device. 705 * 706 * @return IPRT status code. 707 * @param pThis Driver instance. 708 * @param pCAStream Audio stream to re-initialize. 709 * @param pDev Audio device to use for re-initialization. 710 */ 711 static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PCOREAUDIODEVICEDATA pDev) 712 { 713 int rc = coreAudioStreamUninit(pCAStream); 714 if (RT_SUCCESS(rc)) 715 { 716 /* Use the acquired stream configuration from the former initialization to 717 * re-initialize the stream. */ 718 PDMAUDIOSTREAMCFG CfgAcq; 719 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, &CfgAcq); 720 if (RT_SUCCESS(rc)) 721 rc = coreAudioStreamReinitEx(pThis, pCAStream, pDev, &CfgAcq); 722 } 723 724 return rc; 725 } 726 727 #endif /* !VBOX_WITH_AUDIO_CALLBACKS */ 728 729 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 730 /* Callback to convert audio input data from one format to another. */ 731 static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef inAudioConverter, 732 UInt32 *ioNumberDataPackets, 733 AudioBufferList *ioData, 734 AudioStreamPacketDescription **ppASPD, 735 void *pvUser) 736 { 737 RT_NOREF(inAudioConverter); 738 739 AssertPtrReturn(ioNumberDataPackets, caConverterEOFDErr); 740 AssertPtrReturn(ioData, caConverterEOFDErr); 741 742 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser; 743 AssertPtr(pConvCbCtx); 744 745 /* Initialize values. */ 746 ioData->mBuffers[0].mNumberChannels = 0; 747 ioData->mBuffers[0].mDataByteSize = 0; 748 ioData->mBuffers[0].mData = NULL; 749 750 if (ppASPD) 751 { 752 Log3Func(("Handling packet description not implemented\n")); 753 } 754 else 755 { 756 /** @todo Check converter ID? */ 757 758 /** @todo Handled non-interleaved data by going through the full buffer list, 759 * not only through the first buffer like we do now. */ 760 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets)); 761 762 UInt32 cNumberDataPackets = *ioNumberDataPackets; 763 Assert(pConvCbCtx->uPacketIdx + cNumberDataPackets <= pConvCbCtx->uPacketCnt); 764 765 if (cNumberDataPackets) 766 { 767 AssertPtr(pConvCbCtx->pBufLstSrc); 768 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers == 1); /* Only one buffer for the source supported atm. */ 769 770 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc; 771 AudioBuffer *pSrcBuf = &pConvCbCtx->pBufLstSrc->mBuffers[0]; 772 773 size_t cbOff = pConvCbCtx->uPacketIdx * pSrcASBD->mBytesPerPacket; 774 775 cNumberDataPackets = RT_MIN((pSrcBuf->mDataByteSize - cbOff) / pSrcASBD->mBytesPerPacket, 776 cNumberDataPackets); 777 778 void *pvAvail = (uint8_t *)pSrcBuf->mData + cbOff; 779 size_t cbAvail = RT_MIN(pSrcBuf->mDataByteSize - cbOff, cNumberDataPackets * pSrcASBD->mBytesPerPacket); 780 781 Log3Func(("cNumberDataPackets=%RU32, cbOff=%zu, cbAvail=%zu\n", cNumberDataPackets, cbOff, cbAvail)); 782 783 /* Set input data for the converter to use. 784 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */ 785 ioData->mNumberBuffers = 1; 786 787 ioData->mBuffers[0].mNumberChannels = pSrcBuf->mNumberChannels; 788 ioData->mBuffers[0].mDataByteSize = cbAvail; 789 ioData->mBuffers[0].mData = pvAvail; 790 791 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA 792 RTFILE fh; 793 int rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caConverterCbInput.pcm", 794 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 795 if (RT_SUCCESS(rc)) 796 { 797 RTFileWrite(fh, pvAvail, cbAvail, NULL); 798 RTFileClose(fh); 799 } 800 else 801 AssertFailed(); 802 #endif 803 pConvCbCtx->uPacketIdx += cNumberDataPackets; 804 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt); 805 806 *ioNumberDataPackets = cNumberDataPackets; 807 } 808 } 809 810 Log3Func(("%RU32 / %RU32 -> ioNumberDataPackets=%RU32\n", 811 pConvCbCtx->uPacketIdx, pConvCbCtx->uPacketCnt, *ioNumberDataPackets)); 812 813 return noErr; 814 } 815 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 816 817 818 /** 819 * Thread for a Core Audio stream's audio queue handling. 820 * 821 * This thread is required per audio queue to pump data to/from the Core Audio 822 * stream and handling its callbacks. 823 * 824 * @returns IPRT status code. 825 * @param hThreadSelf Thread handle. 826 * @param pvUser User argument. 827 */ 828 static DECLCALLBACK(int) coreAudioQueueThread(RTTHREAD hThreadSelf, void *pvUser) 829 { 830 RT_NOREF(hThreadSelf); 831 832 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 833 AssertPtr(pCAStream); 834 AssertPtr(pCAStream->pCfg); 835 836 const bool fIn = pCAStream->pCfg->enmDir == PDMAUDIODIR_IN; 837 838 LogFunc(("Thread started for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn)); 839 840 /* 841 * Create audio queue. 842 */ 843 OSStatus err; 844 if (fIn) 845 err = AudioQueueNewInput(&pCAStream->asbdStream, coreAudioInputQueueCb, pCAStream /* pvData */, 846 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue); 847 else 848 err = AudioQueueNewOutput(&pCAStream->asbdStream, coreAudioOutputQueueCb, pCAStream /* pvData */, 849 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue); 850 851 if (err != noErr) 852 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 853 854 /* 855 * Assign device to queue. 856 */ 857 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pCAStream->Unit.pDevice; 858 AssertPtr(pDev); 859 860 UInt32 uSize = sizeof(pDev->UUID); 861 err = AudioQueueSetProperty(pCAStream->audioQueue, kAudioQueueProperty_CurrentDevice, &pDev->UUID, uSize); 862 if (err != noErr) 863 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 864 865 const size_t cbBufSize = PDMAudioPropsFramesToBytes(&pCAStream->pCfg->Props, pCAStream->pCfg->Backend.cFramesPeriod); 866 867 /* 868 * Allocate audio buffers. 869 */ 870 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 871 { 872 err = AudioQueueAllocateBuffer(pCAStream->audioQueue, cbBufSize, &pCAStream->audioBuffer[i]); 873 if (err != noErr) 874 break; 875 } 876 877 if (err != noErr) 878 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 879 880 /* Signal the main thread before entering the main loop. */ 881 RTThreadUserSignal(RTThreadSelf()); 882 883 /* 884 * Enter the main loop. 885 */ 886 while (!ASMAtomicReadBool(&pCAStream->fShutdown)) 887 { 888 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); 889 } 890 891 /* 892 * Cleanup. 893 */ 894 if (fIn) 895 { 896 AudioQueueStop(pCAStream->audioQueue, 1); 897 } 898 else 899 { 900 AudioQueueStop(pCAStream->audioQueue, 0); 901 } 902 903 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 904 { 905 if (pCAStream->audioBuffer[i]) 906 AudioQueueFreeBuffer(pCAStream->audioQueue, pCAStream->audioBuffer[i]); 907 } 908 909 AudioQueueDispose(pCAStream->audioQueue, 1); 910 911 LogFunc(("Thread ended for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn)); 912 return VINF_SUCCESS; 913 } 914 915 /** 916 * Processes input data of an audio queue buffer and stores it into a Core Audio stream. 917 * 918 * @returns IPRT status code. 919 * @param pCAStream Core Audio stream to store input data into. 920 * @param audioBuffer Audio buffer to process input data from. 921 */ 922 static int coreAudioInputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer) 923 { 924 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf; 925 AssertPtr(pCircBuf); 926 927 UInt8 *pvSrc = (UInt8 *)audioBuffer->mAudioData; 928 UInt8 *pvDst = NULL; 929 930 size_t cbWritten = 0; 931 932 size_t cbToWrite = audioBuffer->mAudioDataByteSize; 933 size_t cbLeft = RT_MIN(cbToWrite, RTCircBufFree(pCircBuf)); 934 935 while (cbLeft) 936 { 937 /* Try to acquire the necessary block from the ring buffer. */ 938 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, (void **)&pvDst, &cbToWrite); 939 940 if (!cbToWrite) 941 break; 942 943 /* Copy the data from our ring buffer to the core audio buffer. */ 944 memcpy((UInt8 *)pvDst, pvSrc + cbWritten, cbToWrite); 945 946 /* Release the read buffer, so it could be used for new data. */ 947 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite); 948 949 cbWritten += cbToWrite; 950 951 Assert(cbLeft >= cbToWrite); 952 cbLeft -= cbToWrite; 953 } 954 955 Log3Func(("pCAStream=%p, cbBuffer=%RU32/%zu, cbWritten=%zu\n", 956 pCAStream, audioBuffer->mAudioDataByteSize, audioBuffer->mAudioDataBytesCapacity, cbWritten)); 957 958 return VINF_SUCCESS; 959 } 960 961 /** 962 * Input audio queue callback. Called whenever input data from the audio queue becomes available. 963 * 964 * @param pvUser User argument. 965 * @param audioQueue Audio queue to process input data from. 966 * @param audioBuffer Audio buffer to process input data from. Must be part of audio queue. 967 * @param pAudioTS Audio timestamp. 968 * @param cPacketDesc Number of packet descriptors. 969 * @param paPacketDesc Array of packet descriptors. 970 */ 971 static DECLCALLBACK(void) coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer, 972 const AudioTimeStamp *pAudioTS, 973 UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc) 974 { 975 RT_NOREF(audioQueue, pAudioTS, cPacketDesc, paPacketDesc); 976 977 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 978 AssertPtr(pCAStream); 979 980 int rc = RTCritSectEnter(&pCAStream->CritSect); 981 AssertRC(rc); 982 983 rc = coreAudioInputQueueProcBuffer(pCAStream, audioBuffer); 984 if (RT_SUCCESS(rc)) 985 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL); 986 987 rc = RTCritSectLeave(&pCAStream->CritSect); 988 AssertRC(rc); 989 } 990 991 /** 992 * Processes output data of a Core Audio stream into an audio queue buffer. 993 * 994 * @returns IPRT status code. 995 * @param pCAStream Core Audio stream to process output data for. 996 * @param audioBuffer Audio buffer to store data into. 997 */ 998 int coreAudioOutputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer) 999 { 1000 AssertPtr(pCAStream); 1001 1002 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf; 1003 AssertPtr(pCircBuf); 1004 1005 size_t cbRead = 0; 1006 1007 UInt8 *pvSrc = NULL; 1008 UInt8 *pvDst = (UInt8 *)audioBuffer->mAudioData; 1009 1010 size_t cbToRead = RT_MIN(RTCircBufUsed(pCircBuf), audioBuffer->mAudioDataBytesCapacity); 1011 size_t cbLeft = cbToRead; 1012 1013 while (cbLeft) 1014 { 1015 /* Try to acquire the necessary block from the ring buffer. */ 1016 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, (void **)&pvSrc, &cbToRead); 1017 1018 if (cbToRead) 1019 { 1020 /* Copy the data from our ring buffer to the core audio buffer. */ 1021 memcpy((UInt8 *)pvDst + cbRead, pvSrc, cbToRead); 1022 } 1023 1024 /* Release the read buffer, so it could be used for new data. */ 1025 RTCircBufReleaseReadBlock(pCircBuf, cbToRead); 1026 1027 if (!cbToRead) 1028 break; 1029 1030 /* Move offset. */ 1031 cbRead += cbToRead; 1032 Assert(cbRead <= audioBuffer->mAudioDataBytesCapacity); 1033 1034 Assert(cbToRead <= cbLeft); 1035 cbLeft -= cbToRead; 1036 } 1037 1038 audioBuffer->mAudioDataByteSize = cbRead; 1039 1040 if (audioBuffer->mAudioDataByteSize < audioBuffer->mAudioDataBytesCapacity) 1041 { 1042 RT_BZERO((UInt8 *)audioBuffer->mAudioData + audioBuffer->mAudioDataByteSize, 1043 audioBuffer->mAudioDataBytesCapacity - audioBuffer->mAudioDataByteSize); 1044 1045 audioBuffer->mAudioDataByteSize = audioBuffer->mAudioDataBytesCapacity; 1046 } 1047 1048 Log3Func(("pCAStream=%p, cbCapacity=%RU32, cbRead=%zu\n", 1049 pCAStream, audioBuffer->mAudioDataBytesCapacity, cbRead)); 1050 1051 return VINF_SUCCESS; 1052 } 1053 1054 /** 1055 * Output audio queue callback. Called whenever an audio queue is ready to process more output data. 1056 * 1057 * @param pvUser User argument. 1058 * @param audioQueue Audio queue to process output data for. 1059 * @param audioBuffer Audio buffer to store output data in. Must be part of audio queue. 1060 */ 1061 static DECLCALLBACK(void) coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer) 1062 { 1063 RT_NOREF(audioQueue); 1064 1065 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 1066 AssertPtr(pCAStream); 1067 1068 int rc = RTCritSectEnter(&pCAStream->CritSect); 1069 AssertRC(rc); 1070 1071 rc = coreAudioOutputQueueProcBuffer(pCAStream, audioBuffer); 1072 if (RT_SUCCESS(rc)) 1073 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL); 1074 1075 rc = RTCritSectLeave(&pCAStream->CritSect); 1076 AssertRC(rc); 1077 } 1078 1079 /** 1080 * Invalidates a Core Audio stream's audio queue. 1081 * 1082 * @returns IPRT status code. 1083 * @param pCAStream Core Audio stream to invalidate its queue for. 1084 */ 1085 static int coreAudioStreamInvalidateQueue(PCOREAUDIOSTREAM pCAStream) 1086 { 1087 int rc = VINF_SUCCESS; 1088 1089 Log3Func(("pCAStream=%p\n", pCAStream)); 1090 1091 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 1092 { 1093 AudioQueueBufferRef pBuf = pCAStream->audioBuffer[i]; 1094 1095 if (pCAStream->pCfg->enmDir == PDMAUDIODIR_IN) 1096 { 1097 int rc2 = coreAudioInputQueueProcBuffer(pCAStream, pBuf); 1098 if (RT_SUCCESS(rc2)) 1099 { 1100 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL); 1101 } 1102 } 1103 else if (pCAStream->pCfg->enmDir == PDMAUDIODIR_OUT) 1104 { 1105 int rc2 = coreAudioOutputQueueProcBuffer(pCAStream, pBuf); 1106 if ( RT_SUCCESS(rc2) 1107 && pBuf->mAudioDataByteSize) 1108 { 1109 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL); 1110 } 1111 1112 if (RT_SUCCESS(rc)) 1113 rc = rc2; 1114 } 1115 else 1116 AssertFailed(); 1117 } 1118 1119 return rc; 1120 } 1121 1122 1123 /* Callback for getting notified when some of the properties of an audio device have changed. */ 1124 static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID, 1125 UInt32 cAddresses, 1126 const AudioObjectPropertyAddress properties[], 1127 void *pvUser) 1128 { 1129 RT_NOREF(cAddresses, properties, pvUser); 1130 1131 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pvUser; 1132 AssertPtr(pDev); 1133 1134 LogFlowFunc(("propertyID=%u, nAddresses=%u, pDev=%p\n", propertyID, cAddresses, pDev)); 1135 1136 switch (propertyID) 1137 { 1138 #ifdef DEBUG 1139 case kAudioDeviceProcessorOverload: 1140 { 1141 LogFunc(("Processor overload detected!\n")); 1142 break; 1143 } 1144 #endif /* DEBUG */ 1145 case kAudioDevicePropertyNominalSampleRate: 1146 { 1147 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1148 int rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT); 1149 AssertRC(rc2); 1150 #else 1151 RT_NOREF(pDev); 1152 #endif 1153 break; 1154 } 1155 1156 default: 1157 /* Just skip. */ 1158 break; 1159 } 1160 1161 return noErr; 1162 } 1163 1164 1165 /********************************************************************************************************************************* 1166 * PDMIHOSTAUDIO * 1167 *********************************************************************************************************************************/ 1168 1169 /** 1170 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 1171 */ 1172 static DECLCALLBACK(int) drvHostCoreAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 1173 { 1174 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 1175 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 1176 1177 /* 1178 * Fill in the config structure. 1179 */ 1180 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Core Audio"); 1181 pBackendCfg->cbStream = sizeof(COREAUDIOSTREAM); 1182 pBackendCfg->fFlags = 0; 1183 /* For Core Audio we provide one stream per device for now. */ 1184 pBackendCfg->cMaxStreamsIn = PDMAudioHostEnumCountMatching(&pThis->Devices, PDMAUDIODIR_IN); 1185 pBackendCfg->cMaxStreamsOut = PDMAudioHostEnumCountMatching(&pThis->Devices, PDMAUDIODIR_OUT); 1186 1187 LogFlowFunc(("Returning %Rrc\n", VINF_SUCCESS)); 1188 return VINF_SUCCESS; 1189 } 1190 1191 1192 /** 1193 * Initializes a Core Audio-specific device data structure. 1194 * 1195 * @returns IPRT status code. 1196 * @param pDevData Device data structure to initialize. 1197 * @param deviceID Core Audio device ID to assign this structure to. 1198 * @param fIsInput Whether this is an input device or not. 1199 * @param pDrv Driver instance to use. 1200 */ 1201 static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv) 1202 { 1203 AssertPtrReturnVoid(pDevData); 1204 AssertPtrReturnVoid(pDrv); 1205 1206 pDevData->deviceID = deviceID; 1207 pDevData->pDrv = pDrv; 1208 1209 /* Get the device UUID. */ 1210 AudioObjectPropertyAddress PropAddrDevUUID = 1211 { 1212 kAudioDevicePropertyDeviceUID, 1213 fIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 1214 kAudioObjectPropertyElementMaster 1215 }; 1216 UInt32 uSize = sizeof(pDevData->UUID); 1217 OSStatus err = AudioObjectGetPropertyData(pDevData->deviceID, &PropAddrDevUUID, 0, NULL, &uSize, &pDevData->UUID); 1218 if (err != noErr) 1219 LogRel(("CoreAudio: Failed to retrieve device UUID for device %RU32 (%RI32)\n", deviceID, err)); 1220 1221 RTListInit(&pDevData->lstStreams); 1222 } 1223 503 1224 504 1225 /** … … 885 1606 886 1607 /** 887 * Initializes a Core Audio-specific device data structure. 1608 * Registers callbacks for a specific Core Audio device. 1609 * 1610 * @return IPRT status code. 1611 * @param pThis Host audio driver instance. 1612 * @param pDev Audio device to use for the registered callbacks. 1613 */ 1614 static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1615 { 1616 RT_NOREF(pThis); 1617 1618 AudioDeviceID deviceID = kAudioDeviceUnknown; 1619 Assert(pDev && pDev->Core.cbSelf == sizeof(*pDev)); 1620 if (pDev && pDev->Core.cbSelf == sizeof(*pDev)) /* paranoia or actually needed? */ 1621 deviceID = pDev->deviceID; 1622 1623 if (deviceID != kAudioDeviceUnknown) 1624 { 1625 LogFunc(("deviceID=%RU32\n", deviceID)); 1626 1627 /* 1628 * Register device callbacks. 1629 */ 1630 AudioObjectPropertyAddress PropAddr = 1631 { 1632 kAudioDevicePropertyDeviceIsAlive, 1633 kAudioObjectPropertyScopeGlobal, 1634 kAudioObjectPropertyElementMaster 1635 }; 1636 OSStatus err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDeviceStateChangedCb, pDev /* pvUser */); 1637 if ( err != noErr 1638 && err != kAudioHardwareIllegalOperationError) 1639 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err)); 1640 1641 PropAddr.mSelector = kAudioDeviceProcessorOverload; 1642 PropAddr.mScope = kAudioUnitScope_Global; 1643 err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1644 if (err != noErr) 1645 LogRel(("CoreAudio: Failed to register processor overload listener (%RI32)\n", err)); 1646 1647 PropAddr.mSelector = kAudioDevicePropertyNominalSampleRate; 1648 PropAddr.mScope = kAudioUnitScope_Global; 1649 err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1650 if (err != noErr) 1651 LogRel(("CoreAudio: Failed to register sample rate changed listener (%RI32)\n", err)); 1652 } 1653 1654 return VINF_SUCCESS; 1655 } 1656 1657 1658 /** 1659 * Unregisters all formerly registered callbacks of a Core Audio device again. 1660 * 1661 * @return IPRT status code. 1662 * @param pThis Host audio driver instance. 1663 * @param pDev Audio device to use for the registered callbacks. 1664 */ 1665 static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1666 { 1667 RT_NOREF(pThis); 1668 1669 AudioDeviceID deviceID = kAudioDeviceUnknown; 1670 Assert(pDev && pDev->Core.cbSelf == sizeof(*pDev)); 1671 if (pDev && pDev->Core.cbSelf == sizeof(*pDev)) /* paranoia or actually needed? */ 1672 deviceID = pDev->deviceID; 1673 1674 if (deviceID != kAudioDeviceUnknown) 1675 { 1676 LogFunc(("deviceID=%RU32\n", deviceID)); 1677 1678 /* 1679 * Unregister per-device callbacks. 1680 */ 1681 AudioObjectPropertyAddress PropAddr = 1682 { 1683 kAudioDeviceProcessorOverload, 1684 kAudioObjectPropertyScopeGlobal, 1685 kAudioObjectPropertyElementMaster 1686 }; 1687 OSStatus err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1688 if ( err != noErr 1689 && err != kAudioHardwareBadObjectError) 1690 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err)); 1691 1692 PropAddr.mSelector = kAudioDevicePropertyNominalSampleRate; 1693 err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1694 if ( err != noErr 1695 && err != kAudioHardwareBadObjectError) 1696 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err)); 1697 1698 PropAddr.mSelector = kAudioDevicePropertyDeviceIsAlive; 1699 err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDeviceStateChangedCb, pDev /* pvUser */); 1700 if ( err != noErr 1701 && err != kAudioHardwareBadObjectError) 1702 LogRel(("CoreAudio: Failed to remove the device alive listener (%RI32)\n", err)); 1703 } 1704 1705 return VINF_SUCCESS; 1706 } 1707 1708 1709 /** 1710 * Enumerates all available host audio devices internally. 888 1711 * 889 1712 * @returns IPRT status code. 890 * @param pDevData Device data structure to initialize. 891 * @param deviceID Core Audio device ID to assign this structure to. 892 * @param fIsInput Whether this is an input device or not. 893 * @param pDrv Driver instance to use. 894 */ 895 static void coreAudioDeviceDataInit(PCOREAUDIODEVICEDATA pDevData, AudioDeviceID deviceID, bool fIsInput, PDRVHOSTCOREAUDIO pDrv) 896 { 897 AssertPtrReturnVoid(pDevData); 898 AssertPtrReturnVoid(pDrv); 899 900 pDevData->deviceID = deviceID; 901 pDevData->pDrv = pDrv; 902 903 /* Get the device UUID. */ 904 AudioObjectPropertyAddress PropAddrDevUUID = 905 { 906 kAudioDevicePropertyDeviceUID, 907 fIsInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput, 908 kAudioObjectPropertyElementMaster 909 }; 910 UInt32 uSize = sizeof(pDevData->UUID); 911 OSStatus err = AudioObjectGetPropertyData(pDevData->deviceID, &PropAddrDevUUID, 0, NULL, &uSize, &pDevData->UUID); 912 if (err != noErr) 913 LogRel(("CoreAudio: Failed to retrieve device UUID for device %RU32 (%RI32)\n", deviceID, err)); 914 915 RTListInit(&pDevData->lstStreams); 916 } 917 918 919 /** 920 * Propagates an audio device status to all its connected Core Audio streams. 921 * 922 * @return IPRT status code. 923 * @param pDev Audio device to propagate status for. 924 * @param enmSts Status to propagate. 925 */ 926 static int coreAudioDevicePropagateStatus(PCOREAUDIODEVICEDATA pDev, COREAUDIOSTATUS enmSts) 927 { 928 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 929 930 931 /* Sanity. */ 932 AssertPtr(pDev->pDrv); 933 934 LogFlowFunc(("pDev=%p enmSts=%RU32\n", pDev, enmSts)); 935 936 PCOREAUDIOSTREAM pCAStream; 937 RTListForEach(&pDev->lstStreams, pCAStream, COREAUDIOSTREAM, Node) 938 { 939 LogFlowFunc(("pCAStream=%p\n", pCAStream)); 940 941 /* We move the reinitialization to the next output event. 942 * This make sure this thread isn't blocked and the 943 * reinitialization is done when necessary only. */ 944 ASMAtomicXchgU32(&pCAStream->enmStatus, enmSts); 945 } 946 947 return VINF_SUCCESS; 948 } 949 950 951 static DECLCALLBACK(OSStatus) coreAudioDeviceStateChangedCb(AudioObjectID propertyID, 952 UInt32 nAddresses, 953 const AudioObjectPropertyAddress properties[], 954 void *pvUser) 955 { 956 RT_NOREF(propertyID, nAddresses, properties); 957 958 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 959 960 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pvUser; 961 AssertPtr(pDev); 962 963 PDRVHOSTCOREAUDIO pThis = pDev->pDrv; 964 AssertPtr(pThis); 965 966 int rc2 = RTCritSectEnter(&pThis->CritSect); 967 AssertRC(rc2); 968 969 UInt32 uAlive = 1; 970 UInt32 uSize = sizeof(UInt32); 971 972 AudioObjectPropertyAddress PropAddr = 973 { 974 kAudioDevicePropertyDeviceIsAlive, 975 kAudioObjectPropertyScopeGlobal, 976 kAudioObjectPropertyElementMaster 977 }; 978 979 AudioDeviceID deviceID = pDev->deviceID; 980 981 OSStatus err = AudioObjectGetPropertyData(deviceID, &PropAddr, 0, NULL, &uSize, &uAlive); 982 983 bool fIsDead = false; 984 985 if (err == kAudioHardwareBadDeviceError) 986 fIsDead = true; /* Unplugged. */ 987 else if ((err == kAudioHardwareNoError) && (!RT_BOOL(uAlive))) 988 fIsDead = true; /* Something else happened. */ 989 990 if (fIsDead) 991 { 992 LogRel2(("CoreAudio: Device '%s' stopped functioning\n", pDev->Core.szName)); 993 994 /* Mark device as dead. */ 995 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_UNINIT); 996 AssertRC(rc2); 997 } 998 999 rc2 = RTCritSectLeave(&pThis->CritSect); 1000 AssertRC(rc2); 1001 1002 return noErr; 1003 } 1004 1005 /* Callback for getting notified when the default recording/playback device has been changed. */ 1006 /** @todo r=bird: Why DECLCALLBACK? */ 1007 static DECLCALLBACK(OSStatus) coreAudioDefaultDeviceChangedCb(AudioObjectID propertyID, 1008 UInt32 nAddresses, 1009 const AudioObjectPropertyAddress properties[], 1010 void *pvUser) 1011 { 1012 RT_NOREF(propertyID, nAddresses); 1013 1014 LogFlowFunc(("propertyID=%u, nAddresses=%u, pvUser=%p\n", propertyID, nAddresses, pvUser)); 1015 1016 PDRVHOSTCOREAUDIO pThis = (PDRVHOSTCOREAUDIO)pvUser; 1017 AssertPtr(pThis); 1018 1019 int rc2 = RTCritSectEnter(&pThis->CritSect); 1020 AssertRC(rc2); 1021 1022 for (UInt32 idxAddress = 0; idxAddress < nAddresses; idxAddress++) 1023 { 1024 PCOREAUDIODEVICEDATA pDev = NULL; 1025 1713 * @param pThis Host audio driver instance. 1714 */ 1715 static int coreAudioEnumerateDevices(PDRVHOSTCOREAUDIO pThis) 1716 { 1717 LogFlowFuncEnter(); 1718 1719 /* 1720 * Unregister old default devices, if any. 1721 */ 1722 if (pThis->pDefaultDevIn) 1723 { 1724 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevIn); 1725 pThis->pDefaultDevIn = NULL; 1726 } 1727 1728 if (pThis->pDefaultDevOut) 1729 { 1730 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevOut); 1731 pThis->pDefaultDevOut = NULL; 1732 } 1733 1734 /* Remove old / stale device entries. */ 1735 PDMAudioHostEnumDelete(&pThis->Devices); 1736 1737 /* Enumerate all devices internally. */ 1738 int rc = coreAudioDevicesEnumerateAll(pThis, &pThis->Devices); 1739 if (RT_SUCCESS(rc)) 1740 { 1026 1741 /* 1027 * Check if the default input / output device has been changed.1742 * Default input device. 1028 1743 */ 1029 const AudioObjectPropertyAddress *pProperty = &properties[idxAddress]; 1030 switch (pProperty->mSelector) 1031 { 1032 case kAudioHardwarePropertyDefaultInputDevice: 1033 LogFlowFunc(("kAudioHardwarePropertyDefaultInputDevice\n")); 1034 pDev = pThis->pDefaultDevIn; 1035 break; 1036 1037 case kAudioHardwarePropertyDefaultOutputDevice: 1038 LogFlowFunc(("kAudioHardwarePropertyDefaultOutputDevice\n")); 1039 pDev = pThis->pDefaultDevOut; 1040 break; 1041 1042 default: 1043 /* Skip others. */ 1044 break; 1744 pThis->pDefaultDevIn = (PCOREAUDIODEVICEDATA)PDMAudioHostEnumGetDefault(&pThis->Devices, PDMAUDIODIR_IN); 1745 if (pThis->pDefaultDevIn) 1746 { 1747 LogRel2(("CoreAudio: Default capturing device is '%s'\n", pThis->pDefaultDevIn->Core.szName)); 1748 LogFunc(("pDefaultDevIn=%p, ID=%RU32\n", pThis->pDefaultDevIn, pThis->pDefaultDevIn->deviceID)); 1749 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevIn); 1045 1750 } 1046 1047 LogFlowFunc(("pDev=%p\n", pDev)); 1048 1049 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1050 if (pDev) 1051 { 1052 /* This listener is called on every change of the hardware 1053 * device. So check if the default device has really changed. */ 1054 UInt32 uSize = sizeof(AudioDeviceID); 1055 UInt32 uResp = 0; 1056 1057 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, pProperty, 0, NULL, &uSize, &uResp); 1058 if (err == noErr) 1751 else 1752 LogRel2(("CoreAudio: No default capturing device found\n")); 1753 1754 /* 1755 * Default output device. 1756 */ 1757 pThis->pDefaultDevOut = (PCOREAUDIODEVICEDATA)PDMAudioHostEnumGetDefault(&pThis->Devices, PDMAUDIODIR_OUT); 1758 if (pThis->pDefaultDevOut) 1759 { 1760 LogRel2(("CoreAudio: Default playback device is '%s'\n", pThis->pDefaultDevOut->Core.szName)); 1761 LogFunc(("pDefaultDevOut=%p, ID=%RU32\n", pThis->pDefaultDevOut, pThis->pDefaultDevOut->deviceID)); 1762 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevOut); 1763 } 1764 else 1765 LogRel2(("CoreAudio: No default playback device found\n")); 1766 } 1767 1768 LogFunc(("Returning %Rrc\n", rc)); 1769 return rc; 1770 } 1771 1772 1773 /** 1774 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices} 1775 */ 1776 static DECLCALLBACK(int) drvHostCoreAudioHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum) 1777 { 1778 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1779 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER); 1780 1781 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 1782 1783 int rc = RTCritSectEnter(&pThis->CritSect); 1784 if (RT_SUCCESS(rc)) 1785 { 1786 rc = coreAudioEnumerateDevices(pThis); 1787 if (RT_SUCCESS(rc)) 1788 { 1789 if (pDeviceEnum) 1059 1790 { 1060 if (pDev->deviceID != uResp) /* Has the device ID changed?*/1061 {1062 rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT);1063 AssertRC(rc2);1064 }1791 /* Return a copy with only PDMAUDIOHOSTDEV, none of the extra bits in COREAUDIODEVICEDATA. */ 1792 PDMAudioHostEnumInit(pDeviceEnum); 1793 rc = PDMAudioHostEnumCopy(pDeviceEnum, &pThis->Devices, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/); 1794 if (RT_FAILURE(rc)) 1795 PDMAudioHostEnumDelete(pDeviceEnum); 1065 1796 } 1066 1797 } 1067 #endif /* VBOX_WITH_AUDIO_CALLBACKS */ 1068 } 1069 1070 /* Make sure to leave the critical section before notify higher drivers/devices. */ 1071 rc2 = RTCritSectLeave(&pThis->CritSect); 1072 AssertRC(rc2); 1073 1074 #ifdef VBOX_WITH_AUDIO_CALLBACKS 1075 /* Notify the driver/device above us about possible changes in devices. */ 1076 if (pThis->pIHostAudioPort) 1077 pThis->pIHostAudioPort->pfnNotifyDevicesChanged(pThis->pIHostAudioPort); 1078 #endif 1079 1080 return noErr; 1081 } 1082 1083 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1084 1085 /** 1086 * Re-initializes a Core Audio stream with a specific audio device and stream configuration. 1087 * 1088 * @return IPRT status code. 1089 * @param pThis Driver instance. 1090 * @param pCAStream Audio stream to re-initialize. 1091 * @param pDev Audio device to use for re-initialization. 1092 * @param pCfg Stream configuration to use for re-initialization. 1093 */ 1094 static int coreAudioStreamReinitEx(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, 1095 PCOREAUDIODEVICEDATA pDev, PPDMAUDIOSTREAMCFG pCfg) 1096 { 1097 LogFunc(("pCAStream=%p\n", pCAStream)); 1098 1099 int rc = coreAudioStreamUninit(pCAStream); 1100 if (RT_SUCCESS(rc)) 1101 { 1102 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 1103 if (RT_SUCCESS(rc)) 1104 { 1105 rc = coreAudioStreamInitQueue(pCAStream, pCfg /* pCfgReq */, NULL /* pCfgAcq */); 1106 if (RT_SUCCESS(rc)) 1107 rc = coreAudioStreamControl(pCAStream->pDrv, pCAStream, PDMAUDIOSTREAMCMD_ENABLE); 1108 1109 if (RT_FAILURE(rc)) 1110 { 1111 int rc2 = coreAudioStreamUninit(pCAStream); 1112 AssertRC(rc2); 1113 } 1114 } 1115 } 1116 1117 if (RT_FAILURE(rc)) 1118 LogRel(("CoreAudio: Unable to re-init stream: %Rrc\n", rc)); 1119 1798 1799 int rc2 = RTCritSectLeave(&pThis->CritSect); 1800 AssertRC(rc2); 1801 } 1802 1803 LogFlowFunc(("Returning %Rrc\n", rc)); 1120 1804 return rc; 1121 1805 } 1122 1806 1123 /** 1124 * Re-initializes a Core Audio stream with a specific audio device. 1125 * 1126 * @return IPRT status code. 1127 * @param pThis Driver instance. 1128 * @param pCAStream Audio stream to re-initialize. 1129 * @param pDev Audio device to use for re-initialization. 1130 */ 1131 static int coreAudioStreamReinit(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PCOREAUDIODEVICEDATA pDev) 1132 { 1133 int rc = coreAudioStreamUninit(pCAStream); 1134 if (RT_SUCCESS(rc)) 1135 { 1136 /* Use the acquired stream configuration from the former initialization to 1137 * re-initialize the stream. */ 1138 PDMAUDIOSTREAMCFG CfgAcq; 1139 rc = coreAudioASBDToStreamCfg(&pCAStream->Unit.streamFmt, &CfgAcq); 1140 if (RT_SUCCESS(rc)) 1141 rc = coreAudioStreamReinitEx(pThis, pCAStream, pDev, &CfgAcq); 1142 } 1143 1144 return rc; 1145 } 1146 1147 #endif /* !VBOX_WITH_AUDIO_CALLBACKS */ 1148 1149 #ifdef VBOX_WITH_AUDIO_CA_CONVERTER 1150 /* Callback to convert audio input data from one format to another. */ 1151 static DECLCALLBACK(OSStatus) coreAudioConverterCb(AudioConverterRef inAudioConverter, 1152 UInt32 *ioNumberDataPackets, 1153 AudioBufferList *ioData, 1154 AudioStreamPacketDescription **ppASPD, 1155 void *pvUser) 1156 { 1157 RT_NOREF(inAudioConverter); 1158 1159 AssertPtrReturn(ioNumberDataPackets, caConverterEOFDErr); 1160 AssertPtrReturn(ioData, caConverterEOFDErr); 1161 1162 PCOREAUDIOCONVCBCTX pConvCbCtx = (PCOREAUDIOCONVCBCTX)pvUser; 1163 AssertPtr(pConvCbCtx); 1164 1165 /* Initialize values. */ 1166 ioData->mBuffers[0].mNumberChannels = 0; 1167 ioData->mBuffers[0].mDataByteSize = 0; 1168 ioData->mBuffers[0].mData = NULL; 1169 1170 if (ppASPD) 1171 { 1172 Log3Func(("Handling packet description not implemented\n")); 1173 } 1174 else 1175 { 1176 /** @todo Check converter ID? */ 1177 1178 /** @todo Handled non-interleaved data by going through the full buffer list, 1179 * not only through the first buffer like we do now. */ 1180 Log3Func(("ioNumberDataPackets=%RU32\n", *ioNumberDataPackets)); 1181 1182 UInt32 cNumberDataPackets = *ioNumberDataPackets; 1183 Assert(pConvCbCtx->uPacketIdx + cNumberDataPackets <= pConvCbCtx->uPacketCnt); 1184 1185 if (cNumberDataPackets) 1186 { 1187 AssertPtr(pConvCbCtx->pBufLstSrc); 1188 Assert(pConvCbCtx->pBufLstSrc->mNumberBuffers == 1); /* Only one buffer for the source supported atm. */ 1189 1190 AudioStreamBasicDescription *pSrcASBD = &pConvCbCtx->asbdSrc; 1191 AudioBuffer *pSrcBuf = &pConvCbCtx->pBufLstSrc->mBuffers[0]; 1192 1193 size_t cbOff = pConvCbCtx->uPacketIdx * pSrcASBD->mBytesPerPacket; 1194 1195 cNumberDataPackets = RT_MIN((pSrcBuf->mDataByteSize - cbOff) / pSrcASBD->mBytesPerPacket, 1196 cNumberDataPackets); 1197 1198 void *pvAvail = (uint8_t *)pSrcBuf->mData + cbOff; 1199 size_t cbAvail = RT_MIN(pSrcBuf->mDataByteSize - cbOff, cNumberDataPackets * pSrcASBD->mBytesPerPacket); 1200 1201 Log3Func(("cNumberDataPackets=%RU32, cbOff=%zu, cbAvail=%zu\n", cNumberDataPackets, cbOff, cbAvail)); 1202 1203 /* Set input data for the converter to use. 1204 * Note: For VBR (Variable Bit Rates) or interleaved data handling we need multiple buffers here. */ 1205 ioData->mNumberBuffers = 1; 1206 1207 ioData->mBuffers[0].mNumberChannels = pSrcBuf->mNumberChannels; 1208 ioData->mBuffers[0].mDataByteSize = cbAvail; 1209 ioData->mBuffers[0].mData = pvAvail; 1210 1211 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA 1212 RTFILE fh; 1213 int rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caConverterCbInput.pcm", 1214 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 1215 if (RT_SUCCESS(rc)) 1216 { 1217 RTFileWrite(fh, pvAvail, cbAvail, NULL); 1218 RTFileClose(fh); 1219 } 1220 else 1221 AssertFailed(); 1222 #endif 1223 pConvCbCtx->uPacketIdx += cNumberDataPackets; 1224 Assert(pConvCbCtx->uPacketIdx <= pConvCbCtx->uPacketCnt); 1225 1226 *ioNumberDataPackets = cNumberDataPackets; 1227 } 1228 } 1229 1230 Log3Func(("%RU32 / %RU32 -> ioNumberDataPackets=%RU32\n", 1231 pConvCbCtx->uPacketIdx, pConvCbCtx->uPacketCnt, *ioNumberDataPackets)); 1232 1233 return noErr; 1234 } 1235 #endif /* VBOX_WITH_AUDIO_CA_CONVERTER */ 1236 1237 1238 /** 1239 * Initializes a Core Audio stream. 1240 * 1241 * @return IPRT status code. 1242 * @param pThis Driver instance. 1243 * @param pCAStream Stream to initialize. 1244 * @param pDev Audio device to use for this stream. 1245 */ 1246 static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1247 { 1248 AssertPtrReturn(pCAStream, VERR_INVALID_POINTER); 1249 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 1250 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 1251 1252 Assert(pCAStream->Unit.pDevice == NULL); /* Make sure no device is assigned yet. */ 1253 Assert(pDev->Core.cbSelf == sizeof(COREAUDIODEVICEDATA)); 1254 1255 LogFunc(("pCAStream=%p, pDev=%p ('%s', ID=%RU32)\n", pCAStream, pDev, pDev->Core.szName, pDev->deviceID)); 1256 1257 pCAStream->Unit.pDevice = pDev; 1258 pCAStream->pDrv = pThis; 1259 1260 return VINF_SUCCESS; 1261 } 1262 1263 # define CA_BREAK_STMT(stmt) \ 1264 stmt; \ 1265 break; 1266 1267 /** 1268 * Thread for a Core Audio stream's audio queue handling. 1269 * 1270 * This thread is required per audio queue to pump data to/from the Core Audio 1271 * stream and handling its callbacks. 1272 * 1273 * @returns IPRT status code. 1274 * @param hThreadSelf Thread handle. 1275 * @param pvUser User argument. 1276 */ 1277 static DECLCALLBACK(int) coreAudioQueueThread(RTTHREAD hThreadSelf, void *pvUser) 1278 { 1279 RT_NOREF(hThreadSelf); 1280 1281 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 1282 AssertPtr(pCAStream); 1283 AssertPtr(pCAStream->pCfg); 1284 1285 const bool fIn = pCAStream->pCfg->enmDir == PDMAUDIODIR_IN; 1286 1287 LogFunc(("Thread started for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn)); 1288 1289 /* 1290 * Create audio queue. 1291 */ 1292 OSStatus err; 1293 if (fIn) 1294 err = AudioQueueNewInput(&pCAStream->asbdStream, coreAudioInputQueueCb, pCAStream /* pvData */, 1295 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue); 1296 else 1297 err = AudioQueueNewOutput(&pCAStream->asbdStream, coreAudioOutputQueueCb, pCAStream /* pvData */, 1298 CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 0, &pCAStream->audioQueue); 1299 1300 if (err != noErr) 1301 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 1302 1303 /* 1304 * Assign device to queue. 1305 */ 1306 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pCAStream->Unit.pDevice; 1307 AssertPtr(pDev); 1308 1309 UInt32 uSize = sizeof(pDev->UUID); 1310 err = AudioQueueSetProperty(pCAStream->audioQueue, kAudioQueueProperty_CurrentDevice, &pDev->UUID, uSize); 1311 if (err != noErr) 1312 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 1313 1314 const size_t cbBufSize = PDMAudioPropsFramesToBytes(&pCAStream->pCfg->Props, pCAStream->pCfg->Backend.cFramesPeriod); 1315 1316 /* 1317 * Allocate audio buffers. 1318 */ 1319 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 1320 { 1321 err = AudioQueueAllocateBuffer(pCAStream->audioQueue, cbBufSize, &pCAStream->audioBuffer[i]); 1322 if (err != noErr) 1323 break; 1324 } 1325 1326 if (err != noErr) 1327 return VERR_GENERAL_FAILURE; /** @todo Fudge! */ 1328 1329 /* Signal the main thread before entering the main loop. */ 1330 RTThreadUserSignal(RTThreadSelf()); 1331 1332 /* 1333 * Enter the main loop. 1334 */ 1335 while (!ASMAtomicReadBool(&pCAStream->fShutdown)) 1336 { 1337 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.10, 1); 1338 } 1339 1340 /* 1341 * Cleanup. 1342 */ 1343 if (fIn) 1344 { 1345 AudioQueueStop(pCAStream->audioQueue, 1); 1346 } 1347 else 1348 { 1349 AudioQueueStop(pCAStream->audioQueue, 0); 1350 } 1351 1352 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 1353 { 1354 if (pCAStream->audioBuffer[i]) 1355 AudioQueueFreeBuffer(pCAStream->audioQueue, pCAStream->audioBuffer[i]); 1356 } 1357 1358 AudioQueueDispose(pCAStream->audioQueue, 1); 1359 1360 LogFunc(("Thread ended for pCAStream=%p, fIn=%RTbool\n", pCAStream, fIn)); 1361 return VINF_SUCCESS; 1362 } 1363 1364 /** 1365 * Processes input data of an audio queue buffer and stores it into a Core Audio stream. 1366 * 1367 * @returns IPRT status code. 1368 * @param pCAStream Core Audio stream to store input data into. 1369 * @param audioBuffer Audio buffer to process input data from. 1370 */ 1371 int coreAudioInputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer) 1372 { 1373 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf; 1374 AssertPtr(pCircBuf); 1375 1376 UInt8 *pvSrc = (UInt8 *)audioBuffer->mAudioData; 1377 UInt8 *pvDst = NULL; 1378 1379 size_t cbWritten = 0; 1380 1381 size_t cbToWrite = audioBuffer->mAudioDataByteSize; 1382 size_t cbLeft = RT_MIN(cbToWrite, RTCircBufFree(pCircBuf)); 1383 1384 while (cbLeft) 1385 { 1386 /* Try to acquire the necessary block from the ring buffer. */ 1387 RTCircBufAcquireWriteBlock(pCircBuf, cbLeft, (void **)&pvDst, &cbToWrite); 1388 1389 if (!cbToWrite) 1390 break; 1391 1392 /* Copy the data from our ring buffer to the core audio buffer. */ 1393 memcpy((UInt8 *)pvDst, pvSrc + cbWritten, cbToWrite); 1394 1395 /* Release the read buffer, so it could be used for new data. */ 1396 RTCircBufReleaseWriteBlock(pCircBuf, cbToWrite); 1397 1398 cbWritten += cbToWrite; 1399 1400 Assert(cbLeft >= cbToWrite); 1401 cbLeft -= cbToWrite; 1402 } 1403 1404 Log3Func(("pCAStream=%p, cbBuffer=%RU32/%zu, cbWritten=%zu\n", 1405 pCAStream, audioBuffer->mAudioDataByteSize, audioBuffer->mAudioDataBytesCapacity, cbWritten)); 1406 1407 return VINF_SUCCESS; 1408 } 1409 1410 /** 1411 * Input audio queue callback. Called whenever input data from the audio queue becomes available. 1412 * 1413 * @param pvUser User argument. 1414 * @param audioQueue Audio queue to process input data from. 1415 * @param audioBuffer Audio buffer to process input data from. Must be part of audio queue. 1416 * @param pAudioTS Audio timestamp. 1417 * @param cPacketDesc Number of packet descriptors. 1418 * @param paPacketDesc Array of packet descriptors. 1419 */ 1420 static DECLCALLBACK(void) coreAudioInputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer, 1421 const AudioTimeStamp *pAudioTS, 1422 UInt32 cPacketDesc, const AudioStreamPacketDescription *paPacketDesc) 1423 { 1424 RT_NOREF(audioQueue, pAudioTS, cPacketDesc, paPacketDesc); 1425 1426 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 1427 AssertPtr(pCAStream); 1428 1429 int rc = RTCritSectEnter(&pCAStream->CritSect); 1430 AssertRC(rc); 1431 1432 rc = coreAudioInputQueueProcBuffer(pCAStream, audioBuffer); 1433 if (RT_SUCCESS(rc)) 1434 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL); 1435 1436 rc = RTCritSectLeave(&pCAStream->CritSect); 1437 AssertRC(rc); 1438 } 1439 1440 /** 1441 * Processes output data of a Core Audio stream into an audio queue buffer. 1442 * 1443 * @returns IPRT status code. 1444 * @param pCAStream Core Audio stream to process output data for. 1445 * @param audioBuffer Audio buffer to store data into. 1446 */ 1447 int coreAudioOutputQueueProcBuffer(PCOREAUDIOSTREAM pCAStream, AudioQueueBufferRef audioBuffer) 1448 { 1449 AssertPtr(pCAStream); 1450 1451 PRTCIRCBUF pCircBuf = pCAStream->pCircBuf; 1452 AssertPtr(pCircBuf); 1453 1454 size_t cbRead = 0; 1455 1456 UInt8 *pvSrc = NULL; 1457 UInt8 *pvDst = (UInt8 *)audioBuffer->mAudioData; 1458 1459 size_t cbToRead = RT_MIN(RTCircBufUsed(pCircBuf), audioBuffer->mAudioDataBytesCapacity); 1460 size_t cbLeft = cbToRead; 1461 1462 while (cbLeft) 1463 { 1464 /* Try to acquire the necessary block from the ring buffer. */ 1465 RTCircBufAcquireReadBlock(pCircBuf, cbLeft, (void **)&pvSrc, &cbToRead); 1466 1467 if (cbToRead) 1468 { 1469 /* Copy the data from our ring buffer to the core audio buffer. */ 1470 memcpy((UInt8 *)pvDst + cbRead, pvSrc, cbToRead); 1471 } 1472 1473 /* Release the read buffer, so it could be used for new data. */ 1474 RTCircBufReleaseReadBlock(pCircBuf, cbToRead); 1475 1476 if (!cbToRead) 1477 break; 1478 1479 /* Move offset. */ 1480 cbRead += cbToRead; 1481 Assert(cbRead <= audioBuffer->mAudioDataBytesCapacity); 1482 1483 Assert(cbToRead <= cbLeft); 1484 cbLeft -= cbToRead; 1485 } 1486 1487 audioBuffer->mAudioDataByteSize = cbRead; 1488 1489 if (audioBuffer->mAudioDataByteSize < audioBuffer->mAudioDataBytesCapacity) 1490 { 1491 RT_BZERO((UInt8 *)audioBuffer->mAudioData + audioBuffer->mAudioDataByteSize, 1492 audioBuffer->mAudioDataBytesCapacity - audioBuffer->mAudioDataByteSize); 1493 1494 audioBuffer->mAudioDataByteSize = audioBuffer->mAudioDataBytesCapacity; 1495 } 1496 1497 Log3Func(("pCAStream=%p, cbCapacity=%RU32, cbRead=%zu\n", 1498 pCAStream, audioBuffer->mAudioDataBytesCapacity, cbRead)); 1499 1500 return VINF_SUCCESS; 1501 } 1502 1503 /** 1504 * Output audio queue callback. Called whenever an audio queue is ready to process more output data. 1505 * 1506 * @param pvUser User argument. 1507 * @param audioQueue Audio queue to process output data for. 1508 * @param audioBuffer Audio buffer to store output data in. Must be part of audio queue. 1509 */ 1510 static DECLCALLBACK(void) coreAudioOutputQueueCb(void *pvUser, AudioQueueRef audioQueue, AudioQueueBufferRef audioBuffer) 1511 { 1512 RT_NOREF(audioQueue); 1513 1514 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pvUser; 1515 AssertPtr(pCAStream); 1516 1517 int rc = RTCritSectEnter(&pCAStream->CritSect); 1518 AssertRC(rc); 1519 1520 rc = coreAudioOutputQueueProcBuffer(pCAStream, audioBuffer); 1521 if (RT_SUCCESS(rc)) 1522 AudioQueueEnqueueBuffer(audioQueue, audioBuffer, 0, NULL); 1523 1524 rc = RTCritSectLeave(&pCAStream->CritSect); 1525 AssertRC(rc); 1526 } 1527 1528 /** 1529 * Invalidates a Core Audio stream's audio queue. 1530 * 1531 * @returns IPRT status code. 1532 * @param pCAStream Core Audio stream to invalidate its queue for. 1533 */ 1534 static int coreAudioStreamInvalidateQueue(PCOREAUDIOSTREAM pCAStream) 1535 { 1536 int rc = VINF_SUCCESS; 1537 1538 Log3Func(("pCAStream=%p\n", pCAStream)); 1539 1540 for (size_t i = 0; i < RT_ELEMENTS(pCAStream->audioBuffer); i++) 1541 { 1542 AudioQueueBufferRef pBuf = pCAStream->audioBuffer[i]; 1543 1544 if (pCAStream->pCfg->enmDir == PDMAUDIODIR_IN) 1545 { 1546 int rc2 = coreAudioInputQueueProcBuffer(pCAStream, pBuf); 1547 if (RT_SUCCESS(rc2)) 1548 { 1549 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL); 1550 } 1551 } 1552 else if (pCAStream->pCfg->enmDir == PDMAUDIODIR_OUT) 1553 { 1554 int rc2 = coreAudioOutputQueueProcBuffer(pCAStream, pBuf); 1555 if ( RT_SUCCESS(rc2) 1556 && pBuf->mAudioDataByteSize) 1557 { 1558 AudioQueueEnqueueBuffer(pCAStream->audioQueue, pBuf, 0, NULL); 1559 } 1560 1561 if (RT_SUCCESS(rc)) 1562 rc = rc2; 1563 } 1564 else 1565 AssertFailed(); 1566 } 1567 1568 return rc; 1569 } 1807 1808 /** 1809 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 1810 */ 1811 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 1812 { 1813 RT_NOREF(pInterface, enmDir); 1814 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 1815 1816 return PDMAUDIOBACKENDSTS_RUNNING; 1817 } 1818 1570 1819 1571 1820 /** … … 1634 1883 } 1635 1884 1636 /** 1637 * Unitializes a Core Audio stream's audio queue. 1638 * 1639 * @returns IPRT status code. 1640 * @param pCAStream Core Audio stream to unitialize audio queue for. 1641 */ 1642 static int coreAudioStreamUninitQueue(PCOREAUDIOSTREAM pCAStream) 1643 { 1644 LogFunc(("pCAStream=%p\n", pCAStream)); 1645 1646 if (pCAStream->hThread != NIL_RTTHREAD) 1647 { 1648 LogFunc(("Waiting for thread ...\n")); 1649 1650 ASMAtomicXchgBool(&pCAStream->fShutdown, true); 1651 1652 int rcThread; 1653 int rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread); 1654 if (RT_FAILURE(rc)) 1655 return rc; 1656 1657 RT_NOREF(rcThread); 1658 LogFunc(("Thread stopped with %Rrc\n", rcThread)); 1659 1660 pCAStream->hThread = NIL_RTTHREAD; 1661 } 1662 1663 if (pCAStream->pCfg) 1664 { 1665 PDMAudioStrmCfgFree(pCAStream->pCfg); 1666 pCAStream->pCfg = NULL; 1667 } 1668 1669 if (pCAStream->pCircBuf) 1670 { 1671 RTCircBufDestroy(pCAStream->pCircBuf); 1672 pCAStream->pCircBuf = NULL; 1673 } 1674 1675 LogFunc(("Returning\n")); 1885 1886 /** 1887 * Initializes a Core Audio stream. 1888 * 1889 * @return IPRT status code. 1890 * @param pThis Driver instance. 1891 * @param pCAStream Stream to initialize. 1892 * @param pDev Audio device to use for this stream. 1893 */ 1894 static int coreAudioStreamInit(PCOREAUDIOSTREAM pCAStream, PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1895 { 1896 AssertPtrReturn(pCAStream, VERR_INVALID_POINTER); 1897 AssertPtrReturn(pThis, VERR_INVALID_POINTER); 1898 AssertPtrReturn(pDev, VERR_INVALID_POINTER); 1899 1900 Assert(pCAStream->Unit.pDevice == NULL); /* Make sure no device is assigned yet. */ 1901 Assert(pDev->Core.cbSelf == sizeof(COREAUDIODEVICEDATA)); 1902 1903 LogFunc(("pCAStream=%p, pDev=%p ('%s', ID=%RU32)\n", pCAStream, pDev, pDev->Core.szName, pDev->deviceID)); 1904 1905 pCAStream->Unit.pDevice = pDev; 1906 pCAStream->pDrv = pThis; 1907 1676 1908 return VINF_SUCCESS; 1677 1909 } 1678 1910 1679 /** 1680 * Unitializes a Core Audio stream. 1681 * 1682 * @returns IPRT status code. 1683 * @param pCAStream Core Audio stream to uninitialize. 1684 */ 1685 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream) 1686 { 1687 LogFunc(("pCAStream=%p\n", pCAStream)); 1688 1689 int rc = coreAudioStreamUninitQueue(pCAStream); 1690 if (RT_SUCCESS(rc)) 1691 { 1692 pCAStream->Unit.pDevice = NULL; 1693 pCAStream->pDrv = NULL; 1694 } 1695 1696 return rc; 1697 } 1698 1699 /** 1700 * Registers callbacks for a specific Core Audio device. 1701 * 1702 * @return IPRT status code. 1703 * @param pThis Host audio driver instance. 1704 * @param pDev Audio device to use for the registered callbacks. 1705 */ 1706 static int coreAudioDeviceRegisterCallbacks(PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1707 { 1708 RT_NOREF(pThis); 1709 1710 AudioDeviceID deviceID = kAudioDeviceUnknown; 1711 Assert(pDev && pDev->Core.cbSelf == sizeof(*pDev)); 1712 if (pDev && pDev->Core.cbSelf == sizeof(*pDev)) /* paranoia or actually needed? */ 1713 deviceID = pDev->deviceID; 1714 1715 if (deviceID != kAudioDeviceUnknown) 1716 { 1717 LogFunc(("deviceID=%RU32\n", deviceID)); 1718 1719 /* 1720 * Register device callbacks. 1721 */ 1722 AudioObjectPropertyAddress PropAddr = 1723 { 1724 kAudioDevicePropertyDeviceIsAlive, 1725 kAudioObjectPropertyScopeGlobal, 1726 kAudioObjectPropertyElementMaster 1727 }; 1728 OSStatus err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDeviceStateChangedCb, pDev /* pvUser */); 1729 if ( err != noErr 1730 && err != kAudioHardwareIllegalOperationError) 1731 LogRel(("CoreAudio: Failed to add the recording device state changed listener (%RI32)\n", err)); 1732 1733 PropAddr.mSelector = kAudioDeviceProcessorOverload; 1734 PropAddr.mScope = kAudioUnitScope_Global; 1735 err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1736 if (err != noErr) 1737 LogRel(("CoreAudio: Failed to register processor overload listener (%RI32)\n", err)); 1738 1739 PropAddr.mSelector = kAudioDevicePropertyNominalSampleRate; 1740 PropAddr.mScope = kAudioUnitScope_Global; 1741 err = AudioObjectAddPropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1742 if (err != noErr) 1743 LogRel(("CoreAudio: Failed to register sample rate changed listener (%RI32)\n", err)); 1744 } 1745 1746 return VINF_SUCCESS; 1747 } 1748 1749 /** 1750 * Unregisters all formerly registered callbacks of a Core Audio device again. 1751 * 1752 * @return IPRT status code. 1753 * @param pThis Host audio driver instance. 1754 * @param pDev Audio device to use for the registered callbacks. 1755 */ 1756 static int coreAudioDeviceUnregisterCallbacks(PDRVHOSTCOREAUDIO pThis, PCOREAUDIODEVICEDATA pDev) 1757 { 1758 RT_NOREF(pThis); 1759 1760 AudioDeviceID deviceID = kAudioDeviceUnknown; 1761 Assert(pDev && pDev->Core.cbSelf == sizeof(*pDev)); 1762 if (pDev && pDev->Core.cbSelf == sizeof(*pDev)) /* paranoia or actually needed? */ 1763 deviceID = pDev->deviceID; 1764 1765 if (deviceID != kAudioDeviceUnknown) 1766 { 1767 LogFunc(("deviceID=%RU32\n", deviceID)); 1768 1769 /* 1770 * Unregister per-device callbacks. 1771 */ 1772 AudioObjectPropertyAddress PropAddr = 1773 { 1774 kAudioDeviceProcessorOverload, 1775 kAudioObjectPropertyScopeGlobal, 1776 kAudioObjectPropertyElementMaster 1777 }; 1778 OSStatus err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1779 if ( err != noErr 1780 && err != kAudioHardwareBadObjectError) 1781 LogRel(("CoreAudio: Failed to remove the recording processor overload listener (%RI32)\n", err)); 1782 1783 PropAddr.mSelector = kAudioDevicePropertyNominalSampleRate; 1784 err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDevPropChgCb, pDev /* pvUser */); 1785 if ( err != noErr 1786 && err != kAudioHardwareBadObjectError) 1787 LogRel(("CoreAudio: Failed to remove the sample rate changed listener (%RI32)\n", err)); 1788 1789 PropAddr.mSelector = kAudioDevicePropertyDeviceIsAlive; 1790 err = AudioObjectRemovePropertyListener(deviceID, &PropAddr, coreAudioDeviceStateChangedCb, pDev /* pvUser */); 1791 if ( err != noErr 1792 && err != kAudioHardwareBadObjectError) 1793 LogRel(("CoreAudio: Failed to remove the device alive listener (%RI32)\n", err)); 1794 } 1795 1796 return VINF_SUCCESS; 1797 } 1798 1799 /* Callback for getting notified when some of the properties of an audio device have changed. */ 1800 static DECLCALLBACK(OSStatus) coreAudioDevPropChgCb(AudioObjectID propertyID, 1801 UInt32 cAddresses, 1802 const AudioObjectPropertyAddress properties[], 1803 void *pvUser) 1804 { 1805 RT_NOREF(cAddresses, properties, pvUser); 1806 1807 PCOREAUDIODEVICEDATA pDev = (PCOREAUDIODEVICEDATA)pvUser; 1808 AssertPtr(pDev); 1809 1810 LogFlowFunc(("propertyID=%u, nAddresses=%u, pDev=%p\n", propertyID, cAddresses, pDev)); 1811 1812 switch (propertyID) 1813 { 1814 #ifdef DEBUG 1815 case kAudioDeviceProcessorOverload: 1816 { 1817 LogFunc(("Processor overload detected!\n")); 1818 break; 1911 1912 /** 1913 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 1914 */ 1915 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 1916 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 1917 { 1918 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 1919 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1920 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 1921 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 1922 1923 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 1924 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 1925 1926 int rc = RTCritSectInit(&pCAStream->CritSect); 1927 if (RT_FAILURE(rc)) 1928 return rc; 1929 1930 pCAStream->hThread = NIL_RTTHREAD; 1931 pCAStream->fRun = false; 1932 pCAStream->fIsRunning = false; 1933 pCAStream->fShutdown = false; 1934 1935 /* Input or output device? */ 1936 bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN; 1937 1938 /* For now, just use the default device available. */ 1939 PCOREAUDIODEVICEDATA pDev = fIn ? pThis->pDefaultDevIn : pThis->pDefaultDevOut; 1940 1941 LogFunc(("pStream=%p, pCfgReq=%p, pCfgAcq=%p, fIn=%RTbool, pDev=%p\n", pStream, pCfgReq, pCfgAcq, fIn, pDev)); 1942 1943 if (pDev) /* (Default) device available? */ 1944 { 1945 /* Sanity. */ 1946 Assert(pDev->Core.cbSelf == sizeof(*pDev)); 1947 1948 /* Init the Core Audio stream. */ 1949 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 1950 if (RT_SUCCESS(rc)) 1951 { 1952 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_INIT); 1953 1954 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq); 1955 if (RT_SUCCESS(rc)) 1956 { 1957 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT); 1958 } 1959 else 1960 { 1961 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT); 1962 1963 int rc2 = coreAudioStreamUninit(pCAStream); 1964 AssertRC(rc2); 1965 1966 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT); 1967 } 1819 1968 } 1820 #endif /* DEBUG */ 1821 case kAudioDevicePropertyNominalSampleRate: 1822 { 1823 #ifndef VBOX_WITH_AUDIO_CALLBACKS 1824 int rc2 = coreAudioDevicePropagateStatus(pDev, COREAUDIOSTATUS_REINIT); 1825 AssertRC(rc2); 1826 #else 1827 RT_NOREF(pDev); 1828 #endif 1829 break; 1830 } 1831 1832 default: 1833 /* Just skip. */ 1834 break; 1835 } 1836 1837 return noErr; 1838 } 1839 1840 /** 1841 * Enumerates all available host audio devices internally. 1842 * 1843 * @returns IPRT status code. 1844 * @param pThis Host audio driver instance. 1845 */ 1846 static int coreAudioEnumerateDevices(PDRVHOSTCOREAUDIO pThis) 1847 { 1848 LogFlowFuncEnter(); 1849 1850 /* 1851 * Unregister old default devices, if any. 1852 */ 1853 if (pThis->pDefaultDevIn) 1854 { 1855 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevIn); 1856 pThis->pDefaultDevIn = NULL; 1857 } 1858 1859 if (pThis->pDefaultDevOut) 1860 { 1861 coreAudioDeviceUnregisterCallbacks(pThis, pThis->pDefaultDevOut); 1862 pThis->pDefaultDevOut = NULL; 1863 } 1864 1865 /* Remove old / stale device entries. */ 1866 PDMAudioHostEnumDelete(&pThis->Devices); 1867 1868 /* Enumerate all devices internally. */ 1869 int rc = coreAudioDevicesEnumerateAll(pThis, &pThis->Devices); 1870 if (RT_SUCCESS(rc)) 1871 { 1872 /* 1873 * Default input device. 1874 */ 1875 pThis->pDefaultDevIn = (PCOREAUDIODEVICEDATA)PDMAudioHostEnumGetDefault(&pThis->Devices, PDMAUDIODIR_IN); 1876 if (pThis->pDefaultDevIn) 1877 { 1878 LogRel2(("CoreAudio: Default capturing device is '%s'\n", pThis->pDefaultDevIn->Core.szName)); 1879 LogFunc(("pDefaultDevIn=%p, ID=%RU32\n", pThis->pDefaultDevIn, pThis->pDefaultDevIn->deviceID)); 1880 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevIn); 1881 } 1882 else 1883 LogRel2(("CoreAudio: No default capturing device found\n")); 1884 1885 /* 1886 * Default output device. 1887 */ 1888 pThis->pDefaultDevOut = (PCOREAUDIODEVICEDATA)PDMAudioHostEnumGetDefault(&pThis->Devices, PDMAUDIODIR_OUT); 1889 if (pThis->pDefaultDevOut) 1890 { 1891 LogRel2(("CoreAudio: Default playback device is '%s'\n", pThis->pDefaultDevOut->Core.szName)); 1892 LogFunc(("pDefaultDevOut=%p, ID=%RU32\n", pThis->pDefaultDevOut, pThis->pDefaultDevOut->deviceID)); 1893 rc = coreAudioDeviceRegisterCallbacks(pThis, pThis->pDefaultDevOut); 1894 } 1895 else 1896 LogRel2(("CoreAudio: No default playback device found\n")); 1897 } 1969 } 1970 else 1971 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; 1898 1972 1899 1973 LogFunc(("Returning %Rrc\n", rc)); … … 1901 1975 } 1902 1976 1903 /**1904 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}1905 */1906 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,1907 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)1908 {1909 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);1910 AssertPtrReturn(pStream, VERR_INVALID_POINTER);1911 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);1912 1913 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;1914 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);1915 1916 #ifndef VBOX_WITH_AUDIO_CALLBACKS1917 /* Check if the audio device should be reinitialized. If so do it. */1918 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)1919 {1920 /* For now re just re-initialize with the current input device. */1921 if (pThis->pDefaultDevIn)1922 {1923 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevIn);1924 if (RT_FAILURE(rc2))1925 return VERR_NOT_AVAILABLE;1926 }1927 else1928 return VERR_NOT_AVAILABLE;1929 }1930 #else1931 RT_NOREF(pThis);1932 #endif1933 1934 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)1935 {1936 *pcbRead = 0;1937 return VINF_SUCCESS;1938 }1939 1940 int rc = VINF_SUCCESS;1941 1942 uint32_t cbReadTotal = 0;1943 1944 rc = RTCritSectEnter(&pCAStream->CritSect);1945 AssertRC(rc);1946 1947 do1948 {1949 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufUsed(pCAStream->pCircBuf));1950 1951 uint8_t *pvChunk;1952 size_t cbChunk;1953 1954 Log3Func(("cbToWrite=%zu/%zu\n", cbToWrite, RTCircBufSize(pCAStream->pCircBuf)));1955 1956 while (cbToWrite)1957 {1958 /* Try to acquire the necessary block from the ring buffer. */1959 RTCircBufAcquireReadBlock(pCAStream->pCircBuf, cbToWrite, (void **)&pvChunk, &cbChunk);1960 if (cbChunk)1961 memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk);1962 1963 /* Release the read buffer, so it could be used for new data. */1964 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbChunk);1965 1966 if (RT_FAILURE(rc))1967 break;1968 1969 Assert(cbToWrite >= cbChunk);1970 cbToWrite -= cbChunk;1971 1972 cbReadTotal += cbChunk;1973 }1974 }1975 while (0);1976 1977 int rc2 = RTCritSectLeave(&pCAStream->CritSect);1978 AssertRC(rc2);1979 1980 if (RT_SUCCESS(rc))1981 *pcbRead = cbReadTotal;1982 1983 return rc;1984 }1985 1986 /**1987 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}1988 */1989 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,1990 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)1991 {1992 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);1993 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream;1994 1995 #ifndef VBOX_WITH_AUDIO_CALLBACKS1996 /* Check if the audio device should be reinitialized. If so do it. */1997 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT)1998 {1999 if (pThis->pDefaultDevOut)2000 {2001 /* For now re just re-initialize with the current output device. */2002 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevOut);2003 if (RT_FAILURE(rc2))2004 return VERR_NOT_AVAILABLE;2005 }2006 else2007 return VERR_NOT_AVAILABLE;2008 }2009 #else2010 RT_NOREF(pThis);2011 #endif2012 2013 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT)2014 {2015 *pcbWritten = 0;2016 return VINF_SUCCESS;2017 }2018 2019 uint32_t cbWrittenTotal = 0;2020 2021 int rc = VINF_SUCCESS;2022 2023 rc = RTCritSectEnter(&pCAStream->CritSect);2024 AssertRC(rc);2025 2026 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufFree(pCAStream->pCircBuf));2027 Log3Func(("cbToWrite=%zu\n", cbToWrite));2028 2029 uint8_t *pvChunk;2030 size_t cbChunk;2031 2032 while (cbToWrite)2033 {2034 /* Try to acquire the necessary space from the ring buffer. */2035 RTCircBufAcquireWriteBlock(pCAStream->pCircBuf, cbToWrite, (void **)&pvChunk, &cbChunk);2036 if (!cbChunk)2037 {2038 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk);2039 break;2040 }2041 2042 Assert(cbChunk <= cbToWrite);2043 Assert(cbWrittenTotal + cbChunk <= cbBuf);2044 2045 memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);2046 2047 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA2048 RTFILE fh;2049 rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caPlayback.pcm",2050 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE);2051 if (RT_SUCCESS(rc))2052 {2053 RTFileWrite(fh, pvChunk, cbChunk, NULL);2054 RTFileClose(fh);2055 }2056 else2057 AssertFailed();2058 #endif2059 2060 /* Release the ring buffer, so the read thread could start reading this data. */2061 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk);2062 2063 if (RT_FAILURE(rc))2064 break;2065 2066 Assert(cbToWrite >= cbChunk);2067 cbToWrite -= cbChunk;2068 2069 cbWrittenTotal += cbChunk;2070 }2071 2072 if ( RT_SUCCESS(rc)2073 && pCAStream->fRun2074 && !pCAStream->fIsRunning)2075 {2076 rc = coreAudioStreamInvalidateQueue(pCAStream);2077 if (RT_SUCCESS(rc))2078 {2079 AudioQueueStart(pCAStream->audioQueue, NULL);2080 pCAStream->fRun = false;2081 pCAStream->fIsRunning = true;2082 }2083 }2084 2085 int rc2 = RTCritSectLeave(&pCAStream->CritSect);2086 AssertRC(rc2);2087 2088 if (RT_SUCCESS(rc))2089 *pcbWritten = cbWrittenTotal;2090 2091 return rc;2092 }2093 1977 2094 1978 static int coreAudioStreamControl(PDRVHOSTCOREAUDIO pThis, PCOREAUDIOSTREAM pCAStream, PDMAUDIOSTREAMCMD enmStreamCmd) … … 2165 2049 2166 2050 /** 2167 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig} 2168 */ 2169 static DECLCALLBACK(int) drvHostCoreAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg) 2170 { 2171 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2172 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER); 2173 2174 /* 2175 * Fill in the config structure. 2176 */ 2177 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Core Audio"); 2178 pBackendCfg->cbStream = sizeof(COREAUDIOSTREAM); 2179 pBackendCfg->fFlags = 0; 2180 /* For Core Audio we provide one stream per device for now. */ 2181 pBackendCfg->cMaxStreamsIn = PDMAudioHostEnumCountMatching(&pThis->Devices, PDMAUDIODIR_IN); 2182 pBackendCfg->cMaxStreamsOut = PDMAudioHostEnumCountMatching(&pThis->Devices, PDMAUDIODIR_OUT); 2183 2184 LogFlowFunc(("Returning %Rrc\n", VINF_SUCCESS)); 2051 * Unitializes a Core Audio stream's audio queue. 2052 * 2053 * @returns IPRT status code. 2054 * @param pCAStream Core Audio stream to unitialize audio queue for. 2055 */ 2056 static int coreAudioStreamUninitQueue(PCOREAUDIOSTREAM pCAStream) 2057 { 2058 LogFunc(("pCAStream=%p\n", pCAStream)); 2059 2060 if (pCAStream->hThread != NIL_RTTHREAD) 2061 { 2062 LogFunc(("Waiting for thread ...\n")); 2063 2064 ASMAtomicXchgBool(&pCAStream->fShutdown, true); 2065 2066 int rcThread; 2067 int rc = RTThreadWait(pCAStream->hThread, 30 * 1000, &rcThread); 2068 if (RT_FAILURE(rc)) 2069 return rc; 2070 2071 RT_NOREF(rcThread); 2072 LogFunc(("Thread stopped with %Rrc\n", rcThread)); 2073 2074 pCAStream->hThread = NIL_RTTHREAD; 2075 } 2076 2077 if (pCAStream->pCfg) 2078 { 2079 PDMAudioStrmCfgFree(pCAStream->pCfg); 2080 pCAStream->pCfg = NULL; 2081 } 2082 2083 if (pCAStream->pCircBuf) 2084 { 2085 RTCircBufDestroy(pCAStream->pCircBuf); 2086 pCAStream->pCircBuf = NULL; 2087 } 2088 2089 LogFunc(("Returning\n")); 2185 2090 return VINF_SUCCESS; 2186 2091 } … … 2188 2093 2189 2094 /** 2190 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetDevices}2191 * /2192 static DECLCALLBACK(int) drvHostCoreAudioHA_GetDevices(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHOSTENUM pDeviceEnum) 2193 { 2194 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);2195 AssertPtrReturn(pDeviceEnum, VERR_INVALID_POINTER); 2196 2197 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface);2198 2199 int rc = RTCritSectEnter(&pThis->CritSect);2095 * Unitializes a Core Audio stream. 2096 * 2097 * @returns IPRT status code. 2098 * @param pCAStream Core Audio stream to uninitialize. 2099 */ 2100 static int coreAudioStreamUninit(PCOREAUDIOSTREAM pCAStream) 2101 { 2102 LogFunc(("pCAStream=%p\n", pCAStream)); 2103 2104 int rc = coreAudioStreamUninitQueue(pCAStream); 2200 2105 if (RT_SUCCESS(rc)) 2201 2106 { 2202 rc = coreAudioEnumerateDevices(pThis); 2203 if (RT_SUCCESS(rc)) 2204 { 2205 if (pDeviceEnum) 2206 { 2207 /* Return a copy with only PDMAUDIOHOSTDEV, none of the extra bits in COREAUDIODEVICEDATA. */ 2208 PDMAudioHostEnumInit(pDeviceEnum); 2209 rc = PDMAudioHostEnumCopy(pDeviceEnum, &pThis->Devices, PDMAUDIODIR_INVALID /*all*/, true /*fOnlyCoreData*/); 2210 if (RT_FAILURE(rc)) 2211 PDMAudioHostEnumDelete(pDeviceEnum); 2212 } 2213 } 2214 2215 int rc2 = RTCritSectLeave(&pThis->CritSect); 2216 AssertRC(rc2); 2217 } 2218 2219 LogFlowFunc(("Returning %Rrc\n", rc)); 2220 return rc; 2221 } 2222 2223 2224 /** 2225 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus} 2226 */ 2227 static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostCoreAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir) 2228 { 2229 RT_NOREF(pInterface, enmDir); 2230 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN); 2231 2232 return PDMAUDIOBACKENDSTS_RUNNING; 2233 } 2234 2235 2236 /** 2237 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} 2238 */ 2239 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 2240 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) 2241 { 2242 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2243 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2244 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); 2245 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); 2246 2247 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2248 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2249 2250 int rc = RTCritSectInit(&pCAStream->CritSect); 2251 if (RT_FAILURE(rc)) 2252 return rc; 2253 2254 pCAStream->hThread = NIL_RTTHREAD; 2255 pCAStream->fRun = false; 2256 pCAStream->fIsRunning = false; 2257 pCAStream->fShutdown = false; 2258 2259 /* Input or output device? */ 2260 bool fIn = pCfgReq->enmDir == PDMAUDIODIR_IN; 2261 2262 /* For now, just use the default device available. */ 2263 PCOREAUDIODEVICEDATA pDev = fIn ? pThis->pDefaultDevIn : pThis->pDefaultDevOut; 2264 2265 LogFunc(("pStream=%p, pCfgReq=%p, pCfgAcq=%p, fIn=%RTbool, pDev=%p\n", pStream, pCfgReq, pCfgAcq, fIn, pDev)); 2266 2267 if (pDev) /* (Default) device available? */ 2268 { 2269 /* Sanity. */ 2270 Assert(pDev->Core.cbSelf == sizeof(*pDev)); 2271 2272 /* Init the Core Audio stream. */ 2273 rc = coreAudioStreamInit(pCAStream, pThis, pDev); 2274 if (RT_SUCCESS(rc)) 2275 { 2276 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_INIT); 2277 2278 rc = coreAudioStreamInitQueue(pCAStream, pCfgReq, pCfgAcq); 2279 if (RT_SUCCESS(rc)) 2280 { 2281 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_INIT); 2282 } 2283 else 2284 { 2285 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_IN_UNINIT); 2286 2287 int rc2 = coreAudioStreamUninit(pCAStream); 2288 AssertRC(rc2); 2289 2290 ASMAtomicXchgU32(&pCAStream->enmStatus, COREAUDIOSTATUS_UNINIT); 2291 } 2292 } 2293 } 2294 else 2295 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; 2296 2297 LogFunc(("Returning %Rrc\n", rc)); 2107 pCAStream->Unit.pDevice = NULL; 2108 pCAStream->pDrv = NULL; 2109 } 2110 2298 2111 return rc; 2299 2112 } … … 2446 2259 } 2447 2260 2261 /** 2262 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} 2263 */ 2264 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 2265 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 2266 { 2267 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2268 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2269 2270 #ifndef VBOX_WITH_AUDIO_CALLBACKS 2271 /* Check if the audio device should be reinitialized. If so do it. */ 2272 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT) 2273 { 2274 if (pThis->pDefaultDevOut) 2275 { 2276 /* For now re just re-initialize with the current output device. */ 2277 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevOut); 2278 if (RT_FAILURE(rc2)) 2279 return VERR_NOT_AVAILABLE; 2280 } 2281 else 2282 return VERR_NOT_AVAILABLE; 2283 } 2284 #else 2285 RT_NOREF(pThis); 2286 #endif 2287 2288 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT) 2289 { 2290 *pcbWritten = 0; 2291 return VINF_SUCCESS; 2292 } 2293 2294 uint32_t cbWrittenTotal = 0; 2295 2296 int rc = VINF_SUCCESS; 2297 2298 rc = RTCritSectEnter(&pCAStream->CritSect); 2299 AssertRC(rc); 2300 2301 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufFree(pCAStream->pCircBuf)); 2302 Log3Func(("cbToWrite=%zu\n", cbToWrite)); 2303 2304 uint8_t *pvChunk; 2305 size_t cbChunk; 2306 2307 while (cbToWrite) 2308 { 2309 /* Try to acquire the necessary space from the ring buffer. */ 2310 RTCircBufAcquireWriteBlock(pCAStream->pCircBuf, cbToWrite, (void **)&pvChunk, &cbChunk); 2311 if (!cbChunk) 2312 { 2313 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk); 2314 break; 2315 } 2316 2317 Assert(cbChunk <= cbToWrite); 2318 Assert(cbWrittenTotal + cbChunk <= cbBuf); 2319 2320 memcpy(pvChunk, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk); 2321 2322 #ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA 2323 RTFILE fh; 2324 rc = RTFileOpen(&fh,VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "caPlayback.pcm", 2325 RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 2326 if (RT_SUCCESS(rc)) 2327 { 2328 RTFileWrite(fh, pvChunk, cbChunk, NULL); 2329 RTFileClose(fh); 2330 } 2331 else 2332 AssertFailed(); 2333 #endif 2334 2335 /* Release the ring buffer, so the read thread could start reading this data. */ 2336 RTCircBufReleaseWriteBlock(pCAStream->pCircBuf, cbChunk); 2337 2338 if (RT_FAILURE(rc)) 2339 break; 2340 2341 Assert(cbToWrite >= cbChunk); 2342 cbToWrite -= cbChunk; 2343 2344 cbWrittenTotal += cbChunk; 2345 } 2346 2347 if ( RT_SUCCESS(rc) 2348 && pCAStream->fRun 2349 && !pCAStream->fIsRunning) 2350 { 2351 rc = coreAudioStreamInvalidateQueue(pCAStream); 2352 if (RT_SUCCESS(rc)) 2353 { 2354 AudioQueueStart(pCAStream->audioQueue, NULL); 2355 pCAStream->fRun = false; 2356 pCAStream->fIsRunning = true; 2357 } 2358 } 2359 2360 int rc2 = RTCritSectLeave(&pCAStream->CritSect); 2361 AssertRC(rc2); 2362 2363 if (RT_SUCCESS(rc)) 2364 *pcbWritten = cbWrittenTotal; 2365 2366 return rc; 2367 } 2368 2369 2370 /** 2371 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture} 2372 */ 2373 static DECLCALLBACK(int) drvHostCoreAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, 2374 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) 2375 { 2376 AssertPtrReturn(pInterface, VERR_INVALID_POINTER); 2377 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 2378 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER); 2379 2380 PCOREAUDIOSTREAM pCAStream = (PCOREAUDIOSTREAM)pStream; 2381 PDRVHOSTCOREAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTCOREAUDIO(pInterface); 2382 2383 #ifndef VBOX_WITH_AUDIO_CALLBACKS 2384 /* Check if the audio device should be reinitialized. If so do it. */ 2385 if (ASMAtomicReadU32(&pCAStream->enmStatus) == COREAUDIOSTATUS_REINIT) 2386 { 2387 /* For now re just re-initialize with the current input device. */ 2388 if (pThis->pDefaultDevIn) 2389 { 2390 int rc2 = coreAudioStreamReinit(pThis, pCAStream, pThis->pDefaultDevIn); 2391 if (RT_FAILURE(rc2)) 2392 return VERR_NOT_AVAILABLE; 2393 } 2394 else 2395 return VERR_NOT_AVAILABLE; 2396 } 2397 #else 2398 RT_NOREF(pThis); 2399 #endif 2400 2401 if (ASMAtomicReadU32(&pCAStream->enmStatus) != COREAUDIOSTATUS_INIT) 2402 { 2403 *pcbRead = 0; 2404 return VINF_SUCCESS; 2405 } 2406 2407 int rc = RTCritSectEnter(&pCAStream->CritSect); 2408 AssertRCReturn(rc, rc); 2409 2410 size_t cbToWrite = RT_MIN(cbBuf, RTCircBufUsed(pCAStream->pCircBuf)); 2411 Log3Func(("cbToWrite=%zu/%zu\n", cbToWrite, RTCircBufSize(pCAStream->pCircBuf))); 2412 2413 uint32_t cbReadTotal = 0; 2414 while (cbToWrite > 0) 2415 { 2416 void *pvChunk = NULL; 2417 size_t cbChunk = 0; 2418 RTCircBufAcquireReadBlock(pCAStream->pCircBuf, cbToWrite, &pvChunk, &cbChunk); 2419 2420 AssertStmt(cbChunk <= cbToWrite, cbChunk = cbToWrite); 2421 memcpy((uint8_t *)pvBuf + cbReadTotal, pvChunk, cbChunk); 2422 2423 RTCircBufReleaseReadBlock(pCAStream->pCircBuf, cbChunk); 2424 2425 cbToWrite -= cbChunk; 2426 cbReadTotal += cbChunk; 2427 } 2428 2429 *pcbRead = cbReadTotal; 2430 2431 RTCritSectLeave(&pCAStream->CritSect); 2432 return VINF_SUCCESS; 2433 } 2434 2435 2436 /********************************************************************************************************************************* 2437 * PDMIBASE * 2438 *********************************************************************************************************************************/ 2448 2439 2449 2440 /** … … 2460 2451 return NULL; 2461 2452 } 2453 2454 2455 /********************************************************************************************************************************* 2456 * PDMDRVREG * 2457 *********************************************************************************************************************************/ 2462 2458 2463 2459 /** … … 2548 2544 /* IHostAudio */ 2549 2545 pThis->IHostAudio.pfnGetConfig = drvHostCoreAudioHA_GetConfig; 2546 pThis->IHostAudio.pfnGetDevices = drvHostCoreAudioHA_GetDevices; 2550 2547 pThis->IHostAudio.pfnGetStatus = drvHostCoreAudioHA_GetStatus; 2551 2548 pThis->IHostAudio.pfnDoOnWorkerThread = NULL; … … 2562 2559 pThis->IHostAudio.pfnStreamPlay = drvHostCoreAudioHA_StreamPlay; 2563 2560 pThis->IHostAudio.pfnStreamCapture = drvHostCoreAudioHA_StreamCapture; 2564 pThis->IHostAudio.pfnGetDevices = drvHostCoreAudioHA_GetDevices;2565 2561 2566 2562 int rc = RTCritSectInit(&pThis->CritSect);
Note:
See TracChangeset
for help on using the changeset viewer.