Audio Queue Services Programming Guide

介紹

Audio Queue服務是比較高級的服務。它可讓你的應用程序使用硬件進行錄音和回放音頻(如麥克風和揚聲器),而不須要知道硬件接口。同時音頻隊列服務支持一些高級功能。它提供精細的定時控制以及支持預約播放和同步。你可使用它來同步多個音頻隊列的播放,並使音頻與視頻同步。ios

音頻隊列服務是一個純C接口,您能夠在Cocoa應用程序以及Mac OS X命令行工具中使用。 爲了幫助保持專一於音頻隊列服務,本文檔中的代碼示例有時經過使用Core Audio SDK中的C ++類進行簡化。 可是,使用音頻隊列服務不須要SDK和C ++語言。web

Audio Queues

在這個章節中,你會了解到音頻隊列的功能,體系結構和內部工做原理。將會向你介紹音頻隊列,音頻隊列緩衝區以及音頻隊列用於音頻錄製和回放的回調函數。你還能夠了解到關於音頻隊列狀態和參數的信息。編程

什麼是 Audio Queue

一個 Audio queue(音頻隊列)是用於在 ios 和 mac 平臺上的播放和錄製音頻的軟件對象。它由 AudioQueueRef 類型表示,在 AudioQueue.h 頭文件中聲明。markdown

一個 Audio queue(音頻隊列)作以下工做:數據結構

  • 鏈接音頻硬件
  • 管理內存
  • 採用的編解碼器會根據須要壓縮音頻格式
  • 音頻錄製和回放

Audio Queue結構

全部的 audio queue 都大體有相同的結構,都有如下部分構成:app

  • 一組音頻隊列緩衝區,每一個緩衝區是一些音頻數據的臨時存儲庫
  • 緩衝區隊列,音頻隊列緩衝區的有序列表
  • 一個音頻隊列 callback 函數,這是須要你定義的。

該結構根據音頻隊列是用於記錄仍是播放而變化。 差別在於音頻隊列如何鏈接其輸入和輸出,以及在回調函數的做用。異步

錄製的音頻隊列async

一個錄製的音頻隊列,使用 AudioQueueNewInput 函數建立,具備以下結構:函數

記錄音頻隊列的輸入側一般鏈接到外部音頻硬件,例如麥克風.在iOS中,音頻來自麥克風或耳機鏈接的設備。 在Mac OS X的默認狀況下,音頻來自系統的默認音頻輸入設備,由用戶在系統首選項中設置。工具

錄製音頻隊列的輸出端是你的回調函數。當記錄到磁盤時,回調將從其音頻隊列接收的新音頻數據的緩衝器寫入音頻文件。然而,能夠以其它方式使用記錄音頻隊列。您還可使用一個,例如,在實時音頻分析儀。 在這種狀況下,您的回調將直接提供音頻數據到您的應用程序,而不是寫入磁盤。

每個音頻隊列——不管是錄製的或者是播放的,都有一個或多個音頻隊列緩衝器。這些緩衝器以稱爲緩衝器隊列的特定順序排列。如圖所示,音頻隊列緩衝器根據它們被填充的順序被編號,這是它們被切換到回調的相同的順序。

播放的音頻隊列

一個播放的音頻隊列,使用AudioQueueNewOutput函數建立,具備以下結構:

在播放音頻隊列中,回調在輸入側。回調負責從磁盤(或一些其餘源)獲取音頻數據並將其移交到音頻隊列。當沒有更多的數據要播放時,回放回調也會告訴他們的音頻隊列中止。

Audio Queue Buffers(音頻隊列緩衝區)

一個 audio queue buffer(音頻隊列緩衝區)是一個 AudioQueueBuffer 類型的數據結構,聲明在 AudioQueue.h 中:

