幾種播放音頻文件的方式(六) —— 音頻隊列服務(wù)(Audio Queue Services)之關(guān)于音頻隊列(三)

版本記錄

版本號 時間
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所示。

Figure 1-1 A recording audio queue

記錄音頻隊列的輸入側(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所示。

Figure 1-2 A playback audio queue

在回放音頻隊列中,回調(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說明了使用音頻隊列時錄制的工作原理。

Figure 1-3 The recording process

在圖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說明了使用音頻隊列時的播放方式。

Figure 1-4 The playback process

在圖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ù)。
  • inNumberPacketDescriptionsinPacketDescs參數(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 AudioPlaying Audio中設(shè)置音頻數(shù)據(jù)格式的示例。

錄制音頻隊列使用已安裝的編解碼器,如圖1-5所示。

Figure 1-5 Audio format conversion during recording

在圖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所示。

Figure 1-6 Audio format conversion during playback

在圖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):

  • StartAudioQueueStart)。調(diào)用開始錄制或播放。
  • PrimeAudioQueuePrime)。要進行播放,請在調(diào)用AudioQueueStart之前調(diào)用,以確保立即有數(shù)據(jù)可供音頻隊列播放。此功能與錄音無關(guān)。
  • StopAudioQueueStop)。調(diào)用以重置音頻隊列(請參閱下面的說明AudioQueueReset),然后停止錄制或播放。播放音頻隊列回調(diào)callback在沒有更多數(shù)據(jù)播放時調(diào)用此函數(shù)。
  • PauseAudioQueuePause)。調(diào)用暫停錄音或播放而不影響緩沖區(qū)或重置音頻隊列。要恢復(fù),請調(diào)用AudioQueueStart函數(shù)。
  • FlushAudioQueueFlush)。排隊最后一個音頻隊列緩沖區(qū)后調(diào)用,以確保所有緩沖的數(shù)據(jù)以及處理過程中的所有音頻數(shù)據(jù)都被記錄或播放。
  • ResetAudioQueueReset)。調(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ù)~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容