なんでこんなことしたいか
BGMにおいて、最初にイントロを流した後、同じフレーズを無限ループするようなものがあると思うんですよね。作曲の人が気合を入れて、一つのwavファイルに対してイントロを加えてカッコイイ感じにしたもの。
例えばこんな感じのものです。これはループの終了位置が最後です。ものによっては終了位置も途中なものもあると思いますが、手持ちになかったので…。
上記のものだと一気に上がった部分からループです。
で、これをそのままループさせれば毎回イントロが流れてしまい、気持ちのいいものではなくなります。
なので、以下のようにしたいと思います。
イントロ→曲終了→イントロ終了位置からループ開始→曲終了→イントロ終了位置からループ開始→…
では、これをするためにwavファイルのイントロの終了位置を把握しないといけません。
ぶっちゃけ作曲者に聞けば早いんですが、そうも言ってられない場合だったりした場合、自力で調べないといけません。
サンプル数値を知るためのソフトウェアを使うとか、自作するとか、作曲者が作ったmidiファイルとソフトウェアで確認するとかいくつか手法はありますが、
今回はDxLibという題で始めたようにDxLibの独自のツールを使います。
あなたがDxLibを置いた場所をCドライブとするなら以下の場所にそのためのツールがあります。
C:\DxLib_VC\Tool\SoundLoopAreaEditor
ここにある「SoundLoopAreaEditor.exe」を使い調べます。先ほどのものだとこんな感じになります。
少しめんどくさいですが、十字キーで微調整しながらループの開始位置を調べます。今回は左上に書いてある通り「558060」だと分かりました。正直アバウトですが。
ということで、それに対応した形でサウンドの途中ループをさせます。
コード
/// --------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (Init(640, 480, 32, "Game") == false) return -1;
int sound = LoadSoundMem("めいん.wav");
SetLoopSamplePosSoundMem(558060, sound);
PlaySoundMem(sound, DX_PLAYTYPE_LOOP);
// メインループ
while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen())
{
}
StopSoundMem(sound);
DeleteSoundMem(sound);
// 削除
DxLib_End(); // DXライブラリの後始末
return 0;
}
これで、サウンドのループ位置を途中から開始させることはできました。
ループの終了も途中からしたい
今回の場合はループの終了位置がサウンドの終了と一緒だったのでそこについての調整はしていません。
ですが、そうするとなると少し無理矢理感ありますが、次のコードのようになると思います。
なぜ無理矢理なのかかというと、このやり方をライブラリ化するってなると一つ一つのサウンドごとに調整がいりますし、何より現在のサンプル数値を常に取得しているのですごい負荷がかかってしまいます。
では、どうすればいいのかといわれれば、現在の私では最適解を見いだせていないので特に言及しません。すみません。精進します。
あと、先ほど書いたように丁度いい素材が手元になかったため、コード上で「60000」としたあと「0」から開始してますがこれについては特に理由はないです。テスト用です。めんどくさいんでね。
コード
/// --------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (Init(640, 480, 32, "Game") == false) return -1;
int sound = LoadSoundMem("めいん.wav");
SetLoopSamplePosSoundMem(558060, sound);
PlaySoundMem(sound, DX_PLAYTYPE_LOOP);
int freqency = GetCurrentPositionSoundMem(sound);
// メインループ
while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen())
{
freqency = GetCurrentPositionSoundMem(sound);
if (freqency > 600000)
{
StopSoundMem(sound);
SetCurrentPositionSoundMem(0, sound);
PlaySoundMem(sound, DX_PLAYTYPE_LOOP, false);
}
}
StopSoundMem(sound);
DeleteSoundMem(sound);
// 削除
DxLib_End(); // DXライブラリの後始末
return 0;
}
補足:BGMの扱いについて
先ほどからさも同然のようにBGMのwavファイルをロードしていますが、正直このやり方はよくないです。
というのもwavファイルは容量がバカでかいですし、SEに比べてBGMはクソほど長いですし容量もでかいです。
なので、BGMに対して、ロードのやり方は、
「SetCreateSoundDataType」というのを使います。これもDxLibの標準関数です。非公開関数ではないです。
扱い方も簡単でロードの直前にその関数を呼び出すだけです。以下に軽くコードを示します。先ほどのコードに付け加えた感じです。
ですが、補足の補足的に言うと、正直これでちゃんと出来ているのか分かってないです。内部みたーい。
コード
/// --------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
if (Init(640, 480, 32, "Game") == false) return -1;
SetCreateSoundDataType(DX_SOUNDDATATYPE_FILE);
int sound = LoadSoundMem("めいん.wav");
SetCreateSoundDataType(DX_SOUNDDATATYPE_MEMNOPRESS); // 今回は要らないけど本来なら戻すためにここで呼び出す必要がある。
SetLoopSamplePosSoundMem(558060, sound);
PlaySoundMem(sound, DX_PLAYTYPE_LOOP);
int freqency = GetCurrentPositionSoundMem(sound);
// メインループ
while (!ScreenFlip() && !ProcessMessage() && !ClearDrawScreen())
{
freqency = GetCurrentPositionSoundMem(sound);
if (freqency > 600000)
{
StopSoundMem(sound);
SetCurrentPositionSoundMem(0, sound);
PlaySoundMem(sound, DX_PLAYTYPE_LOOP, false);
}
}
StopSoundMem(sound);
DeleteSoundMem(sound);
// 削除
DxLib_End(); // DXライブラリの後始末
return 0;
}