Mac OS X 10.6 Snow Leopardから、AudioHardware〜とかAudioDevice〜とかAudioStream〜とかのGet・Set系がことごとくDEPRECATEDになってしまいました。
僕の認識では、もともとAudioObject〜があって、そのコンビニエンスメソッドとしてAudioDeviceとかの関数があると思っていたのですけど、どうやら歴史的には逆だったようです。ヘッダをよく見てみたら、AudioObject系はわりと最近の10.4から増えてたんですね。
あと、AudioUnitがiPhoneと同じようにAudioComponentにいきなり変わっててびっくりしました。iPhoneで慣れ親しんでいたとはいえ、もちょっと緩やかに移行してくれてもいいんじゃないかなと思います。
プロパティの定数系も結構Deprecatedしてます。これはXcodeで警告を出してくれないので注意が必要です。ヘッダのコメントに”Some Day Be Deprecated”なんてこっそりかかれてます。Device系だとkAudioDevicePropertyDeviceNameから始まるところですね。10.7になっていきなりエラー出まくりなんて事のないようにしっかりチェックしておきましょう。
ということで、取得・設定系をAudioObjectを使ってやってみたいと思います。
AudioObjectでオーディオ情報の取得・設定をするにはAudioObjectGetPropertyDataやAudioObjectSetPropertyDataという関数を使います。これはいままでAudioHardware〜やAudioDevice〜など別々の関数でやっていたオーディオ情報の取得や設定を、ひとつでまかなえるものです。プロパティのサイズを調べるのにも、AudioObjectGetPropertyDataSizeという関数で行う事になります。
たとえば、プロパティの取得する関数はこんな感じで定義されています。
extern OSStatus
AudioObjectGetPropertyData(AudioObjectID inObjectID,
const AudioObjectPropertyAddress* inAddress,
UInt32 inQualifierDataSize,
const void* inQualifierData,
UInt32* ioDataSize,
void* outData)
最初のAudioObjectIDというのはAudioDeviceIDやAudioStreamIDをそのまま渡します。前の記事のその3でも説明しましたが、AudioDeviceIDもAudioStreamIDもAudioObjectIDをtypedefしているだけのもので、つまり全てAudioObjectIDです(リファレンス的にAudioDeviceなどはAudioObjectのサブクラスってことらしい)。AudioHardware〜系の関数をつかっていたプロパティのときはkAudioObjectSystemObjectを渡します。
ちょっとひとつ飛ばしまして、inQualifierDataSizeとinQualifierDataは、サンプルで使っているものが無かったのでよくわかりません。基本0とNULLで大丈夫なようです。もしかしたらまだ機能していないかもしれません。ioDataSizeとoutDataは取得するプロパティのサイズと受け取るメモリ領域で、前と同じです。
戻ってAudioObjectPropertyAddressは、
struct AudioObjectPropertyAddress
{
AudioObjectPropertySelector mSelector;
AudioObjectPropertyScope mScope;
AudioObjectPropertyElement mElement;
};
typedef struct AudioObjectPropertyAddress AudioObjectPropertyAddress;
と定義されてます。デバイスとかストリームとか関係なくプロパティを特定するのに必要な情報ですね。
Selectorは〜PropertyIDの事です。Scopeは、ほとんどの場合はkAudioObjectPropertyScopeGlobalで、Deviceなどの入出力部分のときはInputとかOutputで指定したりします。Elementは基本kAudioObjectPropertyElementMaster(= 0)でOKだと思います(これ以外指定するパターンがすぐに見つけられませんでした。Channel的なところで0以外を使うのではないかと思います)。
最後に、サンプルソースです。Xcodeで新規プロジェクト→Command Line Tool→Foundationでプロジェクトを作成して、CoreAudio.Frameworkを追加して、main.mを以下のソースに差し替えてください。走らせると、デフォルトになっているオーディオデバイスの名前を表示して、サンプルレートを設定可能な中で変更します。
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
int main (int argc, const char * argv[]) {
AudioDeviceID devID;
UInt32 size;
AudioObjectPropertyAddress address;
AudioValueRange *sampleRates;
CFStringRef deviceName = NULL;
//デフォルトのアウトプットに設定されているオーディオデバイスを取得する
address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
address.mScope = kAudioObjectPropertyScopeGlobal;
address.mElement = kAudioObjectPropertyElementMaster;
size = sizeof(devID);
AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &size, &devID);
//アウトプットの名前を取得する
address.mSelector = kAudioObjectPropertyName;
size = sizeof(deviceName);
AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, &deviceName);
//アウトプットのデバイスが対応しているサンプルレートを取得する
address.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
AudioObjectGetPropertyDataSize(devID, &address, 0, NULL, &size);
UInt32 numOfSampleRates = size / sizeof(AudioValueRange);
sampleRates = calloc(numOfSampleRates, sizeof(AudioValueRange));
AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, sampleRates);
//現在のサンプルレートを取得する
Float64 currentSampleRate;
address.mSelector = kAudioDevicePropertyNominalSampleRate;
size = sizeof(Float64);
AudioObjectGetPropertyData(devID, &address, 0, NULL, &size, ¤tSampleRate);
//サンプルレートを別のにする
int currentIndex = 0;
for (int i = 0; i < numOfSampleRates; i++) {
if (sampleRates[i].mMinimum == currentSampleRate) {
currentIndex = i;
break;
}
}
int newIndex = currentIndex + 1;
if (newIndex >= numOfSampleRates) newIndex = 0;
//サンプルレートを設定する
Float64 newSampleRate = sampleRates[newIndex].mMinimum;
size = sizeof(Float64);
AudioObjectSetPropertyData(devID, &address, 0, NULL, size, &newSampleRate);
NSLog(@"device name = %@", deviceName);
NSLog(@"new samplerate = %f", newSampleRate);
free(sampleRates);
CFRelease(deviceName);
return 0;
}