iOS音频之AudioUnit简单播放音频 -(2)

概念

实现AudioUnit录制声音和播放音频

主要功能:

  • 实现低延迟的音频I/O
  • 多录声音的的合成并且回访
  • 回声消除,Mix两轨音频,以及均衡器,压缩器,混响器等

从以下步骤详解

设置AVAudioSession

该类具有以下作用

  • 此类用来管理多个APP对音频资源的利用,
  • 设置自己的APP是否和其他APP音频同时存在,还是中断其他APP声音
  • 在手机调到静音模式下,自己的APP音频是否可以播放出声音
  • 电话或者其他APP中断自己APP的音频的事件处理
  • 指定音频输入和输出的设备(比如是听筒输出声音,还是扬声器输出声音)
  • 是否支持录音,录音同时是否支持音频播放
-(void)setupSession
{
    NSError *error = nil;
    AVAudioSession* session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
    [session setActive:YES error:nil];
}

参考资料:iOS 音频-AVAudioSession

二、设置Audiocomponentdescription

音频单元描述

struct AudioComponentDescription {
    OSType              componentType; //一个音频组件通用的独特的四字节码标识
    OSType              componentSubType;//由上面决定,设置相应的类型
    OSType              componentManufacturer;//厂商身份验证
    UInt32              componentFlags;//没有明确的指定,设置0
    UInt32              componentFlagsMask;//没有明确的指定,设置0
}

//实例代码
AudioComponentDescription inputDesc;
    desc.componentType = kAudioUnitType_Output; // we want to ouput
    desc.componentSubType = kAudioUnitSubType_RemoteIO; // we want in and ouput
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; // select provider
    desc.componentFlags = 0; // must be zero
    desc.componentFlagsMask = 0; // must be zero
    

用来描述unit的类型,包括均衡器,3D混音,多路混音,远端输入输出

componentType:类型是相对应的,什么样的功能设置什么样的类型,componentSubType是根据componentType设置的
componentSubType
componentManufacturer:厂商,一般设置kAudioUnitManufacturer_Apple
componentFlags:没有明确指定的值,设置0
componentFlagsMask:没有明确指定的值,设置0

  • AudioComponentFindNext
AudioComponent outputComponent = AudioComponentFindNext(NULL, &inputDesc);

通过AudioComponentInstanceNew创建

AudioComponentInstanceNew(outputComponent, &audioUnit);

通过AUGraph创建

-(void)createAUGraph:(MyAUGraphStruct*)augStruct{
    //Create graph
    CheckError(NewAUGraph(&augStruct->graph),
               "NewAUGraph failed");
    
    //Create nodes and add to the graph
    AudioComponentDescription inputcd = {0};
    inputcd.componentType = kAudioUnitType_Output;
    inputcd.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
    inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
    
    AUNode remoteIONode;
    //Add node to the graph
    CheckError(AUGraphAddNode(augStruct->graph,
                              &inputcd,
                              &remoteIONode),
               "AUGraphAddNode failed");
    
    //Open the graph
    CheckError(AUGraphOpen(augStruct->graph),
               "AUGraphOpen failed");
    
    //Get reference to the node
    CheckError(AUGraphNodeInfo(augStruct->graph,
                               remoteIONode,
                               &inputcd,
                               &augStruct->remoteIOUnit),
               "AUGraphNodeInfo failed");
}

设置输出格式描述

struct AudioStreamBasicDescription
{
    Float64             mSampleRate; //音频频率,也是采样频率
    AudioFormatID       mFormatID;//音频格式
    AudioFormatFlags    mFormatFlags;//格式标签
    UInt32              mBytesPerPacket;//每个数据包的字节数
    UInt32              mFramesPerPacket;//每个数据包的帧数,固定填1
    UInt32              mBytesPerFrame;//每一帧的字节数
    UInt32              mChannelsPerFrame;//每一帧声道数,单声道和双声道 1/2
    UInt32              mBitsPerChannel;//每条声道占的位数,采样位数/采样精度 8/16
    UInt32              mReserved;//保留
};

mSampleRate:采样率,每秒未压缩的数据的样本数=每秒的帧数

mFormatID:音频格式,必须设置的一个值,设置PCM编码

mFormatFlags:该元标记负责类型为AudioUnitSampleType线性PCM样本值中的bits指定所有布局细节

mFramesPerPacket
mChannelsPerFrame:每一帧声道数
mBitsPerChannel

  • mFormatID
typedef UInt32  AudioFormatID;

