c++ - iOS:如何在运行时使用音频单元对音频(PCM 数据)进行重新采样?

标签 c++ ios objective-c audiounit resampling

如何在运行时/实时使用音频单元对音频(PCM 数据)进行重新采样?

我有一个音频单元设置如下。

- (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));


}

音频设置如下。

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

我正在接收来自记录回调的 PCM 数据

audioBufferList->mBuffers[0].mData

那么,我如何才能将此 PCM 数据从 44.1KHz 重新采样到 8KHz,反之亦然? 我在谷歌上搜索了很多,但没有找到任何代码示例或直接说明。

找到了这些线程,但没有一个提供明确的说明。

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

非常感谢任何代码示例或信息。

最佳答案

转换器音频单元将处理您的采样率转换。我发现处理这个问题的最好方法是让你的链适应硬件的 native 功能。这意味着您应该获取系统 AudioStreamBasicDescription (sysASBD),然后将转换器单元放在系统和链中需要不同东西的部分之间。因此,对于使用 8K 采样率播放音频,您可以这样做:ReomoteIO(mic) -> 转换器 -> your8Kprocessing -> 转换器 -> RemoteIO(out)。

这是转换器的描述。

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

这是获取系统 ASBDin 和 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);

要使用转换器,您只需将其输入 ASBD 和输出 ASBD 设置为所需格式,它就会完成所有工作。建立联系,您就可以玩 8K 了。

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));

关于c++ - iOS:如何在运行时使用音频单元对音频(PCM 数据)进行重新采样?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32607418/

相关文章:

ios - 方法 Swizzling 未正确触发?

c++ - 如何使openCV的solvePNP头部姿势估计输出更准确

c++ - 这个c++代码有什么问题?

javascript - iOS 13+ 设备的网络音频音量衰减

ios - UITableView 不读取数组

ios - NSAttributedString 上的辅助功能(画外音)

ios - UITableView 中的图像混合 - XML 解析

ios - 释放iOS应用中SKMap(Skobbler)的内存

c++ - c++模板中实例化和特化的区别

C++:我们可以收集类型吗?