版本記錄
| 版本號 | 時間 |
|---|---|
| V1.0 | 2017.12.27 |
前言
ios系統(tǒng)中有很多方式可以播放音頻文件,這里我們就詳細的說明下播放音樂文件的原理和實例。感興趣的可以看我寫的上面幾篇。
1. 幾種播放音頻文件的方式(一) —— 播放本地音樂
2. 幾種播放音頻文件的方式(二) —— 音效播放
3. 幾種播放音頻文件的方式(三) —— 網(wǎng)絡(luò)音樂播放
4. 幾種播放音頻文件的方式(四) —— 音頻隊列服務(wù)(Audio Queue Services)(一)
5. 幾種播放音頻文件的方式(五) —— 音頻隊列服務(wù)(Audio Queue Services)簡介(二)
About Audio Queues - 關(guān)于音頻隊列
在本章中,您將了解音頻隊列的功能,體系結(jié)構(gòu)和內(nèi)部工作原理。 您將了解音頻隊列audio queues,音頻隊列緩沖區(qū)audio queue buffers以及音頻隊列用于錄制或回放的回調(diào)函數(shù)callback。 您還可以了解有關(guān)音頻隊列狀態(tài)和參數(shù)的信息。 到本章結(jié)束時,您將獲得有效使用此技術(shù)所需的概念性理解。
What Is an Audio Queue? - 音頻隊列是什么?
音頻隊列Audio Queue是用于在iOS或Mac OS X中錄制或播放音頻的軟件對象。它由AudioQueue.h頭文件中聲明的AudioQueueRef不透明數(shù)據(jù)類型表示。
音頻隊列的工作是:
- 連接到音頻硬件
- 管理內(nèi)存
- 根據(jù)需要使用編解碼器壓縮音頻格式
- 調(diào)解錄制或播放
您可以將音頻隊列與其他Core Audio接口以及相對較少量的自定義代碼一起使用,以在應(yīng)用程序中創(chuàng)建完整的數(shù)字音頻錄制或播放解決方案。
Audio Queue Architecture - 音頻隊列結(jié)構(gòu)
下面我們就看一下音頻隊列的結(jié)構(gòu)。
所有音頻隊列具有相同的總體結(jié)構(gòu),由以下部分組成:
- 一組音頻隊列緩沖區(qū)
audio queue buffers,每個緩沖區(qū)都是一些音頻數(shù)據(jù)的臨時存儲庫 - 緩沖隊列
buffer queue,音頻隊列緩沖區(qū)audio queue buffers的有序列表 - 一個你寫的音頻隊列回調(diào)函數(shù)
audio queue callback
結(jié)構(gòu)的改變?nèi)Q于音頻隊列是用于記錄還是回放。 不同點在于音頻隊列如何連接其輸入和輸出,以及回調(diào)函數(shù)的作用。
Audio Queues for Recording - 音頻隊列用于記錄
使用AudioQueueNewInput函數(shù)創(chuàng)建的錄制音頻隊列的結(jié)構(gòu)如圖1-1所示。

記錄音頻隊列的輸入側(cè)通常連接到外部音頻硬件,如麥克風。例如,在iOS中,音頻來自用戶內(nèi)置麥克風或耳機麥克風所連接的設(shè)備。在Mac OS X的默認情況下,音頻來自系統(tǒng)偏好設(shè)置中由用戶設(shè)置的系統(tǒng)的默認音頻輸入設(shè)備。
錄制音頻隊列的輸出端使用您編寫的回調(diào)函數(shù)。在錄制到磁盤時,回調(diào)會將新音頻數(shù)據(jù)的緩沖區(qū)(從音頻隊列接收)寫入音頻文件。但是,記錄音頻隊列可以以其他方式使用。例如,您的回調(diào)可以將音頻數(shù)據(jù)直接提供給您的應(yīng)用程序,而不是將其寫入磁盤。
您將在 The Recording Audio Queue Callback Function中了解有關(guān)此回調(diào)的更多信息。
每個音頻隊列(無論是用于記錄還是播放)都有一個或多個音頻隊列緩沖區(qū)audio queue buffers。這些緩沖區(qū)按照一個稱為緩沖隊列buffer queue的特定順序排列。在圖中,音頻隊列緩沖區(qū)按照它們被填充的順序進行編號 - 這與它們被交給回調(diào)的順序是相同的。您將了解音頻隊列如何在The Buffer Queue and Enqueuing中使用其緩沖區(qū)buffers。
Audio Queues for Playback - 音頻隊列回放
播放音頻隊列(使用AudioQueueNewOutput函數(shù)創(chuàng)建)的結(jié)構(gòu)如圖1-2所示。