typedef struct AudioQueueBuffer {
    const UInt32   mAudioDataBytesCapacity;
    void *const    mAudioData;
    UInt32         mAudioDataByteSize;
    void           *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;
複製代碼

上面結構中的 mAudioData 字段,它指向緩衝區自己:a block of memory that serves as a container for transient blocks of audio data being played or recorded. 其它字段幫助 audio queue 管理緩衝區。

音頻隊列可使用你的應用程序指定的任意數量的緩衝區。不過通常都是指定爲3個。這樣就容許一個去寫數據到磁盤,另外一個正在填充新的數據,若是須要補償諸如磁盤I / O延遲之類的事情,則第三緩衝器可用。

Audio queues(音頻隊列)爲其緩衝區提供內存管理。

  • 當您調用AudioQueueAllocateBuffer函數時,audio queue會分配一個緩衝區
  • 當您經過調用AudioQueue Dispose函數釋放audio queue(音頻隊列)時,隊列釋放其緩衝區

這提升了添加到應用程序中的錄製和播放功能的魯棒性。 它還有助於優化資源使用。

The Buffer Queue and Enqueuing

錄音過程

播放過程

控制播放過程

音頻隊列緩衝區老是按它們入隊的順序播放。 可是,音頻隊列服務使用AudioQueueEnqueueBufferWithParameters函數爲您提供了對播放過程的一些控制。 此功能容許您:

  • 設置緩衝區的精確播放時間。 這容許您支持同步。
  • 修剪音頻隊列緩衝區的開始或結束處的幀。 這容許您刪除前導或尾部靜音。
  • 以緩衝區的粒度設置播放增益

Audio Queue的回調函數

一般,使用音頻隊列服務的大部分編程工做包括編寫音頻隊列回調函數。

在記錄或播放期間,音頻隊列回調由擁有它的音頻隊列重複調用。 呼叫之間的時間取決於音頻隊列緩衝器的容量,而且一般在半秒到幾秒的範圍內。

音頻隊列回調的一個職責,不論是用於記錄仍是播放,都是將音頻隊列緩衝區返回到緩衝區隊列。 回調使用AudioQueueEnqueueBuffer函數將緩衝區添加到緩衝區隊列的末尾。 對於播放,若是須要更多的控制,您能夠改用AudioQueueEnqueueBufferWithParameters函數,如控制播放過程當中所述。

錄製音頻隊列的回調函數

本節介紹了在將音頻錄製到磁盤文件的常見狀況下編寫的回調。 下面是AudioQueue.h頭文件中聲明的錄音音頻隊列回調的原型:

AudioQueueInputCallback (
    void                               *inUserData,
    AudioQueueRef                      inAQ,
    AudioQueueBufferRef                inBuffer,
    const AudioTimeStamp               *inStartTime,
    UInt32                             inNumberPacketDescriptions,
    const AudioStreamPacketDescription *inPacketDescs
);
複製代碼

記錄音頻隊列在調用回調時提供回調須要將下一組音頻數據寫入音頻文件的全部內容:

回放音頻隊列的回調函數

AudioQueueOutputCallback (
    void                  *inUserData,
    AudioQueueRef         inAQ,
    AudioQueueBufferRef   inBuffer
);
複製代碼

回放音頻隊列在調用回調時提供回調須要從音頻文件讀取下一組音頻數據的內容:

使用編解碼器和音頻數據格式

音頻隊列服務能夠根據不一樣的音頻格式來轉換須要使用的編解碼器(音頻數據編碼和解碼)。您的錄音或播放應用程序可使用任何已安裝編解碼器的音頻格式,你不須要去編寫自定義代碼來處理各類音頻格式。簡單來講也就是你的回調不須要知道具體的音頻數據格式。

下圖所示的是它如何工做的。每個音頻隊列對應一種音頻數據格式,音頻數據格式使用AudioStreamBasicDescription描述。當你爲 mFormatID字段設置值得時候,音頻隊列就會使用與之相對應的編解碼器,而後你再爲其定義採樣率和通道數。

如上圖所示,第一步,你的應用程序告訴音頻隊列要使用的音頻數據格式,並開始錄製。第二步,音頻隊列將根據您指定的格式使用編解碼器獲取新的音頻數據並對其進行轉換。 音頻隊列而後調用回調,處理一個包含適當造成的音頻數據的緩衝區。第三部,你的回調將格式化的音頻數據寫入磁盤。 一樣,你的回調不須要知道數據格式。

下圖是表示音頻回放是如何回放的。

  • 第一步,你的應用程序告訴音頻隊列所要播放的音頻數據的格式,並開始播放。
  • 第二步,音頻隊列調用你的回調,從音頻文件讀取數據。 回調將數據以其原始格式移交給音頻隊列。
  • 第三步,音頻隊列使用適當的編解碼器,而後將音頻發送到目的地。

音頻隊列可使用任何已安裝的編解碼器,不管是Mac OS X原生的仍是由第三方提供的。

音頻隊列控制和狀態

音頻隊列在建立和處理之間有一個生命週期。 您的應用程序管理今生命週期,並使用AudioQueue.h頭文件中聲明的六個函數來控制音頻隊列的狀態:

  • Start(AudioQueueStart). Call to initiate recording or playback.
  • Prime (AudioQueuePrime).For playback, call before calling AudioQueueStart to ensure that there is data available immediately for the audio queue to play. This function is not relevant to recording。
  • Stop (AudioQueueStop).Call to reset the audio queue (see the description below for AudioQueueReset) and to then stop recording or playback. A playback audio queue callback calls this function when there’s no more data to play.
  • Pause (AudioQueuePause).Call to pause recording or playback without affecting buffers or resetting the audio queue. To resume, call the AudioQueueStart function.
  • Flush (AudioQueueFlush).Call after enqueuing the last audio queue buffer to ensure that all buffered data, as well as all audio data in the midst of processing, gets recorded or played.
  • Reset (AudioQueueReset).Call to immediately silence an audio queue, remove all buffers from previously scheduled use, and reset all decoder and DSP state.

你可使用 AudioQueueStop函數在同步或異步模式下:

  • Synchronous stopping happens immediately, without regard for previously buffered audio data.
  • Asynchronous stopping happens after all queued buffers have been played or recorded.

音頻錄製

使用Audio Queue須要注意的問題

在這裏將會羅列出我使用Audio Queue 的時候所遇到的問題,及須要注意的事項的等問題。

若是音頻緩衝隊列中長時間沒有音頻數據的時播放回調會終止

利用Audio Queue播放音頻是,若是音頻隊列中長時間沒有數據時,播放的回調會自動中止。因此即便往音頻隊列中拷貝空的音頻數據也能夠,這樣就避免了播放的回調自動終止。

下面是示例代碼:

void AudioPlayer::handleTVUWebRTCAudioOutputBuffer(void * aqData,AudioQueueRef inAQ , AudioQueueBufferRef inBuffer)
{   
    TVUWebRTCManager *webRTCManager = [TVUWebRTCManager shareInstance];
    
    if (!webRTCManager) {
        return;
    }
    
    if (webRTCManager.isEndupCall) {
        NSLog(@"isEndupCall is YES...");
    }else{
        NSLog(@"isEndupCall is NO...");
    }
    
    RTCPeerConnection *rtcPeerConn = [webRTCManager getNewestPeerConnection];
    
    if (rtcPeerConn == NULL || rtcPeerConn == nil) {
        //        [mLock unlock];
        return;
    }
    int8_t * source = [rtcPeerConn getPlayoutAudioBuffer];   // 拷貝空的音頻數據到緩衝隊列中
    if (source == NULL || source == nil) {
        inBuffer->mAudioDataByteSize = kTVUWebRTCAudioDataByteSize;
        memset(inBuffer->mAudioData,0,kTVUWebRTCAudioDataByteSize);
        AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
        //        [mLock unlock];
        return;
    }
    inBuffer->mAudioDataByteSize = kTVUWebRTCAudioDataByteSize;
    if ([rtcPeerConn isWebRTCPlayoutActived] || [rtcPeerConn isWebRTCAudioFrameAvailable])
    {
        memset(inBuffer->mAudioData,0,kTVUWebRTCAudioDataByteSize);
        AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
        //        [mLock unlock];
        return;
    }
    
    // demon add , caculate volume db
    caculateVolumeDB(inBuffer, 0, k_Mono);
    
    memcpy(inBuffer->mAudioData, source, kTVUWebRTCAudioDataByteSize);
    AudioQueueEnqueueBuffer(inAQ,inBuffer,0,NULL);
    //    [mLock unlock];
    return ;

}
複製代碼

AudioQueue中的一些方法

AudioQueueStop

功能:中止音頻的播放或錄製

描述:若是音頻隊列未被其它音頻服務使用,則此功能將重置音頻隊列並中止與隊列相關聯的音頻硬件。同步中止(synchronous stops)會馬上執行播放或錄製,不管音頻隊列中是否有音頻內容;異步中止(asynchronous stops)直到音頻隊列中的音頻數據被播放完後,纔會中止播放或者錄製。

參數:

inAQ : 要中止的音頻隊列;

inImmediate:值爲yes時,同步中止即馬上執行;值爲NO時,異步中止,要等到音頻隊列中的內容播放或錄製完成後纔會中止。

注意:

  • 當音頻隊列中沒有音頻數據播放時,這個方法會被自動調用。
  • 噹噹即中止音頻隊列時,全部掛起的緩衝區回調一般在中止過程當中被調用。 可是若是調用線程響應緩衝區回調,則AudioQueueStop返回後可能會發生額外的緩衝區回調。

方法原型:

extern OSStatus
	AudioQueueStop(                     AudioQueueRef           inAQ,
	                                    Boolean                 inImmediate)            __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
複製代碼

AudioQueuePause

功能: 暫停音頻播放或錄製

描述:暫停隊列不影響緩衝區或充值音頻隊列。要使用音頻隊列恢復播放或錄製,請調用 AudioQueueStart

方法原型:

extern OSStatus
AudioQueuePause(                    AudioQueueRef           inAQ)       __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
複製代碼

AudioQueueFlush

功能:重置音頻隊列的解碼器狀態

描述:在全部的音頻隊列被播放完成以後,這個函數清除全部解碼器狀態的信息。您必須按照編碼音頻的緩衝區序列調用此函數; 不然,某些音頻可能不會在下一組排隊的緩衝區中播放。 惟一沒有必要調用AudioQueueFlush的時間是使用inImmediate = false的AudioQueueStop。 (此操做內部調用AudioQueueFlush。)

此外,您可能但願在調用AudioQueueStop以前調用此函數,具體取決於您是否要當即中止,不管播放什麼,或者是否要確保全部緩衝數據和處理中間的全部數據都被記錄或播放中止。

函數原型:

extern OSStatus
AudioQueueFlush(                    AudioQueueRef           inAQ)            __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);
複製代碼

AudioQueueReset

功能:重置音頻隊列

描述:該功能當即重置音頻隊列,刷新任何音頻隊列的緩衝區,從先前調度的使用中刪除全部緩衝區,並重置任何解碼器和數字信號處理(DSP)狀態信息。

注意: 在重置過程當中,一般會調用全部掛起的緩衝區回調,可是若是調用線程響應緩衝區回調,則能夠在AudioQueueReset返回後發生額外的緩衝區回調。

函數原型:

extern OSStatus
AudioQueueReset(                    AudioQueueRef           inAQ)            __OSX_AVAILABLE_STARTING(__MAC_10_5,__IPHONE_2_0);複製代碼
相關文章
相關標籤/搜索