BGMのストリーミング再生について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
波尭

BGMのストリーミング再生について

#1

投稿記事 by 波尭 » 13年前

皆様こんにちは。
当方、現在DirectXを勉強中の者です。

さて表題の件についてなのですが、
アプリケーションがウィンドウモードの時に、位置を変えようとタイトルバーを持ってドラッグさせた際
BGMが思ったように再生されない問題に突き当たっています。
具体的には、バッファの更新が為されない、つまりドラッグ中は延々と更新されないバッファ内をループし続けているというものです。
四聖龍神録や東方Project作品などではこの症状は見られず、曲ループもドラッグ中の再生も問題はありません。
ドラッグ時間が短ければ問題はないのですが、長くなってくると曲の再生に影響が出る可能性があるので不安要素は消しておきたかったのですが、
どうにも解決されないため質問いたしました。

DirectSound/Notify使用  ->実質自己検証なし。ネットからDLしてきたサンプルではドラッグを離すと位置修正がかかるが、ドラッグ中はバッファ内ループ。
DirectSound/Notify不使用 ->現在暫定的に使っている再生方法。ドラッグ中は更新されないバッファ内でループ再生を続けている。
XAudio              ->ポーリング方式で検証。再生が一時的に止まる。ドラッグを離すとその地点から再開する。
PlatformSDK(DirectShow) ->ドラッグ中も問題なく再生は続く。ただし曲ループの際に音が若干途切れる。

以上が当方で現在確認できている状態です。
即ち、DirectShowではループの際に音が途切れる以外は問題がなく、
逆にそれ以外ではドラッグ中に問題が起きる以外は正常であるということです。こちらでは曲ループはスムーズにいきます。

こんな場合、解決策はあるのでしょうか。
どなたか判る方いましたら、ご教授のほどお願いします。
ちなみにBGMはWAVファイルを使っています。

一応現在使っているバッファの書き換えプログラムも併せて記載しておきます。

コード:

bool Music::GetCurrentPos(){
	HRESULT hr;

	const static long sign = (16 / 8) * 2 * 44100 * 2 / 4;		//確保するバッファは2秒分、それを4分割
	static long bufaddr = sign * 4;				//予めバッファに読み込んである分を加算
	DWORD	pos,sw;
	static bool flag[4] = {false,false,false,true};			//書き換えられるかのフラグ

	LPVOID	lpWrite[2]	= {NULL};
	DWORD	dwLength[2]	= {NULL};
	pSndBuf->GetCurrentPosition(&pos,NULL);			//現在のカーソル位置を取得
	sw = pos / sign;						//カーソルがあるブロックを計算

	switch(sw){
		case 0:
			if(flag[3] == false){			//書き換えられるなら
				hr = pSndBuf->Lock(sign * 3,sign,&lpWrite[0],&dwLength[0],&lpWrite[1],&dwLength[1],0);
				if(DSERR_Check(hr))break;		//エラーが出たらすぐに処理から抜ける
				fread_s(lpWrite[0],dwLength[0],dwLength[0],1,fh);
				if(lpWrite[1] != NULL)fread_s(lpWrite[1],dwLength[1],dwLength[1],1,fh);
				pSndBuf->Unlock(lpWrite[0],dwLength[0],lpWrite[1],dwLength[1]);
				flag[2] = false; flag[3] = true;	//書き換えたブロックを上書き不能にしてその1コ前のブロックに許可を出す
				bufaddr += sign;			//ブロック分を加算
			}
			break;
		default:
			if(flag[sw - 1] == false){
				hr = pSndBuf->Lock(sign * (sw - 1),sign,&lpWrite[0],&dwLength[0],&lpWrite[1],&dwLength[1],0);
				if(DSERR_Check(hr))break;
				fread_s(lpWrite[0],dwLength[0],dwLength[0],1,fh);
				if(lpWrite[1] != NULL)fread_s(lpWrite[1],dwLength[1],dwLength[1],1,fh);
				pSndBuf->Unlock(lpWrite[0],dwLength[0],lpWrite[1],dwLength[1]);
				flag[(((sw - 2) == -1) ? 3 : sw - 2)] = false; flag[sw - 1] = true;
				bufaddr += sign;
			}
			break;
	}

	if(bufaddr + sign > (long)EndPos){				//現在位置+ブロック分が曲の終端を超えていたら
		fseek(fh,-((long)(EndPos - LoopPos)),SEEK_CUR);	//ファイル位置をループ地点まで戻す
		bufaddr -= (EndPos - LoopPos);
	}
	return true;
}
※1 EndPos,LoopPosには予め別ファイルからサンプル単位で終端とループ地点を読み込んでバイト単位位置に変換してあります
※2 DSERR_Check関数はどのエラーに該当するか調べてメッセージボックスで表示する自作関数です。

アバター
うしお
記事: 56
登録日時: 14年前

Re: BGMのストリーミング再生について

#2

投稿記事 by うしお » 13年前

バッファの更新を行っているのがウィンドウのスレッド(メインループ)と同一スレッドのため、
ドラッグ中にはウィンドウのメッセージ処理がストップさせられるために、
時間が経過してもいつまでもバッファが更新されないのでしょう。

改善するにはマルチスレッドを使用して、
バッファの更新をメインスレッドから分離する必要があります。

また、サウンド再生に時間をかけたくないのであれば、
外部ライブラリの導入がおすすめできます。

たとえば、
商用利用にはお金がかかりますが、
クロスプラットフォームかつたくさんの圧縮形式のサポート、およびストリーミング、
3Dサウンドに対応しているライブラリです。
irrklang
http://www.ambiera.com/irrklang/

波尭

Re: BGMのストリーミング再生について

#3

投稿記事 by 波尭 » 13年前

なるほど、マルチスレッドですか・・・。
確かに、私の作っていたプログラムではスレッドと呼べるエリアは1つしかないようです。
元々サンプルコードを弄くりまわして作っていたので当然かもしれませんが…。

なかなか調整が難しそうですが、何とかやってみます。
ご回答ありがとうございます。

閉鎖

“C言語何でも質問掲示板” へ戻る