SlimDXでDirectSound
SlimDXではXAudio2に一歩遅れましたがDirectSoundも実装されています。
しかしSlimDXのサンプルには含まれていません。
Managed DirectXからの類推でやろうとしても結構違いがあります。
SlimDXのインターフェイスはC++寄りです。
今回C++側のヘルプ・サンプルを参考にしながらSlimDXでDirectSoundを使うコードを書いたので、少ない日本語SlimDX情報の足しになればと公開します。
でもたぶん間違い・勘違いも含まれるので参考にする場合は注意してくださいね
ASoundクラスの使い方は、
- ASound.Open を関連付けるFormを引数に与え呼び出す。
- new ASound(<ファイル名>) でインスタンス作成。あとはPlayなりStopなり。
- インスタンスを使い終わったらDispose。
- ASound.Close を最後に呼び出し後始末。
となっています。
また、Loggerクラスは自作のログ出力クラスなので、このソースを利用する場合は削除や置き換えをしてください。
バグなど発見されましたらコメントいただけると幸い。
class ASound : IDisposable { static DirectSound.DirectSound directSound; SecondarySoundBuffer buffer; /// <summary> /// 音量を0.0f(最小:無音)〜1.0f(最大)の間で設定する。 /// </summary> public float Volume { get { return (float)(buffer.Volume - DirectSound.Volume.Minimum) / (DirectSound.Volume.Maximum - DirectSound.Volume.Minimum); } set { if(value < 0) value = 0; if(1 < value) value = 1; buffer.Volume = (int)(value * (DirectSound.Volume.Maximum - DirectSound.Volume.Minimum) + (int)DirectSound.Volume.Minimum); } } public static void Open(Form form) { Logger.WriteLine("音声機能の初期化を開始..."); Logger.Indent(); Logger.Write("DirectSoundオブジェクトの作成..."); directSound = new SlimDX.DirectSound.DirectSound(); Logger.WriteLine("完了"); Logger.Write("優先度の設定..."); directSound.SetCooperativeLevel( ADirect3D.Window.Handle, SlimDX.DirectSound.CooperativeLevel.Priority); Logger.WriteLine("完了"); //SetPrimaryBufferFormat(2, 44100, 16); // 必要ないらしい。 Logger.Unindent(); Logger.WriteLine("...音声機能の初期化を完了"); } public static void Close() { Logger.WriteLine("音声機能の終了処理を開始..."); Logger.Indent(); Debug.Assert(directSound != null); if(directSound != null) { Logger.Write("DirectSoundオブジェクトの破棄..."); directSound.Dispose(); directSound = null; Logger.WriteLine("完了"); } Logger.Unindent(); Logger.WriteLine("...音声機能の終了処理を完了"); } public ASound(string fileName) { Logger.Write("音声ファイル:" + fileName + " の読み込み..."); Logger.Indent(); // Waveファイル読み込み using(WaveFile waveFile = new WaveFile(fileName)) { // Flagsの設定はてきとー。パンはそのうち使うかもしれない SoundBufferDescription description = new SoundBufferDescription(); description.Flags = BufferFlags.ControlVolume | BufferFlags.ControlPan; description.Format = waveFile.Format; description.SizeInBytes = waveFile.Size; // セカンダリバッファ作成 buffer = new SecondarySoundBuffer(directSound, description); // バッファにWaveファイルの内容を転送するため読み出し byte[] data = new byte[waveFile.Size]; if(waveFile.Read(data, data.Length) != waveFile.Size) { Logger.WriteLine("!!! Waveファイルから全ての内容を読み出すことが出来ませんでした。 !!!"); } // バッファとwavの大きさはそろえてあるのでLockFlags.EntireBufferに実効的な意味はない buffer.Write(data, 0, DirectSound.LockFlags.EntireBuffer); } // DXUTのSDKsound.cppではバッファがロストしたときの復旧に使用するため // Waveファイルを破棄していないが、今の実装ではロストしたらそのままなので破棄してしまう。 Logger.Unindent(); Logger.WriteLine("完了"); } #region IDisposable メンバ public void Dispose() { if(buffer != null) { buffer.Dispose(); buffer = null; } } #endregion public void Play() { if(buffer.Status == BufferStatus.Playing) { buffer.Stop(); } buffer.CurrentPlayPosition = 0; buffer.Play(0, PlayFlags.None); } public void Stop() { buffer.Stop(); } /// <summary> /// SDKドキュメントより /// > For applications that require specialized mixing or other effects not supported /// > by secondary buffers, DirectSound allows direct access to the primary buffer. /// この辺は参考にしたサイト・ソースでやっていたから作っておいたけど、なくても動く。 /// </summary> public static void SetPrimaryBufferFormat(short channels, int frequency, short bitrate) { // 参考にしたもの: // http://dn.codegear.com/jp/article/20941 // DirectX SDK(Aug 2007)のSDKsound.cpp SoundBufferDescription description = new SoundBufferDescription(); description.Flags = BufferFlags.PrimaryBuffer; description.SizeInBytes = 0; // プライマリバッファは0でなければならないらしい description.Format = null; // nullable? C++はポインタであり同じくNULLでなきゃならんと Logger.Write("プライマリバッファを作成..."); PrimarySoundBuffer primaryBuffer = new PrimarySoundBuffer(directSound, description); Logger.WriteLine("完了"); WaveFormat wf = new WaveFormat(); wf.FormatTag = WaveFormatTag.Pcm; wf.Channels = channels; wf.SamplesPerSecond = frequency; wf.BitsPerSample = bitrate; wf.BlockAlignment = (short)(wf.BitsPerSample / 8 * wf.Channels); wf.AverageBytesPerSecond = wf.SamplesPerSecond * wf.BlockAlignment; Logger.Write("プライマリバッファのフォーマットを設定..."); primaryBuffer.Format = wf; Logger.WriteLine("完了"); } }