【问题标题】:iOS: How to resample audio(PCM data) using Audio Unit at runtime?iOS:如何在运行时使用音频单元重新采样音频(PCM 数据)?
【发布时间】:2015-12-12 23:17:20
【问题描述】:

如何在运行时/实时使用音频单元重新采样音频(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

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

【问题讨论】:

  • 您能分享一下您正在尝试做的事情吗?这是否需要在不重新启动会话的情况下在 8k 和 44.1k 之间进行更改?你可以只设置 你可以只设置 audioStreamBasicDescription.mSampleRate = 8000;麦克风硬件不可更改,您只需在 ASBD 中说明您希望它如何交付给您,格式就会为您更改。

标签: c++ ios objective-c audiounit resampling


【解决方案1】:

转换器音频单元将处理您的采样率转换。我发现处理这个问题的最好方法是让你的链适应硬件本身的工作。这意味着您应该获取系统 AudioStreamBasicDescription (sysASBD),然后将转换器单元放在系统和链中需要不同内容的部分之间。因此,对于使用 8K 采样率播放音频,您可以这样做:RemoteIO(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));

【讨论】:

  • 我已经完成了,麦克风 -> VoiceProcessingIO -> 重采样器单元,但我不知道如何从重采样器获取输出,渲染回调不会被调用,我是否必须手动调用 AudioUnitRender?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 2020-05-19
  • 2016-02-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-07
  • 1970-01-01
相关资源
最近更新 更多