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.
- https://developer.apple.com/forums/thread/70694
- iOS: How to resample audio(PCM data) using Audio Unit at runtime?
- iOS convert audio sample rate from 16 kHz to 8 kHz
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