幾種播放音頻文件的方式(七) —— 音頻隊列服務(Audio Queue Services)之錄制音頻(四)

版本記錄

版本號 時間
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 CenterSpeakHere示例代碼。

要將錄制功能添加到應用程序中,通常需要執行以下步驟:

  • 定義一個自定義結構來管理狀態,格式和路徑信息。
  • 編寫一個音頻隊列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
};

以下是這個結構中的字段的描述:

    1. 設置要使用的音頻隊列緩沖區的數量。
    1. 表示要寫入磁盤的音頻數據格式的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
    1. 由您的應用程序創建錄制音頻隊列。
    1. 一個數組,指向由音頻隊列管理的音頻隊列緩沖區的指針。
    1. 音頻文件對象,表示您的程序將音頻數據記錄到其中的文件。
      每個音頻隊列緩沖區的大小(以字節為單位)。在創建音頻隊列之后并在啟動之前,在DeriveBufferSize函數的這些示例中計算此值。請參閱Write a Function to Derive Recording Audio Queue Buffer Size
    1. 要從當前音頻隊列緩沖區寫入的第一個數據包的數據包索引。
    1. 指示音頻隊列是否正在運行的布爾值。

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
)

以下是這段代碼的工作原理:

    1. 通常,aqData是包含音頻隊列的狀態數據的自定義結構,如 Define a Custom Structure to Manage State所述。
    1. 擁有此回調的音頻隊列。
    1. 包含要錄制的傳入音頻數據的音頻隊列緩沖區。
    1. 音頻隊列緩沖區中第一個采樣的采樣時間(簡單記錄不需要)。
    1. inPacketDesc參數中的數據包描述數量。 值為0表示CBR數據。
    1. 對于需要數據包描述的壓縮音頻數據格式,編碼器為緩沖區中數據包生成的數據包描述。

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
);

以下是這段代碼的工作原理:

    1. AudioFile.h頭文件中聲明的AudioFileWritePackets函數將緩沖區的內容寫入音頻數據文件。
    1. 音頻文件對象(類型AudioFileID),表示要寫入的音頻文件。 pAqData變量是一個指向Listing 2-1中描述的數據結構的指針。
    1. 使用false值表示函數在寫入時不應該緩存數據。
    1. 正在寫入的音頻數據的字節數。 inBuffer變量表示由音頻隊列交給回調的音頻隊列緩沖區。
    1. 音頻數據的數據包描述數組。 值為NULL表示不需要數據包描述(例如用于CBR音頻數據)。
    1. 要寫入的第一個數據包的數據包索引。
    1. 在輸入時,要寫入的數據包的數量。 輸出時,實際寫入的數據包數量。
    1. 將新的音頻數據寫入音頻文件。

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
);

以下是這段代碼的工作原理:

    1. AudioQueueEnqueueBuffer函數將音頻隊列緩沖區添加到音頻隊列的緩沖區隊列中。
    1. 將音頻隊列添加到指定的音頻隊列緩沖區。 pAqData變量是一個指向Listing 2-1中描述的數據結構的指針。
    1. 要排隊的音頻隊列緩沖區。
    1. 音頻隊列緩沖區數據中的數據包描述數量。 設置為0,因為此參數未用于記錄。
    1. 描述音頻隊列緩沖區數據的數據包描述數組。 設置為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
    );
}

以下是這段代碼的工作原理:

    1. 實例化時提供給音頻隊列對象的自定義結構,包括表示要錄制的音頻文件的音頻文件對象以及各種狀態數據。請參閱Define a Custom Structure to Manage State
    1. 如果音頻隊列緩沖區包含CBR數據,則計算緩沖區中的數據包數量。此數字等于緩沖區中的數據總字節數除以每個數據包的(恒定)字節數。對于VBR數據,音頻隊列在調用回調時提供緩沖區中的數據包數量。
    1. 將緩沖區的內容寫入音頻數據文件。有關詳細說明,請參閱Writing an Audio Queue Buffer to Disk
    1. 如果成功寫入音頻數據,則增加音頻數據文件的包索引以準備好寫入下一個緩沖器的音頻數據。
    1. 如果音頻隊列已經停止,則返回。
    1. 將剛剛寫入內容的音頻隊列緩沖區重新入隊。有關詳細說明,請參閱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
}

