美文网首页
[Ray's音视频01]Audio Units录制音频

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

作者: xlw92 | 来源:发表于2020-05-08 16:52 被阅读0次

    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录制音频

          本文链接:https://www.haomeiwen.com/subject/wsqaghtx.html