translation:adc:audio:core_audio_overview:0400_aroadmaptocommontasks

一般的な作業の事例

この章では、Core Audio の利用における幾つかの基本的なパターンを解説します。 これら例が、共通の作業を処理するためには、Core Audio のパーツ群をどのように結合するべきかを示しています。

Core Audio は極めてモジュール化されており、その様々なパーツの使い方に殆ど制限がありません。従って、開発者はある機能を実装するのに、ここで示されている物とは別の方法を選択するかもしれません。 例えば、アプリケーションは Audio File サービスと Audio Converter サービスの関数を直接呼び出して、ファイルからデータを読み込み、リニア PCM に変換するかもしれません。あるいは、その機能を独立した生成ユニットとしてカプセル化し、アプリケーションからはそのユニットを発見、読み込むことも可能です。

オーディオを扱う多くのアプリケーションには、ディスクあるいはバッファとのオーディオデータの読み書き機能が必要です。 殆どの場合において、ファイルのデータを読み込み、そしてそれを(Audio Unit などで使用するために)リニア PCM に変換したいと思うでしょう。 拡張 Audio File API を使用することで、これらの作業を1つの工程でこなすことができます。

Figure 3-1 は、拡張 Audio File API が Audio File API を呼び出してオーディオデータを読み込み、それから Audio Converter API を呼び出してそのデータをリニア PCM に変換する様子を示しています(変換前のデータは常にリニアPCMでないと仮定しています)。

Figure 3-1 オーディオデータの読み込み
オーディオデータの読み込み

ファイルの読み込み、変換作業を超えたより細かな制御が必要な場合は、Audio File と Audio Converter の関数を直接呼ぶことができます。 ディスクやバッファからファイルを読み込むには、Audio File API を使用します。そのデータは圧縮されているかもしれませんが、この場合は Audio Converter を使ってリニア PCM へと変換することが可能です。 また Audio Converter を、ビット深度、サンプリングレートなどの、リニア PCM フォーマット間での変換に用いることもできます。 Audio Converter API による変換作業は、希望する入出力フォーマットを指定した Audio Converter オブジェクトを作成することで行います。 それぞれのフォーマットは AudioStreamBasicDescription 構造体で定義され、これは Core Audio でオーディオデータを説明するための、基本データ型となっています。 前途したように リニア PCM への変換であれば、拡張 Audio File API を利用することで、独自の Audio Converter を作る事なく、一発で行うことができます。

一旦リニア PCM に変換してしまえば、そのデータは Audio Unit による処理の準備が整ったことになります。 Audio Unit に接続するには、特定の Audio Unit の入力にコールバック関数を登録します。すると、そのコールバック関数が呼ばれ、必要なオーディオデータが提供されます。

オーディオデータを出力したい場合は、データを出力ユニットに送ります。 出力ユニットはリニア PCM データしか受け付けません。 出力ユニットは通常、ハードウェアデバイスのプロクシとなりますが、これは必須ではありません。

リニア PCM は中間フォーマットの働きをします。そして、それは多くの変換を可能にします。 あるフォーマットの変換が可能かどうかを確かめるには、デコーダ(フォーマット A からリニア PCM)とエンコーダ(リニア PCM からフォーマット B)の両方が利用可能であることを確認する必要があります。 例えば、MP3 から AAC へとデータを変換したい場合、2つの Audio Converter が必要となります。Figure 3-2 のような、1つは MP3 をリニア PCM へと変換するもので、もう1つはリニア PCM から AAC へと変換するものです。

Figure 3-2 2つのコンバータを使ったオーディオデータの変換
2つのコンバータを使ったオーディオデータの変換

Audio File API と Audio Converter API の使用例は、Core Audio SDK の SimpleSDK/ConvertFile と Services/AudioFileTools をご覧ください。 独自の Audio Converter コーデックの記述に興味があるのでしたら、AudioCodec フォルダのサンプルをご覧ください。

