30:効果音を再生しよう
30年前のゲームじゃあるまいし、やっぱり「ちゅどーん」など迫力のある効果音をつけて盛り上げたい。と、いうわけで今回は効果音を再生する方法についてご紹介します。DirectXにはWAVEファイルの再生に特化したDirectSoundと呼ばれる機能が存在し、wav形式のファイルに格納されている各種情報をDirectSoundにあらかじめコピーしておくことで、レスポンスの早い音楽の再生が可能となります。
static
変数として、基盤クラスに置いておくことにします。IDirectSound8を
static
にしたため、コンストラクタ、デストラクタで生成・解放するとややこしくなるため、CGameObject::Initialize()で行った方法と同様に、生成・解放する専用の関数を作成し、WinMain()内で実行させるようにしています。IDirectSound8を作成したら、今動かしているソフトウェアとは別のソフトが音楽を再生したときに対する再生の優先度を指定します(サンプルプログラムではゲームの音楽再生を優先するようにしています)。
#pragma once
#include "GameObject.h"
// DirectSoundを使うためのヘッダ定義とライブラリリンクの指示
#include <dsound.h>
#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")
class CSound : public CGameObject
{
private:
friend int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);
static LPDIRECTSOUND8 pDSound;
static void CreateDirectSound(HWND hWnd);
static void ReleaseDirectSound();
};
#include "Sound.h"
LPDIRECTSOUND8 CSound::pDSound = NULL;
void CSound::CreateDirectSound(HWND hWnd)
{
// IDirectSound8の作成
DirectSoundCreate8(
&DSDEVID_DefaultPlayback, // 標準のハードウェアを利用
&pDSound, // 格納するIDirectSound8インターフェース
NULL // 現行バージョンではNULLを指定
);
// 優先度の指定
pDSound->SetCooperativeLevel(
hWnd, // 音楽再生に利用するウィンドウのハンドル
DSSCL_PRIORITY // 優先度
);
}
void CSound::ReleaseDirectSound()
{
RELEASE(pDSound);
}
/* 省略 */
class CSound : public CGameObject
{
private:
/* 省略 */
LPDIRECTSOUNDBUFFER pDSBuffer;
public:
CSound();
CSound(LPCTSTR filename);
~CSound();
BOOL Load(LPCTSTR filename);
};
CSound::CSound()
{
pDSBuffer = NULL;
}
CSound::CSound(LPCTSTR filename)
{
pDSBuffer = NULL;
Load(filename);
}
CSound::~CSound()
{
RELEASE(pDSBuffer);
}
BOOL CSound::Load(LPCTSTR filename)
{
// waveファイルを開く
FILE *fp;
if(_tfopen_s(&fp, filename, _T("rb"))){
DXTRACE_MSG(_T("ファイルが開けません"));
return FALSE;
}
// 本当にwaveファイルかどうか調べる
char buf[10];
fread(buf, 4, 1, fp);
if(memcmp(buf, "RIFF", 4) != 0){
DXTRACE_MSG(_T("RIFFフォーマットではありません"));
return FALSE;
}
// RIFFデータサイズは省略
fseek(fp, 4, SEEK_CUR);
fread(buf, 8, 1, fp);
if(memcmp(buf, "WAVEfmt ", 8) != 0){
DXTRACE_MSG(_T("WAVEフォーマットではないか、フォーマット定義がありません。"));
return FALSE;
}
// fmtデータサイズエリアを読み飛ばす
fseek(fp, 4, SEEK_CUR);
// フォーマット情報を取得
WAVEFORMATEX wavf;
fread(&wavf, sizeof(WAVEFORMATEX) - 2, 1, fp);
// 音楽データの開始を意味する「data」の文字列があるか調べる
ZeroMemory(buf, 10);
while(strcmp("data", buf)){
buf[0] = fgetc(fp);
if(buf[0] == EOF){
DXTRACE_MSG(_T("波形データ定義が見つかりません。"));
fclose(fp);
return FALSE;
}
if(buf[0] == 'd') fread(&buf[1], 1, 3, fp);
}
// 音楽データサイズ取得
int wsize;
fread(&wsize, sizeof(int), 1, fp);
// CreateSoundBufferに送信するための音楽情報を作成
DSBUFFERDESC desc;
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = LOCDEFER; // ハードウェアでの再生を優先
desc.dwBufferBytes = wsize;
desc.lpwfxFormat = &wavf;
RELEASE(pDSBuffer);
pDSound->CreateSoundBuffer(&desc, &pDSBuffer, NULL);
//アクセス可能なバッファのサイズ
DWORD buff_size;
//WAVバッファアクセスポイントを格納する為のポインタ
LPVOID pvAudioPtr;
// バッファロック
pDSBuffer->Lock(0, 0, // バッファ全体をロックするため、数値の指定は不要
&pvAudioPtr, &buff_size, // 書き込むバッファを取得するためのポインタ
NULL, NULL, // 2つに分けて書き込むこともできる
DSBLOCK_ENTIREBUFFER // バッファすべてをロック
);
//サウンドデータをバッファへ書き込む
fread(pvAudioPtr, buff_size, 1, fp);
//ロック解除
pDSBuffer->Unlock(pvAudioPtr, buff_size, NULL, NULL);
fclose(fp);
return TRUE;
}
void CSound::Play()
{
if(pDSBuffer){
pDSBuffer->SetCurrentPosition(0); // 再生場所を先頭に移動
pDSBuffer->Play(
0, // 必ず0を指定
0, // 再生の優先度。0が最も低く、0xFFFFFFFFが最も高い
0 // 再生オプション。例えばDSBPLAY_LOOPINGならループ再生になる
);
}
}
ボリューム調整の値はDSBVOLUME_MAX(=0)からDSBVOLUME_MIN(=-10000)の間で行うのですが、この単位は(小さくするデシベル数×100)となっています。再生するファイルによっては-5000に達する前でもほぼ無音状態になるものもありますので注意してください。
/* 省略 */
class CSound : public CGameObject
{
private:
/* 省略 */
public:
/* 省略 */
void Stop();
void SetVolume(LONG volume);
void SetPan(LONG lPan);
};
BOOL CSound::Load(LPCTSTR filename)
{
/* 省略 */
DSBUFFERDESC desc;
ZeroMemory(&desc, sizeof(DSBUFFERDESC));
desc.dwSize = sizeof(DSBUFFERDESC);
desc.dwFlags = DSBCAPS_LOCDEFER | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME;
desc.dwBufferBytes = wsize;
desc.lpwfxFormat = &wavf;
RELEASE(pDSBuffer);
pDSound->CreateSoundBuffer(&desc, &pDSBuffer, NULL);
/* 省略 */
}
void CSound::Play()
{
if(pDSBuffer){
pDSBuffer->SetCurrentPosition(0);
pDSBuffer->Play(0, 0, 0);
}
}
void CSound::Stop()
{
if(pDSBuffer) pDSBuffer->Stop();
}
void CSound::SetVolume(LONG volume)
{
if(pDSBuffer) pDSBuffer->SetVolume(volume);
}
void CSound::SetPan(LONG lPan)
{
if(pDSBuffer) pDSBuffer->SetPan(lPan);
}
class CShip : public CITCharactor
{
private:
CSound shootsound;
CInput *input;
protected:
void Init();
void Exec();
};
void CShip::Init()
{
shootsound.Load(_T("missile.wav"));
/* 省略 */
}
void CShip::Exec()
{
/* 省略 */
if(input->IsKeyPressed(DIK_SPACE) || input->IsButtonPressed(0)){
// ミサイル発射
AppendObject(new CShoot(), 900, true);
shootsound.Play();
}
sprite.Draw(x, y);
}
[右クリックしてダウンロード]
IDirectSound8が先に解放されないよう、CSound::ReleaseDirectSound()の処理はCGameObject::Uninitialize()の後に行うようにしましょう。