第2章 OpenALを操作する

第1節 OpenALの基礎

OpenALは出力バッファへのオーディオのレンダリング、および入力バッファからの音声データ収集に関与しています。OpenALの主な使用用途はサンプリングデータオーディオを空間化することでしょう。MIDIのサポートはありません。

OpenALはバッファ・ソース・単独のリスナという、3つの基礎的なプリミティブやオブジェクトを持っています。それぞれのオブジェクトは独立に変える事が出来、あるオブジェクトの設定を変えたとしても他のオブジェクトへは影響を与えません。

またアプリケーションは処理に影響を与えるモードを設定する事が出来ます。モードの設定、オブジェクトの宣言およびその他のOpenALの操作は、関数やプロシージャの形でコマンドが送信される事で行われます。

ソースは三次元空間の物体として位置・方角やその他の属性を持ち、また再生の為に関連づけられた単一のバッファを持ちます。プログラムが音声再生を要求したとき、ソースオブジェクトを通じて実行を制御する事となります。ソースは他のソースとは独立して処理されます。

バッファは圧縮あるいは非圧縮の音声データを持ちます。プログラム起動時やゲームにおけるレベル(訳注:アクションゲーム等のステージのようなものです)間のような、実行に支障を来さないときに大きな固まりのバッファで初期化するのが一般的です。

バッファはソースによって参照されています。音声のサンプルデータはバッファに関連づけられています。

オーディオコンテキスト毎に単一のリスナがあります。リスナの属性はソースの属性と似ていますが、ユーザがオーディををどこから聴いているのか、を表現するのに使われます。全てのソースはリスナとの相対によりミキシングや再生のようなレンダリングがされます。

第2節 プリミティブ型

OpenALライブラリのプリミティブ(スカラ)データ型はシームレスな統合が出来るようにOpenGLのデータ型を模しています。保証最小サイズはOpenGLデータ型のために明記されていますが、しかし実際にC言語のどの型が選ばれるかは実装にまかされています。

しかしながら、与えられたバイナリアーキテクチャ上の全ての実装はこれらのデータ型の一般的な定義を使用しなければなりません。

表2–1 プリミティブデータ型
ALの型説明GLの型
ALboolean8ビットの真偽値型GLboolean
ALchar文字型GLchar
ALbyte2の補数による符号あり8ビット整数GLbyte
ALubyte符号なし8ビット整数GLubyte
ALshort2の補数による符号あり16ビット整数GLshort
ALushort符号なし16ビット整数GLushort
ALint2の補数による符号あり32ビット整数GLint
ALuint符号なし32ビット整数GLuint
ALsizei非負の32ビット整数でサイズを表すGLsizei
ALenum列挙された32ビット値GLenum
ALfloat32ビットのIEEE 754形式の浮動小数点数GLfloat
ALdouble64ビットのIEEE 754形式の浮動小数点数GLdouble

第3節 浮動小数点演算

浮動小数点数を要求するOpenALコマンドに、浮動小数点で表されたどんな値を入力しても問題ありません。そのようなコマンドに浮動小数点数ではない値を提供したことによる結果は未定義ですが、OpenALは中断状態または停止状態に至ってはなりません。

IEEE算術では、例として、NaNや無限大をOpenALに提供すると未定義の結果をもたらすのとは対照的に、−0や正規化されていない数を提供すると予想出来る結果をもたらします。

いくつかの計算は割り算が要求されます。ベクトルの正規化に必要とされる暗黙の割り算のような場合、ゼロによる除算は未定義の結果を生み出しますが、OpenALは中断状態または停止状態に至ってはなりません。

第4節 ステート

OpenALはかなりの数のステートを有しています。この文書は各ステート変数およびいかにして各変数を変更するかの説明を列挙しています。議論の目的の為、ステート変数はそれらの関数により多少恣意的に分類されています。例えば我々がOpenALが暗黙に出力バッファに働きかける操作について述べるとしても出力バッファはOpenALのステートのうちには含まれません。

