トップページ > 過去ログ > 記事閲覧
LoadSoundNumをマルチスレッド化
名前:たかし 日時: 2008/04/21 23:00

LoadSoundNumとPlaySoundNumを使って5分ぐらいのmp3をBGMとして鳴らすときに 2秒ぐらいどうしてもプログラムが反応しなくなる瞬間があったので _beginthreadを使ってその部分をマルチスレッドにしたのですが それでもスレッドを作る瞬間に2秒ぐらい反応しなくなってしまいます。 なぜ反応しなくなるのかわかりません。 LoadSoundNumってマルチスレッドに出来ないのでしょうか? 出来ないのならマルチスレッドを使わずこれを回避する方法ってありますか?

Page: 1 |

Re: LoadSoundNumをマルチスレッド化 ( No.1 )
名前: 日時:2008/04/22 10:30

>それでもスレッドを作る瞬間に2秒ぐらい >応しなくなってしまいます >なぜ反応しなくなるのかわかりません。 さすがにこれだけでは、ProcessMessage関数とか きちんと呼び出していますか?くらいしか、 言えません。。。 最低限、スレッド生成の部分のコードと、 スレッド関数くらいわからないと。。。 あと、普通は長いBGMを使う場合など、 ストリーミング再生を使用します。 読み込みながら再生しなければ、 読み込みだけを別スレッドにしても、 それが完了するまで、ハンドルが取れないので、 どうせメインの処理で音が鳴らせません。 なので、結局読み込みが完了するまで、 待つ事になります。
Re: LoadSoundNumをマルチスレッド化 ( No.2 )
名前:たかし 日時:2008/04/22 22:47

