第6章 ALコンテキストとALC API

OpenAL仕様のこの章ではALC(OpenAL Context API)について説明します。ALCはリソース共有、ロック/アンロックを含む、OpenALコンテキストの管理の為の、移植性のあるAPIです。コアのALのAPIの中ではコンテキストは暗黙のうちに存在しますが、そのコンテキストは露出されません。コンテキストは与えられたOpenAL状態機械の実態の状態をカプセル化します。

コンテキストAPIはOpenALとは区別して定義されたALC型を使用します。例えばALCboolean、ALCchar等があります。

第1節 デバイスの管理

ALCはデバイスの表現方法を導入します。デバイスは実装依存でも、ハードウェアデバイスでも、デーモンやOSサービス、実際のサーバでもかまいません。この機構は異なるドライバ(やハードウェア)が同一システムに共存できるのみならず、いくつかのアプリケーションで単一ハードウェア出力デバイスを含む、オーディオに関するシステムリソースを共有出来ます。

有効なバックエンドをユニークデバイス識別子(訳注:specifier(「指定する」の名詞形)であり、identiferではないのですが、意味合いから『識別子』としました)に位置づける際の詳細は実装に任されます。

第1項 デバイスへ接続する

アプリケーション(クライアント)がデバイス(サーバ)に接続するにはalcOpenDevice関数を用います。

ALCdevice * alcOpenDevice (const ALCchar *deviceSpecifier);

もし関数がNULLを返したならば、サウンドドライバ/デバイスが見つからなかった事を示します。引数は要求するデバイス、またはデバイス設定をNULL終端文字列で渡します。NULLであった場合、実装は既定のデバイスを返すでしょう。

第2項 デバイスから切断する

アプリケーション(クライアント)がデバイス(サーバ)から切断するにはalcCloseDevice関数を用います。

ALCboolean alcCloseDevice (ALCdevice *deviceHandle);

戻り値は成功を示すALC_TRUEか失敗を示すALC_FALSEです。すべてのデバイスのコンテキストおよびバッファが破棄されてない場合、失敗するでしょう。一旦閉じた後はdeviceHandleは無効になります。

第2節 レンダリングコンテキストの管理

全てのOpenALコアAPIの操作は現在のコンテキストに対し働きます。OpenALのスコープ内ではコンテキストは暗黙的です—それはハンドルや関数パラメータのような形で現れません。

同時点で、プロセス毎に1つのOpenALコンテキストのみ適用可能です。アプリケーションは複数のコンテキストを保持する事は出来ず、スレッドか否かによりそれ相応に現在のコンテキストを設定しなければなりません。アプリケーションはもう一つのコンテキストを共有する複数スレッドを持つ事が出来ます。

換言するならば、OpenALおよびALCはスレッドセーフであるということです。

第1項 属性

アプリケーションはコンテキスト生成時にコンテキストの属性の指定を選ぶ事が出来ます。明示的に指定されなかった属性は実装独自の既定値に設定されます。

表6–1 コンテキスト生成属性
名前説明
ALC_FREQUENCYミキシング出力バッファの周波数を[Hz]で示します
ALC_REFRESHリフレッシュ間隔を[Hz]で示します
ALC_SYNC同期コンテキストかどうかを示すフラグ
ALC_MONO_SOURCESモノラルデータをサポートするソースがいくつあるべきかを示すヒント
ALC_STEREO_SOURCESステレオデータをサポートするソースがいくつあるべきかを示すヒント
ALC_STEREO_SOURCESステレオデータをサポートするソースがいくつあるべきかを示すヒント

第2項 生成する

コンテキストはalcCreateContextにより生成されます。デバイスパラメータは有効なデバイスを指定します。属性リストにはNULL、あるいは有効なALC属性トークンと要求する数値で構成されたペアによる、0終端のリストを指定します。

ALCcontext * alcCreateContext (const ALCdevice * deviceHandle, const ALCint * attrList);

