iOS,Audio resampling from 48K to 16K via AudioUnit

699 Views Asked by At

I have been trying to resample my live PCM mono-channel, 48000 sample rate to 16000 sample rate mono-channel using AudioUnit or AudioConverterRef but i am unsuccessful on both of them.

I have looked on the following resources but don't find them much helpful.

My code using AudioConverterRef is

     var inputFormat = CAStreamBasicDescription(
        sampleRate: Double(Constants.SAMPLE_RATE),
        numChannels: 1,
        pcmf: .int16,
        isInterleaved: false
    )
    
    var outputFormat = CAStreamBasicDescription(
        sampleRate: Double(16000),
        numChannels: 1,
        pcmf: .int16,
        isInterleaved: false
    )
    
    status = AudioConverterNew(
            &inputFormat!,
            &outputFormat!,
            &converter48To16
    )
    
    if status != noErr {
        print("ERROR HERE: \(status)")
    }
    
    
    let outputBytes = outputFormat!.mBytesPerPacket * (640 * 1280 / inputFormat!.mBytesPerPacket)
    
    let outputBuffer = UnsafeMutablePointer<CChar>.allocate(capacity: Int(outputBytes))
    
    defer { outputBuffer.deallocate() }
    
    var outputBufferList = AudioBufferList()
    outputBufferList.mNumberBuffers = 1
    outputBufferList.mBuffers.mNumberChannels = 1
    outputBufferList.mBuffers.mDataByteSize = outputBytes
    outputBufferList.mBuffers.mData = UnsafeMutableRawPointer(outputBuffer)
    

    var outputDataPacketSize = outputBytes / outputFormat!.mBytesPerPacket
    
     status = AudioConverterFillComplexBuffer(
        converter48To16!,
        conversionAudioController_Callback,
        UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),
        &outputDataPacketSize,
        &outputBufferList,
        nil
    )
    
    if status != noErr {
        print("ERROR HERE: \(status)")
    }

and the callBack is

let conversionAudioController_Callback: AudioConverterComplexInputDataProc = {(
inAudioConverter,
ioNumberDataPackets,
ioData,
outDataPacketDescription,
inUserData) -> OSStatus in

let delegate = unsafeBitCast(inAudioConverter, to: AudioUnitDataChannelCallbackDelegate.self)

let result = delegate.performConversion(
    ioNumberDataPackets: ioNumberDataPackets,
    ioData: ioData,
    outDataPacketDescription: outDataPacketDescription,
    inUserData: inUserData
)

return noErr
}

and the Error i Received at this line is:

let delegate = unsafeBitCast(inAudioConverter, to: AudioUnitDataChannelCallbackDelegate.self)

Thread 1: EXC_BAD_ACCESS (code=1, address=0xdbd000)

I don't received any problem in setting up my properties but only receive the error when actually executing the code.

I am currently recording PCM 16 bit mono channel at 48000 sample rate and trying to downsample it to 16000

Question updated:

 func performConversion(ioNumberDataPackets: UnsafeMutablePointer<UInt32>, ioData: UnsafeMutablePointer<AudioBufferList>, outDataPacketDescription: UnsafeMutablePointer<UnsafeMutablePointer<AudioStreamPacketDescription>?>?, inUserData: UnsafeMutableRawPointer?) -> OSStatus {
    
    let retained = inUserData!.assumingMemoryBound(to: AudioBufferList.self)

    let maxPackets = retained.pointee.mBuffers.mDataByteSize / 2
    if ioNumberDataPackets.pointee > maxPackets {
        ioNumberDataPackets.pointee = maxPackets
    }

    print("CONVERSION RUNNING...")

    let ioDataPtr = UnsafeMutableAudioBufferListPointer(ioData)
    ioDataPtr[0].mData = UnsafeMutableRawPointer(inputBufferList?.mBuffers.mData)
    ioDataPtr[0].mDataByteSize = 1280
    ioDataPtr[0].mNumberChannels = inputBufferList!.mBuffers.mNumberChannels

    print("ioDataPtr: \(ioDataPtr[0].mDataByteSize)")
    return noErr
}

*************Question edited: *************

Recording Data and downsampling 48K to 16K.

 func performRecording(_ ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>, inTimeStamp: UnsafePointer<AudioTimeStamp>, inBufNumber: UInt32, inNumberFrames: UInt32, ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    
    print("*******************")
    var err: OSStatus = noErr
    
    err = AudioUnitRender(audioUnit!, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData)
    
    var monoSamples = [Int16]()
    
    let rawAudioPointer = ioData[0].mBuffers.mData?.bindMemory(to: Int16.self, capacity: Int(inNumberFrames))
    if rawAudioPointer == nil {
        return noErr
    }
    monoSamples.append(contentsOf: UnsafeBufferPointer(start: rawAudioPointer, count: Int(inNumberFrames)))
    
    inputBufferList = ioData.pointee
    
    let outputBuffer = UnsafeMutablePointer<CChar>.allocate(capacity: 684)
    defer { outputBuffer.deallocate() }
    
    var fillBufferList = AudioBufferList()
    fillBufferList.mNumberBuffers = 1
    fillBufferList.mBuffers.mNumberChannels = 1
    fillBufferList.mBuffers.mDataByteSize = 684
    fillBufferList.mBuffers.mData = UnsafeMutableRawPointer(outputBuffer)

    var ioOutputDataPackets: UInt32 = 384

    inputBufferList = ioData.pointee

    let status = AudioConverterFillComplexBuffer(
        converter48To16!,
        conversionAudioController_Callback,
        UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),
        &ioOutputDataPackets,
        &fillBufferList,
        nil
    )

    if status != noErr {
        print("ERROR HERE CONVERSION: \(status)")
    }
    
    print("MONOSAMPLE DATA \(monoSamples.count) and \(ioOutputDataPackets) and \(fillBufferList.mBuffers.mDataByteSize)")
    
    return err
}

There is no issue with recording but when my output doesn't match mDataByteSize = 684 I get error of ERROR HERE CONVERSION: 1768846202

0

There are 0 best solutions below