在回放音頻隊列中,回調(diào)位于輸入端。 該回調(diào)負責從磁盤(或其他來源)獲取音頻數(shù)據(jù),并將其交給音頻隊列。 回放回調(diào)還會告訴音頻隊列在沒有更多數(shù)據(jù)可以播放時停止播放。 您將在The Playback Audio Queue Callback Function中了解有關(guān)此回調(diào)的更多信息。
回放音頻隊列的輸出通常連接到外部音頻硬件,如揚聲器。 在iOS中,音頻會轉(zhuǎn)到用戶選擇的設(shè)備上,例如接收器或耳機。 在Mac OS X的默認情況下,音頻將轉(zhuǎn)到系統(tǒng)偏好設(shè)置中由用戶設(shè)置的系統(tǒng)的默認音頻輸出設(shè)備。
Audio Queue Buffers - 音頻隊列緩沖
音頻隊列緩沖區(qū)是AudioQueue.h頭文件中聲明的AudioQueueBuffer類型的數(shù)據(jù)結(jié)構(gòu):
typedef struct AudioQueueBuffer {
const UInt32 mAudioDataBytesCapacity;
void *const mAudioData;
UInt32 mAudioDataByteSize;
void *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;
在代碼清單中突出顯示的mAudioData字段指向緩沖區(qū)本身:一塊內(nèi)存,用作正在播放或錄音的音頻數(shù)據(jù)的瞬時塊的容器。其他字段中的信息有助于音頻隊列audio queue管理緩沖區(qū)buffer。
音頻隊列可以使用任意數(shù)量的緩沖區(qū)。你的應(yīng)用程序指定多少。一個典型的數(shù)字是三個。這使得一個用于寫入磁盤,而另一個正在填充新的音頻數(shù)據(jù)。如果需要補償磁盤I / O延遲等問題,那么第三個緩沖區(qū)就有用了。Figure 1-3說明了這一點。
音頻隊列為其緩沖區(qū)執(zhí)行內(nèi)存管理。
- 當您調(diào)用AudioQueueAllocateBuffer函數(shù)時,音頻隊列將分配一個緩沖區(qū)。
- 通過調(diào)用AudioQueueDispose函數(shù)釋放音頻隊列時,隊列釋放緩沖區(qū)。
這提高了您添加到應(yīng)用程序的錄制和播放功能的穩(wěn)健性。它還有助于優(yōu)化資源使用。
有關(guān)AudioQueueBuffer數(shù)據(jù)結(jié)構(gòu)的完整說明,請參閱Audio Queue Services Reference。
The Buffer Queue and Enqueuing - 緩沖隊列和入隊
緩沖隊列就是給音頻隊列,實際上是音頻隊列服務(wù),他們的名字的對象。 您在Audio Queue Architecture中認識了緩沖區(qū)隊列 —— 一個有序的緩沖區(qū)列表。 在這里,您將了解到音頻隊列對象與回調(diào)函數(shù)在錄制或回放過程中如何管理緩沖區(qū)隊列。 尤其是,您了解入隊enqueuing,將音頻隊列緩沖區(qū)添加到緩沖區(qū)隊列。 無論您正在執(zhí)行錄制還是回放,入隊都是回調(diào)執(zhí)行的任務(wù)。
1. The Recording Process - 錄音過程
錄制時,一個音頻隊列緩沖區(qū)正在填充從輸入設(shè)備(如麥克風)獲取的音頻數(shù)據(jù)。 緩沖區(qū)隊列中剩余的緩沖區(qū)排列在當前緩沖區(qū)后面,等待依次填充音頻數(shù)據(jù)。
音頻隊列按已獲取音頻數(shù)據(jù)的填充緩沖區(qū)的順序?qū)⑺鼈儌鬟f給您的回調(diào)。 圖1-3說明了使用音頻隊列時錄制的工作原理。

