ソースの表示以前のリビジョンバックリンク全て展開する/折り畳む文書の先頭へ Share via Share via... Twitter LinkedIn Facebook Pinterest Telegram WhatsApp Yammer Reddit Teams最近の変更Send via e-Mail印刷パーマリンク × 文書の過去の版を表示しています。 <align right>翻訳元:Audio Queue Services Programming Guide: Recording Audio</align> ※以前のwikiで本章は翻訳途中だったため、10.9のリファレンスから再翻訳しています。 音声の記録 Audio Queue Serviceを使って録音する際、その保存先はディスク上のファイル、ネットワークの接続先、メモリ内のオブジェクトなど、殆ど何でも選択出来ます。 本章では最も一般的なシナリオである、ディスクのファイルへの基本的な録音方法を解説します。 本章はMac OS X Core Audio SDKのいくつかのCクラスを用いた、ANSI-Cベースの録音の実装について解説します。Objective-Cベースの例は、[[http://developer.apple.com/devcenter/ios/ iOS Dev Center]]のSpeakHereサンプルコードをご覧ください。</note> アプリケーションに録音機能を追加するには、一般的に以下の手順を踏みます: - 状態(state)、フォーマット、ファイルパス情報を管理する独自構造体を定義する。 - 実際の録音を担うAudio Queueコールバック関数を書く。 - (任意で)Audio Queue Bufferの最適なサイズを決めるコードを書く。マジッククッキーを使うフォーマットで録音するなら、マジッククッキーが機能するコードを書く。 - 独自構造体のフィールドを埋める。これにはファイルへのパスは勿論、Audio Queueがファイルへ送る録音データストリームの指定も含む。 - 録音Audio Queueを生成し、Audio Queue Bufferセット作成のために問い合わせる。また、記録に使うファイルを作成する。 - Audio Queueに録音開始を伝える。 - 録音が終わったらAudio Queueを停止し、そして破棄する。Audio Queueは保持するバッファを破棄する。 以降、本章ではこれら手順の詳細を解説します。 ===== 状態を管理する独自構造体の定義 ===== Audio Queue Serviceを使った録音ソリューション開発の第1ステップは、独自構造体の定義です。 この構造体を使い、音声フォーマットとAudio Queueの状態管理を行います。 リスト2-1は構造体の例です: リスト 2-1 録音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 }; この構造体のフィールドの解説です。 - 使用するAudio Queue Bufferの数の設定。 - <ilcode>AudioStreamBasicDescription</ilcode>構造体(<ilcode>CoreAudioType.h</ilcode>より)は、ディスクに書き込む音声データフォーマットを表します。このフォーマットは<ilcode>mQueue</ilcode>フィールドで指定されるAudio Queueによって使用されます。 - <ilcode>mDataFormat</ilcode>フィールドは、“Set Up an Audio Format for Recording”で解説されるように、最初はあなたのプログラムのコードによって埋められます。“Getting the Full Audio Format from an Audio Queue”で解説するように、Audio Queueに<ilcode>kAudioQueueProperty_StreamDescription</ilcode>プロパティを問い合わせて、このフィールドを更新するのが良い方法です。Mac OS X v10.5では、代わりに<ilcode>kAudioConverterCurrentInputStreamDescription</ilcode>プロパティを使います。 For details on the AudioStreamBasicDescription structure, see Core Audio Data Types Reference. The recording audio queue created by your application. An array holding pointers to the audio queue buffers managed by the audio queue. An audio file object representing the file into which your program records audio data. The size, in bytes, for each audio queue buffer. This value is calculated in these examples in the DeriveBufferSize function, after the audio queue is created and before it is started. See “Write a Function to Derive Recording Audio Queue Buffer Size.” The packet index for the first packet to be written from the current audio queue buffer. A Boolean value indicating whether or not the audio queue is running. Write a Recording Audio Queue Callback Next, write a recording audio queue callback function. This callback does two main things: Writes the contents of a newly filled audio queue buffer to the audio file you’re recording into Enqueues the audio queue buffer (whose contents were just written to disk) to the buffer queue This section shows an example callback declaration, then describes these two tasks separately, and finally presents an entire recording callback. For an illustration of the role of a recording audio queue callback, you can refer back to Figure 1-3. The Recording Audio Queue Callback Declaration Listing 2-2 shows an example declaration for a recording audio queue callback function, declared as AudioQueueInputCallback in the AudioQueue.h header file: 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 ) Here’s how this code works: Typically, aqData is a custom structure that contains state data for the audio queue, as described in “Define a Custom Structure to Manage State.” The audio queue that owns this callback. The audio queue buffer containing the incoming audio data to record. The sample time of the first sample in the audio queue buffer (not needed for simple recording). The number of packet descriptions in the inPacketDesc parameter. A value of 0 indicates CBR data. For compressed audio data formats that require packet descriptions, the packet descriptions produced by the encoder for the packets in the buffer. Writing an Audio Queue Buffer to Disk The first task of a recording audio queue callback is to write an audio queue buffer to disk. This buffer is the one the callback’s audio queue has just finished filling with new audio data from an input device. The callback uses the AudioFileWritePackets function from the AudioFile.h header file, as shown in 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 ); Here’s how this code works: The AudioFileWritePackets function, declared in the AudioFile.h header file, writes the contents of a buffer to an audio data file. The audio file object (of type AudioFileID) that represents the audio file to write to. The pAqData variable is a pointer to the data structure described in Listing 2-1. Uses a value of false to indicate that the function should not cache the data when writing. The number of bytes of audio data being written. The inBuffer variable represents the audio queue buffer handed to the callback by the audio queue. An array of packet descriptions for the audio data. A value of NULL indicates no packet descriptions are required (such as for CBR audio data). The packet index for the first packet to be written. On input, the number of packets to write. On output, the number of packets actually written. The new audio data to write to the audio file. Enqueuing an Audio Queue Buffer Now that the audio data from an audio queue buffer has been written to the audio file, the callback enqueues the buffer, as shown in Listing 2-4. Once back in the buffer queue, the buffer is in line and ready to accept more incoming audio data. Listing 2-4 Enqueuing an audio queue buffer after writing to disk AudioQueueEnqueueBuffer ( 1 pAqData→mQueue, 2 inBuffer, // 3 0, // 4 NULL // 5 ); Here’s how this code works: The AudioQueueEnqueueBuffer function adds an audio queue buffer to an audio queue’s buffer queue. The audio queue to add the designated audio queue buffer to. The pAqData variable is a pointer to the data structure described in Listing 2-1. The audio queue buffer to enqueue. The number of packet descriptions in the audio queue buffer's data. Set to 0 because this parameter is unused for recording. The array of packet descriptions describing the audio queue buffer’s data. Set to NULL because this parameter is unused for recording. A Full Recording Audio Queue Callback Listing 2-5 shows a basic version of a full recording audio queue callback. As with the rest of the code examples in this document, this listing excludes error handling. 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 ); } Here’s how this code works: The custom structure supplied to the audio queue object upon instantiation, including an audio file object representing the audio file to record into as well as a variety of state data. See “Define a Custom Structure to Manage State.” If the audio queue buffer contains CBR data, calculate the number of packets in the buffer. This number equals the total bytes of data in the buffer divided by the (constant) number of bytes per packet. For VBR data, the audio queue supplies the number of packets in the buffer when it invokes the callback. Writes the contents of the buffer to the audio data file. For a detailed description , see “Writing an Audio Queue Buffer to Disk.” If successful in writing the audio data, increment the audio data file’s packet index to be ready for writing the next buffer's worth of audio data. If the audio queue has stopped, return. Enqueues the audio queue buffer whose contents have just been written to the audio file. For a detailed description, see “Enqueuing an Audio Queue Buffer.” Write a Function to Derive Recording Audio Queue Buffer Size Audio Queue Services expects your application to specify a size for the audio queue buffers you use. Listing 2-6 shows one way to do this. It derives a buffer size large enough to hold a given duration of audio data. The calculation here takes into account the audio data format you’re recording to. The format includes all the factors that might affect buffer size, such as the number of audio channels. 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 } Here’s how this code works: The audio queue that owns the buffers whose size you want to specify. The AudioStreamBasicDescription structure for the audio queue. The size you are specifying for each audio queue buffer, in terms of seconds of audio. On output, the size for each audio queue buffer, in terms of bytes. An upper bound for the audio queue buffer size, in bytes. In this example, the upper bound is set to 320 KB. This corresponds to approximately five seconds of stereo, 24 bit audio at a sample rate of 96 kHz. For CBR audio data, get the (constant) packet size from the AudioStreamBasicDescription structure. Use this value as the maximum packet size. This assignment has the side effect of determining if the audio data to be recorded is CBR or VBR. If it is VBR, the audio queue’s AudioStreamBasicDescription structure lists the value of bytes-per-packet as 0. For VBR audio data, query the audio queue to get the estimated maximum packet size. Derive the buffer size, in bytes. Limit the buffer size, if needed, to the previously set upper bound. Set a Magic Cookie for an Audio File Some compressed audio formats, such as MPEG 4 AAC, make use of structures that contain audio metadata. These structures are called magic cookies. When you record to such a format using Audio Queue Services, you must get the magic cookie from the audio queue and add it to the audio file before you start recording. Listing 2-7 shows how to obtain a magic cookie from an audio queue and apply it to an audio file. Your code would call a function like this before recording, and then again after recording—some codecs update magic cookie data when recording has stopped. 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 } Here’s how this code works: The audio queue you’re using for recording. The audio file you’re recording into. A result variable that indicates the success or failure of this function. A variable to hold the magic cookie data size. Gets the data size of the magic cookie from the audio queue and stores it in the cookieSize variable. Allocates an array of bytes to hold the magic cookie information. Gets the magic cookie by querying the audio queue’s kAudioQueueProperty_MagicCookie property. Sets the magic cookie for the audio file you’re recording into. The AudioFileSetProperty function is declared in the AudioFile.h header file. Frees the memory for the temporary cookie variable. Returns the success or failure of this function. Set Up an Audio Format for Recording This section describes how you set up an audio data format for the audio queue. The audio queue uses this format for recording to a file. To set up an audio data format, you specify: Audio data format type (such as linear PCM, AAC, etc.) Sample rate (such as 44.1 kHz) Number of audio channels (such as 2, for stereo) Bit depth (such as 16 bits) Frames per packet (linear PCM, for example, uses one frame per packet) Audio file type (such as CAF, AIFF, etc.) Details of the audio data format required for the file type Listing 2-8 illustrates setting up an audio format for recording, using a fixed choice for each attribute. In production code, you’d typically allow the user to specify some or all aspects of the audio format. With either approach, the goal is to fill the mDataFormat field of the AQRecorderState custom structure, described in “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; Here’s how this code works: Creates an instance of the AQRecorderState custom structure. The structure’s mDataFormat field contains an AudioStreamBasicDescription structure. The values set in the mDataFormat field provide an initial definition of the audio format for the audio queue—which is also the audio format for the file you record into. In Listing 2-10, you obtain a more complete specification of the audio format, which Core Audio provides to you based on the format type and file type. Defines the audio data format type as linear PCM. See Core Audio Data Types Reference for a complete listing of the available data formats. Defines the sample rate as 44.1 kHz. Defines the number of channels as 2. Defines the bit depth per channel as 16. Defines the number of bytes per packet, and the number of bytes per frame, to 4 (that is, 2 channels times 2 bytes per sample). Defines the number of frames per packet as 1. Defines the file type as AIFF. See the audio file types enumeration in the AudioFile.h header file for a complete listing of the available file types. You can specify any file type for which there is an installed codec, as described in “Using Codecs and Audio Data Formats.” Sets the format flags needed for the specified file type. Create a Recording Audio Queue Now, with the recording callback and audio data format set up, you create and configure an audio queue for recording. Creating a Recording Audio Queue Listing 2-9 illustrates how to create a recording audio queue. Notice that the AudioQueueNewInput function uses the callback, the custom structure, and the audio data format that were configured in previous steps. 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 ); Here’s how this code works: The AudioQueueNewInput function creates a new recording audio queue. The audio data format to use for the recording. See “Set Up an Audio Format for Recording.” The callback function to use with the recording audio queue. See “Write a Recording Audio Queue Callback.” The custom data structure for the recording audio queue. See “Define a Custom Structure to Manage State.” The run loop on which the callback will be invoked. Use NULL to specify default behavior, in which the callback will be invoked on a thread internal to the audio queue. This is typical use—it allows the audio queue to record while your application’s user interface thread waits for user input to stop the recording. The run loop modes in which the callback can be invoked. Normally, use the kCFRunLoopCommonModes constant here. Reserved. Must be 0. On output, the newly allocated recording audio queue. Getting the Full Audio Format from an Audio Queue When the audio queue came into existence (see “Creating a Recording Audio Queue”), it may have filled out the AudioStreamBasicDescription structure more completely than you have, particularly for compressed formats. To obtain the complete format description, call the AudioQueueGetProperty function as shown in Listing 2-10. You use the complete audio format when you create an audio file to record into (see “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 ); Here’s how this code works: Gets an expected property value size to use when querying the audio queue about its audio data format. The AudioQueueGetProperty function obtains the value for a specified property in an audio queue. The audio queue to obtain the audio data format from. The property ID for obtaining the value of the audio queue’s data format. On output, the full audio data format, in the form of an AudioStreamBasicDescription structure, obtained from the audio queue. On input, the expected size of the AudioStreamBasicDescription structure. On output, the actual size. Your recording application does not need to make use of this value. Create an Audio File With an audio queue created and configured, you create the audio file that you’ll record audio data into, as shown in Listing 2-11. The audio file uses the data format and file format specifications previously stored in the audio queue’s custom structure. 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 ); Here’s how this code works: The CFURLCreateFromFileSystemRepresentation function, declared in the CFURL.h header file, creates a CFURL object representing a file to record into. Use NULL (or kCFAllocatorDefault) to use the current default memory allocator. The file-system path you want to convert to a CFURL object. In production code, you would typically obtain a value for filePath from the user. The number of bytes in the file-system path. A value of false indicates that filePath represents a file, not a directory. The AudioFileCreateWithURL function, from the AudioFile.h header file, creates a new audio file or initializes an existing file. The URL at which to create the new audio file, or to initialize in the case of an existing file. The URL was derived from the CFURLCreateFromFileSystemRepresentation in step 1. The file type for the new file. In the example code in this chapter, this was previously set to AIFF by way of the kAudioFileAIFFType file type constant. See “Set Up an Audio Format for Recording.” The data format of the audio that will be recorded into the file, specified as an AudioStreamBasicDescription structure. In the example code for this chapter, this was also set in “Set Up an Audio Format for Recording.” Erases the file, in the case that the file already exists. On output, an audio file object (of type AudioFileID) representing the audio file to record into. Set an Audio Queue Buffer Size Before you prepare a set of audio queue buffers that you’ll use while recording, you make use of the DeriveBufferSize function you wrote earlier (see “Write a Function to Derive Recording Audio Queue Buffer Size”). You assign this size to the recording audio queue you are using. Listing 2-12 illustrates this: Listing 2-12 Setting an audio queue buffer size DeriveBufferSize ( 1 aqData.mQueue, 2 aqData.mDataFormat, // 3 0.5, // 4 &aqData.bufferByteSize // 5 ); Here’s how this code works: The DeriveBufferSize function, described in “Write a Function to Derive Recording Audio Queue Buffer Size,” sets an appropriate audio queue buffer size. The audio queue that you’re setting buffer size for. The audio data format for the file you are recording. See “Set Up an Audio Format for Recording.” The number of seconds of audio that each audio queue buffer should hold. One half second, as set here, is typically a good choice. On output, the size for each audio queue buffer, in bytes. This value is placed in the custom structure for the audio queue. Prepare a Set of Audio Queue Buffers You now ask the audio queue that you’ve created (in “Create a Recording Audio Queue”) to prepare a set of audio queue buffers. Listing 2-13 demonstrates how to do this. 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 ); } Here’s how this code works: Iterates to allocate and enqueue each audio queue buffer. The AudioQueueAllocateBuffer function asks an audio queue to allocate an audio queue buffer. The audio queue that performs the allocation and that will own the buffer. The size, in bytes, for the new audio queue buffer being allocated. See “Write a Function to Derive Recording Audio Queue Buffer Size.” On output, the newly allocated audio queue buffer. The pointer to the buffer is placed in the custom structure you’re using with the audio queue. The AudioQueueEnqueueBuffer function adds an audio queue buffer to the end of a buffer queue. The audio queue whose buffer queue you are adding the buffer to. The audio queue buffer you are enqueuing. This parameter is unused when enqueuing a buffer for recording. This parameter is unused when enqueuing a buffer for recording. Record Audio All of the preceding code has led up to the very simple process of recording, as shown in 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 Here’s how this code works: Initializes the packet index to 0 to begin recording at the start of the audio file. Sets a flag in the custom structure to indicate that the audio queue is running. This flag is used by the recording audio queue callback. The AudioQueueStart function starts the audio queue, on its own thread. The audio queue to start. Uses NULL to indicate that the audio queue should start recording immediately. The AudioQueueStop function stops and resets the recording audio queue. The audio queue to stop. Use true to use synchronous stopping. See “Audio Queue Control and State” for an explanation of synchronous and asynchronous stopping. Sets a flag in the custom structure to indicate that the audio queue is not running. Clean Up After Recording When you’re finished with recording, dispose of the audio queue and close the audio file. Listing 2-15 illustrates these steps. Listing 2-15 Cleaning up after recording AudioQueueDispose ( 1 aqData.mQueue, // 2 true // 3 ); AudioFileClose (aqData.mAudioFile); 4 Here’s how this code works: The AudioQueueDispose function disposes of the audio queue and all of its resources, including its buffers. The audio queue you want to dispose of. Use true to dispose of the audio queue synchronously (that is, immediately). Closes the audio file that was used for recording. The AudioFileClose function is declared in the AudioFile.h header file. translation/adc/audio/audio_queue_services_programming_guide/0300_recordingaudio.1409545031.txt.gz 最終更新: 2014-09-01 13:17by Decomo