有關(guān)于蘋果的音頻《Audio Queue Services》

簡介

Audio ?Queue 服務(wù)提供了一種更低開銷辽慕,更簡單直接的方式酬核,用于iOS和MacOSX中錄制和播放音頻。這是一種相對會更好用的更值得推薦的音頻播放碉纳、錄制方式勿负。
Audio Queue Services 可以讓你錄制和播放以下的格式:
1、PCM 格式劳曹;
2奴愉、蘋果平臺所支持的所有音頻格式;

這里大概包括

   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',
    kAudioFormatQUALCOMM                = 'Qclp',
    kAudioFormatMPEGLayer1              = '.mp1',
    kAudioFormatMPEGLayer2              = '.mp2',
    kAudioFormatMPEGLayer3              = '.mp3',
    kAudioFormatTimeCode                = 'time',
    kAudioFormatMIDIStream              = 'midi',
    kAudioFormatParameterValueStream    = 'apvs',
    kAudioFormatAppleLossless           = 'alac',
    kAudioFormatMPEG4AAC_HE             = 'aach',
    kAudioFormatMPEG4AAC_LD             = 'aacl',
    kAudioFormatMPEG4AAC_ELD            = 'aace',
    kAudioFormatMPEG4AAC_ELD_SBR        = 'aacf',
    kAudioFormatMPEG4AAC_ELD_V2         = 'aacg',    
    kAudioFormatMPEG4AAC_HE_V2          = 'aacp',
    kAudioFormatMPEG4AAC_Spatial        = 'aacs',
    kAudioFormatAMR                     = 'samr',
    kAudioFormatAMR_WB                  = 'sawb',
    kAudioFormatAudible                 = 'AUDB',
    kAudioFormatiLBC                    = 'ilbc',
    kAudioFormatDVIIntelIMA             = 0x6D730011,
    kAudioFormatMicrosoftGSM            = 0x6D730031,
    kAudioFormatAES3                    = 'aes3',
    kAudioFormatEnhancedAC3             = 'ec-3'

Audio Queue Services是比較高級的框架铁孵,它可以讓你的程序直接使用硬件錄制或播放音頻锭硼,不需要更多的了解硬件接口,也可以讓你在使用編解碼器的過程中蜕劝,無須再去理解編解碼器的工作原理檀头。
Audio Queue 會支持一些比較高級的特性,它提供了精確的的時間控制岖沛,用來支持定時播放和同步暑始。這就可以讓多組的音頻隊列同時播放,并實現(xiàn)音視頻同步婴削。(這個在做游戲聲效的時候非常好用)

Note: Audio Queue Services 是基于M?ac OSX 的Sound Manager 優(yōu)化出來的廊镜,它增加了一些新的特性,比方說:同步唉俗。因為Sound Manager 已經(jīng)在Mac OSXv10.5 被棄用了嗤朴,而且他不支持64位應(yīng)用程序,所以蘋果才有意的推出Audio Queue Services 給開發(fā)人員使用以及為SoundManager做替換互躬。

Audio Queue Services 是一個純C的接口播赁,你可以在Cocoa 應(yīng)用以及在Mac OSX上一樣的使用颂郎,為了幫助Audio Queue Service類的使用吼渡,可通過Core Audio 框架中的C++類來簡化實現(xiàn),但是并非一定得使用這兩個東西的乓序。
Audio Queue Services包括了以下的內(nèi)容:
About Audio Queues 描述了音頻隊列的功能寺酪,架構(gòu)以及內(nèi)部的工作原理坎背。
Recording Audio 介紹如何進行錄音。
Playing Audio 介紹了如何進行播放寄雀。

關(guān)于Audio Queues

這里會講述關(guān)于音頻隊列的功能得滤、架構(gòu)以及內(nèi)部工作過程。接下來會引入音頻隊列盒犹、音頻隊列緩沖以及音頻隊列的回調(diào)方法來進行錄音或播放音頻懂更。你需要了解音頻隊列的狀態(tài)以及相關(guān)的設(shè)置參數(shù)。
Audio queue 是iOS和Mac OS X通用的一個錄音和播放音頻的軟件對象急膀,它由AudioQueue.h頭文件中聲明的AudioQueueRef數(shù)據(jù)類型表示沮协。
一個AudioQueue會執(zhí)行以下的內(nèi)容:
1、連接音頻采集硬件模塊
2.卓嫂、管理音頻采集所需的內(nèi)存
3慷暂、根據(jù)需求采用壓縮音頻格式以及選擇相關(guān)編解碼器
4、錄音和音頻播放的中轉(zhuǎn)站
你可以通過使用Core Audio 相關(guān)接口晨雳,以及少量的代碼來創(chuàng)建相關(guān)的Audio Queue 來進行錄音或者音頻回放行瑞。

