Seaside Laboratory

Posts

DirectX 7 から DirectX 8 への移行

開発用の DirectX SDK のバージョンと、最新の DirectX ランタイムのバージョンとの差が大きくなってきたので、バージョンを一段階上げて差を縮めることにした。DirectX 8 という数字を見ると少し古そうに思えるが、DirectX 9 以降は更新内容のほとんどが Direct3D なので、それ以外の API はそんなに古くなかったりする。

DirectInput

IDirectInput8::EnumDevices

dwDevType に指定する定数が DI8DEVCLASS_~ といった DirectInput8 専用のものになった。

コールバック関数経由で取得できる DIDEVICEINSTANCE も変更されていて、DI8DEVTYPE_JOYSTICK とは違う DI8DEVTYPE_GAMEPAD という紛らわしいものが追加されている。今までのゲームパッドは、見た目がゲームパッドでも内部はジョイスティックだったので、GAMEPAD が何を指すものなのかいまいちわからない。

マニュアルでも言及されておらず、ネット上に落ちているソースでは JOYSTICK と同じ扱いをしていたので、IDirectInputDevice8::SetDataFormat には c_dfDIJoystick を指定しておいた。

IDirectInput8::CreateDevice

以前の CreateDeviceEx は引数にインターフェイス名を渡さなければいけなかったが、それがなくなり少しすっきりした。

dinput8.lib

DirectInput8 は以前の dinput.lib とは異なる dinput8.lib という専用のリンクライブラリが用意されている。COM 経由で IDirectInput8 を生成していたので、その変化に気づけなかったが、DirectInput8Create を使っている場合は dinput8.lib にしないと動かないっぽい。

定義済みの DIDATAFORMAT 構造体変数 (c_dfDIKeyboard など) の関係で lib がないとコンパイルができない点は変わらない。

DirectSound

DSBUFFERDESC

同じ名前で旧バージョンと違う中身になっている。ややこしい。

DirectX 7 以前との互換性を保つために、この構造体の前のバージョンの DSBUFFERDESC1 も dsound.h で維持されている。

主な変更点は guid3DAlgorithm メンバの追加で、

DirectSound3D HEL が使う二重スピーカの仮想アルゴリズムのユニーク識別子。DSBCAPS_CTRL3D が dwFlags に設定されていない場合、このメンバは GUID_NULL (DS3DALG_DEFAULT) にすべきである。

3D サウンドは使わないので DS3DALG_DEFAULT を設定した。

IDirectSound8::CreateSoundBuffer

CreateSoundBuffer が生成するインターフェイスは IDirectSoundBuffer8 ではない。引数 ppDSBuffer の説明は以下の通り。

新しいバッファオブジェクトの IDirectSoundBuffer インターフェイスを受け取る変数のアドレス。IDirectSoundBuffer8 を取得するには、QueryInterface を使用する。

一度 IDirectSoundBuffer を生成してから QueryInterface で IDirectSoundBuffer8 を取得する。

先程のマニュアルには続きがあり、

IDirectSoundBuffer8 はプライマリバッファには利用できない。

プライマリだけは IDirectSoundBuffer を使う必要がある。これまたややこしい。

DirectSound を初期化すると、DirectSound はサウンドのミキシングと出力デバイスへの送信をするために、プライマリサウンドバッファの作成と管理を自動的に行う。

プライマリサウンドバッファは自動作成されるので、凝ったことをしていなければセカンダリサウンドバッファの生成だけ考えればよい。

DirectMusic

IDirectMusicPerformance8::InitAudio

以前は Init した後に AddPort というコードを書いていたが、

オーディオ出力の以前のモデルと新しいモデルには、次のような違いがある。

アプリケーションは直接ポートを操作しない。DirectMusic がポートの作成およびパフォーマンスチャンネルのマッピングを処理する。

InitAudio を使うのが推奨されている。

HRESULT InitAudio
(
    IDirectMusic** ppDirectMusic,
    IDirectSound** ppDirectSound,
    HWND hWnd,
    DWORD dwDefaultPathType,
    DWORD dwPChannelCount,
    DWORD dwFlags,
    DMUS_AUDIOPARAMS *pParams
);

DirectSound を指定する引数の宣言が IDirectSound になっているので、IDirectSound8 を生成している場合はそのまま渡すことができない。

ppDirectSound で指定するインターフェイスポインタは、CLSID_DirectSound8 クラスのオブジェクトへのインターフェイスを指していなければならない。このクラスのオブジェクトは、IDirectSound と IDirectSound8 の両方をサポートしているが、IDirectSound インターフェイスを渡す必要がある。

QueryInterface で IDirectSound8 から IDirectSound を取得して渡すことにした。

やっと InitAudio できると思って実行してみると DSERR_PRIOLEVELNEEDED (必要な優先レベルを持っていない) が返ってくる。

DirectSound オブジェクトは、InitAudio に渡す前に完全に初期化する必要がある。それが CoCreateInstance を使って作成されたオブジェクトである場合は、IDirectSound8::Initialize を呼び出す。IDirectSound8::SetCooperativeLevel を使って、協調レベルを DSSCL_PRIORITY に設定する。

協調レベルを DSSCL_NORMAL にしていたのが原因らしい。協調レベルを変更することに不安はあったが、

ゲームアプリケーションは、ほとんどすべての環境で優先協調レベルを使用する。このレベルは、サンプリングレートとビット深度に対するアプリケーション制御を可能にしながら、最も強力に動作する。

と書かれていたので DSSCL_PRIORITY に変更した。

IDirectMusicSegment8::SetParam

SetParam は汎用的なメソッドなので、何をするにしても長ったらしい引数をつけて呼ぶ必要があった。

HRESULT SetParam
(
    REFGUID rguidType,
    DWORD dwGroupBits,
    DWORD dwIndex,
    MUSIC_TIME mtTime,
    void* pParam
);

IDirectMusicSegment8 にはいくつか専用のメソッドが用意されているので、簡単に書くことができる。例えば SetParam に GUID_Download を指定するケースは Download メソッドに代替可能。

HRESULT Download
(
    IUnknown *pAudioPath
);
IDirectMusicSegment8::SetRepeats

以前の SetRepeats は繰り返しについての記述がなく、-1 を指定すれば繰り返されるという裏技的な手法だったが、今回のバージョンからは繰り返し再生用の定数 DMUS_SEG_REPEAT_INFINITE が用意されている。

dwRepeats
セグメントのループ部分が反復される回数。明示的に停止されるまで反復するには、DMUS_SEG_REPEAT_INFINITE を指定する。

dmsynth.dll

再生する MIDI ファイルによっては dmsynth.dll 内で謎のクラッシュが発生する。

InitAudio に指定するオーディオパスタイプが DMUS_APATH_SHARED_STEREOPLUSREVERB だと頻繁にクラッシュしたので、試しに DMUS_APATH_DYNAMIC_STEREO に変えたところ良い感じになったが、Visual Studio 上で Debug モード実行していたときに一度クラッシュしたこともあったので DirectMusic 自体のバグっぽい。

ネット上の情報 (ほとんど海外) によると、Windows 8 環境下の DirectMusic はうまく動作しない模様。バグが発生しない Windows 7 バージョンの dll を移植して動かしている強者もいた。