XAPOな話・その1
エフェクトかけたい放題
Write: 09/02/09 UpDate: --/--/--

前の記事でかなり投げっぱなしだったXAPOの説明をします。
XAPO(Cross-platform Audio Processing Object)とは、XAudioで使うDSP(Digital Signal Processing)のことです。
まぁ音にかけるエフェクトを作る機構と思えば良いでしょう。
今回はSampleAPOBase.hを利用しないで、自分で書いてみます。
まずは一番シンプルなソースです。
XAPOの実体は
XAudioXAPOTest.h だけです。
単に、入力された音のボリュームを小さくして出力しているだけです。
パラメータが外部から入力されない場合は、CXAPOBaseを継承したクラスを作ります。
DirectXSDKのマニュアルにある通り、CXAPOBaseはIXAPOの内、IXAPO::Process以外が実装されています。
なので、こいつを継承して Processを実装すれば最低限の事はできます。
ちなみにエフェクト本体はこのProcessに記述します。
しかし、これだけでは入力に関する情報が分からないので、IXAPO::LockForProcessもオーバーライドしておきます。
ついでにUnlockForProcessもオーバーライドしておくと、LockForProcessで初期化、
UnlockForProcessで終了処理を書くことができます。
LockForProcessの引数で、入力されるWavの形式がWAVEFORMATEXで渡されます。
WAVEFORMATEXの中で特に重要なのは、チャンネル数を表すnChannelsです。
ステレオの場合、nChannels=2ですね。
Processの引数XAPO_PROCESS_BUFFER_PARAMETERSで、
入力されたフレーム数ValidFrameCountとwaveデータpBufferが渡されます。
pBufferはfloat型の音声データで±1に正規化され(多分…)、
チャンネル毎のデータが順番に(ステレオの場合、LRLRLR…)入っています(多分…)。
よってpBufferにはbyte数換算で、ValidFrameCount * nChannels * sizeof( float )のデータが入っている訳です。
また、pBufferは入力であると同時に出力でもあるので、ここに入っているデータを書き換えるとそのまま出力に反映されます。

それでは、少しコードを書き換えてスペクトルアナライザを作ってみましょう。
ソースはこちら
スペアナを実現するには、音声データにFFTをかければ良いのですが、
そこそこの長さのデータが必要になます。4096個程度あれば十分でしょう。
しかし、ValidFrameCountをこちらでコントロールする方法が無い(多分…)ので、自分でデータをバッファリングします。
WAVEFORMATEXのnSamplesPerSecに書いてあるサンプリングレートを使えば、
FFTの結果を実際の周波数に読み替えることができます。
面倒なので、FFTの窓関数は矩形窓です。
コンソール画面の8個の数字は、0Hzから12kHzまで1.5kHz刻みの音量をdb表記で示しています。
鳴っている音は、125Hz、250Hz、500Hz、1000Hz、2000Hz、4000Hz、8000Hzのサイン波です。
よって、 125〜1000Hzまでは一つ目の数字が、2000Hzでは2つ目の数字が、4000Hzでは3つ目の数字が、8000Hzでは6つ目の数字が、
-13db程度の値で最大値になります。

いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。

XAudioでOggVorbisな話
これからのDirectXSDKで生き抜くために
Write: 09/01/25 UpDate: --/--/--