コンテキスト生成は以下の場合失敗するでしょう。

  1. もしアプリケーションが自分たちで提供出来ない属性を要求した場合
  2. もし指定された属性の組み合わせが提供出来ない場合
  3. もし指定された属性や属性の組み合わせが、指定されていない属性の既定値に適合しない場合

もしコンテキスト生成が失敗したならば、コンテキストポインタの戻り値はNULLになるでしょう。

第3項 操作に使うコンテキストを選択する

コンテキストをOpenAL操作に関わるものとして適用するにはalcMakeContextCurrentが使われます。

コンテキストパラメータはNULL、あるいは有効なコンテキストポインタを指定します。NULLを使用したときはいずれのコンテキストも適用されず、OpenALを停止させるときに有用です。この操作はコンテキストが生成されたデバイスに対してのみ適用されるでしょう。

ALCboolean alcMakeContextCurrent (ALCcontext * context);

戻り値(ALC_TRUEまたはALC_FALSE)はこの呼び出しにおいてエラーが生じたか否かを反映しています。例えば不正なコンテキストポインタに対してはALC_INVALID_CONTEXTなど、標準エラー状態もまたこの呼び出しの実行中に設定されます。

それぞれのOSプロセス(ふつうはこれはそれぞれのアプリケーションを意味します)について、ある時間について1つのコンテキストのみ適用可能です。

全てのOpenALコマンドは現在のコンテキストに適用されます。バッファ等、コンテキスト間で共有されるオブジェクトに働きかけるコマンドは他コンテキストに副作用を及ぼします。

第4項 初期化処理

現在のコンテキストは、共有オブジェクトに対するステート変化を除き、OpenALコマンドによりステート変化されうる唯一のコンテキストです。 一方、複数コンテキストが同時に処理される事も出来ます。

処理(すなわちオフセット増加のような内部実行ステートが機能)してほしいコンテキストを示すのに、アプリケーションはalcProcessContext関数を使用します。

void alcProcessContext (ALCcontext *context);

繰り返しalcProcessContext関数を呼び出す事は正当で、かつ既に『処理中』となったコンテキストに何も影響を及ぼしません。alcCreateContext関数により生成されたコンテキストの既定ステートは『処理中』です。

第5項 サスペンド処理

アプリケーションは任意の、処理中のコンテキスト(現在適用されているものも含みます)をサスペンドする事が出来ます。処理中から停止(すなわちオフセット増加のような内部実行ステートが変化しないように)してほしいコンテキストを示すのに、アプリケーションはalcSuspendContext関数を使用します。

void alcSuspendContext (ALCcontext *context);

繰り返しalcSuspendContext関数を呼び出す事は正当で、かつ既に『停止中』となったコンテキストに何も影響を及ぼしません。

第6項 破棄する

void alcDestroyContext(ALCcontext * context);

コンテキストを破棄するための正しい方法は、先にそのコンテキストを、alcMakeCurrent関数にNULLコンテキストを渡す事で解放する事です。

アプリケーションは現在のコンテキストの破棄を試みるべきではありません—そのような行いは働かないでしょうし、ALC_INVALID_OPERATIONエラーを返すでしょう。

コンテキストに含まれる全てのソースはコンテキスト破棄時に自動的に削除されるでしょう。

第3節 ALC問い合わせ

第1項 現在のコンテキストを取得する

アプリケーションはその現在のコンテキストを問い合わせ、ハンドルを取得する事が出来ます。もし現在適用されているコンテキストが無い場合、NULLが返されます。

ALCcontext * alcGetCurrentContext(void);

第2項 コンテキストがもつデバイスを取得する

アプリケーションは与えたコンテキストのデバイスを問い合わせ、ハンドルを取得する事が出来ます。

ALCdevice * alcGetContextsDevice(ALCcontext * context);

第3項 エクステンションを取得する

与えられたエクステンションが現在のコンテキストおよびそのデバイスにおいて使用可能かどうか検証するには、

ALCboolean alcIsExtensionPresent(const ALCdevice *deviceHandle, const ALCchar *extName);