キューイングに関するバッファのステートのような、OpenALオブジェクトのあるステートが議論の為に導入されますが、APIを通じて表に出る事はありません。

第5節 コマンド構文

OpenALのコマンドは関数かプロシージャです。コマンドの様々なまとまりは同じ動作をしますが、どのように引数を与えるかに差があります。この差を都合よく収める為に、我々はコマンドの説明およびそれらの引数の為にOpenGLの記法を採用します。

第6節 基本の操作

OpenALはさまざまな音声再生タスクに使用出来、リアルタイムレンダリングにおけるOpenGLを素晴らしく補完するものです。OpenGLに親しんでいるプログラマは似た方法を使い三次元環境を表現することで、2つのAPIの相似性にすぐ気が付くでしょう。

OpenGL/OpenALプログラムのため、多くのオーディオプログラムはプログラムコード中でも、プログラムの初期化部分およびレンダリングループ(訳注:ゲームではイベントドリブンではなく、よく描画シーケンスを半無限ループでくり返してリアルタイムにしています)の2カ所にあるでしょう。

OpenGL/OpenALプログラムはよく、たとえいろいろな関数が広げられるかもしれないですが、グラフィックスおよびオーディオシステムが初期化される部分を含んでいるでしょう。OpenALの場合、初期化は普通、

からなります。

初期化の例
// Initialize Open AL
device = alcOpenDevice(NULL); // open default device
if (device != NULL) {
	context=alcCreateContext(device,NULL); // create context
	if (context != NULL) {
		alcMakeContextCurrent(context); // set active context
	}
}

普通、レンダリングループ中でのオーディオのアップデートは、

からなります。

ループ処理の例
// PlaceCamera - places OpenGL camera & updates OpenAL listener data
void AVEnvironment::PlaceCamera()
{
	// update OpenGL camera position
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-0.1333, 0.1333, -0.1, 0.1, 0.2, 50.0);
	
	gluLookAt(listenerPos[0], listenerPos[1], listenerPos[2],
		(listenerPos[0] + sin(listenerAngle)), listenerPos[1],
		(listenerPos[2] - cos(listenerAngle)),
		0.0, 1.0, 0.0);
		
	// update OpenAL
	// place listener at camera
	alListener3f(AL_POSITION, listenerPos[0], listenerPos[1], listenerPos[2]);
	float directionvect[6];
	directionvect[0] = (float) sin(listenerAngle);
	directionvect[1] = 0;
	directionvect[2] = (float) cos(listenerAngle);
	directionvect[3] = 0;
	directionvect[4] = 1;
	directionvect[5] = 0;
	alListenerfv(AL_ORIENTATION, directionvect);
}

第7節 エラー処理

OpenALはエラーと思われる状態の一部分しか検出しません。これは多くの場合、エラーのチェックは逆にエラーの無いプログラムの性能に影響を及ぼす為です。次のコマンド、

ALenum alGetError(void)

はエラー情報を得るのに試用されます。検出可能エラーそれぞれには数値のコードが割り当てられています。

OpenALがエラーを検出した時、フラグがセットされ、エラーコードが記録されます。エラーが起きたならば、更なるエラーはこの記録されたコードに影響を及ぼしません。alGetErrorが呼び出された時、エラーコードを戻り値として返し、フラグがクリアされ、更なるエラーは再びコードを記録するでしょう。alGetErrorがAL_NO_ERRORを返したならば、前回のalGetErrorの呼び出しまたはOpenALの初期化時から、検出可能なエラーは生じていない事となります。

エラーコードは文字列に対応させる事が出来ます。alGetString関数は仕様書で定義されている、列挙子に使用される識別子と同じ定数リテラル文字列へのポインタを返します。

