我正在寻求创建一个应用程序,该应用程序从麦克风获取输入,对输入音频载体执行处理,并立即通过输出回放.我希望最终对此执行语音处理,因此我需要一个低延迟解决方案.然而,我当前的解决方案大约每100 ms回调一次,这对于我的用例来说太慢了.我希望每8 ms访问缓冲区进行回放.

我目前的解决方案是:

var audioEngine: AVAudioEngine
var inputNode: AVAudioInputNode
var playerNode: AVAudioPlayerNode
var bufferDuration: AVAudioFrameCount

init() {
    audioEngine = AVAudioEngine()
    inputNode = audioEngine.inputNode
    playerNode = AVAudioPlayerNode()
    bufferDuration = 960// AVAudioFrameCount(352)
}

func startStreaming() -> Void {
    // Configure the session
    do {
        let audioSession = AVAudioSession.sharedInstance()
        try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker])
        try audioSession.setPreferredSampleRate(96000)
        try audioSession.setPreferredIOBufferDuration(0.008)
        try audioSession.setActive(true)
        try audioSession.overrideOutputAudioPort(.speaker)
    } catch {
        print("Audio Session error: \(error)")
    }
  
let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: AVAudioSession.sharedInstance().sampleRate, channels: 2, interleaved: false)
    
 // Set the playerNode to immediately queue/play the recorded buffer
inputNode.installTap(onBus: 0, bufferSize: bufferDuration, format: fmt) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
    // Schedule the buffer for playback
    playerNode.scheduleBuffer(buffer, at: nil, options: [], completionHandler: nil)
}
    
// Start the engine
do {
    audioEngine.attach(playerNode)
    audioEngine.connect(playerNode, to: audioEngine.outputNode, format: fmt/*inputNode.inputFormat(forBus: 0)*/)
        
    try audioEngine.start()
    playerNode.play()
} catch {
    print("Audio Engine start error: \(error)")
}

我try 过设置buffer. frameSize等选项,但似乎没有什么改变回调的频率.我不清楚这个问题是否在于框架不允许这么小的缓冲区,或者我是否错过了解决方案.该网站上的其他已解决的解决方案提供了不需要低缓冲区大小的理由,但我确实需要一个非常快的解决方案.如果AVFEaudio无法实现这一点,AudioKit.io和Core Audio C API中是否有潜在的解决方案?

推荐答案

try 将输入 node 直接连接到输出 node ,并完全丢弃tap和AVAudioPlayerNode.(AVAudioPlayerNode有什么用?不是时间敏感的东西.)

这对我有效:

let engine = AVAudioEngine()

init() {
    let session = AVAudioSession.sharedInstance()
    try! session.setCategory(.playAndRecord)
    try! session.setPreferredIOBufferDuration(0.008)
    try! session.setActive(true)

    engine.connect(engine.inputNode, to: engine.outputNode, format: nil)
    try! engine.start()
}

Ios相关问答推荐

Un托管和withUnsafePointer API之间的区别?

使用NavigationStack和.searchable时如何显示大型导航标题?

如何以最可靠的方式删除文本视图中的额外页边距?

验证开发人员应用程序证书是否已在您的设备上验证

自定义UIControl不适用于UITapGestureRecognizer

有没有办法将滚动视图RTL(阿拉伯语)与Ltr动画一起使用?

Flutter应用内购买无法恢复购买

SwiftUI - TextField - 在焦点上清除 Double 类型的值,在取消 Select 时恢复

如何将 -fobjc-arc-exceptions 标志正确添加到 XCode 中?

SwiftUI:如何为 ScrollView 制作可拉伸(灵活)的粘性标题?

在 Swift 中映射 MySQL 数据

SwiftUI 文本字段代理绑定代码忽略空格不起作用?

AVPlayer 退出全屏

如何识别 SwiftUI 中点击的按钮 ID 以使该按钮动画化?

动画 UILabel 字体大小更改

以编程方式获取故事板 ID?

如何在 iOS 中判断暗模式?

将自定义对象保存到 NSUserDefaults

如何计算 Swift 数组中元素的出现次数?

iOS在应用程序中下载并保存图像