Apple Developer Connection
高度な検索
Member Login ログイン | ご入会 ADC連絡先

Technical Note TN2091
HALの出力用Audio Unitを使用したデバイス入力

このテクニカルノートでは、ハードウェア抽象化層のAudioOutputUnit (AUHAL)を使ってオーディオデバイスから入力を取得する方法について説明します。AUHALを使用すると、オーディオデバイスでよく行われる入出力処理を簡素化できます。





アプリケーションは、ハードウェア抽象化層(HAL)のAudioOutputUnitを使って、単一のオーディオデバイスとインターフェイスをとることができます。AudioOutputUnit (AUHAL)ユニットは、<CoreAudio/AudioHardware.h>で定義されているAudioDeviceオブジェクトの上層にあります。AUHALはオーディオデバイスの入力と出力に使用できます。

AUHALを使ってオーディオデバイスから入力を取り込むには、以下の手順に従ってください。

1.AUHALを開きます。

2.AUHALの入力を使用可能にします。

3.デフォルトの入力デバイスをAUHALのカレント入力デバイスに設定します。

4.デバイスフォーマットを取得し、希望のオーディオフォーマットを指定します。

5.入力コールバックを作成し、AUHALに登録します。

6.必要なバッファを割り当てます。

7.AUHALを初期化して開始します。

これらの手順について、このテクニカルノートで詳しく説明します。

AudioOutputUnitの作成

まず、他のAudio Unitを取得する場合と同様に、Component Descriptionを使って、まずAudioOutputUnitを取得する必要があります。

リスト1: AudioOutputUnitを開く方法


    Component comp;
    ComponentDescription desc;

    // 数種類のAudio Unitがある。
    // いくつかのAudio Unitは入力、ミキサー、またはDSPユニットとして機能する。
    // 一覧については、AUComponent.hを参照。
    desc.componentType = kAudioUnitType_Output;

    // すべてのコンポーネントはsubTypeを持っており、これがコンポーネントの機能を
    // 明確に記述する。
    desc.componentSubType = kAudioUnitSubType_HALOutput;

    // AUComponent.hに記載されたすべてのAudio Unitは、
    //"kAudioUnitManufacturer_Apple"をManufacturerとして使用する必要がある。
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    // descのスペックに合ったコンポーネントを検出する。
    comp = FindNextComponent(NULL, &desc);
    if (comp == NULL) exit (-1);

    // コンポーネントの提供するサービスにアクセスする。
    OpenAComponent(comp, &InputUnit);

先頭に戻る

Audio Unit接続の概要

Audio Unitは、異なる2つのAudio Unit間の直接接続の概念を具現化しています。Audio Unitはデータを生成するように要求されると、コールバック関数または接続されている別のAudio Unitからデータを受け取ることができます。接続の例として、A1とA2という2つのAudio Unitがあれば、A1をA2に接続します(A1-->A2)。A2がデータを生成するように要求されると、データストリームは基本的にA1からA2へ「プル」されて処理されます。そのため、Audio Unit間の接続は、同じオーディオストリームフォーマットを共有する必要があります。Audio Unitと接続の詳細については、『Audio and MIDI on Mac OS X』 を参照してください。

図1: AUHALのシグナルフロー

図1 AUHALのシグナルフロー

図1は、オーディオデバイスとアプリケーション間のオーディオデータのフローを示しています。アプリケーションは処理を単純化するために、Audio UnitをAUHALのいずれかの要素(バス)に接続できます。つまり、デバイスにオーディオを出力するソースとしてAudio Unitを使う場合は、次の接続を使用します。

表1: デバイスへのオーディオ出力

ソース出力先
ソースユニット(出力範囲、出力要素)AUHAL(入力範囲、要素 0)

オーディオデバイスの入力データを取得したい場合は、次のように接続します。

表2: デバイスへの音声取り込み

ソース出力先
AUHAL(出力範囲、要素 1)出力先ユニット(入力範囲、入力要素)

もちろん、入出力を提供する内蔵オーディオデバイスのようなデバイスについては、次の接続を作成することで、ソフトウェアプレイスルーのメカニズムを確立することができます。

表3: 簡単なソフトウェアプレイスルー

ソース出力先
AUHAL(出力範囲、要素 1)AUHAL(入力範囲、要素 0)