殆どのオーディオアプリケーションは外部ハードウェアに接続し、オーディオデータを(例えばアンプやスピーカへ)出力したり、(マイクロフォンなどを通して)データを得たりする必要があります。 これら操作は、ハードウェア抽象化層(HAL)を通じて行われなければなりません。 幸いなことに、大部分の場合において HAL にアクセスするために、特別なコードを書く必要はありません。 Apple は3つの標準 Audio Unit である、デフォルト出力ユニット、システム出力ユニット、AUHAL を提供しており、これらが殆どのハードウェアの要求に対処してくれるでしょう。 アプリケーションは、これらユニットを使用する前に Component Manager を呼び出し、ユニットの探索と読み込みを行わなければなりません。

デフォルト出力ユニットとシステム出力ユニットは、オーディオデータを(ユーザーにより選択された)デフォルトの出力や、システム出力(警告音や他のシステムサウンドが出力される場所)のそれぞれに送ります。 (オーディオ処理グラフなどの中で)Audio Unit がこれら出力デバイスの1つに接続すると、出力デバイスがデータを要求した際に、そのユニットのコールバック関数(レンダコールバックとも呼ばれます)が呼ばれます。 出力ユニットは HAL を通して、データを適切な出力デバイスに送ります。そして自動的に、Figure 3-3 で示される以下の仕事を処理します。

Figure 3-3 出力ユニットの内部
出力ユニットの内部

  • 要求されるリニア PCM データへの変換。出力ユニットは Audio Converter を内蔵し、オーディオデータをハードウェアが要求するリニア PCM の仕様へと変換します。
  • 要求されるチャンネルへのマッピング。例えば、開発したユニットは2チャンネル消費するが、出力デバイスは5チャンネル扱えるとします。この場合、どのチャンネルをどのチャンネルへ割り当てるか決めたいと、恐らく思うでしょう。出力ユニットの kAudioOutputUnitProperty_ChannelMap プロパティを使いチャンネルマッピングを指定することで、それを実現できます。チャンネルマップを指定しない場合、デフォルトでは最初のオーディオチャンネルは最初のデバイスチャンネルへ、2番目のオーディオチャンネルは2番目のデバイスチャンネルへ、以下同様に、といったような割り当てがなされます。実際に音が鳴る出力は、Audio MIDI 設定.app でユーザーが構成したデバイスのスピーカーで決定されます。
  • 信号の再生/停止処理。信号チェーン中で、出力ユニットはオーディオデータの流れを制御する Audio Unit に過ぎません。

デフォルト出力ユニットによるオーディオ再生の例は Core Audio SDK の SimpleSDK/DefaultOutputUnit をご覧ください。

入力デバイスやデフォルト出力デバイス以外のハードウェアデバイスと接続する必要がある場合は、AUHAL を用いる必要があります。 AUHAL は出力デバイスとして設計されてはいますが、入力の kAudioOutputUnitProperty_EnableIO プロパティを設定することで、AUHAL で入力を受けられるように設定することができます。 詳しい情報は Technical Note TN2091: Device Input Using the HAL Output Audio Unit をご覧ください。 入力の用意が出来ると、AUHAL は入力チャンネルのマッピングと、(必要ならば)入力データのリニア PCM 形式への変換を行う Audio Converter の利用が可能となります。

AUHAL はデフォルト出力ユニットの、より汎用的(generalized)なものです。 オーディオ変換やチャンネルマッピング機能に加えて、HAL の kAudioOutputUnitProperty_CurrentDeviceAudioDevice オブジェクトの ID を設定することで、接続するデバイスを指定することができます。 一度接続してしまえば、AUHAL を通して関連付けられている AudioDevice オブジェクトのプロパティを操作することもできます。AUHAL は Audio Device のプロパティ呼び出しに沿って、自動的に通過します。

AUHAL インスタンスは1度に1つのデバイスとしか接続できません。従って、デバイスが入出力の両方を持っている場合に限って、入力と出力の両方を有効にすることができます。 例えば、PowerPC ベースの Macintosh の内蔵オーディオは、入力(マイクイン)と出力(スピーカ)の両方が可能な1つのデバイスとして構成されています。

USB オーディオデバイスや、Intel ベースの Macintosh の現在の系列の内蔵オーディオを含むいくつかのオーディオデバイスでは、オーディオデバイスが入力と出力で分かれています。これら分離しているデバイスを1つの AudioDevice オブジェクトに結合する方法については、"機器セットを使う" をご覧ください。

