AVAudioConverter float32/48khz -> int16/16khz 转换失败

Posted

技术标签:

【中文标题】AVAudioConverter float32/48khz -> int16/16khz 转换失败【英文标题】:AVAudioConverter float32/48khz -> int16/16khz conversion failure 【发布时间】:2017-07-28 09:39:49 【问题描述】:

此方法处理来自 AVAudioEngine 输入节点上 installTap 的回调。我已经确认我正在获取单声道 float32/48000hz 缓冲区数据,我想将其转换为单声道 int16/16000hz。

var converter: AVAudioConverter? = nil
var convertBuffer: AVAudioPCMBuffer? = nil
let targetFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 16000, channels: 1, interleaved: false)

func recordCallback(buffer: AVAudioPCMBuffer, time: AVAudioTime) 
    if converter == nil 
        convertBuffer = AVAudioPCMBuffer(pcmFormat: targetFormat, frameCapacity: buffer.frameCapacity)
        convertBuffer?.frameLength = convertBuffer!.frameCapacity
        converter = AVAudioConverter(from: buffer.format, to: convertBuffer!.format)
        converter?.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal
        converter?.sampleRateConverterQuality = .max
    

    guard let convertBuffer = convertBuffer else  return 

    log.info("Converter: \(self.converter!)")
    log.info("Converter buffer: \(self.convertBuffer!)")
    log.info("Converter buffer format: \(self.convertBuffer!.format)")
    log.info("Source buffer: \(buffer)")
    log.info("Source buffer format: \(buffer.format)")

    do 
        try converter!.convert(to: convertBuffer, from: buffer)
     catch let error 
        log.error("Conversion error: \(error)")
        observer?.onError(EngineRecordTaskError.ConversionError(error: error))
        return
    

    …

这给了我一个有用的错误:

Conversion error: Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"

如果我使用替代方法 .convert(to:error:withInputFrom:),它不会填充 NSError,也不会填充目标缓冲区 - 静默失败。

记录的信息:

Converter: <AVAudioConverter: 0x17001cd40>
Converter buffer: <AVAudioPCMBuffer@0x17021e310: 9600/9600 bytes>
Converter buffer format: <AVAudioFormat 0x17448ba90:  1 ch,  16000 Hz, Int16>
Source buffer: <AVAudioPCMBuffer@0x17021e1b0: 19200/19200 bytes>
Source buffer format: <AVAudioFormat 0x174480910:  1 ch,  48000 Hz, Float32>
Conversion error: Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"

来自调试器的其他信息:

(lldb) po buffer.frameCapacity
4800

(lldb) po convertBuffer.frameCapacity
4800

(lldb) po buffer.frameLength
4800

(lldb) po convertBuffer.frameLength
4800

这是我尝试过的替代转换代码:

    var conversionError: NSError? = nil
    converter!.convert(to: convertBuffer, error: &conversionError, withInputFrom:  (_, _) in
        return buffer
    )

    if let conversionError = conversionError 
        log.error("Conversion error: \(conversionError)")
        observer?.onError(EngineRecordTaskError.ConversionError(error: conversionError))
        return
    

【问题讨论】:

【参考方案1】:

来自头文件中convert的讨论:

输出缓冲区的 frameCapacity 至少应大于 inputBuffer 的 frameLength。 如果转换涉及编解码器或采样率转换,则必须使用 convertToBuffer:error:withInputFromBlock:.

您正在从 48kHz 转换为 16kHz,这算作一次速率转换,因此您必须使用 convertToBuffer

let inputBlock: AVAudioConverterInputBlock =  inNumPackets, outStatus in
    outStatus.pointee = AVAudioConverterInputStatus.haveData
    return buffer


var error: NSError? = nil
let status: AVAudioConverterOutputStatus = converter!.convert(to: convertBuffer, error: &error, withInputFrom: inputBlock)
// TODO: check status

附言在苹果公司创建新的NSErrorcode'n'strings 一定非常昂贵——他们的预算无法涵盖“速率转换需要 convertToBuffer”消息。

【讨论】:

设置 outStatus 我上次尝试使用 convertToBuffer 方法时错过的内容。谢谢!

以上是关于AVAudioConverter float32/48khz -> int16/16khz 转换失败的主要内容,如果未能解决你的问题,请参考以下文章

带有 AVAudioConverterInputBlock 的 AVAudioConverter 处理后音频口吃

从 float32 -> float64 回到 float32,我会失去精度吗?

使用 CMSampleBuffer 生成 Float32 数组(Float32 PCM 数据)

卷积神经网络的 float16 与 float32

Float32与Float16转换

从 numpy 数组 float32 转换为 numpy 数组 float64