入出力の間に1つ以上のAudio Unitを挿入することにより、入力から出力まで、オーディオに必要な処理操作を何度でも実行できます。ここで、マルチバンドコンプレッサAudio Unitを使って内蔵オーディオ入力を処理する例を挙げてみましょう。これを行うには、次の接続を作成します。

表4: マルチバンドコンプレッサAudio Unitによる内蔵オーディオ入力の処理

ソース出力先
AUHAL(出力範囲、要素 1)マルチバンドコンプレッサ(入力範囲、要素 0)
マルチバンドコンプレッサ(出力範囲、要素 0)AUHAL(入力範囲、要素 0)

これらの接続は(AudioToolbox.frameworkの)AUGraph APIで管理できます。AUGraphは、一組のAudio Unitとそれらの接続の高レベル表現です。この便利なAPIの使い方に関する詳細は、『Audio and MIDI on Mac OS X -May 2001』の第4章および『Core Audio Overview』を参照してください。

2つの別々のオーディオデバイスを使う場合は、2つのAUHALが必要となります。しかし、それぞれのAUHALは個別のI/Oプロセスで実行するため、2つのAUHALの間に直接の接続を確立できません。通知メカニズムを使用して、出力デバイスに対してデータが到着したことを通知し、データを渡す必要があります。

注: AUGraphごとに使用できるAudioOutputUnitは1つのみです。

先頭に戻る

IOの有効化

AUHALオブジェクトを作成したら、デバイス入力を取得するために、Audio Unitの入力範囲にあるIOを使用可能にする必要があります。入力は、AUHALの要素 1にあるkAudioOutputUnitProperty_EnableIOプロパティで明示的に使用可能にする必要があります。AUHALは入出力の両方に使用できるため、この例では出力範囲のIOを使用不可にする必要があります。

リスト2: AudioOutputUnitの入力の有効化と出力の無効化

     UInt32 enableIO;
     UInt32 size=0;

     // AudioUnitSetPropertyを使用する場合は、メソッドの4番目のパラメータが
     // AudioUnitElementを参照する。AudioOutputUnitを使用する場合は、
     // 入力要素を「1」にして、出力要素を「0」にする。


     enableIO = 1;
     AudioUnitSetProperty(InputUnit,
                kAudioOutputUnitProperty_EnableIO,
                kAudioUnitScope_Input,
                1, // 入力要素
                &enableIO,
                sizeof(enableIO));

      enableIO = 0;
      AudioUnitSetProperty(InputUnit,
                kAudioOutputUnitProperty_EnableIO,
                kAudioUnitScope_Output,
                0,   // 出力要素
                &enableIO,
                sizeof(enableIO));

先頭に戻る

AudioOutputUnitのカレントデバイスの設定

AUHALには、インターフェイスをとるデバイスがなければなりません。この例では、カレントデバイスとしてシステムのデフォルト入力デバイスを選択します。AudioHardwareGetPropertykAudioHardwarePropertyDefaultInputDeviceパラメータ の組み合わせで、ユーザによって選択されたカレント入力デバイスを取得します。AudioDeviceIDを取得した後、kAudioOutputUnitProperty_CurrentDeviceAudioUnitSetPropertyパラメータにより、オーディオデバイスをAudio Unitのカレントデバイスとして設定できます。デバイスをAUHALに設定できるのは、入出力を有効にしてからのみである点を忘れないでください。

リスト3: AudioOutputUnitのカレントデバイスをデフォルト入力デバイスに設定する方法

OSStatus SetDefaultInputDeviceAsCurrent(){
    UInt32 size;
    OSStatus err =noErr;
    size = sizeof(AudioDeviceID);

    AudioDeviceID inputDevice;
    err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
                                                  &size,
                                                  &inputDevice);

    if (err)
        return err;

                         err =AudioUnitSetProperty(InputUnit,
                         kAudioOutputUnitProperty_CurrentDevice,
                         kAudioUnitScope_Global,
                         0,
                         &inputDevice,
                         sizeof(inputDevice));

   return err;

}

先頭に戻る

オーディオデータのフォーマットについて