音頻隊列的架構(gòu)

所有的音頻隊列都具有相同的通用架構(gòu),主要包括以下內(nèi)容:
1餐禁、一組音頻隊列緩沖區(qū)audio queue buffers,作為音頻數(shù)據(jù)的臨時存儲血久;
2、一個音頻隊列buffer queue坠宴,用于控制音頻隊列緩沖的數(shù)據(jù)洋魂;
3、一個音頻隊列回調(diào)函數(shù)喜鼓,可用于寫入副砍、發(fā)送等操作。
這個架構(gòu)決定了音頻隊列是用于錄制還是播放庄岖,會體現(xiàn)音頻隊列的輸入豁翎、輸出以及回調(diào)函數(shù)的差異。

用于錄音的音頻隊列

使用AudioQueueNewInput可創(chuàng)建一個用于錄音的音頻隊列隅忿,其工作過程如下圖所示:

recording_architecture

錄音音頻隊列的輸出端是通過回調(diào)函數(shù)實現(xiàn)的心剥,當回調(diào)從音頻隊列接收到新的音頻數(shù)據(jù)時,就可以從緩沖區(qū)讀取音頻數(shù)據(jù)寫到磁盤中背桐,或者發(fā)送出去优烧。

總的來說就是通過麥克風采集音頻數(shù)據(jù),通過隊列的形式链峭,回調(diào)給上層使用畦娄。

用于播放的音頻隊列

使用AudioQueueNewOutput可創(chuàng)建一個用于播放的音頻隊列,其工作過程如下圖所示:

playback_architecture

在播放隊列中,回調(diào)函數(shù)是作為一個輸入端熙卡,它從磁盤或者其他地方獲取到音頻數(shù)據(jù)杖刷,然后將其闖入到音頻隊列,通過隊列緩沖播放驳癌。

音頻隊列緩沖區(qū)

Audio queue buffer 是AudioQueue.h類里面聲明的AudioQueueBuffer類型的結(jié)構(gòu)體滑燃。

typedef struct AudioQueueBuffer {
    const UInt32   mAudioDataBytesCapacity;
    void *const    mAudioData;
    UInt32         mAudioDataByteSize;
    void           *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;

其中mAudioData屬性是指向緩沖區(qū)本身(一個正在播放或者錄音的音頻數(shù)據(jù)的臨時緩存內(nèi)存塊)。其他字段中的信息是用于Audio Queue管理Buffer的颓鲜。
開發(fā)者可以隨意定制音頻隊列的緩沖區(qū)數(shù)量表窘,一般我們使用三個就滿足了。這樣就可以讓一個用來讀甜滨,一個用來寫入蚊丐,另一個就可以用來緩存新的音頻數(shù)據(jù)。

音頻隊列對其緩沖區(qū)的內(nèi)存管理
1艳吠、當調(diào)用AudioQueueAllocateBuffer函數(shù)時麦备,音頻隊列會分配一個緩沖區(qū)。
2昭娩、當通過調(diào)用AudioQueueDispose函數(shù)釋放音頻隊列時凛篙,隊列釋放其緩沖區(qū)。

這里值得一提的是栏渺,無論錄音操作還是音頻播放操作呛梆,音頻隊列的入隊和出隊都是在回調(diào)函數(shù)實現(xiàn)的。

設(shè)置音頻隊列緩沖區(qū)大小

DeriveBufferSize (                               
    aqData.mQueue,                               // 當前設(shè)置緩沖區(qū)大小的音頻隊列
    aqData.mDataFormat,                          // 當前錄制的文件的音頻數(shù)據(jù)格式磕诊。
    0.5,                                         // 每個音頻隊列緩沖區(qū)應(yīng)該保持的音頻的秒數(shù)
    &aqData.bufferByteSize                       // 在輸出時填物,每個音頻隊列緩沖區(qū)的大小(以字節(jié)為單位)霎终。
);

錄音過程

1滞磺、開始錄音后,音頻數(shù)據(jù)從設(shè)備麥克風采集音頻數(shù)據(jù)然后填入音頻隊列緩沖器中莱褒;
2击困、當?shù)谝粋€緩沖區(qū)滿了,音頻隊列就回調(diào)給上層广凸;
3阅茶、把回調(diào)的音頻數(shù)據(jù)寫入磁盤中;
4谅海、回調(diào)完成后的緩沖區(qū)會繼續(xù)被音頻隊列重新使用脸哀;
5、音頻隊列傳遞完整緩沖區(qū)然后回調(diào)并填充另一個緩沖區(qū)扭吁;
整體過程如下圖所示:

recording_callback_function

錄音音頻隊列的回調(diào)聲明

static void HandleInputBuffer (
    void                                *aqData,             // 1
    AudioQueueRef                       inAQ,                // 2
    AudioQueueBufferRef                 inBuffer,            // 3
    const AudioTimeStamp                *inStartTime,        // 4
    UInt32                              inNumPackets,        // 5
    const AudioStreamPacketDescription  *inPacketDesc        // 6
)

1撞蜂、通常aqData時報喊音頻隊列的狀態(tài)數(shù)據(jù)的自定義數(shù)據(jù)白筹;
2、擁有當前回調(diào)的音頻隊列谅摄。
3、音頻隊列緩沖區(qū)包含要錄制的傳入音頻數(shù)據(jù)系馆。
4送漠、音頻隊列緩沖區(qū)中第一個采樣的采樣時間(簡單記錄是不需要的)。
5由蘑、AudioStreamPacketDescription 參數(shù)中的數(shù)據(jù)包數(shù)闽寡,當值是0時表示CBR數(shù)據(jù)。
6尼酿、編碼器為緩沖區(qū)中的數(shù)據(jù)包生成對應(yīng)的壓縮音頻數(shù)據(jù)格式描述內(nèi)容爷狈。

static void HandleInputBuffer (
    void                                 *aqData,
    AudioQueueRef                        inAQ,
    AudioQueueBufferRef                  inBuffer,
    const AudioTimeStamp                 *inStartTime,
    UInt32                               inNumPackets,
    const AudioStreamPacketDescription   *inPacketDesc
) {
    AQRecorderState *pAqData = (AQRecorderState *) aqData;               // 1在實例化時提供給音頻隊列對象的定制結(jié)構(gòu),包括表示要記錄的音頻文件的音頻文件對象以及各種狀態(tài)數(shù)據(jù)裳擎。
 
    if (inNumPackets == 0 &&                                             // 2 如果音頻隊列緩沖區(qū)包含CBR數(shù)據(jù)涎永,則計算緩沖區(qū)中的數(shù)據(jù)包數(shù)。 該數(shù)字等于緩沖區(qū)中數(shù)據(jù)的總字節(jié)數(shù)除以(常數(shù))每個數(shù)據(jù)包的字節(jié)數(shù)鹿响。 對于VBR數(shù)據(jù)羡微,音頻隊列在調(diào)用回調(diào)時提供緩沖區(qū)中的數(shù)據(jù)包數(shù)。
          pAqData->mDataFormat.mBytesPerPacket != 0)
       inNumPackets =
           inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;
 
    if (AudioFileWritePackets (                                          // 3 將緩沖區(qū)的內(nèi)容寫入音頻數(shù)據(jù)文件惶我。
            pAqData->mAudioFile,
            false,
            inBuffer->mAudioDataByteSize,
            inPacketDesc,
            pAqData->mCurrentPacket,
            &inNumPackets,
            inBuffer->mAudioData
        ) == noErr) {
            pAqData->mCurrentPacket += inNumPackets;                     // 4 如果成功寫入音頻數(shù)據(jù)妈倔,則將音頻數(shù)據(jù)文件的數(shù)據(jù)包索引增加到準備寫入下一個緩沖區(qū)的音頻數(shù)據(jù)。
    }
   if (pAqData->mIsRunning == 0)                                         // 5 如果音頻隊列已停止绸贡,則返回
      return;
 
    AudioQueueEnqueueBuffer (                                            // 6 將內(nèi)容剛被寫入音頻文件的音頻隊列緩沖區(qū)排隊盯蝴。
        pAqData->mQueue,
        inBuffer,
        0,
        NULL
    );
}