以下是這段代碼的工作原理:

    1. 音頻隊列擁有您要指定大小的緩沖區。
    1. 音頻隊列的AudioStreamBasicDescription結構。
    1. 按照音頻的秒數指定每個音頻隊列緩沖區的大小。
    1. 輸出時,按字節計算每個音頻隊列緩沖區的大小。
    1. 音頻隊列緩沖區大小的上限(以字節為單位)。在這個例子中,上限設置為320 KB。這對應于大約五秒鐘的立體聲,采樣率為96kHz的24位音頻。
    1. 對于CBR音頻數據,從AudioStreamBasicDescription結構獲取(恒定)數據包大小。使用此值作為最大數據包大小。該做法具有確定要記錄的音頻數據是CBR還是VBR的副作用。如果是VBR,則音頻隊列的AudioStreamBasicDescription結構將每個數據包的字節數值列為0。
    1. 對于VBR音頻數據,查詢音頻隊列以獲取估計的最大數據包大小。
    1. 以字節為單位獲取緩沖區大小。
    1. 將緩沖區大小(如果需要)限制到先前設置的上限。

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
}

以下是這段代碼的工作原理:

    1. 您用于錄制的音頻隊列。
    1. 您正在錄制寫入的音頻文件。
    1. 指示此函數成功或失敗的結果變量。
    1. 一個變量來保存magic cooki的數據大小。
    1. 從音頻隊列中獲取magic cooki的數據大小,并將其存儲在cookieSize變量中。
    1. 分配一個字節數組來保存魔法cookie信息。
    1. 通過查詢音頻隊列的kAudioQueueProperty_MagicCookie屬性獲取magic cooki。
    1. 為正在錄制寫入的音頻文件設置magic cookieAudioFileSetProperty函數在AudioFile.h頭文件中聲明。
    1. 釋放臨時cookie變量的內存。
    1. 返回此函數的成功或失敗。

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;

以下是這段代碼的工作原理:

    1. 創建AQRecorderState自定義結構的一個實例。結構的mDataFormat字段包含一個AudioStreamBasicDescription結構。在mDataFormat字段中設置的值為音頻隊列提供了音頻格式的初始定義,這也是您錄制文件的音頻格式。在Listing 2-10中,您可以獲得更完整的音頻格式規范,Core Audio根據格式類型和文件類型為您提供了音頻格式。
    1. 將音頻數據格式類型定義為線性PCM。請參閱Core Audio Data Types Reference以獲取可用數據格式的完整列表。
    1. 定義采樣率為44.1 kHz。
    1. 將通道數量定義為2。
    1. 將每個通道的位深度定義為16。
    1. 將每個數據包的字節數和每個字節的字節數定義為4(即每個采樣2個通道乘以2個字節)。
    1. 將每個數據包的幀數定義為1。
    1. 將文件類型定義為AIFF。請參閱AudioFile.h頭文件中的音頻文件類型枚舉以獲取可用文件類型的完整列表。您可以指定已安裝編解碼器的任何文件類型,如Using Codecs and Audio Data Formats中所述。
    1. 設置指定文件類型所需的格式標志。

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
);

以下是這段代碼的工作原理:

    1. AudioQueueNewInput函數創建一個新的錄音音頻隊列。
    1. 用于錄制的音頻數據格式。 請參閱Set Up an Audio Format for Recording
    1. 用于錄音音頻隊列的回調函數。 請參閱Write a Recording Audio Queue Callback
    1. 記錄音頻隊列的自定義數據結構。 請參閱Define a Custom Structure to Manage State
    1. 將在其上調用回調的運行循環。 使用NULL來指定默認行為,在該行為中將在音頻隊列內部的線程上調用回調。 這是典型的用法 - 它允許音頻隊列在您的應用程序的用戶界面線程等待用戶輸入停止錄制時進行錄制。
    1. 可以調用回調的運行循環模式。 通常,在這里使用kCFRunLoopCommonModes常量。
    1. 保留。 必須為0。
    1. 輸出時,新分配錄音音頻隊列。

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
);

以下是這段代碼的工作原理:

    1. 獲取預期的屬性值大小,以便在查詢有關其音頻數據格式的音頻隊列時使用。
    1. AudioQueueGetProperty函數獲取音頻隊列中指定屬性的值。
    1. 音頻隊列從中獲取音頻數據格式。
    1. 用于獲取音頻隊列數據格式值的屬性ID。
    1. 在輸出時,從音頻隊列中獲得完整的音頻數據格式,以AudioStreamBasicDescription結構的形式。
    1. 在輸入時,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
);