AUHALは、入出力ともに、デバイスのオーディオデータストリームを単一のインターリーブされていないストリームにフラット化します。AUHALには、この変換を実行するAudioConverterが組み込まれています。AUHALはフラット化されたデバイスフォーマットとクライアントの希望フォーマットを比較して、どの種類のAudioConverterが必要であるかを判断します。デバイスフォーマットとクライアントフォーマットのどちらかを設定し直すと、通常は中断が生じるため、AUHALは新しいAudioConverterを設定する必要があります。デバイスフォーマットと希望のフォーマットのチャネルが1:1の比率でない場合、AUHALユニットはチャネルマップを使って、ユーザに提示するチャネルを決定することができます。最後に、デバイスのサンプルレートは、希望のサンプルレートと一致している必要があります。

オーディオデバイスにデータを出力するには、フォーマットを必ずAUHALの要素 0の出力範囲で明示します。オーディオデバイスフォーマットを取得するには、AudioUnitGetPropertyと定数kAudioUnitProperty_StreamFormatを使います。この情報は取得できますが、決して書き込み可能ではありません。ユーザはこれらの設定自体を明示的に変更する必要があります。

デバイスから入力を取得するには、デバイスフォーマットを必ずAUHALの要素 1の入力範囲で明示します。このため、希望のフォーマットはAUHAの要素 1の出力範囲に設定する必要があります。内部のAudioConverterは、あらゆる「単純な」変換を処理できます。つまり、通常はクライアントがPCMフォーマットの任意のバリエーションを指定できるということです。最後に、デバイスのサンプルレートは、希望のサンプルレートと一致している必要があります。サンプルレートを変換する必要がある場合は、別のAudioConverterを使って、独立したスレッドで入力をバッファリングして、データを変換できます。