信号の流れの目的に応じて、入力と出力の両方を設定された AUHAL は2つの Audio Unit として振る舞います。 例えば、出力が有効の時、AUHAL は1つ前の Audio Unit のレンダコールバックを呼び出します。 Audio Unit がデバイスからの入力データを必要としている場合、その Audio Unit は AUHAL のレンダコールバックを呼び出します。 Figure 3-4 は入出力の両方に対応した AUHAL の様子を示しています。

Figure 3-4 入出力に対応した AUHAL
入出力に対応した AUHAL

外部デバイスを通してやって来るオーディオ信号は、オーディオデータストリームへと変換され、AUHAL を通過します。そして、他の Audio Unit へ送ることが可能になります。 データの処理後(例えば、エフェクトの追加や他のオーディオデータとのミキシングの後)、出力端はデータを AUHAL へと送り返します。そして、同じ外部デバイスを通してオーディオが出力されます。

AUHAL の入出力を使った例は、ADC リファレンスライブラリの SimplePlayThruComplexPlayThru サンプルコードをご覧ください。 SimplePlayThru は、1つの AUHAL インスタンスを通しての入出力の方法を示します。 ComplexPlayThru は、入力に AUHAL を、出力にデフォルト出力ユニットを使用した入出力の実装方法について示しています。

オーディオハードウェアデバイスとのインターフェースを構築するにあたって、Core Audio は抽象化の追加階層を許可することで、複数のデバイスの入出力を結合し1つのデバイスとして振る舞わせる、機器セットを作成します。 例えば、5チャンネルのオーディオ出力を扱う必要がある場合、うち2つを1つのデバイスに割り当て、そして残りの3つを別のデバイスへと割り当てることが出来ます。 Core Audio は自動的に、データの経路を振り分けて両デバイスへと送ります。然るにアプリケーションは、あたかも1つのデバイスであるかのように、その出力とやり取りすることができます。 また Core Audio は、あなたに代わって、適切なオーディオの同期の保証と最小限の遅延を実現することで、あなたはアプリケーションやプラグインの仕様の細部に注力することが出来るようになります。

ユーザーは、Audio MIDI 設定.app のメニューから [オーディオ]-[機器セットエディタを開く] を選択することで、機器セットを作成することができます。 機器セットとして統合するデバイスを選んだ後は、まるでもう1つのハードウェアデバイスが出来たかのように、そのセットの入出力チャンネルの設定を行うことが出来ます。 また、機器セットを構成するデバイスのうち、どのクロックを同期のためのマスタークロックとするか、指定する必要があります。

ユーザーが作成した機器セットは、システム全体で有効となります。 しかし、HAL サービスの関数呼び出しを使って、そのアプリケーション固有のローカルな機器セットをプログラム的に作る事も出来ます。 機器セットは HAL 中では AudioAggregateDevice(AudioDevice のサブクラス) として表現されます。

機器セットは実装の詳細を隠蔽することに長けています。 例えば、通常 USB オーディオデバイスは入力と出力で別々のドライバを要求し、別々の AudioDevice オブジェクトとして表わされます。 しかし、グローバルな機器セットを作成することにより、HAL はそれらドライバを1つの AudioDevice オブジェクトとして表現します。

機器セットは、セットを構成するデバイスの情報を保存しています。 ユーザーがそのデバイスを取り外す(あるいは、他のデバイスと共存できないような値を設定する)と、そのチャンネルはセットから消失します。しかし、デバイスを再接続したり設定し直したりすると、チャンネルは再び出現します。

機器セットにはいくつかの制限があります:

  • 機器セットを構成するデバイスは、全て同じサンプリング周波数で動作せねばならず、そして、それらのデータストリームはミックス可能でなければなりません。
  • 機器セットは、音量、ミュート、入力源選択といった設定変更可能なコントローラを、一切提供しません。
  • セットを構成する全デバイスがデフォルトデバイスに指定されている場合を除いて、機器セットをデフォルト入出力に設定することはできません。さもなければ、アプリケーションは使用する機器セットを明示的に選択しなければなりません。
  • 今のところ、IOAudio ファミリで表現される(すなわち、カーネルレベルの)ドライバだけは、機器セットに加えることができません。

Audio Unit の作成についての詳しい情報は Audio Unit Programming Guide をご覧ください。