以下是這段代碼的工作原理:

    1. CFURL.h頭文件中聲明的CFURLCreateFromFileSystemRepresentation函數創建一個表示要記錄的文件的CFURL對象。
    1. 使用NULL(或kCFAllocatorDefault)來使用當前的默認內存分配器。
    1. 您要轉換為CFURL對象的文件系統路徑。在生產代碼中,通常會從用戶獲取filePath的值。
    1. 文件系統路徑中的字節數。
    1. 值為false表示代表文件的filePath,而不是目錄directory
    1. AudioFile.h頭文件中的AudioFileCreateWithURL函數創建一個新的音頻文件或初始化現有的文件。
    1. 用于創建新音頻文件或者在現有文件的情況下進行初始化的URL,該URL是從步驟1中的CFURLCreateFromFileSystemRepresentation派生的。
    1. 新文件的文件類型。在本章的示例代碼中,這是以前通過kAudioFileAIFFType文件類型常量設置為AIFF的。請參閱 Set Up an Audio Format for Recording
    1. 將被記錄到文件中的音頻的數據格式,指定為AudioStreamBasicDescription結構。在本章的示例代碼中,這也是在 Set Up an Audio Format for Recording中設置的。
    1. 在文件已經存在的情況下擦除文件。
    1. 在輸出上,表示要錄制的音頻文件的音頻文件對象(類型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
);

以下是這段代碼的工作原理:

    1. Write a Function to Derive Recording Audio Queue Buffer Size中描述的DeriveBufferSize函數設置適當的音頻隊列緩沖區大小。
    1. 您正在設置緩沖區大小的音頻隊列。
    1. 您正在錄制的文件的音頻數據格式。 請參閱Set Up an Audio Format for Recording
    1. 每個音頻隊列緩沖區應該容納的音頻的秒數。 半秒鐘,如這里設置,通常是一個不錯的選擇。
    1. 輸出時,每個音頻隊列緩沖區的大小(以字節為單位)。 該值放置在音頻隊列的自定義結構中。

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
    );
}

以下是這段代碼的工作原理:

    1. 迭代分配和排隊每個音頻隊列緩沖區。
    1. AudioQueueAllocateBuffer函數為音頻隊列分配一個音頻隊列緩沖區。
    1. 執行分配并擁有緩沖區的音頻隊列。
    1. 新分配的音頻隊列緩沖區的大小(以字節為單位)。 請參閱Write a Function to Derive Recording Audio Queue Buffer Size
    1. 輸出時,新分配的音頻隊列緩沖區。 指向緩沖區的指針位于您正在使用音頻隊列的自定義結構中。
    1. AudioQueueEnqueueBuffer函數將音頻隊列緩沖區添加到緩沖區隊列的末尾。
    1. 將緩沖添加到緩沖隊列的音頻隊列。
    1. 您排隊的音頻隊列緩沖區。
    1. 入隊用于記錄的緩沖區時,此參數未使用。
    1. 入隊用于記錄的緩沖區時,此參數未使用。

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

以下是這段代碼的工作原理:

    1. 將數據包索引初始化為0,開始在音頻文件的開頭進行錄制。
    1. 在自定義結構中設置一個標志來指示音頻隊列正在運行。 記錄音頻隊列回調callback使用此標志。
    1. AudioQueueStart函數在其自己的線程上啟動音頻隊列。
    1. 音頻隊列開始。
    1. 使用NULL來指示音頻隊列應該立即開始記錄。
    1. AudioQueueStop函數停止并重置錄音音頻隊列。
    1. 音頻隊列停止。
    1. 使用true來使用同步停止。 有關同步和異步停止的說明,請參閱Audio Queue Control and State
    1. 在自定義結構中設置一個標志來指示音頻隊列沒有運行。

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

以下是這段代碼的工作原理:

    1. AudioQueueDispose函數銷毀音頻隊列及其所有資源,包括其緩沖區。
    1. 您想要銷毀的音頻隊列。
    1. 使用true來同步銷毀音頻隊列(即立即)。
    1. 關閉用于錄制的音頻文件。 AudioFileClose函數在AudioFile.h頭文件中聲明。

后記

未完,待續~~~

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容