リスト4: 希望する「入力」フォーマットの設定

    CAStreamBasicDescription DeviceFormat;
    CAStreamBasicDescription DesiredFormat;
    // CAStreamBasicDescriptionsを「裸」の
    // AudioStreamBasicDescriptionsの代わりに使うことでエラーを最小限にする。
    // CAStreamBasicDescription.hはCoreAudio SDKに含まれている。

    UInt32 size = sizeof(CAStreamBasicDescription);

    // 入力デバイスフォーマットを取得する
    AudioUnitGetProperty (InputUnit,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   1,
                                   &DeviceFormat,
                                   &size);

    // 希望のフォーマットをデバイスのサンプルレートに設定する
    DesiredFormat.mSampleRate =  DeviceFormat.mSampleRate;

    // フォーマットを出力範囲に設定する
    AudioUnitSetProperty(
                            InputUnit,
                            kAudioUnitProperty_StreamFormat,
                            kAudioUnitScope_Output,
                            1,
                            &DesiredFormat,
                            sizeof(CAStreamBasicDescription);

先頭に戻る

チャネルマッピング

オーディオデバイスのチャネルと希望フォーマットのチャネルのデータフォーマットが1:1の比率に対応していない場合は、チャネルマッピングが必要です。チャンルマッピングでは、Audio Unitがどんなデバイスチャネルと対話するかを指定します。これを設定する必要があるのは、デフォルトのマッピング以外の設定を使う予定がある場合に限られます。

表5: デフォルトのチャネルマップ(チャネルマッピングは不要)

デバイスチャネル使用チャネル
00
11
22
......

たとえば、入力に使用する4チャネルのデバイスがあり、ステレオ入力としてデバイスの2または3チャネルのみが必要であったとします。必要なチャネルをAUHALのチャネルに割り当てる(マップする)必要があります。AUHALのチャネルマップを作成するには、マップの「出力先」ごとにSInt32の配列を作成する必要があります。Sint32の配列の各要素は、出力先に送られるソースチャネルのインデックス、または「ソースなし」を意味する-1を参照します。この例では、2つの要素の配列を作成し、その値を-1に初期設定します。マップしたいチャネルには、チャネルマップ配列の要素の値を2と3に設定します。その結果、チャネルマップは [2,3] になります。

表6: 4 -> 2チャネルマップ

デバイスチャネル使用チャネル
20
31

リスト5: 入力用の4 -> 2チャネルマッピングの例


    SInt32 *channelMap =NULL;
    UInt32 numOfChannels = DesiredFormat.mChannelsPerFrame;  // 2チャネル
    UInt32 mapSize = numOfChannels *sizeof(SInt32);
    channelMap = (SInt32 *)malloc(size);

    // 必要な入力の各チャネルに、デバイスの出力チャネルから
    // チャネルをマップする。
    for(UInt32 i=0;i<numOfChannels;i++)
    {
               channelMap[i]=-1;
    }
     //channelMap[desiredInputChannel] = deviceOutputChannel;
     channelMap[0] = 2;
     channelMap[1] = 3;
     AudioUnitSetProperty(InputUnit,
                                        kAudioOutputUnitProperty_ChannelMap,
                                        kAudioUnitScope_Output,
                                        1,
                                        channelMap,
                                        size);
    free(channelMap);

先頭に戻る

AudioOutputUnitの入力プロシージャの作成

次に、AUHALの入力プロシージャを登録する必要があります。AUHALが入力デバイスから新しいデータを受け取ると、このプロシージャが呼び出されます。

リスト6: AudioOutputUnitの入力プロシージャの作成

void MyInputCallbackSetup()
{
    AURenderCallbackStruct input;
    input.inputProc = InputProc;
    input.inputProcRefCon = 0;

    AudioUnitSetProperty(
            InputUnit,
            kAudioOutputUnitProperty_SetInputCallback,
            kAudioUnitScope_Global,
            0,
            &input,
            sizeof(input));
}

先頭に戻る

AudioOutputUnitの初期化と開始

AUHALがデバイスから入力を受け取るように設定します。データの取得を開始するには、Audio Unitを初期化して開始する必要があります。

リスト7: AUHALの開始

OSStatus InitAndStartAUHAL()
{
   OSStatus err= noErr;

   err = AudioUnitInitialize(InputUnit);
   if(err)
       return err;

   err = AudioOutputUnitStart(InputUnit);

   return err;
}

先頭に戻る

AudioOutputUnitからのデータの取得

AUHALは、オーディオデバイスとの間でオーディオデータを送受信できるAudio Unitです。AUHALからオーディオを受信するには、Audio Unitの出力範囲から取得する必要があります。実際には、これはクライアントがAudioUnitRenderを呼び出すことにより行われます。オーディオをAUHALに送信するには、入力範囲でデータを提供する必要があります。これは、入力コールバックをAudio Unitに提供することによって行われます。

下の例では、入力プロシージャ内からAudioUnitRenderを呼び出します。入力プロシージャのレンダリングアクションフラグ、タイムスタンプ、バス番号、および必要なフレーム数をAudioUnitRenderに伝達する必要があります。AudioBufferList、すなわちioDataはNULLになるため、独自に割り当てたAudioBufferListを提供しなければなりません。

リスト8: AudioUnitRenderを使ったデータの取得

AudioBufferList * theBufferList;
/* バッファデータを保持するために割り当て */

OSStatus InputProc(
                    void *inRefCon,
                    AudioUnitRenderActionFlags *ioActionFlags,
                    const AudioTimeStamp *inTimeStamp,
                    UInt32 inBusNumber,
                    UInt32 inNumberFrames,
                    AudioBufferList * ioData)
{
    OSStatus err =noErr;

    err= AudioUnitRender(InputUnit,
                    ioActionFlags,
                    inTimeStamp,
                    inBusNumber,     // 入力データの '1' になる
                    inNumberFrames, // 必要なフレーム数
                    theBufferList);

    return err;
}

先頭に戻る

まとめ

AUHALを使ってオーディオデバイスとインターフェイスをとると、アプリケーションとオーディオデバイス間の対話を大幅に簡素化できます。このAudio Unitは、オーディオデバイス情報の取得と、オーディオデータの転送や取得を行う際にオーディオデベロッパを支援します。

先頭に戻る

サンプルコード、参考資料、注意事項

サンプルコード - RecordAudioToFile
サンプルコード - ComplexPlayThru
サンプルコード - SimplePlayThru
Core Audio Preliminary doc

Audio and MIDI on Mac OS X -May 2001

注: システムの条件:QuickTime v6.5をインストールしたMac OS X v10.2およびv10.3。

先頭に戻る

ドキュメント改訂履歴

日付メモ
2006-07-25RecordAudioToFileサンプルコードへのリンクを追加
2006-07-18Unicode文字の不正な表示を修正、リンクを更新
2004-08-23AUHALサンプルコードプロジェクトへのリンクを追加。入出力有効化プロセスの訂正を含め、若干の変更。
2004-07-06ソースリストの若干の訂正。
2004-03-22改訂詳細不明。
2004-03-04新規ドキュメント

掲載日: 2006-07-25




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.