Audio Queue Services是官方推薦的方式以一種直接的,低開銷的方式在iOS與Mac OS X中完成錄製與播放的操做.不像上層的API,它能夠經過回調拿到音頻幀數據,以完成更加精細的操做.html
比上層API而言,能夠直接獲取每一幀音頻數據,所以能夠對音頻幀作一些須要的處理. 可是沒法對聲音作一些更加精細的處理,如回聲消除,混音,降噪等等,若是須要作更底層的操做,須要使用Audio Unit.數組
Audio Queue Service是Core Audio的Audio Toolbox框架中的基於C語言的一套接口.緩存
Audio Queue Services是一套高級的API. 它不只能夠在無需瞭解硬件的基礎上使程序與音頻硬件(麥克風,揚聲器等)之間完成交互,也在無需瞭解編解碼器的原理狀況下讓咱們使用複雜的編解碼器.bash
同時,Audio Queue Services還提供了更加精細的定時控制以支持預約的播放與同步任務.可使用它同步多個音頻播放隊列或者音視頻間進行同步.cookie
支持如下格式網絡
注意: Audio Queue Services是一套純C的接口,因此基礎的C,C++須要有必定了解.數據結構
在iOS, Mac OS X中audio queue是一個軟件層面的對象,能夠用來作錄製與播放操做.使用AudioQueueRef
表明其數據結構.架構
做用app
若是要使用audio queue的錄製功能,經過AudioQueueNewInput
建立錄音隊列.框架
錄製使用的audio queue的輸入端一般是當前設備鏈接的音頻設備,如內置的麥克風,或外置的帶麥克風功能的輸入設備.輸出端是咱們定義的回調函數.若是將音頻數據錄製成文件,能夠在回調函數中將從audio queue中取出的音頻數據寫入文件.固然錄製的音頻數據也能夠直接送給當前App以實現邊錄製邊播放的功能.
每一個audio queue,不論是用於錄製或播放,都至少有一個或多個音頻數據.全部的音頻數據被放在一個被稱爲音頻隊列buffer特殊的數據結構中,能夠理解成隊列中的結點.如上圖所示,指定數量的buffer按順序依次被放入音頻隊列中,它們最終也將在回調函數中按順序取出.
若是要使用audio queue的播放功能,經過AudioQueueNewOutput
建立播放隊列對象.
播放使用的音頻隊列,回調函數在輸入端.該回調函數將從本地或其餘音頻數據源獲取到的數據交給音頻隊列中.當沒有數據裝入播放回調函數也會告訴音頻隊列中止播放.
用於播放的音頻隊列的輸出端則鏈接着音頻輸出硬件,如揚聲器或外接的具備揚聲器功能的音頻設備(如:耳機,音響等).
AudioQueueBuffer
用於存放音頻隊列數據.
typedef struct AudioQueueBuffer {
const UInt32 mAudioDataBytesCapacity;
void *const mAudioData;
UInt32 mAudioDataByteSize;
void *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;
複製代碼
mAudioData
: 當前取出的隊列中存放的即時的音頻數據指針,它指向真正存放音頻數據的內存地址.mAudioDataBytesCapacity
: 當前音頻數據最大存儲空間mAudioDataByteSize
: 當前存儲的音頻數據實際的大小mUserData
: 開發者能夠存放一些自定義的數據音頻隊列可使用任意數量的音頻數據結點,但通常建議用3個便可.由於若是太少則存取過於頻繁,太多則增長應用程序內存消耗,正常狀況下兩個便可,咱們可使用第三個當有延遲狀況出現時做爲補償數據.
由於Audio Queue的純C函數,內存須要咱們手動管理.
AudioQueueAllocateBuffer
分配內存AudioQueueDispose
回收內存經過內存管理,可使錄製播放更加穩定,同時優化App資源使用.
audio queue: 音頻隊列, 即Audio Queue Services的名字
audio queue buffer : 音頻隊列中存放一個或多個結點數據
作錄製操做時,一個audio queue buffer將被從輸入設備(如:麥克風)採集的音頻數據填充.音頻隊列中剩餘的buffer按順序排列在當前填充數據的buffer以後,依次等待被填充數據.在輸出端,回調函數將按照指定時間間隔依次接收音頻隊列中按順序排列好的音頻數據.工做原理以下圖:
圖一: 錄製開始,音頻隊列中填充須要的音頻數據.
圖二: 第一個buffer被填充,對調函數取出buffer 1並將其寫入文件,同時buffer2也被填充完數據.
圖三: 在第4步,回調函數將用完的buffer 1從新放回音頻隊列,隨後第五步回調函數再次取出音頻數據buffer2,最終將其寫入文件然後從新放回音頻隊列此後循環往復直到錄製中止.
作播放操做時,一個audio queue buffer須要交給輸出設備(如:揚聲器).剩餘的音頻數據也將按順序排列在當前取出播放的音頻數據以後,等待播放.回調函數將按順序取出音頻隊列中的數據交給揚聲器,隨後將用完的audio queue buffer從新放入音頻隊列.
圖1: 應用程序啓動音頻播放隊列,每調用依次回調函數填充一個audio queue buffers,填充完後將其放入音頻隊列. 當應用程序調用AudioQueueStart
當即開始播放.
圖2: 音頻隊列輸出第一個音頻數據
圖3: 用完的audio queue buffer從新放入音頻隊列.一旦播放了第一個音頻數據,音頻隊列會進入一個循環穩定的狀態,即開始播放下一個buffer2(第4步)而後調用回調函數準備填充數據(第5步),最後(第6步)buffer1從新被填充並裝入音頻隊列依次循環直到音頻隊列中止.
Audio queue buffers始終按照入隊順序進行播放.然而可使用AudioQueueEnqueueBufferWithParameters
函數作一些額外控制
a. 設置緩衝區精確的播放時間,用於同步
b. 能夠裁剪開始或結尾的audio queue buffer,這使咱們能夠作到開始或結尾的靜音效果.
c. 增長播放的聲音
後文播放章節中將具體介紹.
不管錄製仍是播放,一旦註冊好回調函數,它將頻繁的被調用.調用時間取決於咱們的設置.回調函數的一個重要職責是將用完的數據從新交給音頻隊列.使用AudioQueueEnqueueBuffer
入隊.
AudioQueueInputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs
);
複製代碼
當輸入端採集到音頻數據時就會觸發回調,能夠從回調函數中取出裝有音頻數據的audio queue buffer.
AudioFileWritePackets
函數.CBR格式不使用此參數.AudioFileWritePackets
函數AudioQueueOutputCallback (
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
);
複製代碼
在回調函數中將讀取音頻數據以用來播放
若是應用程序正在播放VBR格式數據,這個回調函數須要經過
AudioFileReadPackets
獲取音頻數據包信息.而後,回調將數據包信息放入自定義數據結構中,以使其可用於播放音頻隊列。
Audio Queue Services使音頻編解碼器用於轉換音頻數據格式.你的錄製或播放可使用編解碼器支持的任意格式.
每一個audio queue有一個本身的音頻數據格式,被封裝在AudioStreamBasicDescription
中,經過mFormatID
能夠指定音頻數據格式,audio queue會自動選擇適當編解碼器對其壓縮.開發者能夠指定採樣率,聲道數等等參數自定義音頻數據.
如上圖,應用程序告訴音頻隊列使用指定格式開始錄製,音頻隊列在獲取到原生的PCM數據後使用編碼器將其轉換爲AAC類型數據,而後音頻隊列通知回調函數,將轉換好的數據放入audio queue buffer中傳給回調函數.最後,回調函數拿到轉換好的AAC數據進行使用.
如上圖,應用程序告訴音頻隊列播放指定的格式(AAC)的文件,音頻隊列調用回調函數從音頻文件中讀取音頻數據,回調函數將原始格式的數據傳給音頻隊列.最後,音頻隊列使用合適的解碼器將音頻數據(PCM)交給揚聲器.
音頻隊列能夠利用任何編解碼器不管是系統自帶的仍是第三方安裝的(僅Mac OS)
音頻隊列在建立與銷燬間的活動範圍稱爲它的聲明週期.
AudioQueueStart
前調用它確保當有可用的音頻數據時可以當即播放.AudioQueueStart
恢復.AudioQueueStop
能夠選擇以同步或異步的方式中止.
音頻隊列有一個能夠調節的設置稱爲參數,每一個參數都有一個枚舉常量做爲其鍵,一個浮點型做爲其值,該值僅用於播放.
如下有兩種方式設置參數
AudioQueueSetParameter
:當即改變AudioQueueEnqueueBufferWithParameters
,在入隊時進行設置,播放時,此類更改將生效。使用
kAudioQueueParam_Volume
能夠調節播放音量(0.0~1.0)
使用Audio Queue Services進行錄製,輸出端能夠是一個文件,網絡協議傳輸,拷貝給一個對象等等.這裏僅介紹輸出到文件.
流程
第一步是自定義一個結構體管理音頻格式及狀態信息.
static const int kNumberBuffers = 3; // 1
struct AQRecorderState {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueueRef mQueue; // 3
AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFileID mAudioFile; // 5
UInt32 bufferByteSize; // 6
SInt64 mCurrentPacket; // 7
bool mIsRunning; // 8
};
複製代碼
static void HandleInputBuffer (
void *aqData, // 1
AudioQueueRef inAQ, // 2
AudioQueueBufferRef inBuffer, // 3
const AudioTimeStamp *inStartTime, // 4
UInt32 inNumPackets, // 5
const AudioStreamPacketDescription *inPacketDesc // 6
)
複製代碼
AudioFileWritePackets
函數.CBR格式不使用此參數(值爲0).AudioFileWritePackets
函數使用AudioFileWritePackets
將數據寫入音頻文件.
AudioFileWritePackets ( // 1
pAqData->mAudioFile, // 2
false, // 3
inBuffer->mAudioDataByteSize, // 4
inPacketDesc, // 5
pAqData->mCurrentPacket, // 6
&inNumPackets, // 7
inBuffer->mAudioData // 8
);
複製代碼
inPacketDesc
參數.當音頻數據在回調函數中用完後,須要從新放回音頻隊列以便存儲新的音頻數據
AudioQueueEnqueueBuffer ( // 1
pAqData->mQueue, // 2
inBuffer, // 3
0, // 4
NULL // 5
);
複製代碼
static void HandleInputBuffer (
void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumPackets,
const AudioStreamPacketDescription *inPacketDesc
) {
AQRecorderState *pAqData = (AQRecorderState *) aqData; // 1
if (inNumPackets == 0 && // 2
pAqData->mDataFormat.mBytesPerPacket != 0)
inNumPackets =
inBuffer->mAudioDataByteSize / pAqData->mDataFormat.mBytesPerPacket;
if (AudioFileWritePackets ( // 3
pAqData->mAudioFile,
false,
inBuffer->mAudioDataByteSize,
inPacketDesc,
pAqData->mCurrentPacket,
&inNumPackets,
inBuffer->mAudioData
) == noErr) {
pAqData->mCurrentPacket += inNumPackets; // 4
}
if (pAqData->mIsRunning == 0) // 5
return;
AudioQueueEnqueueBuffer ( // 6
pAqData->mQueue,
inBuffer,
0,
NULL
);
}
複製代碼
inNumPackets
參數獲取.void DeriveBufferSize (
AudioQueueRef audioQueue, // 1
AudioStreamBasicDescription &ASBDescription, // 2
Float64 seconds, // 3
UInt32 *outBufferSize // 4
) {
static const int maxBufferSize = 0x50000; // 5
int maxPacketSize = ASBDescription.mBytesPerPacket; // 6
if (maxPacketSize == 0) { // 7
UInt32 maxVBRPacketSize = sizeof(maxPacketSize);
AudioQueueGetProperty (
audioQueue,
kAudioQueueProperty_MaximumOutputPacketSize,
// in Mac OS X v10.5, instead use
// kAudioConverterPropertyMaximumOutputPacketSize
&maxPacketSize,
&maxVBRPacketSize
);
}
Float64 numBytesForTime =
ASBDescription.mSampleRate * maxPacketSize * seconds; // 8
*outBufferSize =
UInt32 (numBytesForTime < maxBufferSize ?
numBytesForTime : maxBufferSize); // 9
}
複製代碼
對於一些壓縮音頻數據格式,如AAC,MPEG 4 AAC等,必須包含音頻元數據.包含該元數據信息的數據結構稱爲magic cookies.當你錄製壓縮音頻數據格式的音頻文件時,必須從audio queue中獲取元數據並將其設置給音頻文件.
注意: 咱們在錄製前與中止錄製後兩個時間點都設置一次magin cookie,由於有的編碼器須要在中止錄製後更新magin cookie.
OSStatus SetMagicCookieForFile (
AudioQueueRef inQueue, // 1
AudioFileID inFile // 2
) {
OSStatus result = noErr; // 3
UInt32 cookieSize; // 4
if (
AudioQueueGetPropertySize ( // 5
inQueue,
kAudioQueueProperty_MagicCookie,
&cookieSize
) == noErr
) {
char* magicCookie =
(char *) malloc (cookieSize); // 6
if (
AudioQueueGetProperty ( // 7
inQueue,
kAudioQueueProperty_MagicCookie,
magicCookie,
&cookieSize
) == noErr
)
result = AudioFileSetProperty ( // 8
inFile,
kAudioFilePropertyMagicCookieData,
cookieSize,
magicCookie
);
free (magicCookie); // 9
}
return result; // 10
}
複製代碼
主要關注如下參數
AQRecorderState aqData; // 1
aqData.mDataFormat.mFormatID = kAudioFormatLinearPCM; // 2
aqData.mDataFormat.mSampleRate = 44100.0; // 3
aqData.mDataFormat.mChannelsPerFrame = 2; // 4
aqData.mDataFormat.mBitsPerChannel = 16; // 5
aqData.mDataFormat.mBytesPerPacket = // 6
aqData.mDataFormat.mBytesPerFrame =
aqData.mDataFormat.mChannelsPerFrame * sizeof (SInt16);
aqData.mDataFormat.mFramesPerPacket = 1; // 7
AudioFileTypeID fileType = kAudioFileAIFFType; // 8
aqData.mDataFormat.mFormatFlags = // 9
kLinearPCMFormatFlagIsBigEndian
| kLinearPCMFormatFlagIsSignedInteger
| kLinearPCMFormatFlagIsPacked;
複製代碼
AudioQueueNewInput ( // 1
&aqData.mDataFormat, // 2
HandleInputBuffer, // 3
&aqData, // 4
NULL, // 5
kCFRunLoopCommonModes, // 6
0, // 7
&aqData.mQueue // 8
);
複製代碼
當audio queue開始工做後,它可能會產生更多音頻格式信息比咱們初始化設置時,因此咱們須要對獲取到的音頻數據作一個檢查.
UInt32 dataFormatSize = sizeof (aqData.mDataFormat); // 1
AudioQueueGetProperty ( // 2
aqData.mQueue, // 3
kAudioQueueProperty_StreamDescription, // 4
// in Mac OS X, instead use
// kAudioConverterCurrentInputStreamDescription
&aqData.mDataFormat, // 5
&dataFormatSize // 6
);
複製代碼
CFURLRef audioFileURL =
CFURLCreateFromFileSystemRepresentation ( // 1
NULL, // 2
(const UInt8 *) filePath, // 3
strlen (filePath), // 4
false // 5
);
AudioFileCreateWithURL ( // 6
audioFileURL, // 7
fileType, // 8
&aqData.mDataFormat, // 9
kAudioFileFlags_EraseFile, // 10
&aqData.mAudioFile // 11
);
複製代碼
CFURL
類型的對象表明錄製文件路徑使用2.6.章節中的函數設置音頻隊列數據的大小以便後續使用.
DeriveBufferSize ( // 1
aqData.mQueue, // 2
aqData.mDataFormat, // 3
0.5, // 4
&aqData.bufferByteSize // 5
);
複製代碼
for (int i = 0; i < kNumberBuffers; ++i) { // 1
AudioQueueAllocateBuffer ( // 2
aqData.mQueue, // 3
aqData.bufferByteSize, // 4
&aqData.mBuffers[i] // 5
);
AudioQueueEnqueueBuffer ( // 6
aqData.mQueue, // 7
aqData.mBuffers[i], // 8
0, // 9
NULL // 10
);
}
複製代碼
aqData.mCurrentPacket = 0; // 1
aqData.mIsRunning = true; // 2
AudioQueueStart ( // 3
aqData.mQueue, // 4
NULL // 5
);
// Wait, on user interface thread, until user stops the recording
AudioQueueStop ( // 6
aqData.mQueue, // 7
true // 8
);
aqData.mIsRunning = false; // 9
複製代碼
錄製完成後,回收音頻隊列數據,關閉音頻文件.
AudioQueueDispose ( // 1
aqData.mQueue, // 2
true // 3
);
AudioFileClose (aqData.mAudioFile); // 4
複製代碼
使用 Audio Queue Services播放音頻時,源數據能夠是本地文件, 內存中的對象或者其餘音頻存儲方式.本章中僅介紹經過本地文件播放.
static const int kNumberBuffers = 3; // 1
struct AQPlayerState {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueueRef mQueue; // 3
AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFileID mAudioFile; // 5
UInt32 bufferByteSize; // 6
SInt64 mCurrentPacket; // 7
UInt32 mNumPacketsToRead; // 8
AudioStreamPacketDescription *mPacketDescs; // 9
bool mIsRunning; // 10
};
複製代碼
此結構體中的數據基本與錄製時相同.
做用
static void HandleOutputBuffer (
void *aqData, // 1
AudioQueueRef inAQ, // 2
AudioQueueBufferRef inBuffer // 3
)
複製代碼
AudioFileReadPackets ( // 1
pAqData->mAudioFile, // 2
false, // 3
&numBytesReadFromFile, // 4
pAqData->mPacketDescs, // 5
pAqData->mCurrentPacket, // 6
&numPackets, // 7
inBuffer->mAudioData // 8
);
複製代碼
讀取完音頻數據後,執行入隊操做.
AudioQueueEnqueueBuffer ( // 1
pAqData->mQueue, // 2
inBuffer, // 3
(pAqData->mPacketDescs ? numPackets : 0), // 4
pAqData->mPacketDescs // 5
);
複製代碼
若是檢查到當前音頻文件讀取完畢,應該中止音頻隊列.
if (numPackets == 0) { // 1
AudioQueueStop ( // 2
pAqData->mQueue, // 3
false // 4
);
pAqData->mIsRunning = false; // 5
}
複製代碼
AudioFileReadPackets
檢查數據包是否爲0static void HandleOutputBuffer (
void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer
) {
AQPlayerState *pAqData = (AQPlayerState *) aqData; // 1
if (pAqData->mIsRunning == 0) return; // 2
UInt32 numBytesReadFromFile; // 3
UInt32 numPackets = pAqData->mNumPacketsToRead; // 4
AudioFileReadPackets (
pAqData->mAudioFile,
false,
&numBytesReadFromFile,
pAqData->mPacketDescs,
pAqData->mCurrentPacket,
&numPackets,
inBuffer->mAudioData
);
if (numPackets > 0) { // 5
inBuffer->mAudioDataByteSize = numBytesReadFromFile; // 6
AudioQueueEnqueueBuffer (
pAqData->mQueue,
inBuffer,
(pAqData->mPacketDescs ? numPackets : 0),
pAqData->mPacketDescs
);
pAqData->mCurrentPacket += numPackets; // 7
} else {
AudioQueueStop (
pAqData->mQueue,
false
);
pAqData->mIsRunning = false;
}
}
複製代碼
咱們須要指定一個音頻隊列buffer的大小.根據計算出來的大小爲音頻隊列數據分配內存.
AudioFileReadPackets
獲取讀取到的包數void DeriveBufferSize (
AudioStreamBasicDescription &ASBDesc, // 1
UInt32 maxPacketSize, // 2
Float64 seconds, // 3
UInt32 *outBufferSize, // 4
UInt32 *outNumPacketsToRead // 5
) {
static const int maxBufferSize = 0x50000; // 6
static const int minBufferSize = 0x4000; // 7
if (ASBDesc.mFramesPerPacket != 0) { // 8
Float64 numPacketsForTime =
ASBDesc.mSampleRate / ASBDesc.mFramesPerPacket * seconds;
*outBufferSize = numPacketsForTime * maxPacketSize;
} else { // 9
*outBufferSize =
maxBufferSize > maxPacketSize ?
maxBufferSize : maxPacketSize;
}
if ( // 10
*outBufferSize > maxBufferSize &&
*outBufferSize > maxPacketSize
)
*outBufferSize = maxBufferSize;
else { // 11
if (*outBufferSize < minBufferSize)
*outBufferSize = minBufferSize;
}
*outNumPacketsToRead = *outBufferSize / maxPacketSize; // 12
}
複製代碼
AudioFileGetProperty
查詢kAudioFilePropertyPacketSizeUpperBound
屬性可得CFURL
對象表示音頻文件路徑CFURL
對象表示音頻文件路徑CFURLRef audioFileURL =
CFURLCreateFromFileSystemRepresentation ( // 1
NULL, // 2
(const UInt8 *) filePath, // 3
strlen (filePath), // 4
false // 5
);
複製代碼
CFURL
類型的對象表明錄製文件路徑AQPlayerState aqData; // 1
OSStatus result =
AudioFileOpenURL ( // 2
audioFileURL, // 3
fsRdPerm, // 4
0, // 5
&aqData.mAudioFile // 6
);
CFRelease (audioFileURL); // 7
複製代碼
UInt32 dataFormatSize = sizeof (aqData.mDataFormat); // 1
AudioFileGetProperty ( // 2
aqData.mAudioFile, // 3
kAudioFilePropertyDataFormat, // 4
&dataFormatSize, // 5
&aqData.mDataFormat // 6
);
複製代碼
AudioQueueNewOutput ( // 1
&aqData.mDataFormat, // 2
HandleOutputBuffer, // 3
&aqData, // 4
CFRunLoopGetCurrent (), // 5
kCFRunLoopCommonModes, // 6
0, // 7
&aqData.mQueue // 8
);
複製代碼
UInt32 maxPacketSize;
UInt32 propertySize = sizeof (maxPacketSize);
AudioFileGetProperty ( // 1
aqData.mAudioFile, // 2
kAudioFilePropertyPacketSizeUpperBound, // 3
&propertySize, // 4
&maxPacketSize // 5
);
DeriveBufferSize ( // 6
aqData.mDataFormat, // 7
maxPacketSize, // 8
0.5, // 9
&aqData.bufferByteSize, // 10
&aqData.mNumPacketsToRead // 11
);
複製代碼
bool isFormatVBR = ( // 1
aqData.mDataFormat.mBytesPerPacket == 0 ||
aqData.mDataFormat.mFramesPerPacket == 0
);
if (isFormatVBR) { // 2
aqData.mPacketDescs =
(AudioStreamPacketDescription*) malloc (
aqData.mNumPacketsToRead * sizeof (AudioStreamPacketDescription)
);
} else { // 3
aqData.mPacketDescs = NULL;
}
複製代碼
對於壓縮的音頻數據格式(AAC...),咱們在播放前必須爲音頻隊列設置magic cookies,即元數據信息.
UInt32 cookieSize = sizeof (UInt32); // 1
bool couldNotGetProperty = // 2
AudioFileGetPropertyInfo ( // 3
aqData.mAudioFile, // 4
kAudioFilePropertyMagicCookieData, // 5
&cookieSize, // 6
NULL // 7
);
if (!couldNotGetProperty && cookieSize) { // 8
char* magicCookie =
(char *) malloc (cookieSize);
AudioFileGetProperty ( // 9
aqData.mAudioFile, // 10
kAudioFilePropertyMagicCookieData, // 11
&cookieSize, // 12
magicCookie // 13
);
AudioQueueSetProperty ( // 14
aqData.mQueue, // 15
kAudioQueueProperty_MagicCookie, // 16
magicCookie, // 17
cookieSize // 18
);
free (magicCookie); // 19
}
複製代碼
aqData.mCurrentPacket = 0; // 1
for (int i = 0; i < kNumberBuffers; ++i) { // 2
AudioQueueAllocateBuffer ( // 3
aqData.mQueue, // 4
aqData.bufferByteSize, // 5
&aqData.mBuffers[i] // 6
);
HandleOutputBuffer ( // 7
&aqData, // 8
aqData.mQueue, // 9
aqData.mBuffers[i] // 10
);
}
複製代碼
開始播放前,能夠設置音量(0~1)
Float32 gain = 1.0; // 1
// Optionally, allow user to override gain setting here
AudioQueueSetParameter ( // 2
aqData.mQueue, // 3
kAudioQueueParam_Volume, // 4
gain // 5
);
複製代碼
aqData.mIsRunning = true; // 1
AudioQueueStart ( // 2
aqData.mQueue, // 3
NULL // 4
);
do { // 5
CFRunLoopRunInMode ( // 6
kCFRunLoopDefaultMode, // 7
0.25, // 8
false // 9
);
} while (aqData.mIsRunning);
CFRunLoopRunInMode ( // 10
kCFRunLoopDefaultMode,
1,
false
);
複製代碼
播放完成後應該回收音頻隊列,關閉音頻文件,釋放全部相關資源
AudioQueueDispose ( // 1
aqData.mQueue, // 2
true // 3
);
AudioFileClose (aqData.mAudioFile); // 4
free (aqData.mPacketDescs); // 5
複製代碼