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

时间:2017-03-08 00:29:03

标签: ios swift avfoundation avaudioengine

此方法处理来自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 个答案:

答案 0 :(得分:3)

来自头文件中convert的讨论:

  

输出缓冲区的frameCapacity应该至少与inputBuffer的frameLength一样大。               如果转换涉及编解码器或采样率转换,则必须使用               convertToBuffer:错误: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

P.S。在苹果公司创建新的NSError代码字符串必须非常昂贵 - 他们的预算不能涵盖“费率转换需要convertToBuffer”的消息。