AudioUnit實(shí)現(xiàn)簡(jiǎn)單的錄音和耳返

最近學(xué)習(xí)了AudioUnit的官方指南讥巡,按照官方文檔簡(jiǎn)單實(shí)現(xiàn)錄音和耳返的功能。
1舔哪、首先配置AudioSession欢顷,代碼如下

  self.graphSampleRate = 44100.0;
    self.ioBufferDuration = 0.005;
    NSError *error = nil;
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setPreferredHardwareSampleRate:self.graphSampleRate error:&error];
    
    if (error) {
        NSLog(@"=====error===%@",error);
        exit(-1);
        return;
    }
    [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
    if (error) {
        NSLog(@"=====error===%@",error);
        exit(-1);
        return;
    }
    [audioSession setActive:YES error:&error];
    if (error) {
        NSLog(@"=====error===%@",error);
        exit(-1);
        return;
    }
    //    音頻會(huì)話激活后,根據(jù)系統(tǒng)提供的實(shí)際采樣率更新您自己的采樣率變量捉蚤。
    self.graphSampleRate = [audioSession currentHardwareSampleRate];
    
    //    還有一個(gè)其他硬件特性可能需要配置:音頻硬件I / O緩沖區(qū)持續(xù)時(shí)間抬驴。44.1 kHz采樣率的默認(rèn)持續(xù)時(shí)間約為23 ms,相當(dāng)于1,024個(gè)采樣的切片大小缆巧。如果I / O延遲對(duì)您的應(yīng)用程序至關(guān)重要布持,則可以請(qǐng)求較短的持續(xù)時(shí)間,下降到大約0.005 ms(相當(dāng)于256個(gè)采樣)陕悬,如下所示:
    [audioSession setPreferredIOBufferDuration:self.ioBufferDuration error:&error];
    if (error) {
        NSLog(@"=====error===%@",error);
        exit(-1);
        return;
    }

2题暖、創(chuàng)建一個(gè)AudioUnit對(duì)象
2.1:構(gòu)建AudioComponentDescription,主要需要設(shè)置componentTypecomponentSubType。由于我們這里是需要錄音然后輸出耳機(jī),設(shè)置如下:

 //1.創(chuàng)建AudioUnit
    AudioComponentDescription ioUnitDes;
    ioUnitDes.componentType = kAudioUnitType_Output;
    ioUnitDes.componentSubType = kAudioUnitSubType_RemoteIO;
    ioUnitDes.componentManufacturer = kAudioUnitManufacturer_Apple;
    ioUnitDes.componentFlags = 0;
    ioUnitDes.componentFlagsMask = 0;

不同的的componentTypecomponentSubType組成有不能的作用捉超,下圖為常用不同組合的作用

391523962280_.pic_hd.jpg

2.2 創(chuàng)建AudioUnit,AudioUnit的創(chuàng)建方式有兩種胧卤,這里使用官方推薦的方式AUGraph來(lái)創(chuàng)建,

  //1.創(chuàng)建一個(gè)圖
   OSStatus status;
    status = NewAUGraph(&processingGraph);
    CheckStatus(status, @"不能構(gòu)造圖", YES);
//2.創(chuàng)建一個(gè)結(jié)點(diǎn)。
    AUNode ioNode;
    status = AUGraphAddNode(processingGraph, &ioUnitDes, &ioNode);
    CheckStatus(status, @"添加節(jié)點(diǎn)失敗", YES);
    
    //3狂秦、打開(kāi)圖灌侣,相當(dāng)于間接創(chuàng)建了音頻處理單元
    status = AUGraphOpen(processingGraph);
    CheckStatus(status, @"打開(kāi)圖失敗", YES);
    //4推捐、獲取ioUnit
    status = AUGraphNodeInfo(processingGraph, ioNode, NULL, &_ioUnit);
    CheckStatus(status, @"不能獲取 node info", YES);

上面代碼中CheckStatus為檢測(cè)是否成功的函數(shù)

static void CheckStatus(OSStatus status, NSString *message, BOOL fatal)
{
    if(status != noErr)
    {
        char fourCC[16];
        *(UInt32 *)fourCC = CFSwapInt32HostToBig(status);
        fourCC[4] = '\0';
        
        if(isprint(fourCC[0]) && isprint(fourCC[1]) && isprint(fourCC[2]) && isprint(fourCC[3]))
            NSLog(@"%@: %s", message, fourCC);
        else
            NSLog(@"%@: %d", message, (int)status);
        
        if(fatal)
            exit(-1);
    }
}

2.3 設(shè)置AudioUnit的屬性

  //2.1設(shè)置
    UInt32 flag = 1;
    status =  AudioUnitSetProperty(_ioUnit,kAudioOutputUnitProperty_EnableIO , kAudioUnitScope_Input,1, &flag, sizeof(flag));
    CheckStatus(status, @"設(shè)置輸入scope 失敗", YES);
    status =  AudioUnitSetProperty(_ioUnit,kAudioOutputUnitProperty_EnableIO , kAudioUnitScope_Output,0, &flag, sizeof(flag));
    CheckStatus(status, @"設(shè)置輸出scope 失敗", YES);

上面的1表示Element1為和錄音的麥克風(fēng)相連裂问,0表示Element0和輸出硬件相連。
2.4 設(shè)置流的格式AudioStreamBasicDescription

 size_t bytesPerSample = sizeof(AudioUnitSampleType);
    AudioStreamBasicDescription asbd = {0};
    asbd.mFormatID = kAudioFormatLinearPCM;
    asbd.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
    asbd.mBytesPerFrame = bytesPerSample;
    asbd.mBytesPerPacket = bytesPerSample;
    asbd.mBitsPerChannel = 8*bytesPerSample;
    asbd.mFramesPerPacket = 1;
    asbd.mChannelsPerFrame = 2;
    asbd.mSampleRate = self.graphSampleRate;
    
    status = AudioUnitSetProperty(_ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, sizeof(AudioStreamBasicDescription));
    CheckStatus(status, @"設(shè)置輸入流格式失敗", YES);
    status = AudioUnitSetProperty(_ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &asbd, sizeof(AudioStreamBasicDescription));
    CheckStatus(status, @"設(shè)置輸出流格式失敗", YES);

下面的圖片為不同用途的AudioUnit的設(shè)置格式

401524022790_.pic_hd.jpg

411524022810_.pic_hd.jpg

421524022825_.pic_hd.jpg

431524022849_.pic_hd.jpg

3牛柒、設(shè)置回調(diào)函數(shù)堪簿,設(shè)置回調(diào)函數(shù)也要兩種不同的方式,一種直接為AudioUnit設(shè)置可能線程不安全皮壁,還有一種就是AUGraph來(lái)設(shè)置
3.1 直接使用AudioUnit來(lái)設(shè)置

    //3椭更、設(shè)置播放回調(diào)函數(shù)
    AURenderCallbackStruct playCallBack;
    playCallBack.inputProc = playCallBackFuc;
    playCallBack.inputProcRefCon = (__bridge void*)self;
    
    
   status =  AudioUnitSetProperty(_ioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Output, 0, &playCallBack, sizeof(playCallBack));
    CheckStatus(status, @"set renderCallBackError", YES);

  // 設(shè)置錄音回調(diào)函數(shù)
    AURenderCallbackStruct recordCallback;
    recordCallback.inputProc = RecordCallbackFuc;
    recordCallback.inputProcRefCon = (__bridge void *)self;
    AudioUnitSetProperty(_ioUnit,
                         kAudioOutputUnitProperty_SetInputCallback,
                         kAudioUnitScope_Input,
                         1,
                         &recordCallback,
                         sizeof(recordCallback));

錄音回調(diào)函數(shù)如下:

static OSStatus RecordCallbackFuc(    void *          inRefCon,
                                AudioUnitRenderActionFlags *    ioActionFlags,
                                const AudioTimeStamp *            inTimeStamp,
                                UInt32                            inBusNumber,
                                UInt32                            inNumberFrames,
                                AudioBufferList * __nullable    ioData){
    
    
    
    ViewController *viewC = (__bridge ViewController*)inRefCon;
     NSLog(@"錄音");
    if (ioData) {
        NSLog(@"size2 = %d", ioData->mBuffers[0].mDataByteSize);
        //    memcpy(ioData->mBuffers[0].mData, buffList->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize);
        AudioUnitRender(viewC.ioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
    }
    return noErr; 
}

播放回調(diào)函數(shù)

 static OSStatus playCallBackFuc(    void *          inRefCon,
                    AudioUnitRenderActionFlags *    ioActionFlags,
                    const AudioTimeStamp *            inTimeStamp,
                    UInt32                            inBusNumber,
                    UInt32                            inNumberFrames,
                             AudioBufferList * __nullable    ioData){
    
    ViewController *viewC = (__bridge ViewController*)inRefCon;
    NSLog(@"size2 = %d", ioData->mBuffers[0].mDataByteSize);
//    memcpy(ioData->mBuffers[0].mData, buffList->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize);
    AudioUnitRender(viewC.ioUnit, ioActionFlags, inTimeStamp, 1, inNumberFrames, ioData);
    NSLog(@"播放");
    return noErr;
}

3.2 使用AUGraph來(lái)設(shè)置(缺點(diǎn)只能是指播放回調(diào)函數(shù),優(yōu)點(diǎn)線程安全)

   //3蛾魄、設(shè)置回調(diào)函數(shù)
    AURenderCallbackStruct playCallBack;
    playCallBack.inputProc = playCallBackFuc;
    playCallBack.inputProcRefCon = (__bridge void*)self;
    
    //該方法是線程安全的(但是只能設(shè)置renderCallBack)
    AUGraphSetNodeInputCallback(processingGraph, ioNode, 0, &playCallBack);

4 初始化AUGraph

  //4初始化啟動(dòng)一個(gè)音頻處理圖
    OSStatus result = AUGraphInitialize(processingGraph);
    
    CheckStatus(result, @"初始化失敗", YES);

5 開(kāi)啟AUGraph

 OSStatus status =  AUGraphStart(processingGraph);
    CheckStatus(status, @"啟動(dòng)圖失敗", YES);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末虑瀑,一起剝皮案震驚了整個(gè)濱河市湿滓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舌狗,老刑警劉巖叽奥,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異痛侍,居然都是意外死亡朝氓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門主届,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赵哲,“玉大人,你說(shuō)我怎么就攤上這事君丁》愣幔” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵绘闷,是天一觀的道長(zhǎng)筷屡。 經(jīng)常有香客問(wèn)我,道長(zhǎng)簸喂,這世上最難降的妖魔是什么毙死? 我笑而不...
    開(kāi)封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮喻鳄,結(jié)果婚禮上扼倘,老公的妹妹穿的比我還像新娘。我一直安慰自己除呵,他們只是感情好再菊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著颜曾,像睡著了一般纠拔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泛豪,一...
    開(kāi)封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天稠诲,我揣著相機(jī)與錄音,去河邊找鬼诡曙。 笑死臀叙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的价卤。 我是一名探鬼主播劝萤,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼慎璧!你這毒婦竟也來(lái)了床嫌?” 一聲冷哼從身側(cè)響起跨释,我...
    開(kāi)封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厌处,沒(méi)想到半個(gè)月后煤傍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嘱蛋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年蚯姆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洒敏。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡龄恋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出凶伙,到底是詐尸還是另有隱情郭毕,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布函荣,位于F島的核電站显押,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏傻挂。R本人自食惡果不足惜乘碑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望金拒。 院中可真熱鬧兽肤,春花似錦、人聲如沸绪抛。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)幢码。三九已至笤休,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間症副,已是汗流浹背店雅。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓦糕,地道東北人底洗。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓腋么,卻偏偏與公主長(zhǎng)得像咕娄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子珊擂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容