カテゴリー
iOS Objective-C

CoreAudioを学ぶ(4)

パート4はRecording With Audio Queuesです。

まずは、UtilityFunctionsから見ていきましょう。見やすくフォーマットされた文字列を表示するためのカスタム関数NSPrintを定義しています。ブログの著者さんはこの記事を参考にしたようです。

void NSPrint(NSString *format, ...) { // [1]
  va_list args; // [2]

  va_start(args, format);
  NSString *string  = [[NSString alloc] initWithFormat:format arguments:args]; // [3]
  va_end(args);

  fprintf(stdout, "%s", [string UTF8String]); // [4]
    
#if !__has_feature(objc_arc)
  [string release]; // [5]
#endif
}

[1] … はC言語の文法で、この関数は「いくつかの引数を得られる」という意味です。stdarg.hに宣言されている va_list, va_start, va_endを使うことが出来ます。

[2] va_list型の変数argsを作成しています。va_listはvariable argument list(可変の変数のリスト)の略で、…の内容が格納されます。このva_listにはva_startとva_endの中でアクセスすることが出来ます。

[3] NSStringの標準のinitializerのinitWithFormatでインスタンス*stringを作り、arguments:に引数argsを渡しています。

[4] fprintfはC言語の関数で、fprintf(ストリーム、フォーマット、…(文字列))のように使います。

[5] ARC (Automatic Reference Counting)がない場合は手動でstringをリリースします。

次は、エラーの有無をチェックするカスタム関数CheckErrorです。

void CheckError(OSStatus error, const char *operation) {
  if (error == noErr) {
    return;
  }
  
  char errorString[20]; // [6]
  *(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(error); // [7] we have 4 bytes and we put them in Big-endian ordering. 1st byte the biggest
  if (isprint(errorString[1]) && isprint(errorString[2]) && // [8]
      isprint(errorString[3]) && isprint(errorString[4])) {
    errorString[0] = errorString[5] = '\'';
    errorString[6] = '\0';
  } else {
    sprintf(errorString, "%d", (int) error);
  }
  NSLog(@"Error: %s (%s)\n", operation, errorString);
  exit(1);
}

[6] 20バイトの空のcharの配列を作成しています。

[7] *(errorString + 1) のerrorString + 1 はポインタ演算です。errorString配列の2番目の要素(つまりerrorString[1]と同じ)にCFSwapInt32HostToBig(error)の戻り値unsigned intを代入しています。Big-endianは、UInt32の4byteのうち、最初のbyteが一番大きな値となるメモリ上の保存方法です。

[8] isprint( ) はC言語の関数で、そのcharがcharとして表示可能かをチェックするものです。例として’a’は表示可能、’\t’は表示可能ではない、となります。

次に、デフォルトのインプットデバイスのサンプルレートを確認するカスタム関数を見てみましょう。

void GetDefaultInputDeviceSampleRate(Float64 *oSampleRate) {
  AudioObjectPropertyAddress propertyAddress; // [9]
  
  propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
  propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
  propertyAddress.mElement = 0; // master element
  
  AudioDeviceID deviceID = 0;
  UInt32 propertySize = sizeof(AudioDeviceID);
  
  CheckError(AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                        &propertyAddress,
                                        0,
                                        NULL,
                                        &propertySize,
                                        &deviceID), "Getting default input device ID from Audio System Object"); // [9]'
  
  propertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; // [10]
  propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
  propertyAddress.mElement = 0;
  propertySize = sizeof(Float64);
  
  CheckError(AudioObjectGetPropertyData(deviceID,
                                        &propertyAddress,
                                        0,
                                        NULL,
                                        &propertySize,
                                        oSampleRate), "Getting nominal sample rate for the default device"); // [10]'
}

[9] ~ [9]’ でdeviceIDを求め、[10] ~ [10]’でsampleRateを求めています。どちらもCore AudioのAudioObjectGetPropertyData( )関数を使っています。

// 以下執筆中。。。