CF_ENUM(AudioFormatID)
{
    kAudioFormatLinearPCM               = 'lpcm',
    kAudioFormatAC3                     = 'ac-3',
    kAudioFormat60958AC3                = 'cac3',
    kAudioFormatAppleIMA4               = 'ima4',
    kAudioFormatMPEG4AAC                = 'aac ',
    kAudioFormatMPEG4CELP               = 'celp',
    kAudioFormatMPEG4HVXC               = 'hvxc',
    kAudioFormatMPEG4TwinVQ             = 'twvq',
    kAudioFormatMACE3                   = 'MAC3',
    kAudioFormatMACE6                   = 'MAC6',
    kAudioFormatULaw                    = 'ulaw',
    kAudioFormatALaw                    = 'alaw',
    kAudioFormatQDesign                 = 'QDMC',
    kAudioFormatQDesign2                = 'QDM2'
}

AudioUnitSetProperty配置表

//1、 设置的这几个参数的含义
CF_ENUM(AudioUnitScope) {
kAudioUnitScope_Global        = 0,//设置回调函数
kAudioUnitScope_Input        = 1,
kAudioUnitScope_Output        = 2,//设置音频格式描述的时候
kAudioUnitScope_Group        = 3,
kAudioUnitScope_Part        = 4,
kAudioUnitScope_Note        = 5,
kAudioUnitScope_Layer        = 6,
kAudioUnitScope_LayerItem    = 7
};

实例代码

AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;

AudioUnitSetProperty(audioUnit,
                     kAudioUnitProperty_StreamFormat,
                     kAudioUnitScope_Output,
                     INPUT_BUS,
                     &audioFormat,
                     sizeof(audioFormat));
AudioUnitSetProperty(audioUnit,
                     kAudioUnitProperty_StreamFormat,
                     kAudioUnitScope_Input,
                     OUTPUT_BUS,
                     &audioFormat,
                     sizeof(audioFormat));

打开输入输出端口

-(void)setupRemoteIOUnit{
    //Open input of the bus 1(input mic)
    UInt32 inputEnableFlag = 1;
    CheckError(AudioUnitSetProperty(augStruct->remoteIOUnit,
                                    kAudioOutputUnitProperty_EnableIO,
                                    kAudioUnitScope_Input,
                                    1,
                                    &inputEnableFlag,
                                    sizeof(inputEnableFlag)),
               "Open input of bus 1 failed");
    
    //Open output of bus 0(output speaker)
    UInt32 outputEnableFlag = 1;
    CheckError(AudioUnitSetProperty(augStruct->remoteIOUnit,
                                    kAudioOutputUnitProperty_EnableIO,
                                    kAudioUnitScope_Output,
                                    0,
                                    &outputEnableFlag,
                                    sizeof(outputEnableFlag)),
               "Open output of bus 0 failed");
}

设置回调函数

//播放回调函数
static OSStatus inputCallBackFun(    void *                            inRefCon,
                    AudioUnitRenderActionFlags *    ioActionFlags,
                    const AudioTimeStamp *            inTimeStamp,
                    UInt32                            inBusNumber,
                    UInt32                            inNumberFrames,
                    AudioBufferList * __nullable    ioData)
{

    XBAudioUnitRecorder *recorder = (__bridge XBAudioUnitRecorder *)(inRefCon);
    typeof(recorder) __weak weakRecorder = recorder;
    
    AudioBufferList bufferList;
    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = NULL;
    bufferList.mBuffers[0].mDataByteSize = 0;
    
    AudioUnitRender(recorder->audioUnit,
                    ioActionFlags,
                    inTimeStamp,
                    kInputBus,
                    inNumberFrames,
                    &bufferList);
    
    if (recorder.bl_output)
    {
        recorder.bl_output(&bufferList);
    }
    if (recorder.bl_outputFull)
    {
        recorder.bl_outputFull(weakRecorder, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
    }
    
    return noErr;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • [TOC] 0. 目录 {#index} 跳转到的地方 link 1. 斜体和粗体 代码: 显示效果: 这是一段斜...
    Gladall阅读 818评论 0 0
  • 1. 标题 使用 #表示标题 1.1 代码示例 # 一级标题## 二级标题### 三级标题#### 四级标题#...
    1519f8ccc7b0阅读 843评论 0 0
  • 1. 斜体和粗体 代码: *斜体*或_斜体_**粗体*****加粗斜体***~~删除线~~ 2. 分级标题 第一种...
    oneofkind阅读 261评论 3 1
  • 海上月是天上月眼前人是心上人向来心是看客心奈何人是剧中人
    木卯丁阅读 105评论 0 2
  • 我自己是会计专业,转行自学web的,学习有一两年了,也还是新人一个,只不过不是那种超级“新”的,所以有什么话说得不...
    iplaycodex阅读 279评论 0 2