プラグインである Audio Unit は、これらを読み込んで制御する為のホストアプリケーションを必要とします。

Audio Unit は Component Manager コンポーネントである為、ホストアプリケーションは Component Manager を呼び出して、それらを読み込まなければなりません。 ホストアプリケーションは、以下に挙げる何れかのフォルダにインストールされている Audio Unit を発見し、生成することができます:

  • ~/Library/Audio/Plug-Ins/Components ここにインストールされた Audio Unit は、ホームフォルダの所有者のみが使用できます。
  • /Library/Audio/Plug-Ins/Components ここにインストールされた Audio Unit は、全ユーザーが使用できます。
  • /System/Library/Components Apple が提供する Audio Unit のデフォルトの設置場所です。

利用可能な Audio Unit の一覧が(例えば、これをユーザーに示す為に)必要な場合、特定のタイプの Audio Unit(audio units of a particular type)の個数を得るために Component Manager の関数 CountComponets を呼び出し、それから FindNextComponent を繰り返し使って、それぞれのユニットの情報を得る必要があります。 ComponentDescription 構造体は各々の Audio Unit の識別子(タイプ、サブタイプ、製造者コード)を含みます。 Apple が提供する Audio Unit の Component Manager タイプ/サブタイプの一覧は システム組み込み_audio_unit をご覧ください。 ホストは(OpenComponet を呼び出すことで)それぞれのユニットを開くことが出来、また、デフォルトの入出力といったそのユニットの様々な特性情報を問い合わせることが出来ます。そして、ホストはその情報をユーザーへと届けます。

多くの場合において、オーディオ処理グラフは、Audio Unit 同士を繋げる最も簡単な方法です。 処理グラフを使用する1つの利点は、Audio Unit の生成と解放のための個々の Component Manager の呼び出しを、その API が引き受けてくれることです。 グラフを作成するには、NewAUGraph を呼び出します。すると、新規グラフオブジェクトが返されます。 それから、AUGraphNewNode を呼び出して、グラフに Audio Unit を追加します。 グラフは、ハードウェアインターフェース(デフォルト出力ユニット または AUHAL)か標準出力ユニットの、どちらかの出力ユニットで終わっていなければなりません。

処理グラフを構成するユニットの追加が終わったら、AUGraphOpen を呼びます。 この関数は、グラフ中の各 Audio Unit の OpenComponet を呼び出すのと同じ働きをします。 この時、チャンネル構成、サンプリングレート、特定のユニットに対する固有の特性(そのユニットが持つ入出力の個数など)といった情報を、Audio Unit にセットすることができます。

個々の Audio Unit 間の接続を行うには、接続に使う入力と出力を指定した上で AUGraphConnectNodeInput を呼び出します。 Audio Unit のチェーンは出力ユニットで終わっていなければなりません。さもないと、ホストアプリケーションがオーディオ処理の開始・停止をする術がなくなってしまいます。

Audio Unit がユーザーインタフェースを持っている場合、ホストアプリケーションにはそれを表示する責任があります。 Audio Unit は Cocoa あるいは Carbon (もしくはその両方) ベースのインターフェースを提供します。 ユーザーインターフェースのコードは、通常、Audio Unit と一緒にバンドルされています。

  • UI が Cocoa ベースの場合、ホストアプリケーションはユニットのプロパティ kAudioUnitProperty_CocoaUI を問い合わせて、インタフェースを定義している独自クラス(NSView のサブクラス)を見つけ、そして、そのクラスのインスタンスを生成しなければなりません。
  • UI が Carbon ベースの場合、UI は1つないしそれ以上の Componet Manager コンポーネントとして格納されています。コンポーネントの識別子(タイプ、サブタイプ、製造者)は、kAudioUnitProperty_GetUIComponentList を問い合わせることで得られます。その後、ホストアプリケーションは、与えられたコンポーネント上で AudioUnitCarbonViewCreate を呼び出すことで、UI を実体化することができます。その際、UI はウィンドウ中に HIView として表示されます。

