版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.12.28 |
前言
ios系統中有很多方式可以播放音頻文件,這里我們就詳細的說明下播放音樂文件的原理和實例。感興趣的可以看我寫的上面幾篇。
1. 幾種播放音頻文件的方式(一) —— 播放本地音樂
2. 幾種播放音頻文件的方式(二) —— 音效播放
3. 幾種播放音頻文件的方式(三) —— 網絡音樂播放
4. 幾種播放音頻文件的方式(四) —— 音頻隊列服務(Audio Queue Services)(一)
5. 幾種播放音頻文件的方式(五) —— 音頻隊列服務(Audio Queue Services)簡介(二)
6. 幾種播放音頻文件的方式(六) —— 音頻隊列服務(Audio Queue Services)之關于音頻隊列(三)
Recording Audio - 錄制音頻
使用音頻隊列服務進行錄制時,存儲目標可以是任何事物 —— 磁盤上的文件,網絡連接,內存中的對象等等。 本章介紹最常見的情況:基本錄制到磁盤文件。
注意:本章描述了一個基于
ANSI-C
的錄制實現,并使用了Mac OS X Core Audio SDK
中的C ++類。 對于基于Objective-C的示例,請參閱iOS Dev Center的SpeakHere
示例代碼。
要將錄制功能添加到應用程序中,通常需要執行以下步驟:
- 定義一個自定義結構來管理狀態,格式和路徑信息。
- 編寫一個音頻隊列callback回調函數來執行實際錄制。
- 可以編寫代碼來確定音頻隊列緩沖區的大小。 如果您將以使用cookie的格式進行錄制,編寫代碼與
magic cookies
一起配合。 - 填寫自定義結構的字段。 這包括指定音頻隊列發送到正在記錄的文件的數據流,以及該文件的路徑。
- 創建一個錄制音頻隊列,并要求它創建一組音頻隊列緩沖區。 還要創建一個文件來記錄。
- 告訴音頻隊列開始錄制。
- 完成后,告訴音頻隊列停止并銷毀。 音頻隊列銷毀其緩沖區。
本章的其余部分將詳細介紹這些步驟。
Define a Custom Structure to Manage State - 定義一個自定義結構來管理狀態
使用音頻隊列服務開發錄音解決方案的第一步是定義一個自定義結構。 您將使用此結構來管理音頻格式和音頻隊列狀態信息。 Listing 2-1
說明了這樣一個結構:
// 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
};
以下是這個結構中的字段的描述:
- 設置要使用的音頻隊列緩沖區的數量。
- 表示要寫入磁盤的音頻數據格式的
AudioStreamBasicDescription
結構(來自CoreAudioTypes.h
)。該格式被mQueue
字段中指定的音頻隊列使用。
mDataFormat
字段最初由程序中的代碼填充,如 Set Up an Audio Format for Recording中所述。最好通過查詢音頻隊列的kAudioQueueProperty_StreamDescription
屬性來更新此字段的值,如Getting the Full Audio Format from an Audio Queue中所述。在Mac OS X v10.5
上,改用kAudioConverterCurrentInputStreamDescription
屬性。有關AudioStreamBasicDescription
結構的詳細信息,請參閱Core Audio Data Types Reference。
- 表示要寫入磁盤的音頻數據格式的
- 由您的應用程序創建錄制音頻隊列。
- 一個數組,指向由音頻隊列管理的音頻隊列緩沖區的指針。
- 音頻文件對象,表示您的程序將音頻數據記錄到其中的文件。
每個音頻隊列緩沖區的大小(以字節為單位)。在創建音頻隊列之后并在啟動之前,在DeriveBufferSize
函數的這些示例中計算此值。請參閱Write a Function to Derive Recording Audio Queue Buffer Size。
- 音頻文件對象,表示您的程序將音頻數據記錄到其中的文件。
- 要從當前音頻隊列緩沖區寫入的第一個數據包的數據包索引。
- 指示音頻隊列是否正在運行的布爾值。
Write a Recording Audio Queue Callback - 寫一個錄制音頻隊列回調函數
接下來,寫一個錄音音頻隊列回調函數。 這個回調做了兩件事:
- 將新填充的音頻隊列緩沖區的內容寫入到正在錄制的音頻文件中
- 將音頻隊列緩沖區(其內容剛剛寫入磁盤)排入緩沖區隊列
本節展示一個示例回調聲明,然后分別描述這兩個任務,最后展示一個完整的錄制回調。 有關錄制音頻隊列回調角色的說明,請參閱Figure 1-3。
1. The Recording Audio Queue Callback Declaration - 錄制音頻隊列回調函數聲明
Listing 2-2
顯示了一個記錄音頻隊列回調函數的示例聲明,聲明為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
是包含音頻隊列的狀態數據的自定義結構,如 Define a Custom Structure to Manage State所述。
- 通常,
- 擁有此回調的音頻隊列。
- 包含要錄制的傳入音頻數據的音頻隊列緩沖區。
- 音頻隊列緩沖區中第一個采樣的采樣時間(簡單記錄不需要)。
-
inPacketDesc
參數中的數據包描述數量。 值為0表示CBR數據。
-
- 對于需要數據包描述的壓縮音頻數據格式,編碼器為緩沖區中數據包生成的數據包描述。
2. Writing an Audio Queue Buffer to Disk - 將音頻隊列緩沖區寫入磁盤
錄音的音頻隊列回調的第一項任務是將音頻隊列緩沖區寫入磁盤。 這個緩沖區是回調的音頻隊列剛剛完成填充來自輸入設備的新的音頻數據的那個緩沖區。 回調使用AudioFile.h
頭文件中的AudioFileWritePackets
函數,如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
函數將緩沖區的內容寫入音頻數據文件。
- 在
- 音頻文件對象(類型
AudioFileID
),表示要寫入的音頻文件。pAqData
變量是一個指向Listing 2-1
中描述的數據結構的指針。
- 音頻文件對象(類型
- 使用false值表示函數在寫入時不應該緩存數據。
- 正在寫入的音頻數據的字節數。
inBuffer
變量表示由音頻隊列交給回調的音頻隊列緩沖區。
- 正在寫入的音頻數據的字節數。
- 音頻數據的數據包描述數組。 值為
NULL
表示不需要數據包描述(例如用于CBR音頻數據)。
- 音頻數據的數據包描述數組。 值為
- 要寫入的第一個數據包的數據包索引。
- 在輸入時,要寫入的數據包的數量。 輸出時,實際寫入的數據包數量。
- 將新的音頻數據寫入音頻文件。
3. Enqueuing an Audio Queue Buffer - 將一個音頻隊列緩沖入隊
現在來自音頻隊列緩沖區的音頻數據已被寫入音頻文件,回調將緩沖區排入隊列,如Listing 2-4
所示。 一旦回到緩沖區隊列中,緩沖區就準備好接受更多的輸入音頻數據。
// Listing 2-4 Enqueuing an audio queue buffer after writing to disk
AudioQueueEnqueueBuffer ( // 1
pAqData->mQueue, // 2
inBuffer, // 3
0, // 4
NULL // 5
);
以下是這段代碼的工作原理:
-
AudioQueueEnqueueBuffer
函數將音頻隊列緩沖區添加到音頻隊列的緩沖區隊列中。
-
- 將音頻隊列添加到指定的音頻隊列緩沖區。
pAqData
變量是一個指向Listing 2-1
中描述的數據結構的指針。
- 將音頻隊列添加到指定的音頻隊列緩沖區。
- 要排隊的音頻隊列緩沖區。
- 音頻隊列緩沖區數據中的數據包描述數量。 設置為0,因為此參數未用于記錄。
- 描述音頻隊列緩沖區數據的數據包描述數組。 設置為
NULL
,因為此參數未用于記錄。
- 描述音頻隊列緩沖區數據的數據包描述數組。 設置為
4. A Full Recording Audio Queue Callback - 一個完整的錄制音頻隊列回調
Listing 2-5
顯示了完整記錄音頻隊列回調的基本版本。 與本文檔中的其他代碼示例一樣,此列表不包括錯誤處理。
// 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
);
}
以下是這段代碼的工作原理:
- 實例化時提供給音頻隊列對象的自定義結構,包括表示要錄制的音頻文件的音頻文件對象以及各種狀態數據。請參閱Define a Custom Structure to Manage State。
- 如果音頻隊列緩沖區包含CBR數據,則計算緩沖區中的數據包數量。此數字等于緩沖區中的數據總字節數除以每個數據包的(恒定)字節數。對于VBR數據,音頻隊列在調用回調時提供緩沖區中的數據包數量。
- 將緩沖區的內容寫入音頻數據文件。有關詳細說明,請參閱Writing an Audio Queue Buffer to Disk。
- 如果成功寫入音頻數據,則增加音頻數據文件的包索引以準備好寫入下一個緩沖器的音頻數據。
- 如果音頻隊列已經停止,則返回。
- 將剛剛寫入內容的音頻隊列緩沖區重新入隊。有關詳細說明,請參閱Enqueuing an Audio Queue Buffer。
Write a Function to Derive Recording Audio Queue Buffer Size - 編寫一個函數來獲取記錄音頻隊列緩沖區大小
音頻隊列服務期望您的應用程序指定您使用的音頻隊列緩沖區的大小。 Listing 2-6
顯示了一個這樣做的方法。 它產生足夠大的緩沖區大小來保存給定持續時間的音頻數據。
這里的計算考慮了您正在記錄的音頻數據格式。 格式包括可能影響緩沖區大小的所有因素,例如音頻通道的數量。
// 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
}
以下是這段代碼的工作原理:
- 音頻隊列擁有您要指定大小的緩沖區。
- 音頻隊列的
AudioStreamBasicDescription結
構。
- 音頻隊列的
- 按照音頻的秒數指定每個音頻隊列緩沖區的大小。
- 輸出時,按字節計算每個音頻隊列緩沖區的大小。
- 音頻隊列緩沖區大小的上限(以字節為單位)。在這個例子中,上限設置為320 KB。這對應于大約五秒鐘的立體聲,采樣率為96kHz的24位音頻。
- 對于CBR音頻數據,從
AudioStreamBasicDescription
結構獲取(恒定)數據包大小。使用此值作為最大數據包大小。該做法具有確定要記錄的音頻數據是CBR還是VBR的副作用。如果是VBR,則音頻隊列的AudioStreamBasicDescription
結構將每個數據包的字節數值列為0。
- 對于CBR音頻數據,從
- 對于VBR音頻數據,查詢音頻隊列以獲取估計的最大數據包大小。
- 以字節為單位獲取緩沖區大小。
- 將緩沖區大小(如果需要)限制到先前設置的上限。
Set a Magic Cookie for an Audio File - 為聲音文件設置一個Magic Cookie
某些壓縮音頻格式(如MPEG 4 AAC
)利用包含音頻元數據的結構。 這些結構被稱為Magic Cookie
。 當您使用音頻隊列服務錄制這種格式時,您必須從音頻隊列中獲取Magic Cookie
并將其添加到音頻文件,然后再開始錄制。
Listing 2-7
顯示了如何從音頻隊列中獲取Magic Cookie并將其應用于音頻文件。 在錄制之前,您的代碼會調用這樣的函數,然后在錄制之后再次調用一些函數 —— 一些編解碼器在錄制停止時更新Magic Cookie數據。
// 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
}
以下是這段代碼的工作原理:
- 您用于錄制的音頻隊列。
- 您正在錄制寫入的音頻文件。
- 指示此函數成功或失敗的結果變量。
- 一個變量來保存magic cooki的數據大小。
- 從音頻隊列中獲取magic cooki的數據大小,并將其存儲在
cookieSize
變量中。
- 從音頻隊列中獲取magic cooki的數據大小,并將其存儲在
- 分配一個字節數組來保存魔法cookie信息。
- 通過查詢音頻隊列的
kAudioQueueProperty_MagicCookie
屬性獲取magic cooki。
- 通過查詢音頻隊列的
- 為正在錄制寫入的音頻文件設置
magic cookie
。AudioFileSetProperty
函數在AudioFile.h
頭文件中聲明。
- 為正在錄制寫入的音頻文件設置
- 釋放臨時cookie變量的內存。
- 返回此函數的成功或失敗。
Set Up an Audio Format for Recording - 設置錄制的音頻格式
本節介紹如何為音頻隊列設置音頻數據格式。 音頻隊列使用這種格式來記錄到一個文件。
要設置音頻數據格式,請指定:
- 音頻數據格式類型(如線性PCM,AAC等)
- 采樣率(如44.1 kHz)
- 音頻通道數量(如2,立體聲)
- 比特深度(如16比特)
- 每個數據包的幀數(線性PCM,例如,每個數據包使用一個幀)
- 音頻文件類型(如CAF,AIFF等)
- 文件類型所需的音頻數據格式的詳細信息
Listing 2-8
說明了如何為每個屬性設置一個固定的選項來設置錄制的音頻格式。 在產品代碼中,通常允許用戶指定音頻格式的一些或全部方面。 無論采取哪種方法,目標都是填充 AQRecorderState
自定義結構的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;
以下是這段代碼的工作原理:
- 創建
AQRecorderState
自定義結構的一個實例。結構的mDataFormat
字段包含一個AudioStreamBasicDescription
結構。在mDataFormat
字段中設置的值為音頻隊列提供了音頻格式的初始定義,這也是您錄制文件的音頻格式。在Listing 2-10中,您可以獲得更完整的音頻格式規范,Core Audio根據格式類型和文件類型為您提供了音頻格式。
- 創建
- 將音頻數據格式類型定義為線性PCM。請參閱Core Audio Data Types Reference以獲取可用數據格式的完整列表。
- 定義采樣率為44.1 kHz。
- 將通道數量定義為2。
- 將每個通道的位深度定義為16。
- 將每個數據包的字節數和每個字節的字節數定義為4(即每個采樣2個通道乘以2個字節)。
- 將每個數據包的幀數定義為1。
- 將文件類型定義為AIFF。請參閱
AudioFile.h
頭文件中的音頻文件類型枚舉以獲取可用文件類型的完整列表。您可以指定已安裝編解碼器的任何文件類型,如Using Codecs and Audio Data Formats中所述。
- 將文件類型定義為AIFF。請參閱
- 設置指定文件類型所需的格式標志。
Create a Recording Audio Queue - 創建一個錄制音頻隊列
現在,通過設置錄制回調和音頻數據格式,您可以創建并配置一個音頻隊列進行錄制。
1. Creating a Recording Audio Queue - 創建一個錄制音頻隊列
Listing 2-9
演示了如何創建一個記錄音頻隊列。 請注意,AudioQueueNewInput
函數使用前面步驟中配置的回調,自定義結構和音頻數據格式
// 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
函數創建一個新的錄音音頻隊列。
-
- 用于錄制的音頻數據格式。 請參閱Set Up an Audio Format for Recording。
- 用于錄音音頻隊列的回調函數。 請參閱Write a Recording Audio Queue Callback。
- 記錄音頻隊列的自定義數據結構。 請參閱Define a Custom Structure to Manage State。
- 將在其上調用回調的運行循環。 使用NULL來指定默認行為,在該行為中將在音頻隊列內部的線程上調用回調。 這是典型的用法 - 它允許音頻隊列在您的應用程序的用戶界面線程等待用戶輸入停止錄制時進行錄制。
- 可以調用回調的運行循環模式。 通常,在這里使用
kCFRunLoopCommonModes
常量。
- 可以調用回調的運行循環模式。 通常,在這里使用
- 保留。 必須為0。
- 輸出時,新分配錄音音頻隊列。
2. Getting the Full Audio Format from an Audio Queue - 從音頻隊列中獲取完整的音頻格式
當音頻隊列存在時(請參閱Creating a Recording Audio Queue),它可能已經比您更完整地填充了AudioStreamBasicDescription
結構,特別是對于壓縮格式。 要獲得完整的格式描述,請調用AudioQueueGetProperty
函數,如代碼Listing 2-10
所示。 創建要錄制的音頻文件時,請使用完整的音頻格式(請參閱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
);
以下是這段代碼的工作原理:
- 獲取預期的屬性值大小,以便在查詢有關其音頻數據格式的音頻隊列時使用。
-
AudioQueueGetProperty
函數獲取音頻隊列中指定屬性的值。
-
- 音頻隊列從中獲取音頻數據格式。
- 用于獲取音頻隊列數據格式值的屬性ID。
- 在輸出時,從音頻隊列中獲得完整的音頻數據格式,以
AudioStreamBasicDescription
結構的形式。
- 在輸出時,從音頻隊列中獲得完整的音頻數據格式,以
- 在輸入時,
AudioStreamBasicDescription
結構的預期大小。 在輸出上,實際的大小。 您的錄制應用程序不需要使用此值。
- 在輸入時,
Create an Audio File - 創建一個音頻文件
通過創建和配置音頻隊列,您可以創建將錄制音頻數據的音頻文件,如Listing 2-11
所示。 音頻文件使用先前存儲在音頻隊列的自定義結構中的數據格式和文件格式規范。
// 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
函數創建一個表示要記錄的文件的CFURL對象。
- 在
- 使用
NULL
(或kCFAllocatorDefault
)來使用當前的默認內存分配器。
- 使用
- 您要轉換為CFURL對象的文件系統路徑。在生產代碼中,通常會從用戶獲取filePath的值。
- 文件系統路徑中的字節數。
- 值為false表示代表文件的
filePath
,而不是目錄directory
。
- 值為false表示代表文件的
-
AudioFile.h
頭文件中的AudioFileCreateWithURL
函數創建一個新的音頻文件或初始化現有的文件。
-
- 用于創建新音頻文件或者在現有文件的情況下進行初始化的URL,該URL是從步驟1中的
CFURLCreateFromFileSystemRepresentation
派生的。
- 用于創建新音頻文件或者在現有文件的情況下進行初始化的URL,該URL是從步驟1中的
- 新文件的文件類型。在本章的示例代碼中,這是以前通過
kAudioFileAIFFType
文件類型常量設置為AIFF
的。請參閱 Set Up an Audio Format for Recording。
- 新文件的文件類型。在本章的示例代碼中,這是以前通過
- 將被記錄到文件中的音頻的數據格式,指定為
AudioStreamBasicDescription
結構。在本章的示例代碼中,這也是在 Set Up an Audio Format for Recording中設置的。
- 將被記錄到文件中的音頻的數據格式,指定為
- 在文件已經存在的情況下擦除文件。
- 在輸出上,表示要錄制的音頻文件的音頻文件對象(類型
AudioFileID
)。
- 在輸出上,表示要錄制的音頻文件的音頻文件對象(類型
Set an Audio Queue Buffer Size - 設置音頻隊列的緩沖大小
在準備錄制時使用的一組音頻隊列緩沖區之前,請使用您之前編寫的DeriveBufferSize
函數(請參閱 Write a Function to Derive Recording Audio Queue Buffer Size)。 您將此大小分配給正在使用的錄音音頻隊列。 Listing 2-12
說明了這一點。
// 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
函數設置適當的音頻隊列緩沖區大小。
- 在Write a Function to Derive Recording Audio Queue Buffer Size中描述的
- 您正在設置緩沖區大小的音頻隊列。
- 您正在錄制的文件的音頻數據格式。 請參閱Set Up an Audio Format for Recording。
- 每個音頻隊列緩沖區應該容納的音頻的秒數。 半秒鐘,如這里設置,通常是一個不錯的選擇。
- 輸出時,每個音頻隊列緩沖區的大小(以字節為單位)。 該值放置在音頻隊列的自定義結構中。
Prepare a Set of Audio Queue Buffers - 準備一組音頻隊列緩沖
現在您可以詢問您創建的音頻隊列(在Create a Recording Audio Queue中)以準備一組音頻隊列緩沖區。 Listing 2-13
演示了如何做到這一點。
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
);
}
以下是這段代碼的工作原理:
- 迭代分配和排隊每個音頻隊列緩沖區。
-
AudioQueueAllocateBuffer
函數為音頻隊列分配一個音頻隊列緩沖區。
-
- 執行分配并擁有緩沖區的音頻隊列。
- 新分配的音頻隊列緩沖區的大小(以字節為單位)。 請參閱Write a Function to Derive Recording Audio Queue Buffer Size。
- 輸出時,新分配的音頻隊列緩沖區。 指向緩沖區的指針位于您正在使用音頻隊列的自定義結構中。
-
AudioQueueEnqueueBuffer
函數將音頻隊列緩沖區添加到緩沖區隊列的末尾。
-
- 將緩沖添加到緩沖隊列的音頻隊列。
- 您排隊的音頻隊列緩沖區。
- 入隊用于記錄的緩沖區時,此參數未使用。
- 入隊用于記錄的緩沖區時,此參數未使用。
Record Audio - 錄制音頻
所有前面的代碼都導致了非常簡單的記錄過程,如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
以下是這段代碼的工作原理:
- 將數據包索引初始化為0,開始在音頻文件的開頭進行錄制。
- 在自定義結構中設置一個標志來指示音頻隊列正在運行。 記錄音頻隊列回調callback使用此標志。
-
AudioQueueStart
函數在其自己的線程上啟動音頻隊列。
-
- 音頻隊列開始。
- 使用
NULL
來指示音頻隊列應該立即開始記錄。
- 使用
-
AudioQueueStop
函數停止并重置錄音音頻隊列。
-
- 音頻隊列停止。
- 使用
true
來使用同步停止。 有關同步和異步停止的說明,請參閱Audio Queue Control and State。
- 使用
- 在自定義結構中設置一個標志來指示音頻隊列沒有運行。
Clean Up After Recording - 錄制后的清除工作
當您完成錄制時,請銷毀音頻隊列并關閉音頻文件。 Listing 2-15
說明了這些步驟。
// Listing 2-15 Cleaning up after recording
AudioQueueDispose ( // 1
aqData.mQueue, // 2
true // 3
);
AudioFileClose (aqData.mAudioFile); // 4
以下是這段代碼的工作原理:
-
AudioQueueDispose
函數銷毀音頻隊列及其所有資源,包括其緩沖區。
-
- 您想要銷毀的音頻隊列。
- 使用true來同步銷毀音頻隊列(即立即)。
- 關閉用于錄制的音頻文件。
AudioFileClose
函數在AudioFile.h
頭文件中聲明。
- 關閉用于錄制的音頻文件。
后記
未完,待續~~~