を使用します。不正な、あるいはサポートされていない文字列はALC_FALSEを返します。deviceHandleはNULLでもかまいません。extNameがNULLであった場合ALC_INVALID_VALUEエラーになり、ALC_FALSEが返されます。

extNameは大文字小文字を問わず、実装は内部的に全て大文字に変換するでしょう。(そしてエクステンション名は大文字で表現されるでしょう。)

第4項 関数のエントリポイントを取得する

アプリケーションは名前で要求する前に、エクステンションやコア関数のエントリポイントが適当かどうかをalcIsExtensionPresent関数を使って検証する事を期待されています。エクステンションのエントリポイントはalcGetProcAddress関数を使って取得する事ができます。

void * alcGetProcAddress (const ALCdevice *deviceHandle, const ALchar *funcName);

エントリポイントはデバイス固有で、コンテキスト固有ではありません。deviceHandleをNULLにすると、たとえもし有効なデバイスにおいて使用可能であっても、そのエントリポイントが返ってくるかどうかは保証されません。

第5項 列挙子を取得する

列挙子/トークン値はデバイス独立です。しかしエクステンションにより定義されているトークンは、与えられたデバイスには存在しないかもしれません。

NULLハンドルは正当ですが、OpenALコアにより定義されているトークしか保証されません。エクステンショントークンが使用可能かどうかはALCエクステンションに依存します。

ALCenum alcGetEnumValue(const ALCdevice *deviceHandle, const ALCchar *enumName);

enumNameパラメータにNULLを指定すると、ALC_INVALID_VALUEエラーを引き起こし、AL_NONEを返すでしょう。

第6項 エラーの状態を取得する

ALCはエラー処理にOpenALと同じ慣例・機構を用います。特に、ALCはX11(GLX)やWindows(WGL)から派生した慣例を用いません。alcGetError関数はALCエラーを取得するのに用いられます。

ALCenum alcGetError(ALCdevice * deviceHandle);

エラー状態はデバイス固有で、かつOpenALと同様、alcGetError関数呼び出しはエラー状態をリセットします。

表6–2 エラー状態
名前説明
ALC_NO_ERROR現在エラーはありません
ALC_INVALID_DEVICEデバイスハンドラやその指定にはアクセス可能なドライバかサーバを指定してください。(訳注:原文では"The device handle or specifier names an accessible driver/server."『〜〜〜はアクセス可能なドライバまたはサーバを指定しています』という形になっていますが、エラーの意味からこのように訳しました。)
ALC_INVALID_CONTEXTコンテキスト引数は有効なコンテキストを指していません。
ALC_INVALID_ENUM使われているトークンは無効か、適していません。
ALC_OUT_OF_MEMORYメモリ割り当てが出来ません。

第7項 文字列による取得

アプリケーションはALCから特定の文字列を得る事が出来ます。

const ALCchar * alcGetString(ALCdevice * deviceHandle, ALCenum token);

ALC_EXTENSIONSについて問う時、deviceHandleにNULLが指定されているとALC_INVALID_DEVICEエラーが生成されるでしょう。ALC_DEFAULT_DEVICE_SPECIFIERまたはALC_CAPTURE_DEFAULT_DEVICE_SPECIFIERについて問う時、deviceHandleの値は無視されます。

ALC_DEVICE_SPECIFIERまたはALC_CAPTURE_DEVICE_SPECIFIERについて、NULLデバイスを渡したときのalcGetString関数問い合わせは、使用可能なデバイスの一覧を返すでしょう。それぞれのデバイス名は単一NULL文字で区切られ、リストの終端は2つのNULL文字になります

オーディオ出力デバイスを持たないシステムにおけるALC_DEFAULT_DEVICE_SPECIFIERの要求はNULLが返されるでしょう。

オーディオキャプチャデバイスを持たないシステムにおけるALC_CAPTURE_DEFAULT_DEVICE_SPECIFIERの要求はNULLが返されるでしょう。