信号チェーンを作成した後は、AUGraphInitialize を呼び出して Audio Unit を初期化します。 このように初期化関数を呼ぶことで、それぞれの Audio Unit に、処理用のメモリ確保の許可、チャンネル情報の設定などを行います。 それから AUGraphStart を呼べば、処理が開始されます。 出力ユニットはチェーン中の前のユニットに、オーディオデータの要求をし(つまりコールバックです)、そしてそのユニットは更に前のユニットを呼びます。これが延々と続きます。 オーディオのソースは Audio Unit (生成ユニットや AUHAL など)かもしれませんし、あるいは、ホストアプリケーション自身が、信号チェーン中の最初の Audio Unit に登録されたコールバックによって、オーディオデータを提供するかもしれません(コールバックの設定は、そのユニットの kAudioUnitProperty_SetRenderCallback プロパティで行います)。

Audio Unit が実体化されている間、そのユニットのパラメータやプロパティの変化を知りたいと思うかもしれません。 リスナオブジェクトを登録しておけば、変化が起きた際に、それがオブジェクトに通知されます。 このようなリスナの詳しい実装方法については Technical Note TN2104: Handling Audio Unit Events をご覧ください。

信号処理を停止したい時は、AUGraphStop を呼びます。

グラフの全ての Audio Unit の終了準備を行うには AUGraphUninitialize を呼び出します。 終了準備状態では、Audio Unit のプロパティを変更したり、接続の作成や変更を行うことが出来ます。 AUGraphClose を呼ぶと、グラフ中の各 Audio Unit は CloseComponet の呼び出しによって解放されます。 しかし、グラフは依然として、保持していたユニットに関する情報を記憶しています。

処理グラフを解放するには、AUGraphDispose を呼びます。 グラフが解放されると、自動的にそのグラフが持つ Audio Unit の全てのインスタンスが解放されます。

Audio Unit のホスティングの例は、Core Audio SDK 中のサンプル Services/AudioUnitHostingServices/CocoaAUHost をご覧ください。

Audio Unit のユーザーインタフェースの実装例は、Core Audio SDK 中のサンプル AudioUnits/CarbonGenericView をご覧ください。 この例は、ユーザーが調整可能なパラメータを持つ Audio Unit のサンプルとして使うことが出来ます。

Componet Manager の使用についての情報は、以下のドキュメントをご覧ください:

MIDI データを扱う時、アプリケーションは標準 MIDI ファイル(SMF)からトラックデータを読み込む必要があるかもしれません。 Music Player 関数 (MusicSequenceLoadSMFWithFlags または MusicSequenceLoadSMFDataWithFlags) を呼び出して、標準 MIDI 形式のデータを読み込むことができます。その様子を Figure 3-5 に示します。

Figure 3-5 標準 MIDI ファイルを読み込む
標準 MIDI ファイルを読み込む

ファイルを読み込む時は MIDI ファイルの形式とフラグセットに左右されるため、1トラックに全ての MIDI データを格納することもできますし、それぞれの MIDI チャンネルをシーケンス中に分離したトラックとして格納することもできます。 標準では、各 MIDI チャンネルはシーケンス中の新しいトラックに順次割り当てられます。 つまり、MIDI データがチャンネル 1, 3, 4 を持つ場合、3つの新規トラックがシーケンスに追加され、その各々のトラックはチャンネル 1, 2, 3 のデータを持ちます。 これらトラックは、既存のトラックの一番後ろに追加されます。 シーケンス中では各々のトラックは、0から始まる番号が割り当てられます。

タイミング情報(例えばテンポイベント)はテンポトラックとなります。

シーケンスに MIDI データを読み込んでしまえば、Figure 3-6 に示されるように、Music Player インスタンスを割り当てて、データの演奏ができます。

Figure 3-6 MIDI データの再生
MIDI データの再生

シーケンスは特定のオーディオ処理グラフと関連付けられていなければならず、また、シーケンス中のトラックはグラフ中の、1つないしそれ以上の音源ユニットに割り当てられている必要があります(トラックの割り当てを特に指定しない場合、Music Player は、グラフ中に音源ユニットがあれば、その最初のユニットに全 MIDI データを送信します)。 シーケンスに自動的に割り当てられた Music Player は、グラフの出力ユニットとやり取りし、出力されるオーディオデータの同期が正確に取れることを確認します。 コンプレッサユニットは、必須ではありませんが、音源ユニットの出力のダイナミックレンジを確実に一定に保つには、とても有用です。

