簡介
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)建一個用于錄音的音頻隊列隅忿,其工作過程如下圖所示:
錄音音頻隊列的輸出端是通過回調(diào)函數(shù)實現(xiàn)的心剥,當回調(diào)從音頻隊列接收到新的音頻數(shù)據(jù)時,就可以從緩沖區(qū)讀取音頻數(shù)據(jù)寫到磁盤中背桐,或者發(fā)送出去优烧。
總的來說就是通過麥克風采集音頻數(shù)據(jù),通過隊列的形式链峭,回調(diào)給上層使用畦娄。
用于播放的音頻隊列
使用AudioQueueNewOutput可創(chuàng)建一個用于播放的音頻隊列,其工作過程如下圖所示:
在播放隊列中,回調(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ū)扭吁;
整體過程如下圖所示:
錄音音頻隊列的回調(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)容