表6–3 文字列による取得の一覧
名前説明
ALC_DEFAULT_DEVICE_SPECIFIER既定デバイスの固有文字列
ALC_DEVICE_SPECIFIERデバイスの固有文字列
ALC_EXTENSIONSスペース区切りの、使用可能なコンテキストエクステンションの一覧
ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER既定キャプチャデバイスの名前
ALC_CAPTURE_DEVICE_SPECIFIER指定されたキャプチャデバイスの名前、あるいはキャプチャデバイスが指定されなかった場合は全ての使用可能なキャプチャデバイスの一覧

さらに、ALC_NO_ERROR、ALC_INVALID_DEVICE、ALC_INVALID_CONTEXT、ALC_INVALID_ENUM、ALC_INVALID_VALUEを含む全ての有効なエラートークンについて、印字可能エラーメッセージ文字列が提供されます。

第8項 整数による取得

アプリケーションはALCから整数による取得の関数を用いて情報を問い合わせる事が出来ます。

void alcGetIntegerv(ALCdevice * deviceHandle, ALCenum token, ALCsizei size, ALCint *dest);

いくつかのトークンではdeviceHandleがNULLであってもかまいません。その他の場合、NULLデバイスの指定はALC_INVALID_DEVICEエラーを生成するでしょう。アプリケーションはALCintとして与えたdestinationバッファのサイズを指定する必要があります。destinationがNULLもしくはsizeがゼロであった場合、この問い合わせはALCにより無視されるでしょう。

表6–1(コンテキスト生成属性)、表6–4(コンテキスト取得タイプ)の全てのトークンがこの呼び出しにおいて有効です。

表6–4 整数による取得
名前説明
ALC_ATTRIBUTES_SIZE現在のコンテキストにおけるゼロ終端属性リストの要素数を返します。NULLデバイスは無効です。
ALC_ALL_ATTRIBUTESALC_ATTRIBUTES_SIZEの大きさのdestinationを期待し、指定されたデバイスの現在のコンテキストの属性リストを提供します。NULLデバイスは無効です。
ALC_MAJOR_VERSIONこの実装における仕様の版のメジャーバージョンです。NULLデバイスは有効です。
ALC_MINOR_VERSIONこの実装における仕様の版のマイナーバージョンです。NULLデバイスは有効です。
ALC_CAPTURE_SAMPLES仕様可能なキャプチャサンプル数です。NULLデバイスは無効です。

第4節 共有オブジェクト

効率上の理由により、あるALオブジェクトはALCコンテキストをまたがって共有されます。このとき、ALバッファは唯一の共有オブジェクトです。

第1項 共有バッファ

バッファはコンテキスト間で共有されます。バッファの処理ステートは現在のコンテキストのみならず、全てのコンテキストの干渉による依存関係によって決定されます。これは処理中コンテキストと同様、停止中コンテキストを含みます。

第2項 キャプチャ

プロシージャ・関数

ALCdevice* alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint freq, ALCenum fmt, ALCsizei bufsize);
ALCboolean alcCaptureCloseDevice(ALCdevice *device);
void alcCaptureStart(ALCdevice *device);
void alcCaptureStop(ALCdevice *device);
void alcCaptureSamples(ALCdevice *device, ALCvoid *buf, ALCsizei samps);

トークン

従来の出力デバイスから脱却し、OpenALは入力、ユーザ環境からのオーディオデータの『キャプチャ』能力を提供出来ます。

キャプチャデバイスはライブラリの合間で並列動作しますが、コンテキストを含まず、多くのALやALCステートを持ちません。そういうものとして、それらはとても単純化されたインタフェースを表した自身のエントリポイントを含みます。キャプチャはポーリング、OpenAL内の非ブロックサブAPIとして扱われます。

alcCaptureOpenDevice関数によりアプリケーションはキャプチャデバイスに接続する事が出来ます。全ての使用可能なキャプチャデバイスの一覧を得るには、NULLデバイスを指定してALC_CAPTURE_DEVICE_SPECIFIERについてalcGetString関数を用います。それぞれの名前はNULL終端で、かつ一覧は2つのNULL文字で閉じられているでしょう。