表2–2 エラーの種類
エラー定数名説明
AL_NO_ERROR最新のエラーは存在しません。
AL_INVALID_NAME不正なネームの引数です。
AL_INVALID_ENUM不正な列挙値の引数です。
AL_INVALID_VALUE不正な引数です。
AL_INVALID_OPERATION禁止されている呼び出しです。
AL_OUT_OF_MEMORYメモリを割り当てる事が出来ません。

上の表はOpenALのエラーの種類を要約したものです。エラーフラグがセットされた時、AL_OUT_OF_MEMORYが起きたときだけ捜査の結果が不定義になります。他の場合、エラーを生成したコマンドは無視され、ステートや出力バッファの内容に影響を及ぼしません。

もしエラーが生成されたコマンドが値を返す場合、ゼロを返します。もしポインタの引数を通じて値を変化させるコマンドの場合、それらの値は改変されません。これらのエラーの意味はOpenALのエラーにのみ適用され、メモリアクセスエラーのようなシステムエラーには適用されません。

いくつかのエラー発生状態はさまざまなコマンドの説明において明示されていません。始めに、列挙値を要求するコマンドに対し許可すると定義されていない値を渡した場合、エラーであるAL_INVALID_ENUMが結果となります。これは引数が許可されていない値のシンボル定数へのポインタであった場合も同様です。その値が他の関数では許可されている値であったり、不正な整数値であった場合にも起こるでしょう。

バッファやソースのようなOpenALのオブジェクトのネームとして使われる整数のパラメータは妥当性が検証されます。もしOpenALコマンドで不正なネームのパラメータが使われた場合、AL_INVALID_NAMEエラーが生成され、そのコマンドは無視されます。

指定された値域を越える整数や浮動小数点数を設定する試みはAL_INVALID_VALUEエラーという結果になるでしょう。この仕様はもしfloat型やdouble型の引数にNanや無限大の値を渡した時、実装がAL_INVALID_VALUEエラーを出すことを保証しません。同様に仕様は浮動小数点数についての不経済なテスト(訳注:浮動小数点の形式の妥当性の検証の事だと思います。)を施行しないかも知れません。

コマンドは無効にする事が出来ます。例えば、あるコマンドは与えられたオブジェクトに対し適切ではないかもしれません。また、コマンドに対する引数がトークンと値の不正な組み合わせであるというものもあります。OpenALはどんな、そのような不正コマンドに対しAL_INVALID_OPERATIONエラーで応答します。

メモリがコマンド実行の副作用か、システムレベルまたは内部処理で割り当てられたリソース消耗により枯渇した場合、AL_OUT_OF_MEMORYエラーが生成されるかもしれません。これはOpenALが内部タスクの為にメモリを要求せねばならなくて、OSからの要求メモリの割り当てに失敗した場合、過去に実行したコマンドとは無関係に起こり得ます。

さもなければ、エラーはこの仕様で明らかに説明される状態のときのみに生成されます。

第8節 実行の制御

アプリケーションは一時的にコンテキストごとを土台として、一部のOpenALの能力を無効にする事が出来ます。これでドライバの実装をある操作の一部分に最適化することができます。能力の有効化/無効化は関数の対を仕様する事で扱えます。

void alEnable (ALenum target);
void alDisable (ALenum target);

また、アプリケーションは与えられた能力が現在有効か否かを問い合わせる事も出来ます。

ALboolean alIsEnabled (ALenum target);

もしtargetに示すべきトークンが不正であった場合、AL_INVALID_ENUMエラーが生成されるでしょう。

第9節 オブジェクトという枠組み

OpenALはオブジェクト指向APIですが、クラス、構造体やその他の明確なデータ構造はアプリケーション側に公開しません。

第1項 オブジェクトの分類

OpenALはオブジェクトについての3つの主要なカテゴリがあります。

第10節 静的オブジェクトと動的オブジェクト

OpenALのオブジェクトで広く主流なものは動的で、アプリケーションの要求によって作られるだろうものです。またアプリケーションの要求のもと、作る必要がなく作る事が出来ないOpenALオブジェクトもあります。現在、リスナはOpenALで唯一の静的オブジェクトのようなものです。

