MAC上进行音频采集,播放,设备获取(AudioUnit+CoreAudio实现)

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层应用可能不会报错 

posted on 2022-02-11 17:08  邗影  阅读(2290)  评论(0编辑  收藏  举报

导航