本篇是AudioQueue的官方文檔的筆記。Audio Queue Services可以play和record以下三類任何audio data:
- Linear PCM.
- Any compressed format supported natively on the Apple platform you are developing for.
- Any other format for which a user has an installed codec.
對于最后一種類型,我們可以在使用AudioQueue同時自己將自己需要的format轉(zhuǎn)化成LPCM。AudioQueue是對mic和speaker的高度抽象,同時可以非常簡單的時間音頻codecs。與此同時,它也有一些高級功能,例如多個音頻的同步播放,回放等等。
About Audio Queues
這章會了解到audio queue的功能,結(jié)構(gòu)體,以及內(nèi)部運行的機理。具體的內(nèi)容包括audio queues,audio queue buffers,audio queue會使用到的callback等。還有就是audio queue的狀態(tài)以及參數(shù)。
What Is an Audio Queue?
An audio queue 是iOS中play和record audio的對象.底層是AudioQueueRef。Audio queue可以完成以下工作:
- Connecting to audio hardware
- Managing memory
- Employing codecs, as needed, for compressed audio formats
- Mediating recording or playback
Audio Queue Architecture
Audio queue的具體結(jié)構(gòu)有以下幾個部分構(gòu)成:
- A set of audio queue buffers, each of which is a temporary repository for some audio data
- A buffer queue, an ordered list for the audio queue buffers
- An audio queue callback function, that you write
根據(jù)我們使用audio queue的用途(record or play),具體的結(jié)構(gòu)略有不同,僅僅只是callback函數(shù)函數(shù)的內(nèi)容不同。
Audio Queues for Recording
一個用于record 的audio queue,需要使用AudioQueueNewInput方法創(chuàng)建,它的具體結(jié)構(gòu)如圖:

Audio Queues for Playback
一個用于play的audio queue,需要使用AudioQueueNewOutput函數(shù)創(chuàng)建,

Audio Queue Buffers
audio queue buffer的數(shù)據(jù)結(jié)構(gòu)如下:
typedef struct AudioQueueBuffer {
const UInt32 mAudioDataBytesCapacity;
void *const mAudioData;
UInt32 mAudioDataByteSize;
void *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;
其中mAudioData字段表示這個buffer中的有用數(shù)據(jù)的地址,其他的字段用來輔助audio queue來管理使用這個buffer。一個audio queue可以使用任何數(shù)目的buffers。但是我們一般選擇3個,比較好管理。
Audio queue通過下面的方式管理它們內(nèi)部的buffers:
- An audio queue allocates a buffer when you call the AudioQueueAllocateBuffer function.
- When you release an audio queue by calling the AudioQueueDispose function, the queue releases its buffers.
The Buffer Queue and Enqueuing
buffer queue是由audio buffers組成的,是audio queue中的buffers。我們前面介紹了audio queue是如何使用callback管理內(nèi)部的buffers。不論當前是用于record或者是pleyback,將buffer放到audio queue都是需要我們在callback函數(shù)中去手動調(diào)用的。
The Recording Process

- In step 1 , recording begins. The audio queue fills a buffer with acquired data.
- In step 2, the first buffer has been filled. The audio queue invokes the callback, handing it the full buffer (buffer 1). The callback (step 3) writes the contents of the buffer to an audio file. At the same time, the audio queue fills another buffer (buffer 2) with freshly acquired data.
- In step 4, the callback enqueues the buffer (buffer 1) that it has just written to disk, putting it in line to be filled again. The audio queue again invokes the callback (step 5), handing it the next full buffer (buffer 2). The callback (step 6) writes the contents of this buffer to the audio file. This looping steady state continues until the user stops the recording.
The Playback Process

Controlling the Playback Process
Audio queue buffers在queue是順序播放的,我們可以通theAudioQueueEnqueueBufferWithParameters方法來進行控制
The Audio Queue Callback Function
Audio queue在運行過程中會不斷的調(diào)用callback函數(shù),通常間隔時間和audio queue buffer的大小相關(guān),一般是幾秒一次。
audio queue callback主要任務是將audio queue buffer歸還給audio queue。callback中通過AudioQueueEnqueueBuffer方法將buffer加載到audio queue的最后。在playback中,可以使用AudioQueueEnqueueBufferWithParameters在enqueue的過程中進行更多的控制。
The Recording Audio Queue Callback Function
如果你僅僅使用audio queue去將record的audio data寫入file system,callback的方法實現(xiàn)的原型如下:
AudioQueueInputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs
);
一個recording audio queue會觸發(fā)我們注冊的callback,會在callback的參數(shù)中傳入所有需要的關(guān)于audio data的相關(guān)信息:
- inUserData 是一個自定義的結(jié)構(gòu)體,用來存儲audio queueu以及audio queue buffer的狀態(tài)信息,也包括AudioFileID,audio data format等。
- inAQ 表示哪個audio queue觸發(fā)這個callback。
- inBuffer 是一個audio queue buffer,它的內(nèi)容是由audio queue填充的,內(nèi)部包括最新的audio data。并且這些audio data已經(jīng)根據(jù)初始化時候傳遞的格式參數(shù)格式化好的數(shù)據(jù)。
- inStartTime 表示這個buffer中的第一個采樣的采樣時間點,一般app中不太需要這個參數(shù)。
- inNumberPacketDescriptions 表示inPacketDescs參數(shù)中的packet descriptions的個數(shù)。如果你是錄入VBR format,audio queue就會在callback中提供這個參數(shù),如果是CBR,audio queue就不會使用packet descriptions參數(shù),這個參數(shù)會是NULL。
- inPacketDescs 表示buffer中samples相關(guān)的一系列的packet descriptions。是否設置同上一個參數(shù)。
The Playback Audio Queue Callback Function
這個片段會介紹如果使用playing audio queue,那么callback應該的信息:
AudioQueueOutputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
);
一個playback audio queue會觸發(fā)這個callback,提供一些關(guān)于audio data的有用信息:
- inUserData 見上
- inAQ 表示哪個audio queue觸發(fā)這個callback。
- inBuffer 表示被audio queue設置為空的audio queue buffer,你需要在callback中將其內(nèi)部信息填滿,填充內(nèi)容是你從AudioFile中讀取的audio data。
Using Codecs and Audio Data Formats
我們?nèi)粘J褂肁udio Queue Services時,都會使用codecs(audio data coding/decoding componets)用來在不同audio format之間進行轉(zhuǎn)化。
每個audio queue都有一個audio data format,可以在AudioStreamBasicDescription結(jié)構(gòu)體中得到。當我們在ASBD中指定了mFormatID以后,audio queue在向buffer中填充數(shù)據(jù)時候就會使用相應的codec。同樣如果指定sample rate和channel count,audio queue也會同樣。具體的過程見下圖:

- 第一步中,app會告知audio queue開始record,同時告訴它使用的的data format。
- 第二步中,audio queue將獲取到的new data使用codec轉(zhuǎn)化成目標format。然后audio queue會調(diào)用callback函數(shù),傳入格式化以后的audio data。
- 第三步中,callback函數(shù)會將格式化以后的audio data寫入file中。
整個過程中,callback函數(shù)壓根就不需要知道data fromat是什么。

在播放過程中,正好和錄音過程相反,只需要在創(chuàng)建audio queue時候?qū)ata format告知即可。
Audio Queue Control and State
audio queue在創(chuàng)建和銷毀的過程有一個聲明周期。app需要管理它的聲明周期,控制它的狀態(tài),具體控制狀態(tài)的方法如下:
- Start (
AudioQueueStart).初始化audio queue用來record或者playback。 - Prime (
AudioQueuePrime).對于playback,在調(diào)用AudioQueueStart摯愛去哪調(diào)用確保數(shù)據(jù)可用,這個方法和record沒有關(guān)系。 - Stop (
AudioQueueStop). 調(diào)用以后會重置audio queue,然后會停止record或者playback。在playback應用中,一般在沒有audio data可以播放時候調(diào)用。 - Pause (
AudioQueuePause). 在record或者playback中調(diào)用這個方法不會影響到buffers。如果需要恢復,調(diào)用AudioQueueStart。 - Flush (
AudioQueueFlush). 在enqueue最后一個audio queue buffer以后調(diào)用這個方法,確保所有的數(shù)據(jù)被record或者play(主要是在midst processing的數(shù)據(jù))。 - Reset (
AudioQueueReset). 調(diào)用以后立即停止audio queue,然后將所有的buffers移除,重置所有的DSP狀態(tài)等到。
在調(diào)用AudioQueueStop方法時候有兩種模式:同步和異步。
- Synchronous stopping happens immediately, without regard for previously buffered audio data.
- Asynchronous stopping happens after all queued buffers have been played or recorded.
Recording Audio
當我們的record使用Audio Queue Services,存儲的路徑可以是磁盤上的任何地方,或者網(wǎng)絡,或者內(nèi)存中。這部分內(nèi)容記錄大多數(shù)的使用場景,存儲在磁盤中。
具體的步驟如下:
- 定義一個結(jié)構(gòu)體去存儲狀態(tài),format,文件路徑等信息。
- 完成audio queue callback函數(shù),其中將record以后的數(shù)據(jù)進行存儲。
- 為audio queue buffers計算出合適的大小,并且在file中寫入magic cookies。
- 初始化自定義的結(jié)構(gòu)體
- 創(chuàng)建recording audio queue,然后給它創(chuàng)建3個audio queue buffers,然后創(chuàng)建一個file用來存儲record以后的audio data。
- 啟動audio queue
- 當audio queue停止以后,dispose它以及buffers
具體的實現(xiàn)內(nèi)容可以參考Apple官方文檔:Recording Audio。
Playing Audio
當我們使用Audio Queue Service去play audio時,音頻源文件可以是任何在disk file或者memory中,這部分內(nèi)容是如何用Audio Queue Service播放存儲在disk上的audio file。
具體的步驟如下:
- 定義一個結(jié)構(gòu)體管理Audio queue的狀態(tài),format,file path等
- 完成audio queue callback函數(shù)去進行實際的播放
- 創(chuàng)建一個函數(shù)用來計算最適合的audio queue buffer的大小
- 打開audio file,確定它的audio data format
- 創(chuàng)建audio queue,對它進行配置
- 為audio queue創(chuàng)建buffers,然后啟動audio queue,當播放結(jié)束,callback讓audio queue停止播放
- 銷毀audio queue
具體的實現(xiàn)內(nèi)容可以參考Apple官方文檔:Playing Audio。
可運行的Demo
請參考我的github: https://github.com/brownfeng/AudioQueueServiceDemo