第11節 オブジェクトの『ネーム』

動的オブジェクトはOpenGLとの類推でオブジェクトの『ネーム』と呼ばれる整数を用いて操作します。符号なし整数(ALuint)型となっています。

もし問題となっているオブジェクトがコンテキスト間で共有出来る場合、ネームはネームを要求したコンテキストの寿命を越えて妥当である事が可能です。アプリケーションの寿命を越えたものについて正確な値やその分布について、仕様では何の保証も仮定もしません。共有されるかもしれないオブジェクトのネームはOpenALの同一分類の中で唯一である事が保証されますが、違う種類のオブジェクトをまたがっているときは全く保証しません。

リスナのような唯一(シングルトン)のオブジェクトは整数の『ネーム』を要求する事も持つ事もありません。

第12節 オブジェクトネームを要求する

OpenALはオブジェクトのネームを獲得するための呼び出しを提供します。アプリケーションはalGen【オブジェクトの種類】sを用いて与えられた種類について、オブジェクトの集まりを要求します。戻り値となる、ネームの実際の値は実装依存です。生成される値や範囲は保証されません。

オブジェクトのネームの割り当てはリソースの割り当てやオブジェクトの生成が即座に行われるという意味ではありません。実装は与えられたオブジェクトについて、それを変化させる呼び出しの中で使われるまで延期してもかまいません。ネームは呼び出し元によって指定されたメモリ位置に書き込まれます。

void alGenBuffers (ALsizei n, ALuint *bufferNames);
void alGenSources (ALsizei n, ALuint *sourceNames);

零個のネームを要求する事は正当な無命令(a legal NOP)です。実装が与えられた引数にn個のネームを格納出来ない事を知っている時や実装がメモリリソース制限により、要求された数のオブジェクトを生成出来ない事を知っている時、OpenALはAL_INVALID_VALUEエラーを伴って応答します。有効メモリの不足によりオブジェクトを割り当てる事が出来ない時OpenALはAL_OUT_OF_MEMORYエラーを伴って応答します。

第13節 オブジェクトネームを開放する

OpenALはalDelete【オブジェクトの種類】sを用いてオブジェクトのネームを解放し、ネームの開放に連携してさりげなくオブジェクトの削除を要求します。もし指定されたネームの1つ以上が妥当でない場合、AL_INVALID_NAMEエラーが記録され、オブジェクトは削除されないでしょう。

一旦削除されれば、ネームはもうalDeleteBuffersやalDeleteSourcesを含むOpenAL関数呼び出しで使用する事は妥当ではありません。このような使い方はいずれもAL_INVALID_NAMEエラーを引き起こすでしょう。

OpenALの実装は実際のリソース開放を延期してもかまいません。理想的にはリソースは可能な限り速く解放されるべきですが、保証はされません。

void alDeleteBuffers (ALsizei n, ALuint *bufferNames);
void alDeleteSources (ALsizei n, ALuint *sourceNames);

再生中のソースは削除可能です—ソースは自動的に停止され、その後削除されるでしょう。ソースに割り当てられているバッファは削除不能です。

第14節 オブジェクトネームを検証する

OpenALはオブジェクトのネームを検証する呼び出しを提供します.アプリケーションはオブジェクトのネームが妥当かどうかをalIs【オブジェクトの種類】による問い合わせを用いて検証する事が出来ます。もしネームが妥当なオブジェクトのネームであるとされればAL_TRUEを、そうでなければAL_FALSEを返します。alIs【オブジェクトの種類】は無効なのか、削除されたネームなのかを区別しません。

ALboolean alIsBuffer (ALuint bufferName);
ALboolean alIsSource (ALuint sourceName);

第15節 オブジェクト属性の設定

OpenALのオブジェクトの属性を制御する呼び出しが提供されています。それらは与えられたオブジェクトの種類の、実際のプロパティに依存します。それぞれの種類毎の、正確なAPIは以下で議論されています。

