带有AVAudioConverterInputBlock的AVAudioConverter在处理后口吃音频 [英] AVAudioConverter with AVAudioConverterInputBlock stutters audio after processing

查看:216
本文介绍了带有AVAudioConverterInputBlock的AVAudioConverter在处理后口吃音频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将音频缓冲区转换为其他格式,并且正在使用AVAudioConverter.当您具有相同的采样率并且不需要使用AVAudioConverterInputBlock时,AVAudioConverter会执行此工作.

I'm trying to convert audio buffers to a different format, and I'm using AVAudioConverter. AVAudioConverter does the job when you have the same sample rate and you don't need to use the AVAudioConverterInputBlock.

但是,如果我使用相同的采样率,音频数据就会出现奇怪的卡顿现象.我感觉我没有很好地处理输入块.输出的单词重复两次或三次.下面是完整的方法:

But if I'm dealing with the same sample rate, I'm getting a strange stutter in my audio data. I have a feeling I'm not handling the input block well. The output has words repeating two or three times. Below is the full method:

func sendAudio(audioFile: URL, completionHandler: @escaping (Bool, Bool, Data?)->Void) {

    createSession(){ sessionUrl, observeURL, session in
        let file = try! AVAudioFile(forReading: audioFile)
        let formatOfAudio = file.processingFormat
        self.engine = AVAudioEngine()
        guard let input = self.engine.inputNode else {
            print("no input")
            return
        }
        //The audio in format in this case is: <AVAudioFormat 0x61800009d010:  2 ch,  44100 Hz, Float32, non-inter>
        let formatIn = formatOfAudio
        let formatOut = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: true)
        let mixer = AVAudioMixerNode()
        self.engine.attach(mixer)
        mixer.volume = 0.0
        self.engine.attach(self.audioPlayerNode)
        self.engine.connect(self.audioPlayerNode, to: mixer, format: formatIn)
        self.engine.connect(input, to: mixer, format: input.outputFormat(forBus: 0))
        self.engine.connect(mixer, to: self.engine.mainMixerNode, format: formatIn)
        let audioConverter = AVAudioConverter(from: formatIn, to: formatOut)
        mixer.installTap(onBus: 0, bufferSize: 32000, format: formatIn, block: {
            (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
                let convertedBuffer = AVAudioPCMBuffer(pcmFormat: formatOut, frameCapacity: buffer.frameCapacity)
                let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
                    outStatus.pointee = AVAudioConverterInputStatus.haveData
                    return buffer
                }
                var error: NSError? = nil
                let status = audioConverter.convert(to: convertedBuffer, error: &error, withInputFrom: inputBlock)
                let myData = convertedBuffer.toData()
                completionHandler(true, false, myData)
        })
        self.audioPlayerNode.scheduleFile(file, at: nil){
            self.delayWithSeconds(3.0){
            self.engine.stop()
            mixer.removeTap(onBus: 0)
            completionHandler(true, true, nil)
            }
        }
        do {
            try self.engine.start()
        } catch {
            print(error)
        }
        self.audioPlayerNode.play()
    }
}

有什么想法吗?我从 Apple幻灯片示例中获得了这段代码:

Any thoughts? I got this code from an Apple slide sample:

// Create an input block that’s called when converter needs input
let inputBlock : AVAudioConverterInputBlock = {inNumPackets, outStatus in 
    if (<no_data_available>) {   
        outStatus.memory = AVAudioConverterInputStatus.NoDataNow; 
        return nil;  
    } else if (<end_of_stream>) {   
        outStatus.memory = AVAudioConverterInputStatus.EndOfStream; 
        return nil;  
    } else {
        ..outStatus.memory = AVAudioConverterInputStatus.HaveData;   
        return inBuffer; // fill and return input buffer 
    }  
}

推荐答案

对于发现此问题的任何人,真正的根本原因是对 AVAudioConverterInputBlock 的错误使用.只要足够大,目标缓冲区的容量就无关紧要,但是将重复调用该块,直到目标缓冲区被填满为止.

For anybody finding this, the actual underlying cause is the incorrect use of AVAudioConverterInputBlock. The destination buffer capacity doesn't matter as long as it is large enough, however the block will be called repeatedly until the destination buffer is filled.

如果源缓冲区包含 ABC ,它将用 ABCABCABC ... 填充目标.然后,如果将其通过管道传输到实时回放,则会随机切断这些块以适应回放时间,从而产生怪异的裂纹.

If your source buffer contains ABC, it will fill up the destination with ABCABCABC.... Then, if you pipe it to realtime playback, the chunks are getting cut off randomly to fit the playback timing, resulting in this weird crackle.

实际的解决方案是将缓冲区提交到转换器后,将 AVAudioConverterInputStatus 正确设置为 .noDataNow .请注意,返回 .endOfStream 将永远锁定转换器对象.

The actual solution is to properly set AVAudioConverterInputStatus to .noDataNow once the buffer is submitted to the converter. Note that returning .endOfStream will lock up the converter object forever.

var gotData = false
self.converter.convert(to: convertedBuffer, error: nil, withInputFrom: { (_, outStatus) -> AVAudioBuffer? in
    if gotData {
        outStatus.pointee = .noDataNow
        return nil
    }
    gotData = true
    outStatus.pointee = .haveData
    return inputBuffer
})            

这篇关于带有AVAudioConverterInputBlock的AVAudioConverter在处理后口吃音频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