You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS - AudioKit:输入与输出使用不同设备的实现问题

实现AudioKit音频输入输出设备分离(麦克风录制+远程扬声器播放)

我来帮你搞定这个输入输出设备分离的需求!核心是要给AVAudioSession分别指定**输入(内置麦克风)和输出(远程扬声器)**端口,再配合AudioKit的节点连接和启动逻辑来实现。下面是具体的实现步骤和完整代码:

1. 先枚举所有可用音频设备(方便确认目标设备)

先完善你代码里的printDevices()函数,用来列出当前所有的输入/输出设备,这样你能准确找到要使用的内置麦克风和远程扬声器:

func printDevices() {
    let inputs = session.availableInputs ?? []
    let outputs = session.currentRoute.outputs
    
    print("======= 可用输入设备 =======")
    for input in inputs {
        print("设备名: \(input.portName), 类型: \(input.portType.rawValue)")
    }
    
    print("\n======= 当前输出设备 =======")
    for output in outputs {
        print("设备名: \(output.portName), 类型: \(output.portType.rawValue)")
    }
}

运行后你会看到类似这样的输出,其中内置麦克风的portType是.builtInMic,远程扬声器(比如AirPlay/蓝牙)的portType可能是.airPlay或.bluetoothA2DP。

2. 配置AVAudioSession实现设备分离

在按钮点击事件里,我们需要先配置会话的类别、激活会话,然后指定输入和输出端口:

@IBAction func buttonWasPressed() {
    printDevices()
    
    do {
        // 1. 设置会话类别为播放+录制,允许蓝牙/AirPlay输出
        try session.setCategory(.playAndRecord, 
                                mode: .default,
                                options: [.allowBluetoothA2DP, .allowAirPlay])
        
        // 2. 指定输入设备为内置麦克风
        if let builtInMic = session.availableInputs?.first(where: { $0.portType == .builtInMic }) {
            try session.setPreferredInput(builtInMic)
            print("已选择内置麦克风作为输入")
        }
        
        // 3. 指定输出设备为远程扬声器(这里以AirPlay为例,可根据实际设备修改)
        let outputRoutes = session.availableOutputs ?? []
        if let remoteSpeaker = outputRoutes.first(where: { $0.portType == .airPlay }) {
            try session.setPreferredOutput(remoteSpeaker)
            print("已选择AirPlay扬声器作为输出")
        }
        
        // 4. 激活音频会话
        try session.setActive(true)
        
        // 5. 启动AudioKit
        try AudioKit.start()
        print("AudioKit已启动,开始麦克风录制+远程播放")
        
    } catch {
        print("配置音频会话失败: \(error.localizedDescription)")
    }
}

3. 完善AudioKit节点连接和清理逻辑

还要在viewDidLoad里调整节点连接的时机,并在控制器销毁时停止AudioKit:

override func viewDidLoad() {
    super.viewDidLoad()
    // 先连接音频节点,启动AudioKit时才会生效
    mic >>> reverb
    AudioKit.output = reverb
    // 设置低延迟缓冲(0.002可能过于激进,建议从0.01开始测试)
    AKSettings.ioBufferDuration = 0.01
}

deinit {
    // 控制器销毁时停止AudioKit并停用会话
    Task {
        do {
            try await AudioKit.stop()
            try session.setActive(false)
        } catch {
            print("停止AudioKit失败: \(error.localizedDescription)")
        }
    }
}

注意事项

  • 记得在Info.plist中添加麦克风权限描述(NSMicrophoneUsageDescription),否则App会崩溃
  • 如果远程扬声器是蓝牙设备,可能需要额外处理连接状态监听
  • AKSettings.ioBufferDuration不要设置得过小,否则可能导致音频卡顿或设备不支持,建议根据实际设备调整

内容的提问来源于stack exchange,提问作者goldwalk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 10:06:15