1簡介
Mac上采集播放,可以用跨平台的OpenAL(底層基於CoreAudio實現);
也可以用CoreAudio(像webrtc里邊:webrtc/src/modules/audio_device/mac):
Core Audio : https://developer.apple.com/documentation/coreaudio/core_audio_functions?language=objc
也可以用audiounit;或者一些更上層的(集成度)更高的API;
2 基於AudioUnit + Core Audio 組件進行音頻采集,音頻播放
2.1注意事項
MAC 平台不能使用AVaudiosession; (IOS,catalyst是支持的);進而無法使用設置Opthin,mode,檢測麥克風等操作;
MAC集成Audio Unit,播放端音頻需要32位寬;(IOS不限制,catalyst也是需要32位寬)
MAC平台結合Core Audio,用Core Audio的相關函數可以獲取音頻設備,數量,指定使用的設備(譬如插上耳機作為默認;可通過代碼指定使用內置揚聲器播放),其中core Audio 指定要使用哪個麥克風的操作存在一定的問題,無法指定,或者不起作用,甚至是會報錯;
Core Audio 可以不結合audio unit,單獨實現采集播放,指定設備,設置回調等等;
2.2 Core Audio 設備檢測和選取代碼
1 AudioObjectPropertyAddress addr = { 2 kAudioHardwarePropertyDevices, 3 kAudioObjectPropertyScopeGlobal, 4 kAudioObjectPropertyElementMaster 5 }; 6 7 UInt32 size = 0; 8 UInt32 count; 9 OSStatus stat; 10 11 12 stat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &size); 13 14 15 16 count = size / sizeof(AudioDeviceID);//設備數量 17 18 stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 19 0, NULL, &size, &ids);//AudioDeviceID ids[20];//假設本機器最多20個設備
20 21 //與音頻MIDI設置中的設備數量一致;順序也一致,有些設備(虛擬設備)既是采集設備,又是播放設備(輸入源,輸出源) 22 23 CFStringRef cf_name = NULL; 24 CFStringRef cf_uid = NULL; 25 26 27 for (UInt32 i = 0; i < count; i++) 28 { 29 32 AudioObjectPropertyAddress propertyAddress; 33 propertyAddress.mSelector = kAudioDevicePropertyStreams; 34 propertyAddress.mScope = kAudioDevicePropertyScopeInput;//與輸入端連接的 35 //設備具體信息描述 36 37 UInt32 dataSize = 0; 38 OSStatus status = AudioObjectGetPropertyDataSize(ids[i], 39 &propertyAddress, 40 0, 41 NULL, 42 &dataSize); 43 UInt32 streamCount = dataSize / sizeof(AudioStreamID); 44 45 if (streamCount > 0) 46 { 47 //由設備ID獲取設備的UID和Name;這里注意address調用順序,可能會影響某些操作的結果; 48 // propertyAddress有一個使用的問題,如果你使用了上邊的addr,可以不獲取UID,也能正確獲取名字;但是你單獨創建一個address,直接獲取名字,不獲取UID,可能會報錯 49 50 size = sizeof(CFStringRef); 51 52 //propertyAddress.mSelector = kAudioDevicePropertyDeviceUID; 53 propertyAddress.mScope = kAudioDevicePropertyScopeInput; 54 // stat = AudioObjectGetPropertyData(ids[i], &propertyAddress, 0, NULL, &size, &cf_uid); 55 // NSLog((__bridge NSString *)cf_uid); 56 57 propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; 58 stat = AudioObjectGetPropertyData(ids[i], &propertyAddress, 0, NULL, &size, &cf_name); 59 NSLog((__bridge NSString *)cf_name); 60 printf("Device is input device\n"); 61 } 62 63 propertyAddress.mScope = kAudioDevicePropertyScopeOutput; 64 propertyAddress.mSelector = kAudioDevicePropertyStreams;//如果上邊改變了selector就要重置 65 dataSize = 0; 66 status = AudioObjectGetPropertyDataSize(ids[i], 67 &propertyAddress, 68 0, 69 NULL, 70 &dataSize); 71 streamCount = dataSize / sizeof(AudioStreamID); 72 73 if (streamCount > 0) 74 { 75 //由設備ID獲取設備的UID和Name 76 size = sizeof(CFStringRef); 77 // AudioObjectPropertyAddress propertyAddressInfo; 78 propertyAddress.mScope = kAudioDevicePropertyScopeOutput; 79 // propertyAddressInfo.mSelector = kAudioDevicePropertyDeviceUID; 80 // stat = AudioObjectGetPropertyData(ids[i], &propertyAddressInfo, 0, NULL, &size, &cf_uid); 81 // NSLog((__bridge NSString *)cf_uid); 82 // 83 propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; 84 stat = AudioObjectGetPropertyData(ids[i], &propertyAddress, 0, NULL, &size, &cf_name); 85 NSLog((__bridge NSString *)cf_name); 86 87 printf("Device is output device\n"); 88 } 89 90 }
通過獲取的設備ID(注意不是UID)改變Audiounit的播放設備;意圖:我的機器默認選擇耳機播放,但是我希望通過代碼讓它在內置揚聲器播放聲音
uint32_t IDplay = ids[1];//Core Audio 獲取的設備ID;Device對象是一個32位四字節整數;每次運行,同一個設備的 設備ID可能回變,比如內置麥克風,有時候這個值是199,有時候是64等等值 status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Output, 0, &IDplay, sizeof(IDplay)); checkStatus( status, "change output" );
代碼參考:https://stackoverflow.com/questions/4575408/audioobjectgetpropertydata-to-get-a-list-of-input-devices
2.3 MAC 平台AUdiounit實現采集播放
這里就不提供代碼了,找了一些實現,參見:
(audioqueue)http://msching.github.io/blog/2014/08/02/audio-in-ios-5/
https://blog.csdn.net/tong5956/article/details/109564178
https://github.com/pinkydodo/AudioUnitMac
3 無論你是Mac_xcode_audiounit ,還是Mac_catalyst_xcode_audiounit ,注意在cpp的sandbox,上開啟麥克風權限;audio input;否則可能會出現 CannotDoInCurrentContext;你的程序有沒有麥克風權限可以在安全與隱私的麥克風權限里查一下;沒有權限是采集不到聲音的,盡管有時候有些UI層應用可能不會報錯