在圖1-3的步驟1中,開始記錄。 音頻隊列用獲取的數(shù)據(jù)填充緩沖區(qū)。
在第2步中,第一個緩沖區(qū)已被填充。 音頻隊列調(diào)用回調(diào)函數(shù),將其交給完整的緩沖區(qū)(緩沖區(qū)1)。 回調(diào)(步驟3)將緩沖區(qū)的內(nèi)容寫入音頻文件。 同時,音頻隊列用新獲取的數(shù)據(jù)填充另一個緩沖區(qū)(緩沖區(qū)2)。
在第4步中,回調(diào)將剛剛寫入磁盤的緩沖區(qū)(緩沖區(qū)1)排入隊列,以便再次進行填充。 音頻隊列再次調(diào)用回調(diào)(步驟5),把下一個完整的緩沖區(qū)(緩沖區(qū)2)交給它。 回調(diào)(第6步)將此緩沖區(qū)的內(nèi)容寫入音頻文件。 這種循環(huán)穩(wěn)定狀態(tài)一直持續(xù)到用戶停止記錄。
2. The Playback Process - 播放過程
播放時,一個音頻隊列緩沖區(qū)正在發(fā)送到輸出設(shè)備,如揚聲器。 緩沖區(qū)隊列中剩余的緩沖區(qū)排列在當前緩沖區(qū)后面,等待依次播放。
音頻隊列按照播放的順序?qū)⒉シ诺囊纛l數(shù)據(jù)緩沖區(qū)傳遞到您的回調(diào)。 回調(diào)會將新的音頻數(shù)據(jù)讀入緩沖區(qū),然后將其排入隊列。 圖1-4說明了使用音頻隊列時的播放方式。

