.png

OpenAL.zip @ Delphi XE7 Update1

 

マルチプラットフォームのオーディオライブラリである OpenAL を用いて、ゲームなどにも使える高レスポンスの再生や立体音響などに挑戦してみます。

OpenAL - Wikipedia

Cインタフェースが用意されているので、Delphi からも簡単に利用できます。DLL や Cヘッダ は以下からダウンロードできます。

OpenAL Soft - Software 3D Audio

参考として、Delphi 用のチュートリアルも発見しました。

Delphi OpenAL - Tutorials

 

 » ライブラリ
  » Lang
   - LUX.Lang.C.pas  ※ C⇔Delphi 置換定義
  » OpenAL
   - LUX.OpenAL.al.pas  ※ オリジナルヘッダ「al.h」の移植
   - LUX.OpenAL.alc.pas  ※ オリジナルヘッダ「alc.h」の移植
   - LUX.OpenAL.pas
  - LUX.D3.pas
  - LUX.pas

今回、LUX.OpenAL.pas において、以下の二つのクラスを実装しています。

 ・TListener :リスナーを表すクラス。一つしか設置出来ない。
  - Matrix :位置と姿勢を行列によって指定。
  - Velocity :移動速度。

 ・TSpeaker :スピーカを表すクラス。複数設置できる。音の指向性はない。
  - Create( const FileName_:string ); :生成時に再生する WAVファイル を指定。
  - IsLoop :ループ再生するか否か。
  - Gain :音量。
  - Pitch :再生速度。
  - Position :位置。
  - Velocity :移動速度。

それらは以下のように、FMX の 3Dオブジェクト の座標を直接代入できるようにしてあります。リスナーは顔の向きによって聞こえ方が変わりますが、スピーカは無指向性なので位置だけに依存します。もちろん両者とも、Velocity を与えれば、ドップラー効果も表現できます。

 TListener.Matrix := {TControl3D}.AbsoluteMatrix;

 {TSpeaker}.Position := {TControl3D}.AbsolutePosition;

位置/姿勢/速度は内部的に、それぞれ以下のような API で設定されています。

 alListenerfv( AL_POSITION, @{TSingle3D} );
 alListenerfv( AL_ORIENTATION, @{TSingle3D,TSingle3D} );
 alListenerfv( AL_VELOCITY, @{TSingle3D} );

 alSourcefv( _Source, AL_POSITION, @{TSingle3D} );
 alSourcefv( _Source, AL_VELOCITY, @{TSingle3D} );

特に AL_ORIENTATION は、顔の正面方向 と 頭頂方向 の二つのベクトルの塊として与えなくてはならすないので、一旦レコードにまとめてから渡します。

var
V :packed record
F :TSingle3D;
U :TSingle3D;
end;
with V do
begin
F := TVector3D.Create( 0, 0, +1, 0 ) * _Matrix;
U := TVector3D.Create( 0, -1, 0, 0 ) * _Matrix;
end;
alListenerfv( AL_ORIENTATION, @V );

ただ、OpenAL の座標系は OpenGL と同じ形式なので、同じ右手座標系ではありますが、FMX の TPoint3D を直接渡すと座標軸 Y, Z が反転してしまいます。

.png

そこで、以下のような TSingle3D という OpenGL形式 の座標を表すレコードを作り、区別して扱えるようにします。

 TSingle3D = packed record
  X :Single;
  Y :Single;
  Z :Single;
  class operator Implicit( const V_:TPoint3D ) :TSingle3D;
  class operator Implicit( const V_:TSingle3D ) :TPoint3D;
 end; 

TSingle3D と TPoint3D は暗黙の型変換が行えるので、これによりスムーズな座標の受け渡しが可能となります。

class operator TSingle3D.Implicit( const V_:TPoint3D ) :TSingle3D;
begin
     with Result do
     begin
          X := +V_.X;
          Y := -V_.Y;
          Z := -V_.Z;
     end;
end;

class operator TSingle3D.Implicit( const V_:TSingle3D ) :TPoint3D;
begin
     with Result do
     begin
          X := +V_.X;
          Y := -V_.Y;
          Z := -V_.Z;
     end;
end;