Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS: How to resample audio(PCM data) using Audio Unit at runtime?

How can i resample audio(PCM data) using Audio Unit at runtime/live ?

I have an Audio Unit setup as follows.

- (void) setUpAudioUnit {
    OSStatus status;
    AudioComponentInstance audioUnit;
    AudioComponent inputComponent;
    AudioComponentDescription audioComponentDescription;
    AudioStreamBasicDescription audioStreamBasicDescription;

    // Describe audio component
    audioComponentDescription.componentType = kAudioUnitType_Output;
    audioComponentDescription.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
    audioComponentDescription.componentFlags = 0;
    audioComponentDescription.componentFlagsMask = 0;
    audioComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;

    // Get component
    inputComponent = AudioComponentFindNext(NULL, &audioComponentDescription);

    // Get audio units
    status = AudioComponentInstanceNew(inputComponent, &audioUnit);
    checkStatus(status);

    // Enable IO for recording
    UInt32 flag = 1;
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Input,
                                  kInputBus,
                                  &flag,
                                  sizeof(flag));
    checkStatus(status);

    // Enable IO for playback
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_EnableIO,
                                  kAudioUnitScope_Output,
                                  kOutputBus,
                                  &flag,
                                  sizeof(flag));
    checkStatus(status);

    // Describe format
    audioStreamBasicDescription.mSampleRate         = AUDIO_SAMPLE_RATE;
    audioStreamBasicDescription.mFormatID           = kAudioFormatLinearPCM;
    audioStreamBasicDescription.mFormatFlags        = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
    audioStreamBasicDescription.mFramesPerPacket    = AUDIO_FRAMES_PER_PACKET;
    audioStreamBasicDescription.mChannelsPerFrame   = AUDIO_CHANNELS_PER_FRAME;
    audioStreamBasicDescription.mBitsPerChannel     = AUDIO_BITS_PER_CHANNEL;
    audioStreamBasicDescription.mBytesPerPacket     = AUDIO_BYTES_PER_PACKET;
    audioStreamBasicDescription.mBytesPerFrame      = AUDIO_BYTES_PER_FRAME;

    // Apply format
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &audioStreamBasicDescription,
                                  sizeof(audioStreamBasicDescription));
    checkStatus(status);


    /* Make sure we set the correct audio category before restarting */
    UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord;
    status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
                                     sizeof(audioCategory),
                                     &audioCategory);

    checkStatus(status);


    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  kOutputBus,
                                  &audioStreamBasicDescription,
                                  sizeof(audioStreamBasicDescription));
    checkStatus(status);


    // Set input callback
    AURenderCallbackStruct callbackStruct;
    callbackStruct.inputProc = recordingCallback;
    callbackStruct.inputProcRefCon = (__bridge void *)(self);
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioOutputUnitProperty_SetInputCallback,
                                  kAudioUnitScope_Global,
                                  kInputBus,
                                  &callbackStruct,
                                  sizeof(callbackStruct));
    checkStatus(status);

    // Set output callback
    callbackStruct.inputProc = playbackCallback;
    callbackStruct.inputProcRefCon = (__bridge void *)(self);
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_SetRenderCallback,
                                  kAudioUnitScope_Global,
                                  kOutputBus,
                                  &callbackStruct,
                                  sizeof(callbackStruct));
    checkStatus(status);

    // Disable buffer allocation for the recorder (optional - do this if we want to pass in our own)
    flag = 0;
    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_ShouldAllocateBuffer,
                                  kAudioUnitScope_Output,
                                  kInputBus,
                                  &flag,
                                  sizeof(flag));


}

And the Audio settings is as follows.

kOutputBus 0
kInputBus 1
AUDIO_SAMPLE_RATE 44100
AUDIO_FRAMES_PER_PACKET 1
AUDIO_CHANNELS_PER_FRAME 1
AUDIO_BITS_PER_CHANNEL 16 
AUDIO_BYTES_PER_PACKET 2
AUDIO_BYTES_PER_FRAME 2

I am receiving the PCM data from recording callback as

audioBufferList->mBuffers[0].mData

SO, how can i resample this PCM data from 44.1KHz to 8KHz and vice versa ? I have googled a lot but didn't find any code sample or straight instruction for this.

Found these thread but none of these provides clear instruction.

  1. Which built in AudioUnit can resample audio?
  2. Changing sample rate of an AUGraph on iOS

Any code sample or information is highly appreciated.

like image 241
Partho Biswas Avatar asked Sep 27 '22 17:09

Partho Biswas


1 Answers

A converter audio unit will handle your sample rate conversions. I find the best way to deal with this is to adapt your chain to what the hardware is doing natively. This means that you should get the system AudioStreamBasicDescription (sysASBD) then put converter units between the system and the parts of your chain that need something different. So for audio play through with 8K sampleRate you would do this: ReomoteIO(mic) -> converter -> your8Kprocessing -> converter -> RemoteIO(out).

Here is the description for the converter.

AudioComponentDescription convDesc;
convDesc.componentType = kAudioUnitType_FormatConverter;
convDesc.componentSubType = kAudioUnitSubType_AUConverter;
convDesc.componentFlags = 0;
convDesc.componentFlagsMask = 0;
convDesc.componentManufacturer = kAudioUnitManufacturer_Apple;

Here is how you get the system ASBDin and ASBDout

UInt32 sizeASBD = sizeof(AudioStreamBasicDescription);
AudioStreamBasicDescription ioASBDin;
AudioStreamBasicDescription ioASBDout;
AudioUnitGetProperty(remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 1, &ioASBDin, &sizeASBD);
AudioUnitGetProperty(remoteIO, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &ioASBDout, &sizeASBD);

All you have to do to use a converter is set it's input ASBD and output ASBD to the desired formats and it does all the work. The make your connections and you have your 8K play through.

AudioStreamBasicDescription asbd8K;

AudioComponentInstance converter44To8;
AudioUnitSetProperty(converter44To8,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,& ioASBDin,sizeof(AudioStreamBasicDescription));
AudioUnitSetProperty(converter44To8,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,0,&asbd8K,sizeof(AudioStreamBasicDescription));


AudioComponentInstance converter8To44;
AudioUnitSetProperty(converter8To44,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,&asbd8K,sizeof(AudioStreamBasicDescription));
AudioUnitSetProperty(converter8To44,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,0,& ioASBDout,sizeof(AudioStreamBasicDescription));
like image 162
dave234 Avatar answered Nov 15 '22 08:11

dave234