有効なキャプチャデバイスを指定してALC_CAPTURE_DEVICE_SPECIFIERを取得すると、そのデバイスの名前が単一NULL終端文字列で得られます。

ALCdevice* alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint freq, ALCenum fmt, ALCsizei bufsize);

もしこの関数がNULLを返した場合、サウンドドライバ/デバイスが見つからなかったか、要求されたフォーマットを満たす事が出来ませんでした。

deviceName引数は要求するデバイスもしくはデバイス設定のNULL終端文字列です。もしNULLが指定された場合、実装は実装固有の既定デバイスを提供するでしょう。

freq引数とfmt引数にはアプリケーションに提供されるだろうオーディオデータのフォーマットを指定します。そしてこれはalBufferData関数に渡せるものと同じです。実装はアプリケーションの便宜のため、このフォーマットに変換、リサンプルする事が期待されます。

bufsize引数はバッファの、サンプルフレーム数を指定します。例えばフォーマットにAL_FORMAT_STEREO16、バッファサイズに1024を要求した場合、ALは1024 * 4倍とのオーディオデータを蓄える事が要求されます。必要に応じ、実装が要求より大きいバッファを使用するかもしれませんが、少なくとも要求されたサイズのバッファを用意することに注意してください。

圧縮フォーマットやエクステンション提供フォーマットの指定は、たとえエクステンションがレンダリング向けに提供していたとしても、失敗するかもしれません。

alcCaptureCloseDevice関数によりアプリケーションはキャプチャデバイスから切断する事が出来ます。

ALCboolean alcCaptureCloseDevice(ALCdevice *deviceHandle);

戻り値はALC_TRUEかALC_FALSEで、成功したか失敗したかを示しています。もしdeviceHandleがNULLまたは無効であった場合、ALC_INVALID_DEVICEエラーが生成されるでしょう。ひとたび閉じられると、デバイスハンドルは無効です。

オーディオのキャプチャ

ひとたび、alcCaptureOpenDevice関数を通じキャプチャデバイスが開かれると、alcCaptureStartにより音声録音が開始出来る事間違いなしです。

void alcCaptureStart(ALCdevice *device);

ひとたび開始されると、デバイスは開いたときに指定されたサイズの内部リングバッファに音声を録音するでしょう。

アプリケーションはキャプチャデバイスに、ALC_CAPTURE_SAMPLESトークンによるalcGetInteger関数呼び出しを通じ現在使用可能なデータの数を問い合わせる事が出来ます。これは現在使用可能なサンプルフレームの数を報告するでしょう。

アプリケーションが、処理するのに十分なサンプルがあると感じた時、alcCaptureSamplesによりそれらを得る事が出来ます。

void alcCaptureSamples(ALCdevice *device, ALCvoid *buf, ALCsizei samps);

buf引数にはアプリケーションが確保した、少なくともsamps個のサンプルフレームを格納出来るバッファを指定します。まさにブロックすることなく、samps個のサンプルはこの呼び出しにより取り出され、バッファに格納されるでしょう。

もしsampsより少ないフレームしか使用可能でなかった場合、ALC_INVALID_VALUEエラーがセットされます。もしsampsより多いフレームが使用可能であった場合、余分なサンプルはOpenALにより保持され、後で取得する事が出来ます。

実装はこの時点まで変換・リサンプルを後回しにする事が出来ます。現在使用可能なものより多くのサンプルフレームを要求する事は誤りです。

もしアプリケーションが全ての時間に渡って次々オーディオをキャプチャしてほしくない時、alcCaptureStopによりデバイスを閉じる事無く一時停止させる事が出来ます。

void alcCaptureStop(ALCdevice *device);

実装はこの場合に対して最適化する事が奨励されます。停止したキャプチャデバイスが再開された後の、使用可能なオーディオサンプルの総量はゼロにリセットされます。アプリケーションはそれを読むのに、キャプチャデバイスを停止することを必要としません。