AudioKit在Mac上无法显示全部可用输入输出设备的问题求助
AudioKit跨平台音频设备选择与实时监听问题解决
先给你明确结论
完全可以通过AudioKit实现Mac Catalyst、iPad、iOS平台的独立输入输出设备选择+实时麦克风监听,你遇到的Mac端只返回当前设备的问题,是因为Mac Catalyst的音频架构兼容逻辑和纯Mac/iOS不一样,针对性处理就能解决。
为啥会出现这种差异?
不同平台的AudioKit底层音频管理逻辑有区别:
- iOS/iPadOS:AudioKit依赖系统的AVAudioSession,
Settings.session.availableInputs/availableOutputs能直接拿到所有可用设备,因为iOS的音频设备枚举是系统统一管控的,只要设备接入就会被检测到。 - Mac Catalyst:它本质是iOS app跑在Mac上的兼容层,AudioKit默认沿用iOS的会话逻辑,但Mac本身的Core Audio架构和iOS不同。默认情况下,Catalyst app会直接继承Mac系统当前选中的音频上下文,导致
AudioEngine.inputDevices这类API只返回当前在用的设备,而非全部可用的。 - 纯Mac平台:AudioKit直接调用Core Audio的原生API枚举设备,所以能返回全量设备,这就是你看到纯Mac目标下方法不同的原因——底层依赖的框架不一样。
解决方法
针对Mac Catalyst的特殊情况,我们可以通过条件编译,在Catalyst下直接调用Core Audio的API枚举设备,iOS/iPadOS继续用AudioKit的原有逻辑:
1. 封装跨平台的设备枚举工具
import AudioKit import CoreAudio // 封装设备枚举逻辑 enum AudioDeviceHelper { static func getAllInputs() -> [Device] { #if targetEnvironment(macCatalyst) var devices = [Device]() // 用Core Audio获取所有系统音频设备 let systemAudioObject = AudioObjectID(kAudioObjectSystemObject) var address = AudioObjectPropertyAddress( mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster ) var propertySize: UInt32 = 0 guard AudioObjectGetPropertyDataSize(systemAudioObject, &address, 0, nil, &propertySize) == noErr else { return [] } let deviceCount = Int(propertySize) / MemoryLayout<AudioObjectID>.stride var deviceIDs = [AudioObjectID](repeating: 0, count: deviceCount) _ = AudioObjectGetPropertyData(systemAudioObject, &address, 0, nil, &propertySize, &deviceIDs) // 筛选出输入设备 for id in deviceIDs { var inputConfigAddress = AudioObjectPropertyAddress( mSelector: kAudioDevicePropertyStreamConfiguration, mScope: kAudioObjectPropertyScopeInput, mElement: kAudioObjectPropertyElementMaster ) var inputSize: UInt32 = 0 if AudioObjectGetPropertyDataSize(id, &inputConfigAddress, 0, nil, &inputSize) == noErr && inputSize > 0 { // 获取设备名称 var nameAddress = AudioObjectPropertyAddress( mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster ) var name: CFString? var nameSize = UInt32(MemoryLayout<UnsafeMutablePointer<CFString?>>.stride) _ = AudioObjectGetPropertyData(id, &nameAddress, 0, nil, &nameSize, &name) if let deviceName = name { devices.append(Device(name: deviceName as String, id: id)) } } } return devices #else return AudioEngine.inputDevices #endif } static func getAllOutputs() -> [Device] { #if targetEnvironment(macCatalyst) var devices = [Device]() let systemAudioObject = AudioObjectID(kAudioObjectSystemObject) var address = AudioObjectPropertyAddress( mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster ) var propertySize: UInt32 = 0 guard AudioObjectGetPropertyDataSize(systemAudioObject, &address, 0, nil, &propertySize) == noErr else { return [] } let deviceCount = Int(propertySize) / MemoryLayout<AudioObjectID>.stride var deviceIDs = [AudioObjectID](repeating: 0, count: deviceCount) _ = AudioObjectGetPropertyData(systemAudioObject, &address, 0, nil, &propertySize, &deviceIDs) // 筛选出输出设备 for id in deviceIDs { var outputConfigAddress = AudioObjectPropertyAddress( mSelector: kAudioDevicePropertyStreamConfiguration, mScope: kAudioObjectPropertyScopeOutput, mElement: kAudioObjectPropertyElementMaster ) var outputSize: UInt32 = 0 if AudioObjectGetPropertyDataSize(id, &outputConfigAddress, 0, nil, &outputSize) == noErr && outputSize > 0 { var nameAddress = AudioObjectPropertyAddress( mSelector: kAudioDevicePropertyDeviceNameCFString, mScope: kAudioObjectPropertyScopeGlobal, mElement: kAudioObjectPropertyElementMaster ) var name: CFString? var nameSize = UInt32(MemoryLayout<UnsafeMutablePointer<CFString?>>.stride) _ = AudioObjectGetPropertyData(id, &nameAddress, 0, nil, &nameSize, &name) if let deviceName = name { devices.append(Device(name: deviceName as String, id: id)) } } } return devices #else return AudioEngine.outputDevices #endif } }
2. 选中设备并启动实时监听
拿到设备列表后,给AudioEngine指定设备,再连接监听节点即可:
// 示例:选中第一个输入和输出设备 guard let inputDevice = AudioDeviceHelper.getAllInputs().first, let outputDevice = AudioDeviceHelper.getAllOutputs().first else { print("未检测到可用音频设备") return } do { // 设置AudioEngine的输入输出设备 try AudioEngine.setInputDevice(inputDevice) try AudioEngine.setOutputDevice(outputDevice) // 创建输入节点和监听 mixer let inputNode = AudioEngine.inputNode let monitorMixer = Mixer() // 连接节点:输入 → 监听 mixer → 输出 inputNode.connect(to: monitorMixer) monitorMixer.connect(to: AudioEngine.outputNode) // 启动音频引擎 try AudioEngine.start() print("实时监听已启动") } catch { print("音频引擎启动失败:\(error.localizedDescription)") }
额外注意点
- 权限配置:不管哪个平台,都要在Info.plist里加
NSMicrophoneUsageDescription描述麦克风权限;Mac Catalyst还要在项目签名权限中开启音频输入输出权限。 - 设备变化监听:可以用
AudioObjectAddPropertyListener监听设备插拔事件,实时更新设备列表,让用户体验更流畅。
内容的提问来源于stack exchange,提问作者fhamm
相关产品推荐
相关产品推荐

