※2009/10/2追記《このエントリーはMac OS X 10.5での場合について書かれています。10.6以降の場合はこちらをご覧ください。》
今回は、Core Audioのデバイスなどから情報(※プロパティ)を実際にやりとりする方法を見ていきたいと思います。(※使用する関数名や引数でpropertyと名前が付けられているので、この情報の事を以後プロパティと表記する事にします。)
まず、プロパティを取得するにはGetPropertyと名前のついた関数を使います。それぞれ以下の関数があります。
//オーディオシステム全体からの情報を取得する
extern OSStatus
AudioHardwareGetProperty( AudioHardwarePropertyID inPropertyID,
UInt32* ioPropertyDataSize,
void* outPropertyData)
//オーディオデバイスの情報を取得する
extern OSStatus
AudioDeviceGetProperty( AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
UInt32* ioPropertyDataSize,
void* outPropertyData)
//オーディオデバイス内のストリーム(音声の入出力部分)の情報を取得する
extern OSStatus
AudioStreamGetProperty( AudioStreamID inStream,
UInt32 inChannel,
AudioDevicePropertyID inPropertyID,
UInt32* ioPropertyDataSize,
void* outPropertyData)
それぞれの関数の、〜PropertyIDというところにはプロパティIDという、取得したいプロパティの種類を表す定数を指定します(<CoreAudio/AudioHardware.h>で定義されています。)。ioPropertyDataSizeには取得するプロパティのサイズの変数へのポインタを、outPropertyDataには取得するプロパティへのポインタを渡して、プロパティを取得します。
AudioDeviceGetPropertyのisInputは音声の入力側の情報を取得するのであればtrueを、出力側であればfalseを渡します。inChannelは、kAudioDevicePropertyChannel〜あたりを使うのでなければ、0を入れておけば良いようです。
ではためしに、「Audio MIDI 設定」でデフォルトに設定されている出力デバイスを取得して、そのデバイスの名前を取得してみます。
New ProjectでFoundation Toolを選択して新規プロジェクトを作成し、CoreAudio.Frameworkを追加して、mainメソッドを以下のように書き換えてみます。
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
OSStatus err = noErr; //エラーコードを受け取る変数
UInt32 size; //プロパティのサイズを指定する変数
//デフォルトアウトプットデバイスを取得する
AudioDeviceID devID;
size = sizeof(devID);
err = AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultOutputDevice, &size, &devID);
if (err != noErr) {
NSLog(@"AudioHardwareGetProperty err");
goto catchErr;
}
NSLog(@"deviceID = %u", devID);
//オーディオデバイスの名前を取得する
CFStringRef strRef;
size = sizeof(strRef);
err = AudioDeviceGetProperty(
devID, 0, false, kAudioDevicePropertyDeviceNameCFString, &size, &strRef);
if (err != noErr) {
NSLog(@"AudioDeviceGetProperty err");
goto catchErr;
}
NSLog(@"deviceName = %@", (NSString *)strRef);
catchErr:
[pool drain];
return 0;
}
Core Audioの関数はOSStatusという型のエラーコードを返してきますので、noErrでなければエラー処理をします。とりあえず今回は関数の最後にgotoで飛ばしています。
MacBook Proで実行してみると以下のような感じでログに表示されて、情報を取得できている事が分かります。
[Session started at 2008-04-09 23:54:39 +0900.]
2008-04-09 23:54:39.625 PropertyTest01[5027:10b] deviceID = 264
2008-04-09 23:54:39.631 PropertyTest01[5027:10b] deviceName = Built-in Output
The Debugger has exited with status 0.
プロパティはものによってスカラ値であったり構造体であったり配列であったりしますので、サイズをioPropertyDataSizeに渡す必要があります。今回のように前もってサイズが分かるものは良いのですが、すべてのデバイスを取得する場合などは、接続されているデバイスの数によってサイズが変動しますので、事前にそのサイズを取得しておかなければなりません。
取得しようとしているプロパティのサイズと、プロパティが書き込み可能か、という情報を取得するのが以下のGetPropertyInfoの関数です。
extern OSStatus
AudioHardwareGetPropertyInfo( AudioHardwarePropertyID inPropertyID,
UInt32* outSize,
Boolean* outWritable)
extern OSStatus
AudioDeviceGetPropertyInfo( AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
UInt32* outSize,
Boolean* outWritable)
extern OSStatus
AudioStreamGetPropertyInfo( AudioStreamID inStream,
UInt32 inChannel,
AudioDevicePropertyID inPropertyID,
UInt32* outSize,
Boolean* outWritable)
これらを使って情報を取得してみます。マックにつながっているオーディオデバイスを全て取得して、アウトプットがあればフォーマットを取得してみる、というコードが以下のような感じになります。
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
OSStatus err = noErr;
UInt32 size;
NSInteger i, j;
AudioStreamID *streamIDs = NULL;
AudioDeviceID *devIDs = NULL;
//全てのデバイスを取得する
err = AudioHardwareGetPropertyInfo(
kAudioHardwarePropertyDevices, &size, NULL);
if (err != noErr) {
NSLog(@"AudioHardwareGetPropertyInfo err");
goto catchErr;
}
devIDs = malloc(size);
err = AudioHardwareGetProperty(
kAudioHardwarePropertyDevices, &size, devIDs);
if (err != noErr) {
NSLog(@"AudioHardwareGetProperty err");
goto catchErr;
}
//デバイスの数を計算して繰り返す
UInt32 count = size / sizeof(AudioDeviceID);
for (i = 0; i < count ; i++) {
//デバイスの名前を取得する(isInputがfalseだがアウトが無くても取得できる)
CFStringRef strRef;
size = sizeof(strRef);
err = AudioDeviceGetProperty(
devIDs[i], 0, false, kAudioDevicePropertyDeviceNameCFString,
&size, &strRef);
if (err != noErr) {
NSLog(@"AudioDeviceGetProperty deviceNameCFString err");
goto catchErr;
}
//デバイス内のストリームIDを取得する(ここではアウト側のみが取得できる)
err = AudioDeviceGetPropertyInfo(
devIDs[i], 0, false, kAudioDevicePropertyStreams, &size, NULL);
if (err != noErr) {
NSLog(@"AudioDeviceGetPropertyInfo err");
goto catchErr;
}
if (size) NSLog(@"%@ / deviceID = %u", (NSString *)strRef, devIDs[i]);
streamIDs = malloc(size);
UInt32 nStream = size / sizeof(AudioStreamID);
err = AudioDeviceGetProperty(
devIDs[i], 0, false, kAudioDevicePropertyStreams, &size, streamIDs);
if (err != noErr) {
NSLog(@"AudioDeviceGetProperty streams err");
goto catchErr;
}
//ストリームIDの数だけ繰り返す
for (j = 0; j < nStream; j++) {
//フォーマットを取得する
AudioStreamBasicDescription format;
size = sizeof(format);
err = AudioStreamGetProperty(
streamIDs[j], 0, kAudioStreamPropertyVirtualFormat, &size, &format);
if (err != noErr) {
NSLog(@"AudioStreamGetProperty virtualFormat err");
goto catchErr;
}
NSLog(@"streamID = %u / channels = %u",
streamIDs[j], format.mChannelsPerFrame);
}
free(streamIDs);
streamIDs = NULL;
}
catchErr:
if (devIDs) free(devIDs);
if (streamIDs) free(streamIDs);
[pool drain];
return 0;
}<foundation><coreaudio> </coreaudio></foundation>
FIREBOXというインターフェースをつなげて、Bluetoothも生かしたMacBook Proで実行してみると、以下のようにログに表示されます。
[Session started at 2008-04-13 21:53:31 +0900.]
2008-04-13 21:53:31.970 PropertyTest[779:10b]
PreSonus FIREBOX (2343) / deviceID = 258
2008-04-13 21:53:31.974 PropertyTest[779:10b] streamID = 265 / channels = 1
2008-04-13 21:53:31.974 PropertyTest[779:10b] streamID = 266 / channels = 1
2008-04-13 21:53:31.975 PropertyTest[779:10b] streamID = 267 / channels = 1
2008-04-13 21:53:31.975 PropertyTest[779:10b] streamID = 268 / channels = 1
2008-04-13 21:53:31.976 PropertyTest[779:10b] streamID = 269 / channels = 1
2008-04-13 21:53:31.976 PropertyTest[779:10b] streamID = 270 / channels = 1
2008-04-13 21:53:31.977 PropertyTest[779:10b] streamID = 271 / channels = 1
2008-04-13 21:53:31.977 PropertyTest[779:10b] streamID = 272 / channels = 1
2008-04-13 21:53:31.982 PropertyTest[779:10b] Built-in Output / deviceID = 279
2008-04-13 21:53:31.982 PropertyTest[779:10b] streamID = 280 / channels = 2
2008-04-13 21:53:31.983 PropertyTest[779:10b] receiver01 / deviceID = 273
2008-04-13 21:53:31.983 PropertyTest[779:10b] streamID = 274 / channels = 2
The Debugger has exited with status 0.
こうしてみると、DeviceIDとStreamIDがかぶっていないという事も分かります。
こんな感じで、プロパティを取得するときは、AudioHardwareGetPropertyInfo → AudioHardwareGetProperty → AudioDeviceGetPropertyInfo → AudioDeviceGetProperty → AudioStreamGetPropertyInfo → AudioStreamGetPropertyという順番で必要な関数を選択して取得していく事になります。