iOS 通過 Audio Unit 播放音頻數(shù)據(jù)

關(guān)于AudioUnit

Audio Unit 是iOS系統(tǒng)音頻架構(gòu)的最底層了,這一層架構(gòu)是最接近硬件層的妨猩,也是開發(fā)者目前能操作最的層的API架構(gòu)了。


Audio Unit

這里先解釋一下DSP(digital signal processing)數(shù)字信號處理秽褒,音頻信號是需要通過設(shè)備采樣之后變成的數(shù)字信號壶硅,以方便數(shù)據(jù)的傳輸和記錄。目前最常用的是PCM格式的音頻數(shù)據(jù)信號销斟,因為這種高保真的信號方便后續(xù)的處理庐椒,還有就是它保留了數(shù)據(jù)的完整性。

關(guān)于音頻流參數(shù)

1蚂踊、采樣率
每秒鐘采得聲音樣本的次數(shù)约谈,聲音是一種能量波,有振幅和頻率犁钟,人的耳朵可以聽到的頻率在20-Hz~20kHz之間的聲波棱诱,所以采樣率越高,獲取的到的頻率信息就更為豐富涝动,由于人耳的分辨率很有限迈勋,太高的頻率并不能分辨出來。22050 的采樣頻率是常用的醋粟,44100已是CD音質(zhì)靡菇,超過48000或96000的采樣對人耳已經(jīng)沒有意義重归。

常用的采樣率:

8000 Hz - 電話所用采樣率
22050 Hz - 無線電廣播所用采樣率
32000 Hz - miniDV 數(shù)碼視頻 camcorder、DAT (LP mode)所用采樣率
44100 Hz - 音頻 CD, 也常用于 MPEG-1 音頻(VCD厦凤,SVCD鼻吮,MP3)所用采樣率
47250 Hz - 商用 PCM 錄音機所用采樣率
48000 Hz - miniDV、數(shù)字電視较鼓、DVD狈网、DAT、電影和專業(yè)音頻所用的數(shù)字聲音所用采樣率
50000 Hz - 商用數(shù)字錄音機所用采樣率
96000 Hz或者 192000 Hz - DVD-Audio笨腥、一些 LPCM DVD 音軌拓哺、BD-ROM(藍光盤)音軌、和 HD-DVD (高清晰度 DVD)音軌所用所用采樣率

2脖母、采樣位數(shù)
采樣位數(shù)士鸥,他是衡量聲音播到變化的一個參數(shù),它的數(shù)值越大谆级,分辨率就越高烤礁,錄制和回放的聲音就越接近真實。常見的聲卡主要有8位和16位兩種肥照,如今市面上所有的主流產(chǎn)品都是16位及以上的聲卡脚仔。

 每個采樣數(shù)據(jù)記錄的是振幅, 采樣精度取決于采樣位數(shù)的大小:
1 字節(jié)(也就是8bit) 只能記錄 256 個數(shù), 也就是只能將振幅劃分成 256 個等級;
2 字節(jié)(也就是16bit) 可以細到 65536 個數(shù), 這已是 CD 標(biāo)準(zhǔn)了;
4 字節(jié)(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.

3、通道數(shù)
即聲音的通道的數(shù)目舆绎,目前使用較多的是單聲道和立體聲鲤脏,相當(dāng)于從多位置采集聲音。
4吕朵、比特率
每秒的傳輸速率(位速, 也叫比特率)猎醇。如705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒705600bit的容量努溃。不同的音頻格式編碼硫嘶,對PCM都有一個壓縮比,所以比特率一般等于原始比特率/音頻壓縮比梧税。
5沦疾、幀長
幀長記錄了一個聲音單元字節(jié)為單位,其長度為:樣本長度 * 通道數(shù) = 幀長
6第队、幀數(shù)
每秒數(shù)據(jù)分為都少幀: 幀長 * 幀數(shù) * 8 = 比特率

Audio Unit 工作時的腦圖和流程圖

AudioUnit 涉及到的知識圖.png

整體的一個流程圖如下:


Audio Unit Flow.png

使用流程

1哮塞、iOS 涉及音頻使用和會話都需要使用到 AudioSessionInitialize去初始化音頻會話對象。

 // set audio session
    NSError *error = nil;
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
    [audioSession setActive:YES error:&error];

2斥铺、配置音頻組件Audio Unit 并描述輸出的單元

 //set audio component information
    AudioComponentDescription audioDesc;
    audioDesc.componentType = kAudioUnitType_Output;
    audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
    audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    audioDesc.componentFlags = 0;
    audioDesc.componentFlagsMask = 0;

3彻桃、查找、創(chuàng)建對應(yīng)的音頻輸出單元組件

 AudioUnit audioUnit;
//set audio component information
    AudioComponentDescription audioDesc;
    audioDesc.componentType = kAudioUnitType_Output;
    audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
    audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
    audioDesc.componentFlags = 0;
    audioDesc.componentFlagsMask = 0;

    //Finds the next component that matches a specified AudioComponentDescription structure after a specified audio component.
    AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
    //create a new instance of an audio component
    AudioComponentInstanceNew(inputComponent, &audioUnit);
 //audio property
    UInt32 flag = 1;
    if (flag) {
        status = AudioUnitSetProperty(audioUnit,
                                      kAudioOutputUnitProperty_EnableIO,
                                      kAudioUnitScope_Output,
                                      OUTPUT_BUS,
                                      &flag,
                                      sizeof(flag));
    }

4晾蜘、配置對應(yīng)需要播放的音頻數(shù)據(jù)格式內(nèi)容

 // format
    AudioStreamBasicDescription outputFormat;
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mSampleRate       = 44100; // 采樣率
    outputFormat.mFormatID         = kAudioFormatLinearPCM; // PCM格式
    outputFormat.mFormatFlags      = kLinearPCMFormatFlagIsSignedInteger; // 整形
    outputFormat.mFramesPerPacket  = 1; // 每幀只有1個packet
    outputFormat.mChannelsPerFrame = 1; // 聲道數(shù)
    outputFormat.mBytesPerFrame    = 2; // 每幀只有2個byte 聲道*位深*Packet數(shù)
    outputFormat.mBytesPerPacket   = 2; // 每個Packet只有2個byte
    outputFormat.mBitsPerChannel   = 16; // 位深
    [self printAudioStreamBasicDescription:outputFormat];

    status = AudioUnitSetProperty(audioUnit,
                                  kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input,
                                  OUTPUT_BUS,
                                  &outputFormat,
                                  sizeof(outputFormat));
    if (status) {
        NSLog(@"AudioUnitSetProperty eror with status:%d", status);
    }

5邻眷、指定播放源的相關(guān)信息

    // callback
    AURenderCallbackStruct playCallback;
    playCallback.inputProc = PlayCallback;
    playCallback.inputProcRefCon = (__bridge void *)self;
    AudioUnitSetProperty(audioUnit,
                         kAudioUnitProperty_SetRenderCallback,
                         kAudioUnitScope_Input,
                         OUTPUT_BUS,
                         &playCallback,
                         sizeof(playCallback));
    
    
    OSStatus result = AudioUnitInitialize(audioUnit);

6眠屎、給目標(biāo)播放組件輸入播放內(nèi)容


static OSStatus PlayCallback(void *inRefCon,
                             AudioUnitRenderActionFlags *ioActionFlags,
                             const AudioTimeStamp *inTimeStamp,
                             UInt32 inBusNumber,
                             UInt32 inNumberFrames,
                             AudioBufferList *ioData) {
    yourPlayerClass *player = (__bridge yourPlayerClass *)inRefCon;//這里獲取之前初始化時配置給播放組件的類對象
   // 這里是靜音數(shù)據(jù),需要播放更對內(nèi)容可以往ioData->mBuffers輸入數(shù)據(jù)
  for (int iBuffer = 0; iBuffer < ioData->mNumberBuffers; ++iBuffer) {
        memset(ioData->mBuffers[iBuffer].mData, 0, ioData->mBuffers[iBuffer].mDataByteSize);
    }
   return noErr;
  }
}

7肆饶、這里想要補充說明一下
上面的配置和初始中改衩,多次用到了AudioUnitSetProperty,這是一個設(shè)置音頻單元屬性的函數(shù)驯镊,它的幾個參數(shù)如下:


參數(shù)補充.png

上面用到了兩個宏定義 作用于設(shè)置AudioUnit的I/O口設(shè)置

#define INPUT_BUS 1
#define OUTPUT_BUS 0
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葫督,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子板惑,更是在濱河造成了極大的恐慌橄镜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冯乘,死亡現(xiàn)場離奇詭異洽胶,居然都是意外死亡,警方通過查閱死者的電腦和手機裆馒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門姊氓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喷好,你說我怎么就攤上這事翔横。” “怎么了梗搅?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵禾唁,是天一觀的道長。 經(jīng)常有香客問我些膨,道長蟀俊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任订雾,我火速辦了婚禮,結(jié)果婚禮上矛洞,老公的妹妹穿的比我還像新娘洼哎。我一直安慰自己,他們只是感情好沼本,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布噩峦。 她就那樣靜靜地躺著,像睡著了一般抽兆。 火紅的嫁衣襯著肌膚如雪识补。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天辫红,我揣著相機與錄音凭涂,去河邊找鬼祝辣。 笑死,一個胖子當(dāng)著我的面吹牛切油,可吹牛的內(nèi)容都是我干的蝙斜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼澎胡,長吁一口氣:“原來是場噩夢啊……” “哼孕荠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起攻谁,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤稚伍,失蹤者是張志新(化名)和其女友劉穎戚宦,沒想到半個月后槐瑞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁苞,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年那槽,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼沿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡糟趾,死狀恐怖甚牲,靈堂內(nèi)的尸體忽然破棺而出义郑,到底是詐尸還是另有隱情,我是刑警寧澤丈钙,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布非驮,位于F島的核電站,受9級特大地震影響雏赦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜填大,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一俏橘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦靴寂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至虫埂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間掉伏,已是汗流浹背澳窑。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸡捐,地道東北人麻裁。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像煎源,于是被迫代替她去往敵國和親色迂。 傳聞我的和親對象是個殘疾皇子手销,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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