[Ray's音视频01]Audio Units录制音频

iOS录音

  1. 根据开发文档的图可知,iOS音频相关的用的比较多的自顶向下的又 AVFoundation -> AudioToolBox -> Audio Unit

    技术选择上如果是录音然后获取内存中音频的数据,然后进行网络传输或者存本地,那么AudioQueue或者AudioUnit都能做到。因为之后可能会用到一些混响之类的,所以以下说明都是根据AudioUnit来讲述的。

    文章会根据代码来引出每个参数涉及到的知识点,然后进行说明,个人比较喜欢参考官方文档,其实很多东西在官方文档里面都能找到答案。

image.png
  1. iOS上提供了多重audio units,主要的功能大概可以分为,effect mixer io 和 format converter ,这次我们用到的是io模块文档地址

    在iOS当中,涉及到硬件的一般都会初始化一个session,然后软件方面的一般都是一个description
    录音的时候我们用到audio unit,第一部是先初始化一个session

    AVAudioSession *auSession = [AVAudioSession sharedInstance];
    [auSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    [auSession setActive:YES error:nil];

audio unit 提供了 6个常用的session类别对应着不同的功能

AVAudioSessionCategory 描述
AVAudioSessionCategoryAmbient 常用于播放背景音乐
AVAudioSessionCategorySoloAmbient 这个也是用于播放背景音乐但是会打断别的音乐的播放
AVAudioSessionCategoryPlayback 用于播放音乐
AVAudioSessionCategoryRecord 提供同时录音的功能
AVAudioSessionCategoryPlayAndRecord 可以同时录音和播放,适合于通话等场合
AVAudioSessionCategoryAudioProcessing 硬编码音频不能播放和录制

官网地址

  1. 设置buffer的duration,这个值决定音频的延迟。越小的话延迟越低
[auSession setPreferredIOBufferDuration:0.05 error:&error];
  1. 既然要用到audio unit 那么肯定要设置一些参数 AudioComponentDescription
CF_ENUM(UInt32) {
    kAudioUnitType_Output                   = 'auou',
    kAudioUnitType_MusicDevice              = 'aumu',
    kAudioUnitType_MusicEffect              = 'aumf',
    kAudioUnitType_FormatConverter          = 'aufc',
    kAudioUnitType_Effect                   = 'aufx',
    kAudioUnitType_Mixer                    = 'aumx',
    kAudioUnitType_Panner                   = 'aupn',
    kAudioUnitType_Generator                = 'augn',
    kAudioUnitType_OfflineEffect            = 'auol',
    kAudioUnitType_MIDIProcessor            = 'aumi'
};

image.png

其实官方文档已经写得很清楚不同的unit对应的type和sub type 选取要用的设置。audio units guides

  • componentManufacturer 厂家的这个参数一般固定位apple
  • componentFlags和componentFlagsMask如果没有具体要设置的值得话就填写0,其他事根据具体用到的component来设置
  • AudioComponentFindNext 获取系统中的componet
  • AudioComponentInstanceNew 初始化 返回值是 OSStatus类型,具体代表什么可以通过OSStatus 查询
    AudioComponentDescription inputDesc;
    inputDesc.componentType = kAudioUnitType_Output;
    inputDesc.componentSubType = kAudioUnitSubType_RemoteIO;
    inputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    inputDesc.componentFlags = 0;
    inputDesc.componentFlagsMask = 0;
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &inputDesc);
    status = AudioComponentInstanceNew(inputComponent, &audioUnit);
    if (status != noErr) {
        NSLog(@"AudioComponentInstanceNew: %d", status);
    }
  1. 设置AudioStreamBasicDescription(下文简称ASBD)
image.png
  • 先设置图中红色部分的声音要素
  • kAudioFormatFlagIsNonInterleaved 这个参数其实是指定pcm中的左右声道排布,是lrlrlr形式还是说llllrrrr这样
  • mBytesPerFrame 一般可以通过bits depth * channels / 8计算的到
  1. 关于audio bus的概念


    image.png
  • 这个是IO unit的模型,具体可以看出有两个element,一般称为bus,然后 1 是负责输入的,0 是负责输出的。输入的io一般是关闭状态所以要打开
  • 每个bus都一个input scope和output scope 这些在设置参数的时候会用到
  • 这次录音之后都是存本本地,所以不会用到 bus0
AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &inputFlag, sizeof(inputFlag));
AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output , 1, &inputASBD, sizeof(inputASBD));    
image.png
  1. 具体输入有了,那么要设置输出根据上面的思路就是用到element 1 的 output scope
AURenderCallbackStruct recordCallBackStruct;
recordCallBackStruct.inputProc = RecordProc;
recordCallBackStruct.inputProcRefCon = (__bridge void *)self;
AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, 1, &recordCallBackStruct, sizeof(recordCallBackStruct));

inputProc 是AURenderCallback类型的函数,录音的时候回在iodata里面拿到具体的数据,然后调用AudioUnitRender来进行渲染保存到自己的buffers里面

{
    AudioBufferList *buffers;
}

    //这里我先设置为1
    buffers = (AudioBufferList *)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer));
    buffers->mNumberBuffers = 1;
    buffers->mBuffers[0].mNumberChannels = 1;
    buffers->mBuffers[0].mDataByteSize = RH_BFFER_SIZE;
    buffers->mBuffers[0].mData = malloc(RH_BFFER_SIZE);
static OSStatus RecordProc(void *inRefCon,
                           AudioUnitRenderActionFlags *ioActionFlags,
                           const AudioTimeStamp *inTimeStamp,
                           UInt32 inBusNumber,
                           UInt32 inNumberFrames,
                           AudioBufferList *ioData)
{
    
    RHRecorder *objSelf = (__bridge RHRecorder *)inRefCon;
    objSelf->buffers->mNumberBuffers = 1;
    OSStatus status = noErr;
    
    status = AudioUnitRender(objSelf->audioUnit,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             inNumberFrames,
                             objSelf->buffers);
    if (status != noErr) {
        NSLog(@"render error :%d", status);
    }
    
    
    //save pcm steam
    [objSelf saveRecordDataToLocalPCM:objSelf->buffers->mBuffers[0].mData
                                 size:objSelf->buffers->mBuffers[0].mDataByteSize];
    
    
    return 0;
}

你可能感兴趣的:([Ray's音视频01]Audio Units录制音频)