版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2017.12.28 |
前言
ios系統(tǒng)中有很多方式可以播放音頻文件黔寇,這里我們就詳細(xì)的說(shuō)明下播放音樂(lè)文件的原理和實(shí)例。感興趣的可以看我寫的上面幾篇斩萌。
1. 幾種播放音頻文件的方式(一) —— 播放本地音樂(lè)
2. 幾種播放音頻文件的方式(二) —— 音效播放
3. 幾種播放音頻文件的方式(三) —— 網(wǎng)絡(luò)音樂(lè)播放
4. 幾種播放音頻文件的方式(四) —— 音頻隊(duì)列服務(wù)(Audio Queue Services)(一)
5. 幾種播放音頻文件的方式(五) —— 音頻隊(duì)列服務(wù)(Audio Queue Services)簡(jiǎn)介(二)
6. 幾種播放音頻文件的方式(六) —— 音頻隊(duì)列服務(wù)(Audio Queue Services)之關(guān)于音頻隊(duì)列(三)
Recording Audio - 錄制音頻
使用音頻隊(duì)列服務(wù)進(jìn)行錄制時(shí)缝裤,存儲(chǔ)目標(biāo)可以是任何事物 —— 磁盤上的文件,網(wǎng)絡(luò)連接颊郎,內(nèi)存中的對(duì)象等等憋飞。 本章介紹最常見(jiàn)的情況:基本錄制到磁盤文件。
注意:本章描述了一個(gè)基于
ANSI-C
的錄制實(shí)現(xiàn)姆吭,并使用了Mac OS X Core Audio SDK
中的C ++類榛做。 對(duì)于基于Objective-C的示例,請(qǐng)參閱iOS Dev Center的SpeakHere
示例代碼内狸。
要將錄制功能添加到應(yīng)用程序中检眯,通常需要執(zhí)行以下步驟:
- 定義一個(gè)自定義結(jié)構(gòu)來(lái)管理狀態(tài),格式和路徑信息昆淡。
- 編寫一個(gè)音頻隊(duì)列callback回調(diào)函數(shù)來(lái)執(zhí)行實(shí)際錄制锰瘸。
- 可以編寫代碼來(lái)確定音頻隊(duì)列緩沖區(qū)的大小。 如果您將以使用cookie的格式進(jìn)行錄制昂灵,編寫代碼與
magic cookies
一起配合避凝。 - 填寫自定義結(jié)構(gòu)的字段。 這包括指定音頻隊(duì)列發(fā)送到正在記錄的文件的數(shù)據(jù)流眨补,以及該文件的路徑管削。
- 創(chuàng)建一個(gè)錄制音頻隊(duì)列,并要求它創(chuàng)建一組音頻隊(duì)列緩沖區(qū)撑螺。 還要?jiǎng)?chuàng)建一個(gè)文件來(lái)記錄佩谣。
- 告訴音頻隊(duì)列開始錄制。
- 完成后实蓬,告訴音頻隊(duì)列停止并銷毀茸俭。 音頻隊(duì)列銷毀其緩沖區(qū)。
本章的其余部分將詳細(xì)介紹這些步驟安皱。
Define a Custom Structure to Manage State - 定義一個(gè)自定義結(jié)構(gòu)來(lái)管理狀態(tài)
使用音頻隊(duì)列服務(wù)開發(fā)錄音解決方案的第一步是定義一個(gè)自定義結(jié)構(gòu)调鬓。 您將使用此結(jié)構(gòu)來(lái)管理音頻格式和音頻隊(duì)列狀態(tài)信息。 Listing 2-1
說(shuō)明了這樣一個(gè)結(jié)構(gòu):
// Listing 2-1 A custom structure for a recording audio queue
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
};
以下是這個(gè)結(jié)構(gòu)中的字段的描述:
- 設(shè)置要使用的音頻隊(duì)列緩沖區(qū)的數(shù)量酌伊。
- 表示要寫入磁盤的音頻數(shù)據(jù)格式的
AudioStreamBasicDescription
結(jié)構(gòu)(來(lái)自CoreAudioTypes.h
)腾窝。該格式被mQueue
字段中指定的音頻隊(duì)列使用缀踪。
mDataFormat
字段最初由程序中的代碼填充,如 Set Up an Audio Format for Recording中所述虹脯。最好通過(guò)查詢音頻隊(duì)列的kAudioQueueProperty_StreamDescription
屬性來(lái)更新此字段的值驴娃,如Getting the Full Audio Format from an Audio Queue中所述。在Mac OS X v10.5
上循集,改用kAudioConverterCurrentInputStreamDescription
屬性唇敞。有關(guān)AudioStreamBasicDescription
結(jié)構(gòu)的詳細(xì)信息,請(qǐng)參閱Core Audio Data Types Reference咒彤。
- 表示要寫入磁盤的音頻數(shù)據(jù)格式的
- 由您的應(yīng)用程序創(chuàng)建錄制音頻隊(duì)列疆柔。
- 一個(gè)數(shù)組,指向由音頻隊(duì)列管理的音頻隊(duì)列緩沖區(qū)的指針镶柱。
- 音頻文件對(duì)象旷档,表示您的程序?qū)⒁纛l數(shù)據(jù)記錄到其中的文件。
每個(gè)音頻隊(duì)列緩沖區(qū)的大行稹(以字節(jié)為單位)鞋屈。在創(chuàng)建音頻隊(duì)列之后并在啟動(dòng)之前,在DeriveBufferSize
函數(shù)的這些示例中計(jì)算此值故觅。請(qǐng)參閱Write a Function to Derive Recording Audio Queue Buffer Size厂庇。
- 音頻文件對(duì)象旷档,表示您的程序?qū)⒁纛l數(shù)據(jù)記錄到其中的文件。
- 要從當(dāng)前音頻隊(duì)列緩沖區(qū)寫入的第一個(gè)數(shù)據(jù)包的數(shù)據(jù)包索引。
- 指示音頻隊(duì)列是否正在運(yùn)行的布爾值逻卖。
Write a Recording Audio Queue Callback - 寫一個(gè)錄制音頻隊(duì)列回調(diào)函數(shù)
接下來(lái)宋列,寫一個(gè)錄音音頻隊(duì)列回調(diào)函數(shù)。 這個(gè)回調(diào)做了兩件事:
- 將新填充的音頻隊(duì)列緩沖區(qū)的內(nèi)容寫入到正在錄制的音頻文件中
- 將音頻隊(duì)列緩沖區(qū)(其內(nèi)容剛剛寫入磁盤)排入緩沖區(qū)隊(duì)列
本節(jié)展示一個(gè)示例回調(diào)聲明评也,然后分別描述這兩個(gè)任務(wù)炼杖,最后展示一個(gè)完整的錄制回調(diào)。 有關(guān)錄制音頻隊(duì)列回調(diào)角色的說(shuō)明盗迟,請(qǐng)參閱Figure 1-3坤邪。
1. The Recording Audio Queue Callback Declaration - 錄制音頻隊(duì)列回調(diào)函數(shù)聲明
Listing 2-2
顯示了一個(gè)記錄音頻隊(duì)列回調(diào)函數(shù)的示例聲明,聲明為AudioQueue.h
頭文件中的AudioQueueInputCallback:
// Listing 2-2 The recording audio queue callback declaration
static void HandleInputBuffer (
void *aqData, // 1
AudioQueueRef inAQ, // 2
AudioQueueBufferRef inBuffer, // 3
const AudioTimeStamp *inStartTime, // 4
UInt32 inNumPackets, // 5
const AudioStreamPacketDescription *inPacketDesc // 6
)
以下是這段代碼的工作原理:
- 通常罚缕,
aqData
是包含音頻隊(duì)列的狀態(tài)數(shù)據(jù)的自定義結(jié)構(gòu)艇纺,如 Define a Custom Structure to Manage State所述。
- 通常罚缕,
- 擁有此回調(diào)的音頻隊(duì)列邮弹。
- 包含要錄制的傳入音頻數(shù)據(jù)的音頻隊(duì)列緩沖區(qū)黔衡。
- 音頻隊(duì)列緩沖區(qū)中第一個(gè)采樣的采樣時(shí)間(簡(jiǎn)單記錄不需要)。
-
inPacketDesc
參數(shù)中的數(shù)據(jù)包描述數(shù)量腌乡。 值為0表示CBR數(shù)據(jù)盟劫。
-
- 對(duì)于需要數(shù)據(jù)包描述的壓縮音頻數(shù)據(jù)格式税弃,編碼器為緩沖區(qū)中數(shù)據(jù)包生成的數(shù)據(jù)包描述料按。
2. Writing an Audio Queue Buffer to Disk - 將音頻隊(duì)列緩沖區(qū)寫入磁盤
錄音的音頻隊(duì)列回調(diào)的第一項(xiàng)任務(wù)是將音頻隊(duì)列緩沖區(qū)寫入磁盤。 這個(gè)緩沖區(qū)是回調(diào)的音頻隊(duì)列剛剛完成填充來(lái)自輸入設(shè)備的新的音頻數(shù)據(jù)的那個(gè)緩沖區(qū)顷啼。 回調(diào)使用AudioFile.h
頭文件中的AudioFileWritePackets
函數(shù)做修,如Listing 2-3
所示媒怯。
// Listing 2-3 Writing an audio queue buffer to disk
AudioFileWritePackets ( // 1
pAqData->mAudioFile, // 2
false, // 3
inBuffer->mAudioDataByteSize, // 4
inPacketDesc, // 5
pAqData->mCurrentPacket, // 6
&inNumPackets, // 7
inBuffer->mAudioData // 8
);
以下是這段代碼的工作原理:
- 在
AudioFile.h
頭文件中聲明的AudioFileWritePackets
函數(shù)將緩沖區(qū)的內(nèi)容寫入音頻數(shù)據(jù)文件赠制。
- 在
- 音頻文件對(duì)象(類型
AudioFileID
)赶袄,表示要寫入的音頻文件。pAqData
變量是一個(gè)指向Listing 2-1
中描述的數(shù)據(jù)結(jié)構(gòu)的指針猴娩。
- 音頻文件對(duì)象(類型
- 使用false值表示函數(shù)在寫入時(shí)不應(yīng)該緩存數(shù)據(jù)阴幌。
- 正在寫入的音頻數(shù)據(jù)的字節(jié)數(shù)。
inBuffer
變量表示由音頻隊(duì)列交給回調(diào)的音頻隊(duì)列緩沖區(qū)胀溺。
- 正在寫入的音頻數(shù)據(jù)的字節(jié)數(shù)。
- 音頻數(shù)據(jù)的數(shù)據(jù)包描述數(shù)組裂七。 值為
NULL
表示不需要數(shù)據(jù)包描述(例如用于CBR音頻數(shù)據(jù))皆看。
- 音頻數(shù)據(jù)的數(shù)據(jù)包描述數(shù)組裂七。 值為
- 要寫入的第一個(gè)數(shù)據(jù)包的數(shù)據(jù)包索引仓坞。
- 在輸入時(shí),要寫入的數(shù)據(jù)包的數(shù)量腰吟。 輸出時(shí)无埃,實(shí)際寫入的數(shù)據(jù)包數(shù)量。
- 將新的音頻數(shù)據(jù)寫入音頻文件毛雇。
3. Enqueuing an Audio Queue Buffer - 將一個(gè)音頻隊(duì)列緩沖入隊(duì)
現(xiàn)在來(lái)自音頻隊(duì)列緩沖區(qū)的音頻數(shù)據(jù)已被寫入音頻文件嫉称,回調(diào)將緩沖區(qū)排入隊(duì)列,如Listing 2-4
所示灵疮。 一旦回到緩沖區(qū)隊(duì)列中织阅,緩沖區(qū)就準(zhǔn)備好接受更多的輸入音頻數(shù)據(jù)。
// Listing 2-4 Enqueuing an audio queue buffer after writing to disk
AudioQueueEnqueueBuffer ( // 1
pAqData->mQueue, // 2
inBuffer, // 3
0, // 4
NULL // 5
);
以下是這段代碼的工作原理:
-
AudioQueueEnqueueBuffer
函數(shù)將音頻隊(duì)列緩沖區(qū)添加到音頻隊(duì)列的緩沖區(qū)隊(duì)列中震捣。
-
- 將音頻隊(duì)列添加到指定的音頻隊(duì)列緩沖區(qū)荔棉。
pAqData
變量是一個(gè)指向Listing 2-1
中描述的數(shù)據(jù)結(jié)構(gòu)的指針。
- 將音頻隊(duì)列添加到指定的音頻隊(duì)列緩沖區(qū)荔棉。
- 要排隊(duì)的音頻隊(duì)列緩沖區(qū)蒿赢。
- 音頻隊(duì)列緩沖區(qū)數(shù)據(jù)中的數(shù)據(jù)包描述數(shù)量润樱。 設(shè)置為0,因?yàn)榇藚?shù)未用于記錄羡棵。
- 描述音頻隊(duì)列緩沖區(qū)數(shù)據(jù)的數(shù)據(jù)包描述數(shù)組壹若。 設(shè)置為
NULL
,因?yàn)榇藚?shù)未用于記錄皂冰。
- 描述音頻隊(duì)列緩沖區(qū)數(shù)據(jù)的數(shù)據(jù)包描述數(shù)組壹若。 設(shè)置為
4. A Full Recording Audio Queue Callback - 一個(gè)完整的錄制音頻隊(duì)列回調(diào)
Listing 2-5
顯示了完整記錄音頻隊(duì)列回調(diào)的基本版本店展。 與本文檔中的其他代碼示例一樣,此列表不包括錯(cuò)誤處理秃流。
// Listing 2-5 A recording audio queue callback function
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
);
}
以下是這段代碼的工作原理:
- 實(shí)例化時(shí)提供給音頻隊(duì)列對(duì)象的自定義結(jié)構(gòu)赂蕴,包括表示要錄制的音頻文件的音頻文件對(duì)象以及各種狀態(tài)數(shù)據(jù)。請(qǐng)參閱Define a Custom Structure to Manage State剔应。
- 如果音頻隊(duì)列緩沖區(qū)包含CBR數(shù)據(jù)睡腿,則計(jì)算緩沖區(qū)中的數(shù)據(jù)包數(shù)量语御。此數(shù)字等于緩沖區(qū)中的數(shù)據(jù)總字節(jié)數(shù)除以每個(gè)數(shù)據(jù)包的(恒定)字節(jié)數(shù)。對(duì)于VBR數(shù)據(jù)席怪,音頻隊(duì)列在調(diào)用回調(diào)時(shí)提供緩沖區(qū)中的數(shù)據(jù)包數(shù)量应闯。
- 將緩沖區(qū)的內(nèi)容寫入音頻數(shù)據(jù)文件。有關(guān)詳細(xì)說(shuō)明挂捻,請(qǐng)參閱Writing an Audio Queue Buffer to Disk碉纺。
- 如果成功寫入音頻數(shù)據(jù),則增加音頻數(shù)據(jù)文件的包索引以準(zhǔn)備好寫入下一個(gè)緩沖器的音頻數(shù)據(jù)刻撒。
- 如果音頻隊(duì)列已經(jīng)停止骨田,則返回。
- 將剛剛寫入內(nèi)容的音頻隊(duì)列緩沖區(qū)重新入隊(duì)声怔。有關(guān)詳細(xì)說(shuō)明态贤,請(qǐng)參閱Enqueuing an Audio Queue Buffer。
Write a Function to Derive Recording Audio Queue Buffer Size - 編寫一個(gè)函數(shù)來(lái)獲取記錄音頻隊(duì)列緩沖區(qū)大小
音頻隊(duì)列服務(wù)期望您的應(yīng)用程序指定您使用的音頻隊(duì)列緩沖區(qū)的大小醋火。 Listing 2-6
顯示了一個(gè)這樣做的方法悠汽。 它產(chǎn)生足夠大的緩沖區(qū)大小來(lái)保存給定持續(xù)時(shí)間的音頻數(shù)據(jù)。
這里的計(jì)算考慮了您正在記錄的音頻數(shù)據(jù)格式芥驳。 格式包括可能影響緩沖區(qū)大小的所有因素柿冲,例如音頻通道的數(shù)量。
// Listing 2-6 Deriving a recording audio queue buffer size
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
}
以下是這段代碼的工作原理:
- 音頻隊(duì)列擁有您要指定大小的緩沖區(qū)兆旬。
- 音頻隊(duì)列的
AudioStreamBasicDescription結(jié)
構(gòu)假抄。
- 音頻隊(duì)列的
- 按照音頻的秒數(shù)指定每個(gè)音頻隊(duì)列緩沖區(qū)的大小。
- 輸出時(shí)丽猬,按字節(jié)計(jì)算每個(gè)音頻隊(duì)列緩沖區(qū)的大小宿饱。
- 音頻隊(duì)列緩沖區(qū)大小的上限(以字節(jié)為單位)。在這個(gè)例子中宝鼓,上限設(shè)置為320 KB刑棵。這對(duì)應(yīng)于大約五秒鐘的立體聲,采樣率為96kHz的24位音頻愚铡。
- 對(duì)于CBR音頻數(shù)據(jù)蛉签,從
AudioStreamBasicDescription
結(jié)構(gòu)獲取(恒定)數(shù)據(jù)包大小沥寥。使用此值作為最大數(shù)據(jù)包大小碍舍。該做法具有確定要記錄的音頻數(shù)據(jù)是CBR還是VBR的副作用。如果是VBR邑雅,則音頻隊(duì)列的AudioStreamBasicDescription
結(jié)構(gòu)將每個(gè)數(shù)據(jù)包的字節(jié)數(shù)值列為0片橡。
- 對(duì)于CBR音頻數(shù)據(jù)蛉签,從
- 對(duì)于VBR音頻數(shù)據(jù),查詢音頻隊(duì)列以獲取估計(jì)的最大數(shù)據(jù)包大小淮野。
- 以字節(jié)為單位獲取緩沖區(qū)大小捧书。
- 將緩沖區(qū)大写蹬荨(如果需要)限制到先前設(shè)置的上限。
Set a Magic Cookie for an Audio File - 為聲音文件設(shè)置一個(gè)Magic Cookie
某些壓縮音頻格式(如MPEG 4 AAC
)利用包含音頻元數(shù)據(jù)的結(jié)構(gòu)经瓷。 這些結(jié)構(gòu)被稱為Magic Cookie
爆哑。 當(dāng)您使用音頻隊(duì)列服務(wù)錄制這種格式時(shí),您必須從音頻隊(duì)列中獲取Magic Cookie
并將其添加到音頻文件舆吮,然后再開始錄制揭朝。
Listing 2-7
顯示了如何從音頻隊(duì)列中獲取Magic Cookie并將其應(yīng)用于音頻文件。 在錄制之前色冀,您的代碼會(huì)調(diào)用這樣的函數(shù)潭袱,然后在錄制之后再次調(diào)用一些函數(shù) —— 一些編解碼器在錄制停止時(shí)更新Magic Cookie數(shù)據(jù)。
// Listing 2-7 Setting a magic cookie for an audio file
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
}
以下是這段代碼的工作原理:
- 您用于錄制的音頻隊(duì)列锋恬。
- 您正在錄制寫入的音頻文件。
- 指示此函數(shù)成功或失敗的結(jié)果變量伶氢。
- 一個(gè)變量來(lái)保存magic cooki的數(shù)據(jù)大小趟径。
- 從音頻隊(duì)列中獲取magic cooki的數(shù)據(jù)大小瘪吏,并將其存儲(chǔ)在
cookieSize
變量中癣防。
- 從音頻隊(duì)列中獲取magic cooki的數(shù)據(jù)大小瘪吏,并將其存儲(chǔ)在
- 分配一個(gè)字節(jié)數(shù)組來(lái)保存魔法cookie信息。
- 通過(guò)查詢音頻隊(duì)列的
kAudioQueueProperty_MagicCookie
屬性獲取magic cooki掌眠。
- 通過(guò)查詢音頻隊(duì)列的
- 為正在錄制寫入的音頻文件設(shè)置
magic cookie
蕾盯。AudioFileSetProperty
函數(shù)在AudioFile.h
頭文件中聲明。
- 為正在錄制寫入的音頻文件設(shè)置
- 釋放臨時(shí)cookie變量的內(nèi)存蓝丙。
- 返回此函數(shù)的成功或失敗级遭。
Set Up an Audio Format for Recording - 設(shè)置錄制的音頻格式
本節(jié)介紹如何為音頻隊(duì)列設(shè)置音頻數(shù)據(jù)格式。 音頻隊(duì)列使用這種格式來(lái)記錄到一個(gè)文件渺尘。
要設(shè)置音頻數(shù)據(jù)格式挫鸽,請(qǐng)指定:
- 音頻數(shù)據(jù)格式類型(如線性PCM,AAC等)
- 采樣率(如44.1 kHz)
- 音頻通道數(shù)量(如2鸥跟,立體聲)
- 比特深度(如16比特)
- 每個(gè)數(shù)據(jù)包的幀數(shù)(線性PCM丢郊,例如,每個(gè)數(shù)據(jù)包使用一個(gè)幀)
- 音頻文件類型(如CAF医咨,AIFF等)
- 文件類型所需的音頻數(shù)據(jù)格式的詳細(xì)信息
Listing 2-8
說(shuō)明了如何為每個(gè)屬性設(shè)置一個(gè)固定的選項(xiàng)來(lái)設(shè)置錄制的音頻格式枫匾。 在產(chǎn)品代碼中,通常允許用戶指定音頻格式的一些或全部方面拟淮。 無(wú)論采取哪種方法干茉,目標(biāo)都是填充 AQRecorderState
自定義結(jié)構(gòu)的mDataFormat
字段,如 Define a Custom Structure to Manage State中所述很泊。
Listing 2-8 Specifying an audio queue’s audio data format
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;
以下是這段代碼的工作原理:
- 創(chuàng)建
AQRecorderState
自定義結(jié)構(gòu)的一個(gè)實(shí)例角虫。結(jié)構(gòu)的mDataFormat
字段包含一個(gè)AudioStreamBasicDescription
結(jié)構(gòu)沾谓。在mDataFormat
字段中設(shè)置的值為音頻隊(duì)列提供了音頻格式的初始定義,這也是您錄制文件的音頻格式戳鹅。在Listing 2-10中搏屑,您可以獲得更完整的音頻格式規(guī)范,Core Audio根據(jù)格式類型和文件類型為您提供了音頻格式粉楚。
- 創(chuàng)建
- 將音頻數(shù)據(jù)格式類型定義為線性PCM辣恋。請(qǐng)參閱Core Audio Data Types Reference以獲取可用數(shù)據(jù)格式的完整列表。
- 定義采樣率為44.1 kHz模软。
- 將通道數(shù)量定義為2伟骨。
- 將每個(gè)通道的位深度定義為16。
- 將每個(gè)數(shù)據(jù)包的字節(jié)數(shù)和每個(gè)字節(jié)的字節(jié)數(shù)定義為4(即每個(gè)采樣2個(gè)通道乘以2個(gè)字節(jié))燃异。
- 將每個(gè)數(shù)據(jù)包的幀數(shù)定義為1携狭。
- 將文件類型定義為AIFF。請(qǐng)參閱
AudioFile.h
頭文件中的音頻文件類型枚舉以獲取可用文件類型的完整列表回俐。您可以指定已安裝編解碼器的任何文件類型逛腿,如Using Codecs and Audio Data Formats中所述。
- 將文件類型定義為AIFF。請(qǐng)參閱
- 設(shè)置指定文件類型所需的格式標(biāo)志仅颇。
Create a Recording Audio Queue - 創(chuàng)建一個(gè)錄制音頻隊(duì)列
現(xiàn)在单默,通過(guò)設(shè)置錄制回調(diào)和音頻數(shù)據(jù)格式,您可以創(chuàng)建并配置一個(gè)音頻隊(duì)列進(jìn)行錄制忘瓦。
1. Creating a Recording Audio Queue - 創(chuàng)建一個(gè)錄制音頻隊(duì)列
Listing 2-9
演示了如何創(chuàng)建一個(gè)記錄音頻隊(duì)列搁廓。 請(qǐng)注意,AudioQueueNewInput
函數(shù)使用前面步驟中配置的回調(diào)耕皮,自定義結(jié)構(gòu)和音頻數(shù)據(jù)格式
// Listing 2-9 Creating a recording audio queue
AudioQueueNewInput ( // 1
&aqData.mDataFormat, // 2
HandleInputBuffer, // 3
&aqData, // 4
NULL, // 5
kCFRunLoopCommonModes, // 6
0, // 7
&aqData.mQueue // 8
);
以下是這段代碼的工作原理:
-
AudioQueueNewInput
函數(shù)創(chuàng)建一個(gè)新的錄音音頻隊(duì)列境蜕。
-
- 用于錄制的音頻數(shù)據(jù)格式。 請(qǐng)參閱Set Up an Audio Format for Recording凌停。
- 用于錄音音頻隊(duì)列的回調(diào)函數(shù)粱年。 請(qǐng)參閱Write a Recording Audio Queue Callback。
- 記錄音頻隊(duì)列的自定義數(shù)據(jù)結(jié)構(gòu)罚拟。 請(qǐng)參閱Define a Custom Structure to Manage State台诗。
- 將在其上調(diào)用回調(diào)的運(yùn)行循環(huán)。 使用NULL來(lái)指定默認(rèn)行為舟舒,在該行為中將在音頻隊(duì)列內(nèi)部的線程上調(diào)用回調(diào)拉庶。 這是典型的用法 - 它允許音頻隊(duì)列在您的應(yīng)用程序的用戶界面線程等待用戶輸入停止錄制時(shí)進(jìn)行錄制。
- 可以調(diào)用回調(diào)的運(yùn)行循環(huán)模式秃励。 通常氏仗,在這里使用
kCFRunLoopCommonModes
常量。
- 可以調(diào)用回調(diào)的運(yùn)行循環(huán)模式秃励。 通常氏仗,在這里使用
- 保留。 必須為0皆尔。
- 輸出時(shí)呐舔,新分配錄音音頻隊(duì)列。
2. Getting the Full Audio Format from an Audio Queue - 從音頻隊(duì)列中獲取完整的音頻格式
當(dāng)音頻隊(duì)列存在時(shí)(請(qǐng)參閱Creating a Recording Audio Queue)慷蠕,它可能已經(jīng)比您更完整地填充了AudioStreamBasicDescription
結(jié)構(gòu)珊拼,特別是對(duì)于壓縮格式。 要獲得完整的格式描述流炕,請(qǐng)調(diào)用AudioQueueGetProperty
函數(shù)澎现,如代碼Listing 2-10
所示。 創(chuàng)建要錄制的音頻文件時(shí)每辟,請(qǐng)使用完整的音頻格式(請(qǐng)參閱Create an Audio File)剑辫。
Listing 2-10 Getting the audio format from an 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
);
以下是這段代碼的工作原理:
- 獲取預(yù)期的屬性值大小,以便在查詢有關(guān)其音頻數(shù)據(jù)格式的音頻隊(duì)列時(shí)使用渠欺。
-
AudioQueueGetProperty
函數(shù)獲取音頻隊(duì)列中指定屬性的值妹蔽。
-
- 音頻隊(duì)列從中獲取音頻數(shù)據(jù)格式。
- 用于獲取音頻隊(duì)列數(shù)據(jù)格式值的屬性ID挠将。
- 在輸出時(shí)胳岂,從音頻隊(duì)列中獲得完整的音頻數(shù)據(jù)格式,以
AudioStreamBasicDescription
結(jié)構(gòu)的形式舔稀。
- 在輸出時(shí)胳岂,從音頻隊(duì)列中獲得完整的音頻數(shù)據(jù)格式,以
- 在輸入時(shí)乳丰,
AudioStreamBasicDescription
結(jié)構(gòu)的預(yù)期大小。 在輸出上镶蹋,實(shí)際的大小成艘。 您的錄制應(yīng)用程序不需要使用此值。
- 在輸入時(shí)乳丰,
Create an Audio File - 創(chuàng)建一個(gè)音頻文件
通過(guò)創(chuàng)建和配置音頻隊(duì)列贺归,您可以創(chuàng)建將錄制音頻數(shù)據(jù)的音頻文件,如Listing 2-11
所示断箫。 音頻文件使用先前存儲(chǔ)在音頻隊(duì)列的自定義結(jié)構(gòu)中的數(shù)據(jù)格式和文件格式規(guī)范拂酣。
// Listing 2-11 Creating an audio file for recording
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.h
頭文件中聲明的CFURLCreateFromFileSystemRepresentation
函數(shù)創(chuàng)建一個(gè)表示要記錄的文件的CFURL對(duì)象。
- 在
- 使用
NULL
(或kCFAllocatorDefault
)來(lái)使用當(dāng)前的默認(rèn)內(nèi)存分配器仲义。
- 使用
- 您要轉(zhuǎn)換為CFURL對(duì)象的文件系統(tǒng)路徑婶熬。在生產(chǎn)代碼中,通常會(huì)從用戶獲取filePath的值埃撵。
- 文件系統(tǒng)路徑中的字節(jié)數(shù)赵颅。
- 值為false表示代表文件的
filePath
,而不是目錄directory
暂刘。
- 值為false表示代表文件的
-
AudioFile.h
頭文件中的AudioFileCreateWithURL
函數(shù)創(chuàng)建一個(gè)新的音頻文件或初始化現(xiàn)有的文件饺谬。
-
- 用于創(chuàng)建新音頻文件或者在現(xiàn)有文件的情況下進(jìn)行初始化的URL,該URL是從步驟1中的
CFURLCreateFromFileSystemRepresentation
派生的谣拣。
- 用于創(chuàng)建新音頻文件或者在現(xiàn)有文件的情況下進(jìn)行初始化的URL,該URL是從步驟1中的
- 新文件的文件類型募寨。在本章的示例代碼中族展,這是以前通過(guò)
kAudioFileAIFFType
文件類型常量設(shè)置為AIFF
的。請(qǐng)參閱 Set Up an Audio Format for Recording拔鹰。
- 新文件的文件類型募寨。在本章的示例代碼中族展,這是以前通過(guò)
- 將被記錄到文件中的音頻的數(shù)據(jù)格式仪缸,指定為
AudioStreamBasicDescription
結(jié)構(gòu)。在本章的示例代碼中列肢,這也是在 Set Up an Audio Format for Recording中設(shè)置的恰画。
- 將被記錄到文件中的音頻的數(shù)據(jù)格式仪缸,指定為
- 在文件已經(jīng)存在的情況下擦除文件。
- 在輸出上瓷马,表示要錄制的音頻文件的音頻文件對(duì)象(類型
AudioFileID
)锣尉。
- 在輸出上瓷马,表示要錄制的音頻文件的音頻文件對(duì)象(類型
Set an Audio Queue Buffer Size - 設(shè)置音頻隊(duì)列的緩沖大小
在準(zhǔn)備錄制時(shí)使用的一組音頻隊(duì)列緩沖區(qū)之前,請(qǐng)使用您之前編寫的DeriveBufferSize
函數(shù)(請(qǐng)參閱 Write a Function to Derive Recording Audio Queue Buffer Size)决采。 您將此大小分配給正在使用的錄音音頻隊(duì)列自沧。 Listing 2-12
說(shuō)明了這一點(diǎn)。
// Listing 2-12 Setting an audio queue buffer size
DeriveBufferSize ( // 1
aqData.mQueue, // 2
aqData.mDataFormat, // 3
0.5, // 4
&aqData.bufferByteSize // 5
);
以下是這段代碼的工作原理:
- 在Write a Function to Derive Recording Audio Queue Buffer Size中描述的
DeriveBufferSize
函數(shù)設(shè)置適當(dāng)?shù)囊纛l隊(duì)列緩沖區(qū)大小树瞭。
- 在Write a Function to Derive Recording Audio Queue Buffer Size中描述的
- 您正在設(shè)置緩沖區(qū)大小的音頻隊(duì)列拇厢。
- 您正在錄制的文件的音頻數(shù)據(jù)格式。 請(qǐng)參閱Set Up an Audio Format for Recording晒喷。
- 每個(gè)音頻隊(duì)列緩沖區(qū)應(yīng)該容納的音頻的秒數(shù)孝偎。 半秒鐘,如這里設(shè)置凉敲,通常是一個(gè)不錯(cuò)的選擇衣盾。
- 輸出時(shí),每個(gè)音頻隊(duì)列緩沖區(qū)的大幸ァ(以字節(jié)為單位)势决。 該值放置在音頻隊(duì)列的自定義結(jié)構(gòu)中。
Prepare a Set of Audio Queue Buffers - 準(zhǔn)備一組音頻隊(duì)列緩沖
現(xiàn)在您可以詢問(wèn)您創(chuàng)建的音頻隊(duì)列(在Create a Recording Audio Queue中)以準(zhǔn)備一組音頻隊(duì)列緩沖區(qū)蓝撇。 Listing 2-13
演示了如何做到這一點(diǎn)果复。
Listing 2-13 Preparing a set of audio queue buffers
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
);
}
以下是這段代碼的工作原理:
- 迭代分配和排隊(duì)每個(gè)音頻隊(duì)列緩沖區(qū)。
-
AudioQueueAllocateBuffer
函數(shù)為音頻隊(duì)列分配一個(gè)音頻隊(duì)列緩沖區(qū)渤昌。
-
- 執(zhí)行分配并擁有緩沖區(qū)的音頻隊(duì)列虽抄。
- 新分配的音頻隊(duì)列緩沖區(qū)的大小(以字節(jié)為單位)独柑。 請(qǐng)參閱Write a Function to Derive Recording Audio Queue Buffer Size迈窟。
- 輸出時(shí),新分配的音頻隊(duì)列緩沖區(qū)忌栅。 指向緩沖區(qū)的指針位于您正在使用音頻隊(duì)列的自定義結(jié)構(gòu)中车酣。
-
AudioQueueEnqueueBuffer
函數(shù)將音頻隊(duì)列緩沖區(qū)添加到緩沖區(qū)隊(duì)列的末尾。
-
- 將緩沖添加到緩沖隊(duì)列的音頻隊(duì)列。
- 您排隊(duì)的音頻隊(duì)列緩沖區(qū)骇径。
- 入隊(duì)用于記錄的緩沖區(qū)時(shí)躯肌,此參數(shù)未使用。
- 入隊(duì)用于記錄的緩沖區(qū)時(shí)破衔,此參數(shù)未使用清女。
Record Audio - 錄制音頻
所有前面的代碼都導(dǎo)致了非常簡(jiǎn)單的記錄過(guò)程,如Listing 2-14
所示晰筛。
// Listing 2-14 Recording audio
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
以下是這段代碼的工作原理:
- 將數(shù)據(jù)包索引初始化為0嫡丙,開始在音頻文件的開頭進(jìn)行錄制。
- 在自定義結(jié)構(gòu)中設(shè)置一個(gè)標(biāo)志來(lái)指示音頻隊(duì)列正在運(yùn)行读第。 記錄音頻隊(duì)列回調(diào)callback使用此標(biāo)志曙博。
-
AudioQueueStart
函數(shù)在其自己的線程上啟動(dòng)音頻隊(duì)列。
-
- 音頻隊(duì)列開始父泳。
- 使用
NULL
來(lái)指示音頻隊(duì)列應(yīng)該立即開始記錄。
- 使用
-
AudioQueueStop
函數(shù)停止并重置錄音音頻隊(duì)列吴汪。
-
- 音頻隊(duì)列停止。
- 使用
true
來(lái)使用同步停止。 有關(guān)同步和異步停止的說(shuō)明藕各,請(qǐng)參閱Audio Queue Control and State。
- 使用
- 在自定義結(jié)構(gòu)中設(shè)置一個(gè)標(biāo)志來(lái)指示音頻隊(duì)列沒(méi)有運(yùn)行。
Clean Up After Recording - 錄制后的清除工作
當(dāng)您完成錄制時(shí)踢京,請(qǐng)銷毀音頻隊(duì)列并關(guān)閉音頻文件。 Listing 2-15
說(shuō)明了這些步驟宦棺。
// Listing 2-15 Cleaning up after recording
AudioQueueDispose ( // 1
aqData.mQueue, // 2
true // 3
);
AudioFileClose (aqData.mAudioFile); // 4
以下是這段代碼的工作原理:
-
AudioQueueDispose
函數(shù)銷毀音頻隊(duì)列及其所有資源瓣距,包括其緩沖區(qū)。
-
- 您想要銷毀的音頻隊(duì)列代咸。
- 使用true來(lái)同步銷毀音頻隊(duì)列(即立即)蹈丸。
- 關(guān)閉用于錄制的音頻文件。
AudioFileClose
函數(shù)在AudioFile.h
頭文件中聲明。
- 關(guān)閉用于錄制的音頻文件。
后記
未完逻杖,待續(xù)~~~