こんな目にあっても続けることができる…

vectorからのfread/fseek/ftell/fcloseに対応するコールバックをだるいながらも実装し、メモリ上のファイルからのogg->PCMデコード。が、鳴らない。デコード後が0の列になってるんですけど→あっちゃこっちゃ→最初の無音域をなくすように再度mp3->ogg変換→なんかちゃんとした音データ列が出てるね→参考にしてるマルペケのサンプルだと普通に再生できるんですけど→面倒だけどwavファイルとして書きこんでみるか…→普通に再生できるんですけど→すでにある効果音用の関数で再生できるんですけど→泥沼へ……


推定3時間ほどかかって見つかった原因はこれ。

void DSound::playBGM(const std::vector<BYTE> &src)
{
	// いろいろ
	shared_ptr<IDirectSoundBuffer8> pBuffer(ptmp8, iunknown_deleter());
	// バッファへの書き込みなど
	DSoundError::check(pBuffer->Play(0, 0, DSBPLAY_LOOPING));
}


わからない?C++の「}」の力を侮るな。

	pBuffer->Play();
	// ここでpBufferがデストラクト
}


もっと書き下してしまえばこう。

	pBuffer->Play();
	pBuffer->Release();
}

再生を始めた直後に即解放っ…!そこまでの関数呼び出しは全部S_OK(成功)返してるしPlay()の後でGetStatusしてもPLAYING返ってくるし。どんな検証コードを書いてもそのあとに来る「}」が停止させてしまう…!スマートポインタとデストラクタ、恐ろしや。

	DSoundError::check(pBuffer->Play(0, 0, DSBPLAY_LOOPING));
	m_pbgmbuf = pBuffer;
}

メンバ変数に代入したら全て解決しました。よく覚えてないけど推定3時間。


追記:ということで、バッファを半分に区切って再生カーソルが後半部分に入ったら前半部分に次のデータを書き込む的なストリーミングが完成しました。バッファサイズを小さくしすぎるとウィンドウをがちゃがちゃ動かしたりして毎フレのチェックが甘くなった時に同じ部分を繰り返したりしておかしくなるみたい。ただ、大きくしすぎると一度に行うデコード量が増えて一定周期で重くなるという現象に見舞われるでしょう。適当に要調整。
ウィンドウを非アクティブにすると音声がストップするので気になって(なんというかBGM鳴りっぱなしのソフトが多いような…)調べたところ、IDirectSound8::CreateSoundBufferするときの設定、DSBUFFERDESC構造体のdwFlagsメンバにDSBCAPS_GLOBALFOCUSを設定するといいみたい。ついでにIDirectSoundBuffer8::GetCurrentPositionで次のデータの書き込みタイミングを制御してるのでDSBCAPS_GETCURRENTPOSITION2も入れておく。「エミュレートされたサウンドカード」に対してのみ効果があるらしいけどヘルプを読む限り一応。SE*1の方のバッファはいいや。


生のPCM*2のバイト列を素手で触りながら書いたプログラムで再生する不思議の国のアリスは格別だぜ……。ウィンドウをバックグラウンドにしても聞こえるようにしたし。BGMなんか非圧縮じゃあ大きすぎるしもっと高レベルなAPIにシフトしていくくさい。XNAかなんか的な意味で(多分)。過激派が暴れだしてもしょうがないからどっちとは言わないけどDirectSoundはDirectXの中でも最もDirectな気がするなあ。

*1:効果音

*2:非圧縮のwavだと思えばおk