敵の足音が、「右後ろから」聞こえるようにしたい。XAudio2で音は鳴らせたけれど、3D化のやり方が分からない。そんなときに使うのが、X3DAudioです。
整理すると、XAudio2は「音を鳴らすエンジン」、X3DAudioは「3D空間での聞こえ方を計算する電卓」です。X3DAudioが左右・前後の振り分けを計算し、その結果をXAudio2に適用して鳴らします。この記事では、2つのAPIの役割、立体音響の仕組み、リスナーとエミッターの考え方、実装の流れ、ハマりどころを、C++の例つきで解説します。
XAudio2とX3DAudioの役割|エンジンと電卓
2つのAPIは名前が似ていますが、役割はきれいに分かれています。
- XAudio2… 音声データを再生する本体。ソースボイス(音源1つ)とマスターボイス(最終出力)で構成される
- X3DAudio… リスナー(耳)とエミッター(音源)の位置から、各スピーカーへの音量と周波数の補正値を計算する。音そのものは鳴らさない
つまり流れは「X3DAudioで計算 → 結果をXAudio2のソースボイスに適用」の繰り返しです。プレイヤーやカメラが動くたびに計算し直して適用することで、音の方向感が動的に変わります。
立体音響の仕組み|左右・距離・ドップラー
X3DAudioが計算してくれる「立体感」は、主に次の3つの要素でできています。
- 定位(パンニング)… 音源が右にあれば右スピーカーを大きく。左右・前後の振り分け
- 距離減衰… 遠いほど小さく。離れるにつれて自然に音量が下がる
- ドップラー効果… 近づく音は高く、遠ざかる音は低く(救急車のあの感じ)
これらを自分で計算するのは大変ですが、X3DAudioにリスナーとエミッターの位置・向き・速度を渡せば、まとめて計算してくれます。位置ベクトルの扱いでは正規化の知識も役立ちます。
リスナーとエミッター|耳と音源を定義する
3D音響の主役は2つの構造体です。リスナー(X3DAUDIO_LISTENER)=プレイヤーの耳、エミッター(X3DAUDIO_EMITTER)=音を出す物体。それぞれに位置・前方向・上方向・速度を設定します。
// リスナー(プレイヤー/カメラ)
X3DAUDIO_LISTENER listener = {};
listener.Position = { camX, camY, camZ }; // 耳の位置
listener.OrientFront = { 0.0f, 0.0f, 1.0f }; // 前方向
listener.OrientTop = { 0.0f, 1.0f, 0.0f }; // 上方向
listener.Velocity = { vx, vy, vz }; // 速度(ドップラー用)
// エミッター(音源)
X3DAUDIO_EMITTER emitter = {};
emitter.Position = { objX, objY, objZ };
emitter.OrientFront = { 0.0f, 0.0f, 1.0f };
emitter.OrientTop = { 0.0f, 1.0f, 0.0f };
emitter.ChannelCount = 1; // モノラル音源が基本
emitter.CurveDistanceScaler = 10.0f; // 距離減衰のスケール
emitter.DopplerScaler = 1.0f;ここで大事なのが、3D音響用の音源はモノラル(1チャンネル)にすることです。ステレオ音源は「もう左右が決まっている音」なので、方向を付け直せません。方向を持たせたい音は必ずモノラルで用意します。
実装の流れ|計算して適用する
準備ができたら、初期化時にX3DAudioInitializeで計算機を作り、毎フレームX3DAudioCalculateで計算 → 結果をソースボイスに適用します。
// 初期化(1回だけ)
X3DAUDIO_HANDLE x3dInstance;
X3DAudioInitialize(SPEAKER_STEREO, X3DAUDIO_SPEED_OF_SOUND, x3dInstance);
// 計算結果を受け取る入れ物
float matrix[8] = {}; // 出力チャンネル数ぶん
X3DAUDIO_DSP_SETTINGS dsp = {};
dsp.SrcChannelCount = 1; // モノラル音源
dsp.DstChannelCount = 2; // ステレオ出力
dsp.pMatrixCoefficients = matrix;// 毎フレーム:計算して適用
X3DAudioCalculate(x3dInstance, &listener, &emitter,
X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER,
&dsp);
// 左右の振り分け(音量マトリクス)を適用
sourceVoice->SetOutputMatrix(masterVoice, 1, 2, dsp.pMatrixCoefficients);
// ドップラーによる再生速度(ピッチ)を適用
sourceVoice->SetFrequencyRatio(dsp.DopplerFactor);この「計算して適用」をプレイヤーや音源が動くたびに回すだけで、方向・距離・ドップラーが効いた立体音響になります。毎フレームの更新なので、deltaTimeで速度を求めておくとドップラーも自然になります。
なお、SPEAKER_STEREOのような定数や関数はワイド文字ではありませんが、XAudio2まわりはWindows APIと同じくワイド文字(L付き)のファイルパスを扱う場面が多いので、あわせて押さえておくと安心です。
【重要】私が立体音響でハマった体験談
個人開発のホラーゲームで、敵の足音を3D化しようとしたのにどこに敵がいても左右がまったく動かず、ただ音量だけ変わる状態になりました。X3DAudioCalculateは呼んでいるのに、です。
原因は足音のwavがステレオだったことでした。ステレオはすでに左右が固定された音なので、いくら方向を計算しても振り分けようがなかったんです。足音をモノラルで書き出し直し、SrcChannelCount = 1にしたら、いきなり「右後ろから足音」が実現しました。
「3Dで鳴らす音はモノラル」という基本を、身をもって覚えた出来事でした。
立体音響のよくある失敗例と対処法
①左右に定位しない(音量しか変わらない)
体験談のとおり、音源がステレオなのが原因なことが多いです。3D化する音はモノラルで用意し、SrcChannelCountを1にしましょう。
②リスナーの向きを更新し忘れる
プレイヤーが振り向いてもOrientFrontを更新しないと、後ろの音が前から聞こえるなど方向が狂います。カメラの向きに合わせて毎フレーム、前方向・上方向を更新しましょう。
③距離を変えても音量が変化しない
CurveDistanceScalerが未設定(0)だと距離減衰が効きません。ゲームのスケールに合った値(メートル単位なら数〜数十)を設定し、X3DAUDIO_CALCULATE_MATRIXフラグを渡しているかも確認しましょう。
注意点
- 3D化する音はモノラルで用意する
- リスナー/エミッターの位置・向き・速度を毎フレーム更新する
- X3DAudioは計算するだけ。結果をXAudio2に適用して初めて鳴る
まとめ
- XAudio2=鳴らすエンジン、X3DAudio=聞こえ方を計算する電卓
- 立体感は定位・距離減衰・ドップラーの3要素でできている
- リスナーとエミッターを定義し、計算結果をソースボイスへ適用する
- 最大の落とし穴は「3D音はモノラル」
立体感は、定位、距離減衰、ドップラーの3要素でできています。リスナーとエミッターを定義して、計算結果をソースボイスへ適用します。最大の落とし穴は、「3D音はモノラル」であることです。