ひさびさの新記事は、またOggVorbisネタです。ねぎしょ〜ぎのソースをコピペしただけって事は秘密です。
現在配布されているDirectX SDKではDirectMusicのコンポーネントが入っていません。
こんな状況になったのは結構古い話なのですが、DirectX Aug 2007 SDKまでしか対応していなかったようです。
また、Vistaでは事実上DirectSoundの切捨てを行ったので、これを後生大事に使い続けるのも面白くありません。
メンテされなくなったAPIを使い続けるのは気分の良いものではないので 、
ここいらで新しいSoundAPIに乗り換えるのが吉でしょう。
野村XXが乗り換えたのは、2008年夏だったのですが、実はその前からDirectMusicの代替物を探していました。
ところが、OpenAL、XACT、XAudioと次世代のAPIがいくつかつばぜり合いを繰り広げていて、
どれが標準になるのか分からない状況でした。
そんな中で、XAudio2が2008年3月にリリースされ、(個人的には)決着がついたと思っています。
機能的にはDirectSoundを置き換えるもので、DirectMusicのような高級なAPIではありません。
Wav形式を始めとする各種音楽ファイルの読み込みやMIDIの再生などはサポートされていません。
生のWaveデータがストリーミング再生できる機構にGPUのシェーダに近い、エフェクト機構がくっついたものです。
実にシンプルな設計で好感が持てます。
XPとVistaとXBoxのサウンド周りの違いをXAudio2が吸収し、その上にXACT3が乗っかっている形になります。
概要はこちらのサイト(英語)を見ると分かりやすいかと。
XAudio2はSourceVoiceとSubmixVoiceとMasteringVoiceから出来ています。
SourceVoiceはWaveデータが置いてあるメモリ領域+それに付随するデータ(音量やピッチ)と考えれば良いでしょう。
このSourceVoiceに対してエフェクトをかける機構がxAPOと呼ばれるものです。
複数のSourceVoiceをミックスするのが、SubmixVoiceです。
SourceVoiceの入力が生のWaveデータであるのに対し、
SubmixVoiceはSourceVoiceの出力が入力になるというだけの違いです。
MasteringVoiceは、複数のSourceVoiceとSubmixVoiceを一つにまとめて、スピーカーに送ります。
要は、これら3つの要素がツリー状に繋がっている訳です。
一番シンプルなプログラムは、以下の通りです。
1. XAudio2CreateでXAudio2オブジェクトを作る
2. XAudio2のメンバ関数のCreateMasteringVoiceでMasteringVoiceを作る
3. 同様にCreateSourceVoiceでSourceVoiceを作る
4. XAUDIO2_BUFFER型のバッファにPCMデータを書き込む
5. SourceVoiceのメンバ関数SubmitSourceBuffer、Startを使いサウンドを再生する
6. 同様にStop、DestroyVoiceを使ってサウンドを停止する
7. MasteringVoiceのメンバ関数DestroyVoiceでMasteringVoiceを破棄
8. XAudio2のメンバ関数ReleaseでXAudio2オブジェクトを破棄
簡単ですね。
4のPCMデータ書き込みの所で、Waveファイルから読み込むかOggVorbisから読み込むか、
はたまた別のファイルフォーマットを読み込むかは用途次第です。
今回はWaveフォーマットはXAudioWave::Loadの中で地道に解析しています。
OggVorbisについてはlibVorbis内のov_readを使っています。
OggVorbisを再生する場合、曲の全てをあらかじめov_readでPCMに変換してしまえば、
後の処理はWaveの再生と同じです。
実際には、変換時間やメモリの使用量を考えるとストリーミング再生させるのが現実的でしょう。
XAudio2はストリーミング再生が元からサポートされているので、この辺はシンプルに実装できます。
一つのSourceVoiceに対し、複数回
SubmitSourceBufferを実行することで、バッファリングが行われます。
一つのバッファの再生が終わるとSetCallback_BufferEndで設定されたコールバック関数が呼び出されるので、
そのバッファに続きのデータを書き込むだけです。
これで最低限音を鳴らすことが出来るのですが、ゲーム用途を考えるとフェード機能くらいはあっても良いかと思います。
そこで、xAPOをつかってフェードを実現します。
XAPOのセットアップは少しややこしいので、MS謹製のヘルパークラスのSampleAPOBase.hを利用します。
SampleAPOBaseを継承したクラスのInitに初期化、DoProcessに
エフェクトのコードを書き込みます。
DoProcessの引数で自由なパラメータを渡すことが出来るので、
フェードのパラメータはこれを使ってxAPOに設定します。
まぁこの辺はソースを実際に見た方が早いでしょう。

というわけで、今回のソースです。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。


戻る