ネームのあるオブジェクトのステートに影響を及ぼす、OpenAのコマンドは大抵次の形式になります。

void al{Object}{n}{if}{v}(ALuint objectName, ALenum paramName, T values );

{Object}は“Buffer”や“Listener”のようなOpenALのオブジェクトの名前です。

{n}は渡す値の数を示します。もし1つであれば省略されます。

{if}は渡す値の型を示します。“i”は整数を、“f”は浮動小数点数を示します。

{v}は渡されるのが型のベクトルである事を示します。

Tは{if}および{v}で示された通りの型になります。

objectNameパラメータはこの呼び出しにより影響を受けるOpenALオブジェクトを指定します。不正なネームを使用するとAL_INVALID_NAMEエラーを引き起こすでしょう。

影響を及ぼされるオブジェクトの属性はparamNameとなります。あるオブジェクトの種類に対し適切なOpenALパラメタは必ずしもOpenALオブジェクトの他の種類にも有効であるわけではありません。与えられたオブジェクトについて不正なパラメータが指定された場合、AL_INVALID_OPERATIONエラーを引き起こすでしょう。

ある型について、全ての代入可能な値が与えられたobjectNameとparameterNameとにとって適切であるというわけではありません。不正な値やNULL値のポインタの使用はAL_INVALID_VALUEエラーを引き起こすでしょう。

エラーを引き起こしたコマンドは無命令(NOP)となります。

ネームの無い(唯一な)オブジェクトの場合、関数の{Object}で暗示しているので整数型のobjectNameは省略されます。

al{Object}{n}{if}{v} (ALenum paramName, T values );

上にある形式を使ったOpenALコマンドのいくつかの例を挙げます。

void  alListeneri( ALenum param, ALint value );
void  alListener3f( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 );
void  alListenerfv( ALenum param, const ALfloat* values );
void  alSourcef( ALuint sid, ALenum param, ALfloat value );

第16節 オブジェクト属性の取得

ネームのあるオブジェクトや単一オブジェクトについて、現在の属性を取得する呼び出しが提供されています。それらは与えたオブジェクトの種類の、実際のプロパティに依存します。取得のようなもののパフォーマンスは実装に依存しており、保証されません。paramNameパラメータについて妥当な値は、属性設定関数における適切な値と全く同一です。

void alGet{Object}{n}{if}{v} (uint objectName, enum paramName, T * destination);

ネームの無いオブジェクトでは、関数名で暗示しているのでobjectNameは省略されます。

void alGet{Object}{n}{if}{v} (enum paramName, T * destination);

{Object}、{n}、{if}、{v}およびTの定義は、属性設定コマンドと同じです。

不正なネームの使用はAL_INVALID_NAMEエラーを引き起こすでしょう。不正な型(トークン)をパラメータとして与えた場合、AL_INVALID_ENUMエラーを引き起こすでしょう。destinationをNULLポインタとした呼び出しはおとなしく無視されるでしょう。エラーにより、OpenALのステートおよび宛先メモリが影響を受けたり変更される事は無いでしょう。

第17節 オブジェクトの属性について

音声処理に影響を与える属性はいろんなOpenALオブジェクトの種類で設定出来るか、またはOpenAL呼び出しの影響で変化するかもしれません。それらのオブジェクトのプロパティの広く主流なものは一つのOpenALオブジェクトの種類ごとで定義されます。しかしいくつかは2つ以上の種類に適応可能で、別々に記載されています。

この文書でパラメータについて説明する際、一般的な形式を示します。

名前記号デフォルト
paramNameT範囲 または セットスカラ値 または n組の値

解説

解説は追加の制約や詳細を述べます。paramNameにはそのネームで定義されている、OpenALの列挙値を与えます。配列の形であるのみならず、平坦な、展開された形であるときは普通、Tは適切な記号の一覧にする事が出来ます。