たしかに、そうですね。 本当のコードはクラスになってるので簡単に書いたものですが以下のようになります。 _beginthreadをしてから裏で音楽を読みにいき 表の中略の部分でいろいろ他の処理をしようと思っていたのですが 実際表の処理も2秒程度固まってしまい困っています。 ストリーミング再生で一応解決できたのですが 出来れば原因を知りたいです。 よろしくお願いします。 struct SoundData { char file_name[256]; int sound_handle; }; void LoadMusic( LPVOID data ) { SoundData* sound_data = (SoundData*)data; sound_data->sound_handle = LoadSoundMem( sound_data->file_name ); } struct SoundData { char file_name[256]; int sound_handle; }; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { //省略 SoundData data; strcpy( sound_data.file_name, "sample.mp3" ); HANDLE hThread = (HANDLE)_beginthread( func, 0, data ); //省略 WaitForSingleObject( hThread, INFINITE ); PlaySoundMem(sound_data.sound_handle, DX_PLAYTYPE_BACK, 1 ); //省略 }
Re: LoadSoundNumをマルチスレッド化 ( No.3 )
名前: 日時:2008/04/23 10:46

>>(HANDLE)_beginthread( func, 0, data ); funcの中がどうなっているかわかりませんが、 >>WaitForSingleObject( hThread, INFINITE ); この一文でスレッドのハンドルを使用して無限待ち を行っています。 このスレッドが終了するか、シグナル状態から解放 されるまでMainでは待ち続けることになります。 ここが正に前のレスで書いた 「読み込みが完了するまで待つ」 のところですね 画像などをメイン側で読み込んだ場合、 多少はリソースの読み込み時間を短縮する事は できそうですが、結局一番読み込みに時間が かかる物よりは短くならないことになります。
Re: LoadSoundNumをマルチスレッド化 ( No.4 )
名前:たかし 日時:2008/04/23 18:45

すみません、わかりやすくするつもりが逆に 勘違いさせてしまって。 正しくはこうです。_beginthreadのfuncは完全に 書き間違いです。 HANDLE hThread = (HANDLE)_beginthread( LoadMusic, 0, data ); int thread_status; while( thread_status == WAIT_TIMEOUT ) { //now loadingの画面を出す処理 now_loading(); thread_status = WaitForSingleObject( hThread, 0 ); }
Re: LoadSoundNumをマルチスレッド化 ( No.5 )
名前:たかし 日時:2008/04/23 18:46

動くはずのnow_loadingの画面が 固まってしまって困ってます。
Re: LoadSoundNumをマルチスレッド化 ( No.6 )
名前: 日時:2008/04/23 19:16

>>int thread_status; >>while( thread_status == WAIT_TIMEOUT ) { ループ回らない可能性もあります。 #たぶん移しミスだと思いますが。。。 >>while( thread_status == WAIT_TIMEOUT ) { >>  //now loadingの画面を出す処理 >>  now_loading(); >>  thread_status = WaitForSingleObject( hThread, 0 ); >>} 結局やっていることは、スレッドのハンドルが シグナルになるまで待ってるので、 now_loading関数を呼び出すだけで、 無限待ちしているのと変わりません。 #厳密には多少違いますが。 now_loading関数の中身にもよりますが、 ProcessMessage関数を呼び出していないので、 Windowメッセージを処理できていないようです。 OSからは応答なしと判断されている可能性が あります。 while( !ProcessMessage() && thread_status == WAIT_TIMEOUT ) にしてみるとか:-p)
Re: LoadSoundNumをマルチスレッド化 ( No.7 )
名前:IW 日時:2008/04/24 23:17

 DXライブラリの関数は全て複数のスレッドが呼ばれるとき、排他処理されていたはずです。  つまり、スレッド化しても実行されるライブラリの関数は 常に1つだけになるので、処理が停まってしまうのではないでしょうか。
Re: LoadSoundNumをマルチスレッド化 ( No.8 )
名前: 日時:2008/04/25 01:40

>スレッド化しても実行されるライブラリの >関数は常に1つだけになるので 確かに特定の関数がどれか1つしかという可能性は 否定できませんが、全ての関数の実行が常に 1つになる訳ではないのでこれで止まるというのは 少し考えにくいと思います。 #まぁ質問者さんが他の部分でどんなコードを書いて #いるか分からないので何とも言えないという所は #ありますが。。。 排他するなら、きちんと「どこ」と「どこ」を 排他しないと行けないのかというのが分かって はじめて使う物であって何でもかんでも 排他すれば良いと言う物では無いですし、 排他するためのオブジェクトが 全部同じである必要は全くありません。 #例えば、画像の読み込み機能と、 #音声の読み込み機能を排他する必要なって #全くありませんし無意味です
Re: LoadSoundNumをマルチスレッド化 ( No.9 )
名前:IW 日時:2008/04/25 23:34

 あー、失礼。全てでは無かったですね。  CreateIdentityMatrix()なんかは排他処理されてませんでした。 >全ての関数の実行が常に1つになる訳ではないので >これで止まるというのは少し考えにくいと思います。  そーですか?  now_loadingの画面が停まっている、ということなので 仮説ですが、LoadSoundMem()が実行された段階で now_loading()内の LoadGraph()とかの読み込みが  LoadSoundMem()の処理が終わるまで次に進まない、ということは有りそうです。 >まぁ質問者さんが他の部分でどんなコードを書いて  結局はここなんですよね。
Re: LoadSoundNumをマルチスレッド化 ( No.10 )
名前: 日時:2008/04/26 10:44

>LoadGraph()とかの読み込みが  >LoadSoundMem()の処理が終わるまで次に進まない 試してみましたが確かに排他されているような動き ですね。。。 仮にそうだとすると(コードは確認していないので) 画像読み込みと、音声読み込みに排他が必要な 理由が気になるところ。 あと思ったんですが、LoadSoundMemってmp3を PCMに変換するっぽいので、mp3が長いと それだけ時間をとるような気がします。 SetCreateSoundDataType(DX_SOUNDDATATYPE_MEMPRESS ) などにするとどうなりますか? #再生が遅くなってしまいますが。。。 >以下試したコード #include "DxLib.h" #include <process.h> #define MAXFILENAMELEN (256) #define TESTTHREADCOUNT (3) #define TESTGRAPHICCNT (100) typedef struct SoundObj{ int num; char name[MAXFILENAMELEN]; int handle; }SOUNDOBJ; typedef struct ThreadInfo{ HANDLE handle[TESTTHREADCOUNT]; unsigned int tid[TESTTHREADCOUNT]; }THREADINFO; char* gSoundFile[TESTTHREADCOUNT] = { "Sample1.mp3", "Sample2.mp3", "Sample3.mp3", }; unsigned int WINAPI LoadSoundObjThread( void* p ) { SOUNDOBJ *SoundPtr = (SOUNDOBJ*)p; DrawFormatString( 0 , SoundPtr->num * 20 , GetColor( 255, 255, 255 ), "音声[%d]番目読み込み開始...", SoundPtr->num ) ; SoundPtr->handle = LoadSoundMem( SoundPtr->name ); DrawFormatString( 250 , SoundPtr->num * 20 , GetColor( 255, 255, 255 ), "完了" ) ; _endthreadex( SoundPtr->handle ); return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int i, j, k, test[1000]; SOUNDOBJ sound_data[TESTTHREADCOUNT]; THREADINFO thread; ChangeWindowMode( TRUE ) ; SetGraphMode( 800 , 600 , 16 ) ; if( DxLib_Init() == -1 ) return -1; for( i=0; i<TESTTHREADCOUNT; i++ ){ sound_data[i].num = i; strcpy( sound_data[i].name, gSoundFile[i] ); thread.handle[i] = (HANDLE)_beginthreadex((void*)NULL, 0, LoadSoundObjThread, &sound_data[i], 0, &thread.tid[i] ); } for( i=0, j=0, k=0; i<TESTGRAPHICCNT; i++, j++ ){ if( ProcessMessage() < 0 ){ DxLib_End(); return 0; } if ((j * 20) >= 600 ){ k++; j=0; } DrawFormatString( 300+k*100 , j * 20 , GetColor( 255, 255, 255 ), "[%04d]個目", i ) ; test[i] = LoadGraph( "sample.jpg" ); } DrawFormatString( 300+k*100 , j * 20 , GetColor( 255, 255, 255 ), "完了" ) ; // 何かキーが押されるまで待つ while( ProcessMessage() == 0 && CheckHitKeyAll() == 0 ){ } WaitForMultipleObjects( TESTTHREADCOUNT, thread.handle, TRUE, -1 ); // DXライブラリ使用の終了処理 DxLib_End() ; return 0; } #注目するところは、 #最初の音声読み込みの完了前です。
Re: LoadSoundNumをマルチスレッド化 ( No.11 )
名前:たかし 日時:2008/04/26 20:14

通さんのコードを見てもやはり 読み込みに行っても固まる感じですね。 SetCreateSoundDataType(DX_SOUNDDATATYPE_MEMPRESS ) これでもうまくいくんですが BGMより短い音などはスレッドで投げて取得したり スレッドで画像を読みに行ったりしたいので そのたびにゲームが固まるのは困るんです。 IWさんの言うとおり排他利用が関係してるんでしょうか? ちなみに、私の書いたコードはこんな感じです。 Player.bmpはサンプルプログラムから Sample1.mp3は何か適当なものを持ってきてください。 #include "DxLib.h" #include "string.h" #include <stdio.h> #include <windows.h> #include <process.h> struct SoundData { char file_name[256]; int sound_handle; }; void LoadMusic( LPVOID data ) { SoundData* sound_data = (SoundData*)data; sound_data->sound_handle = LoadSoundMem( sound_data->file_name ); } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int Key ; double radian = 0.0; int thread_status; int PlayerGraph ; ChangeWindowMode( TRUE ); //SetGraphMode( 640 , 480 , 16 ) ; if( DxLib_Init() == -1 ) // DXライブラリ初期化処理 { return -1; // エラーが起きたら直ちに終了 } // 描画先画面を裏画面にセット SetDrawScreen( DX_SCREEN_BACK ) ; // グラフィックのロード PlayerGraph = LoadGraph( "Player.bmp" ) ; //読み込むファイルのデータ SoundData sound_data; strcpy( sound_data.file_name, "Sample1.mp3" ); //ストリーミング再生をする SetCreateSoundDataType( DX_SOUNDDATATYPE_MEMPRESS ); HANDLE hThread; hThread = (HANDLE)_beginthread( LoadMusic, 0, (LPVOID)&sound_data ); // ループ while( ProcessMessage() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) { // 画面を初期化する ClearDrawScreen() ; // キー入力取得 Key = GetJoypadInputState( DX_INPUT_KEY_PAD1 ) ; if( Key & PAD_INPUT_UP ) { PlaySoundMem(sound_data.sound_handle, DX_PLAYTYPE_BACK, 1 ); } if( Key & PAD_INPUT_DOWN ) { break; } // プレイヤーを描画する DrawRotaGraph( 550 , 400 , 1.0, radian, PlayerGraph , TRUE ) ; //スレッドの状態を知る thread_status = WaitForSingleObject( hThread, 0 ); // プレイヤーを描画する DrawFormatString( 10 , 50, GetColor( 255 , 255, 255 ), "%d", thread_status ) ; // 裏画面の内容を表画面に反映させる ScreenFlip(); radian += 0.1; } CloseHandle( hThread ); DxLib_End(); // DXライブラリ使用の終了処理 return 0 ; // ソフトの終了 }
Re: LoadSoundNumをマルチスレッド化 ( No.12 )
名前:IW 日時:2008/04/27 10:19

@通さん >画像読み込みと、音声読み込みに排他が必要な >理由が気になるところ。  もう一つの掲示板「雑談&質問掲示板」の [1896]に書いてあるのを見つけました。  やっぱり、一律でマルチスレッドで同時に呼ぶとブロックされるようで、 理由は確実に誤動作を避ける為のようです。 @たかしさん >そのたびにゲームが固まるのは困るんです。  たしかに困りますね。  現状一番手っ取り早いのは、メインスレッド以外のスレッドでは極力 DXライブラリを 呼ばないようにすることです。  例えば [1896]にも書いてありましたが、LoadSoundMem()でお手軽に済ませて しまうのではなく、自前でファイルをメモリにロードする(fopenとか ifstreamとか) 部分をスレッドで書いて、ファイルが読み終わったらスレッドを終了させ、 メインスレッドで LoadSoundMemByMemImage()等のメモリからハンドルを 作成する関数を使いハンドルを取得すれば PlaySoundMem()で再生が出来るはずです。 ※ LoadSoundMemByMemImageが重くなければ、スレッド側で呼んでしまってもいいと思います。  が、逆に重かったら、この方法でも一瞬停まってしまうかもしれませんが・・・。
Re: LoadSoundNumをマルチスレッド化 ( No.13 )
名前: 日時:2008/04/28 12:04

> 理由は確実に誤動作を避ける為のようです。 DirectXのClassObjectから考えても、 GraphicとSoundで特に新しいオブジェクトを作る 場合なので排他は必要ないと思ったのですが。。。 多くの人が利用していて保守作業に追われるのを 考えると確かにそういう作り方のほうが妥当なのかも。 自作関数を作るしかないということですね。 #余裕があればちょっと作ってみたいですね

Page: 1 |