Convert PCM Buffer to AAC ELD Format and vice versa

505 views Asked by At

I'm having trouble converting a linear PCM buffer to a compressed AAC ELD (Enhanced Low Delay) buffer.

I got some working code for the conversion into ilbc format from this question: AVAudioCompressedBuffer to UInt8 array and vice versa

This approach worked fine.

I changed the input for the format to this:

let packetCapacity = 8
let maximumPacketSize = 96
lazy var capacity = packetCapacity * maximumPacketSize // 768

let convertedSampleRate: Double = 16000

lazy var aaceldFormat: AVAudioFormat = {
    var descriptor = AudioStreamBasicDescription(mSampleRate: convertedSampleRate, mFormatID: kAudioFormatMPEG4AAC_ELD, mFormatFlags: 0, mBytesPerPacket: 0, mFramesPerPacket: 0, mBytesPerFrame: 0, mChannelsPerFrame: 1, mBitsPerChannel: 0, mReserved: 0)

    return AVAudioFormat(streamDescription: &descriptor)!
}()

The conversion to a compressed buffer worked fine and I was able to convert the buffer to a UInt8 Array.

However, the conversion back to a PCM Buffer didn't work. The input block for the conversion back to a buffer looks like this:

func convertToBuffer(uints: [UInt8], outcomeSampleRate: Double) -> AVAudioPCMBuffer? {
    // Convert to buffer
    let compressedBuffer: AVAudioCompressedBuffer = AVAudioCompressedBuffer(format: aaceldFormat, packetCapacity: AVAudioPacketCount(packetCapacity), maximumPacketSize: maximumPacketSize)
    compressedBuffer.byteLength = UInt32(capacity)
    compressedBuffer.packetCount = AVAudioPacketCount(packetCapacity)
    
    var compressedBytes = uints
    compressedBytes.withUnsafeMutableBufferPointer {
        compressedBuffer.data.copyMemory(from: $0.baseAddress!, byteCount: capacity)
    }
    
    guard let audioFormat = AVAudioFormat(
        commonFormat: AVAudioCommonFormat.pcmFormatFloat32,
        sampleRate: outcomeSampleRate,
        channels: 1,
        interleaved: false
    ) else { return nil }
    
    guard let uncompressor = getUncompressingConverter(outputFormat: audioFormat) else { return nil }
    
    var newBufferAvailable = true
    
    let inputBlock : AVAudioConverterInputBlock = {
        inNumPackets, outStatus in
        if newBufferAvailable {
            outStatus.pointee = .haveData
            newBufferAvailable = false
            return compressedBuffer
        } else {
            outStatus.pointee = .noDataNow
            return nil
        }
    }
    
    guard let uncompressedBuffer: AVAudioPCMBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: AVAudioFrameCount((audioFormat.sampleRate / 10))) else { return nil }
    
    var conversionError: NSError?
    uncompressor.convert(to: uncompressedBuffer, error: &conversionError, withInputFrom: inputBlock)
    
    if let err = conversionError {
        print("couldnt decompress compressed buffer", err)
    }
    
    return uncompressedBuffer
}

The error block after the convert method triggers and prints out "too few bits left in input buffer". Also, it seems like the input block only gets called once.

I've tried different codes and this seems to be one of the most common outcomes. I'm also not sure if the problem is in the initial conversion from the pcm buffer to uint8 array although I get an UInt8 Array filled with 768 values every 0.1 seconds (Sometimes the array contains a few zeros at the end, which doesn't happen in ilbc format.

Questions:

1. Is the initial conversion from pcm buffer to uint8 array done with the right approach? Are the packetCapacity, capacity and maximumPacketSize valid? -> Again, seems to work

2. Am I missing something at the conversion back to pcm buffer? Also, am I using the variables in the right way?

3. Has anyone achieved this conversion without using C in the project?

** EDIT: ** I also worked with the approach from this post: Decode AAC to PCM format using AVAudioConverter Swift

It works fine with AAC format, but not with AAC_LD or AAC_ELD

0

There are 0 answers