設(shè)置用于錄制的音頻格式

AQRecorderState aqData;                                       // 1創(chuàng)建AQRecorderState自定義結(jié)構(gòu)的實例
 
aqData.mDataFormat.mFormatID         = kAudioFormatLinearPCM; // 2 將音頻數(shù)據(jù)格式類型定義為線性PCM
aqData.mDataFormat.mSampleRate       = 44100.0;               // 3 將采樣率定義為44.1 kHz。
aqData.mDataFormat.mChannelsPerFrame = 2;                     // 4將通道數(shù)定義為2听怕。
aqData.mDataFormat.mBitsPerChannel   = 16;                    // 5 將每個通道的位深度定義為16捧挺。
aqData.mDataFormat.mBytesPerPacket   =                        // 6將每個數(shù)據(jù)包的字節(jié)數(shù)和每幀的字節(jié)數(shù)定義為4(即每個通道2個byte)
   aqData.mDataFormat.mBytesPerFrame =
      aqData.mDataFormat.mChannelsPerFrame * sizeof (SInt16);
aqData.mDataFormat.mFramesPerPacket  = 1;                     // 7 將每個數(shù)據(jù)包的幀數(shù)定義為1
 
AudioFileTypeID fileType             = kAudioFileAIFFType;    // 8 將文件類型定義為AIFF,請參閱AudioFile.h頭文件中的音頻文件類型枚舉尿瞭,以獲取可用文件類型的完整列表松忍。
aqData.mDataFormat.mFormatFlags =                             // 9 設(shè)置指定文件類型所需的格式標志。
    kLinearPCMFormatFlagIsBigEndian
    | kLinearPCMFormatFlagIsSignedInteger
    | kLinearPCMFormatFlagIsPacked;

開始錄音筷厘、停止錄音
aqData.mCurrentPacket = 0;                           // 1
aqData.mIsRunning = true;                            // 2
 //開始錄音
AudioQueueStart (                                    // 3
    aqData.mQueue,                                   // 4
    NULL                                             // 5
);
// 停止錄音
AudioQueueStop (                                     // 6
    aqData.mQueue,                                   // 7
    true                                             // 8
);
 
aqData.mIsRunning = false;    

后續(xù)會繼續(xù)更新音頻播放相關(guān)的內(nèi)容以及音頻轉(zhuǎn)碼內(nèi)容

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸣峭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酥艳,更是在濱河造成了極大的恐慌摊溶,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件充石,死亡現(xiàn)場離奇詭異莫换,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門拉岁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坷剧,“玉大人,你說我怎么就攤上這事喊暖”蛊螅” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵陵叽,是天一觀的道長狞尔。 經(jīng)常有香客問我,道長巩掺,這世上最難降的妖魔是什么偏序? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮胖替,結(jié)果婚禮上研儒,老公的妹妹穿的比我還像新娘。我一直安慰自己独令,他們只是感情好殉摔,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著记焊,像睡著了一般逸月。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遍膜,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天碗硬,我揣著相機與錄音,去河邊找鬼瓢颅。 笑死恩尾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的挽懦。 我是一名探鬼主播翰意,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼信柿!你這毒婦竟也來了冀偶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤渔嚷,失蹤者是張志新(化名)和其女友劉穎进鸠,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體形病,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡客年,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年霞幅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片量瓜。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡司恳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绍傲,到底是詐尸還是另有隱情扔傅,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布唧取,位于F島的核電站,受9級特大地震影響划提,放射性物質(zhì)發(fā)生泄漏枫弟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一鹏往、第九天 我趴在偏房一處隱蔽的房頂上張望淡诗。 院中可真熱鬧,春花似錦伊履、人聲如沸韩容。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽群凶。三九已至,卻和暖如春哄辣,著一層夾襖步出監(jiān)牢的瞬間请梢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工力穗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留毅弧,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓当窗,卻偏偏與公主長得像够坐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子崖面,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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