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

Swift中AVAudioEngine传输音频时如何消除背景噪音与回声

解决iOS音频采集传输中的背景噪音与回声问题

针对你使用AVAudioEngine采集麦克风音频、通过UDP传输后出现背景噪音和回声的问题,可通过以下几个关键优化点解决:


1. 优化AVAudioSession配置,启用系统降噪与回声消除

系统的voiceChat音频会话模式会自动启用内置的回声消除和降噪算法,同时配置合适的选项避免播放端回声:

func sendAudio() {
    do {
        try AVAudioSession.sharedInstance().setCategory(.playAndRecord, 
                                                        mode: .voiceChat, 
                                                        options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker])
        try AVAudioSession.sharedInstance().setActive(true)
    } catch {
        print("Audio Session Error: \(error)")
        return
    }
    // 其余逻辑...
}

说明:

  • voiceChat模式专为语音通话优化,默认开启降噪和回声消除
  • defaultToSpeaker强制使用扬声器播放,避免听筒贴近麦克风产生回声
  • allowBluetooth支持蓝牙音频设备,适配更多场景

2. 正确启用语音处理,调整音频节点连接

setVoiceProcessingEnabled(true)必须在音频节点连接前调用才能生效,同时要避免采集的音频本地播放(这是回声的主要来源之一):

func sendAudio() {
    // ... 音频会话配置 ...
    
    let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16,
                               sampleRate: 44100.0,
                               channels: 1,
                               interleaved: true)
    
    // 先启用语音处理,必须在节点连接前执行
    do {
        try audioEngine.inputNode.setVoiceProcessingEnabled(true)
    } catch {
        print("Failed to enable voice processing: \(error)")
    }
    
    // 仅连接inputNode到mixer用于采集,移除到mainMixer的连接,避免本地播放采集的音频
    audioEngine.connect(audioEngine.inputNode, to: mixer, format: format)
    
    // ... 其余逻辑 ...
}

3. 减小采集缓冲区,降低传输延迟

过大的缓冲区会增加音频传输延迟,进而导致回声。将缓冲区大小调整为1024或512,平衡延迟与性能:

mixer.installTap(onBus: 0, bufferSize: 1024, format: format, block: { (buffer, time) in
    guard let int16Data = buffer.int16ChannelData?.pointee else { return }
    // 按帧字节数计算数据长度,避免格式不匹配
    let data = Data(bytes: int16Data, count: Int(buffer.frameLength * format.streamDescription.pointee.mBytesPerFrame))
    
    DispatchQueue.global(qos: .background).async { [weak self] in
        guard let self = self else { return }
        self.socket.send(data, withTimeout: 0, tag: 0)
    }
})

4. 统一发送与接收端的音频格式

接收端的音频格式必须与发送端完全一致,避免格式转换产生噪音:

// 接收端的getComingAudio方法修改
func getComingAudio(with data: Data) -> AVAudioPCMBuffer {
    // 与发送端完全一致的格式
    guard let format = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 44100.0, channels: 1, interleaved: true) else {
        fatalError("Invalid audio format")
    }
    let frameCount = UInt32(data.count) / UInt32(format.streamDescription.pointee.mBytesPerFrame)
    let audioBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount)!
    
    data.withUnsafeBytes { bufferPointer in
        let int16Pointer = bufferPointer.bindMemory(to: Int16.self).baseAddress!
        audioBuffer.int16ChannelData?.pointee.assign(from: int16Pointer, count: Int(frameCount))
    }
    
    audioBuffer.frameLength = frameCount
    return audioBuffer
}

5. 接收端音频会话优化

接收端同样需要配置合适的音频会话,避免播放时的回声:

// 接收端初始化时配置
do {
    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .voiceChat, options: .defaultToSpeaker)
    try AVAudioSession.sharedInstance().setActive(true)
} catch {
    print("Receiver Audio Session Error: \(error)")
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:54:55