在圖1-4的步驟1中,應(yīng)用程序啟動播放音頻隊列。 應(yīng)用程序為每個音頻隊列緩沖區(qū)調(diào)用一次回調(diào)函數(shù),將其填充并添加到緩沖區(qū)隊列中。 啟動確保播放可以在您的應(yīng)用程序調(diào)用AudioQueueStart函數(shù)時立即啟動(步驟2)。
在步驟3中,音頻隊列發(fā)送第一個緩沖區(qū)(緩沖區(qū)1)輸出。
一旦第一個緩沖器被播放,回放音頻隊列就進入循環(huán)穩(wěn)定狀態(tài)。 音頻隊列開始播放下一個緩沖區(qū)(緩沖區(qū)2,步驟4)并調(diào)用回調(diào)(步驟5),將剛剛播放的緩沖區(qū)(緩沖區(qū)1)傳送給它。 回調(diào)(第6步)從音頻文件中填充緩沖區(qū),然后將其排入隊列進行回放。
3. Controlling the Playback Process - 控制播放過程
音頻隊列緩沖區(qū)總是以它們?nèi)腙牭捻樞虿シ拧?但是,音頻隊列服務(wù)使用AudioQueueEnqueueBufferWithParameters函數(shù)為播放過程提供了一些控制。 這個功能可以讓你:
- 為緩沖區(qū)設(shè)置精確的播放時間。 這可以讓你支持同步。
- 修剪音頻隊列緩沖區(qū)開始或結(jié)束處的幀。 這可以讓你刪除前導(dǎo)或尾隨靜默。
- 以緩沖區(qū)的粒度設(shè)置回放增益。
有關(guān)設(shè)置回放增益的更多信息,請參閱Audio Queue Parameters。 有關(guān)AudioQueueEnqueueBufferWithParameters函數(shù)的完整說明,請參閱 Audio Queue Services Reference。
The Audio Queue Callback Function - 音頻隊列回調(diào)函數(shù)
通常,使用音頻隊列服務(wù)的大部分編程工作包括編寫音頻隊列回調(diào)函數(shù)。
在錄制或回放過程中,音頻隊列回調(diào)函數(shù)由擁有它的音頻隊列重復(fù)調(diào)用。 兩次調(diào)用之間的時間取決于音頻隊列緩沖區(qū)的容量,一般范圍從半秒到幾秒。
無論是用于記錄還是回放,音頻隊列回調(diào)的一個責任是將音頻隊列緩沖audio queue buffers返回到緩沖區(qū)隊列buffer queue。 回調(diào)函數(shù)使用AudioQueueEnqueueBuffer函數(shù)將緩沖區(qū)添加到緩沖區(qū)隊列的末尾。 對于播放,如果需要更多控制,則可以使用AudioQueueEnqueueBufferWithParameters函數(shù),如Controlling the Playback Process中所述。
1. The Recording Audio Queue Callback Function - 錄制音頻隊列回調(diào)函數(shù)
本節(jié)介紹將音頻錄制到磁盤文件的常見情況而寫的回調(diào)。 以下是AudioQueue.h頭文件中聲明的錄音音頻隊列回調(diào)的原型:
AudioQueueInputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs
);
記錄音頻隊列在調(diào)用回調(diào)函數(shù)時提供了回調(diào)函數(shù)將下一組音頻數(shù)據(jù)寫入音頻文件的所有內(nèi)容:
-
inUserData,通常情況下,你已經(jīng)設(shè)置了以包含音頻隊列代表你對文件的寫入狀態(tài)信息和其緩沖區(qū),音頻文件對象(類型AudioFileID的)的自定義結(jié)構(gòu),以及音頻數(shù)據(jù)格式的信息文件。 -
inAQ是調(diào)用回調(diào)的音頻隊列。 -
inBuffer是一個音頻隊列緩沖區(qū),由音頻隊列新填充,包含回調(diào)函數(shù)需要寫入磁盤的新數(shù)據(jù)。數(shù)據(jù)已根據(jù)您在自定義結(jié)構(gòu)中指定的格式(在inUserData參數(shù)中傳遞)格式化。有關(guān)詳情,請參閱Using Codecs and Audio Data Formats。 -
inStartTime是緩沖區(qū)中第一個采樣的采樣時間。對于基本錄音,你的callback不使用此參數(shù)。 -
inNumberPacketDescriptions是inPacketDescs參數(shù)中的數(shù)據(jù)包描述的數(shù)量。如果您正在錄制為VBR(可變比特率)格式,則音頻隊列會將此參數(shù)的值提供給您的callback,然后將該值傳遞AudioFileWritePackets函數(shù)。 CBR(恒定比特率)格式不使用數(shù)據(jù)包描述。對于CBR記錄,音頻隊列將此設(shè)置和inPacketDescs參數(shù)設(shè)置為NULL。 -
inPacketDescs是與緩沖區(qū)中的樣本對應(yīng)的一組數(shù)據(jù)描述。同樣,音頻隊列為此參數(shù)提供值,如果音頻數(shù)據(jù)是在VBR的格式,你的callback將其傳遞到AudioFileWritePackets函數(shù)(在AudioFile.h頭文件中聲明)。
有關(guān)錄音callback的更多信息,請參閱本文檔中的Recording Audio,并參閱Audio Queue Services Reference。
2. The Playback Audio Queue Callback Function - 播放音頻回調(diào)函數(shù)
本節(jié)介紹您為從磁盤文件播放音頻的常見情況而寫的回調(diào)。 以下是AudioQueue.h頭文件中聲明的回放音頻隊列回調(diào)的原型:
AudioQueueOutputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
);
回放音頻隊列在調(diào)用回調(diào)函數(shù)時提供回調(diào)需要從音頻文件讀取下一組音頻數(shù)據(jù)的內(nèi)容:
-
inUserData,通常情況下,你已經(jīng)設(shè)置了以包含音頻隊列代表你對文件的寫入狀態(tài)信息和其緩沖區(qū),音頻文件對象(類型AudioFileID的)的自定義結(jié)構(gòu),以及音頻數(shù)據(jù)格式的信息文件。在播放音頻隊列的情況下,您的回調(diào)會使用此結(jié)構(gòu)中的字段跟蹤當前數(shù)據(jù)包索引。 -
inAQ是調(diào)用callback的音頻隊列。 -
inBuffer是音頻隊列緩沖區(qū),由音頻隊列提供,您的回調(diào)函數(shù)將填充從正在播放的文件中讀取的下一組數(shù)據(jù)。
如果您的應(yīng)用程序正在播放VBR數(shù)據(jù),則回調(diào)callback需要獲取正在讀取的音頻數(shù)據(jù)的數(shù)據(jù)包信息。它通過調(diào)用AudioFile.h頭文件中聲明AudioFileReadPackets函數(shù)來完成此操作。該回調(diào)然后將包信息放置在自定義數(shù)據(jù)結(jié)構(gòu)中以使其可用于回放音頻隊列。
有關(guān)回放回調(diào)callback的更多信息,請參閱本文檔中的Playing Audio,并參閱Audio Queue Services Reference。
Using Codecs and Audio Data Formats - 使用編解碼器和音頻數(shù)據(jù)格式
音頻隊列服務(wù)使用編解碼器(音頻數(shù)據(jù)編碼/解碼組件)根據(jù)需要在音頻格式之間進行轉(zhuǎn)換。 您的錄制或回放應(yīng)用程序可以使用任何已安裝編解碼器的音頻格式。 您不需要編寫自定義代碼來處理各種音頻格式。 具體來說,你的回調(diào)不需要知道數(shù)據(jù)格式。
這里就是如何工作的。 每個音頻隊列都有一個音頻數(shù)據(jù)格式,以AudioStreamBasicDescription結(jié)構(gòu)表示。 當您在結(jié)構(gòu)體的mFormatID字段中指定格式時,音頻隊列使用適當?shù)木幗獯a器。 然后指定采樣率和通道數(shù),這就是它的全部。 您將看到在Recording Audio和Playing Audio中設(shè)置音頻數(shù)據(jù)格式的示例。
錄制音頻隊列使用已安裝的編解碼器,如圖1-5所示。