Figure 3-7 で示されるように、シーケンス中の MIDI データは、外部 MIDI 機器(または仮想 MIDI デバイスとして設定されたソフトウェア)に渡すこともできます。

MIDI 出力に向かうトラックは、MIDI エンドポイントに割り当てられていなければなりません。 Music Player は Core MIDI とやり取りし、MIDI デバイスへのデータの流れの同期が正確に取れることを確認します。 Core MIDI はそれから、MIDI 機器へデータを送信するために、MIDI Server との調整を行います。

Figure 3-7 MIDI デバイスへ MIDI データを送る
MIDI デバイスへ MIDI データを送る

トラックのシーケンスは、音源ユニットと MIDI デバイスの組み合わせを自由に変えて割り当てることができます。 例えば、いくつかのトラックを音源ユニット通して再生するように割り当て、残りのトラックを外部 MIDI デバイスで再生するために Core MIDI に送ることができます。その様子を Figure 3-8 に示します。

Figure 3-8 実音源と仮想音源による再生
実音源と仮想音源による再生

Music Player は、オーディオ処理グラフの出力ユニットと Core MIDI の間で出力データの同期が取れるように、自動的に調停を行います。

他の使われ方としては、Figure 3-9 のように新しい MIDI 入力を受け取りつつ、既にあるトラックのデータを再生するといった事が想定されます。

Figure 3-9 新規トラックデータの入力を受け付ける
新規トラックデータの入力を受け付ける

既存データの再生は、通常、オーディオ処理グラフを通して行われます。グラフが、オーディオデータを出力ユニットへと送るのです。 外部 MIDI デバイスからの新しいデータは Core MIDI を通して入力され、対応するエンドポイントを通じて転送されます。 アプリケーションは、入ってくるデータが無くなるまで受け取りを繰り返し、その MIDI イベントを新規、あるいは既存のトラックに書き込まなければなりません。 Music Player API は、シーケンスに新規トラックを追加する関数、タイムスタンプ付き MIDI イベントやその他のメッセージをトラックに書き込む関数を持ちます。

MIDI データの扱いと再生の例は、以下に挙げる Core Audio SDK のサンプルをご覧下さい:

  • MIDI/SampleTools : 簡単な MIDI データの送受信の方法を示します。
  • SimpleSDK/PlaySoftMIDI : 音源ユニットと出力ユニットからなる、簡単な処理グラフへと MIDI データを送るサンプルです。
  • SimpleSDK/PlaySequence : MIDI ファイルをシーケンスへと読み込み、Music Player を使ってそれを再生するサンプルです。

時として、オーディオデータと同期の取れた MIDI データからの音声を組み合わせて、再生したいことがあります。 例えば、多くのゲームの音声は BGM とイベント(歩行や発砲など)によって引き起こされる効果音で構成され、前者はオーディオファイルとしてディスクに格納されており、また、後者は MIDI データとして生成されます。 Figure 3-10 は Core Audio で、この2つのデータを統合して使う方法を示しています。

Figure 3-10 オーディオと MIDI データの統合
オーディオと MIDI データの統合

音声トラックのオーディオデータは、拡張 Audio File API を使ってディスクまたはメモリ上から読み込まれ、リニア PCM へと変換されます。 MIDI データー音楽シーケンスのトラックとして保存されていますーは、仮想音源ユニットへと送られます。 仮想音源ユニットからの出力はリニア PCM 形式で、音声トラックのデータと結合することができます。 この例では 3D ミキサユニットを使用しています。このユニットは3次元空間中に音源を配置することができます。 シーケンス中の1つのトラックは、イベントデータをミキサユニット(これは位置パラメータを変更します)へ送り、音が時間と共に移動するといったことを実現します。 必要に応じて、アプケーションは (Music)Player の動きを監視し、イベントをスペシャルムーブメントトラックに追加しなければならないでしょう。

オーディオデータファイルの読み込みと再生の例は、Core Audio SDK の SimpleSDK/PlayFile をご覧下さい。

  • translation/adc/audio/core_audio_overview/0400_aroadmaptocommontasks.txt
  • 最終更新: 2020-12-04 13:29
  • by Decomo