在圖1-5的第1步中,你的應(yīng)用程序告訴一個音頻隊列開始記錄,并告訴它要使用的數(shù)據(jù)格式。 在步驟2中,音頻隊列獲取新的音頻數(shù)據(jù),并使用編解碼器根據(jù)您指定的格式進行轉(zhuǎn)換。 音頻隊列然后調(diào)用回調(diào)函數(shù),將緩沖區(qū)中包含適當格式的音頻數(shù)據(jù)。 在第3步中,您的回調(diào)會將格式化的音頻數(shù)據(jù)寫入磁盤。 同樣,你的回調(diào)不需要知道數(shù)據(jù)格式。
播放音頻隊列使用已安裝的編解碼器,如圖1-6所示。

在圖1-6的第1步中,你的應(yīng)用程序通知一個音頻隊列開始播放,并且還告訴它要播放的音頻文件中包含的數(shù)據(jù)格式。在步驟2中,音頻隊列調(diào)用您的回調(diào),它從音頻文件中讀取數(shù)據(jù)?;卣{(diào)將原始格式的數(shù)據(jù)轉(zhuǎn)交給音頻隊列。在步驟3中,音頻隊列使用適當?shù)木幗獯a器,然后將音頻發(fā)送到目的地。
音頻隊列可以使用任何已安裝的編解碼器,無論是原生的Mac OS X還是由第三方提供。要指定要使用的編解碼器,請將其四個字符的編碼ID提供給音頻隊列的AudioStreamBasicDescription結(jié)構(gòu)。您將在Recording Audio中看到這個例子。
Mac OS X包含各種各樣的音頻編碼解碼器,如CoreAudioTypes.h頭文件中的格式ID枚舉所示,以及 Core Audio Data Types Reference中所述。您可以使用Audio Toolbox Framework中的AudioFormat.h頭文件中的接口來確定系統(tǒng)上可用的編解碼器。您可以使用Fiendishthngs應(yīng)用程序在系統(tǒng)上顯示編解碼器,可在http://developer.apple.com/samplecode/Fiendishthngs/上以示例代碼的形式獲得。
Audio Queue Control and State - 音頻隊列控制和狀態(tài)
音頻隊列在創(chuàng)建和銷毀之間有一個生命周期。你的應(yīng)用程序管理這個生命周期,并使用AudioQueue.h頭文件中聲明的六個函數(shù)來控制音頻隊列的狀態(tài):
-
Start(AudioQueueStart)。調(diào)用開始錄制或播放。 -
Prime(AudioQueuePrime)。要進行播放,請在調(diào)用AudioQueueStart之前調(diào)用,以確保立即有數(shù)據(jù)可供音頻隊列播放。此功能與錄音無關(guān)。 -
Stop(AudioQueueStop)。調(diào)用以重置音頻隊列(請參閱下面的說明AudioQueueReset),然后停止錄制或播放。播放音頻隊列回調(diào)callback在沒有更多數(shù)據(jù)播放時調(diào)用此函數(shù)。 -
Pause(AudioQueuePause)。調(diào)用暫停錄音或播放而不影響緩沖區(qū)或重置音頻隊列。要恢復(fù),請調(diào)用AudioQueueStart函數(shù)。 -
Flush(AudioQueueFlush)。排隊最后一個音頻隊列緩沖區(qū)后調(diào)用,以確保所有緩沖的數(shù)據(jù)以及處理過程中的所有音頻數(shù)據(jù)都被記錄或播放。 -
Reset(AudioQueueReset)。調(diào)用立即靜音音頻隊列,刪除以前計劃使用的所有緩沖區(qū),并重置所有解碼器和DSP狀態(tài)。
您可以在同步或異步模式下使用AudioQueueStop功能:
-
Synchronous,停止立即發(fā)生,不考慮以前緩沖的音頻數(shù)據(jù)。 -
Asynchronous,在所有排隊的緩沖區(qū)被播放或記錄之后發(fā)生異步停止。
有關(guān)每個功能的完整說明,請參閱Audio Queue Services Reference,其中包括關(guān)于音頻隊列的同步停止和異步停止的更多信息。
Audio Queue Parameters - 音頻隊列參數(shù)
音頻隊列具有可調(diào)參數(shù)設(shè)置稱為parameters。每個參數(shù)都有一個枚舉常量作為鍵,一個浮點數(shù)作為其值。參數(shù)通常用于回放,而不是記錄。
在Mac OS X v10.5中,唯一可用的音頻隊列參數(shù)用于增益。此參數(shù)的值是使用kAudioQueueParam_Volume常量設(shè)置或檢索的,其可用范圍從0.0到單位增益為1.0。
您的應(yīng)用程序可以通過兩種方式設(shè)置音頻隊列參數(shù)
- 每個音頻隊列
Per audio queue,使用AudioQueueSetParameter函數(shù)。這使您可以直接更改音頻隊列的設(shè)置。這些變化立即生效。 - 每個音頻隊列緩沖區(qū)
Per audio queue buffer,使用AudioQueueEnqueueBufferWithParameters函數(shù)。這使您可以分配音頻隊列設(shè)置,實際上,您在排入音頻隊列緩沖區(qū)時會將其設(shè)置為音頻隊列緩沖區(qū)。音頻隊列緩沖區(qū)開始播放時,這些更改才會生效。
在這兩種情況下,音頻隊列的參數(shù)設(shè)置都會一直有效,直到您更改它們。
您可以隨時使用AudioQueueGetParameter函數(shù)訪問音頻隊列的當前參數(shù)值。有關(guān)獲取和設(shè)置參數(shù)值的函數(shù)的完整說明,請參閱Audio Queue Services Reference。
后記
未完,待續(xù)~~~
