#pragma comment ( lib, "dxguid.lib" ) #pragma comment ( lib, "dsound.lib" ) #pragma comment ( lib, "winmm.lib" ) #include #include #include #include #include const TCHAR g_WindowName[ 100 ] = _T("DirectSound Sample"); const int g_WindowFlameX = GetSystemMetrics( SM_CXSIZEFRAME )*2; const int g_WindowFlameY = GetSystemMetrics( SM_CYSIZEFRAME )*2 + GetSystemMetrics( SM_CYCAPTION ); const int g_WindowSizeX = 800 + g_WindowFlameX; const int g_WindowSizeY = 600 + g_WindowFlameY; const int NOTIFYEVENTS = 4; const DWORD BUF_SIZE = 5000000; const DWORD UPDATE_BUF_SIZE = ( BUF_SIZE >> ( NOTIFYEVENTS >> 1 )); const TCHAR g_FileName[ 100 ] = _T("Test.wav"); LPDIRECTSOUNDNOTIFY g_pDSNotify; HANDLE g_Event[ NOTIFYEVENTS ] = { NULL }; DSBPOSITIONNOTIFY g_dspn[ NOTIFYEVENTS ] = { 0 }; IDirectSound8 *g_pDS8; IDirectSoundBuffer8 *g_pDSBuffer; int g_ThreadLoopFlag = 1; int g_EventNum = 0; DWORD g_WaveSize = 0; DWORD g_OffSet = 0; int UpDateWave( TCHAR *t_pFileName, char** t_ppData, DWORD t_BufSize, DWORD& t_OffSet, WAVEFORMATEX* t_pWaveFormatEx = NULL, DWORD* t_pReadSize = NULL ) { HMMIO hMmio = NULL; MMIOINFO mmioInfo; WAVEFORMATEX t_WaveFormatEx; //引数が与えられなければ、関数内部のWAVEFORMATEXを使用 if( t_pWaveFormatEx == NULL ){ t_pWaveFormatEx = &t_WaveFormatEx; } // Waveファイルオープン memset( &mmioInfo, 0, sizeof( MMIOINFO ) ); hMmio = mmioOpen( t_pFileName, &mmioInfo, MMIO_READ ); if( !hMmio ){ return( -1 ); } // RIFFチャンク検索 MMRESULT mmRes; MMCKINFO riffChunk; riffChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E'); mmRes = mmioDescend( hMmio, &riffChunk, NULL, MMIO_FINDRIFF ); if( mmRes != MMSYSERR_NOERROR ) { mmioClose( hMmio, 0 ); return( -1 ); } // フォーマットチャンク検索 MMCKINFO formatChunk; formatChunk.ckid = mmioFOURCC('f', 'm', 't', ' '); mmRes = mmioDescend( hMmio, &formatChunk, &riffChunk, MMIO_FINDCHUNK ); if( mmRes != MMSYSERR_NOERROR ) { mmioClose( hMmio, 0 ); return( -1 ); } DWORD fmsize = formatChunk.cksize; DWORD size = mmioRead( hMmio, (HPSTR)t_pWaveFormatEx, fmsize ); if( size != fmsize ) { mmioClose( hMmio, 0 ); return( -1 ); } mmioAscend( hMmio, &formatChunk, 0 ); // データチャンク検索 MMCKINFO dataChunk; dataChunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); mmRes = mmioDescend( hMmio, &dataChunk, &riffChunk, MMIO_FINDCHUNK ); if( mmRes != MMSYSERR_NOERROR ) { mmioClose( hMmio, 0 ); return( -1 ); } if( t_pReadSize != NULL ){ //音声データより大きなバッファを確保しようとした場合は、 //バッファサイズを実際の音声データのサイズにする。 if( dataChunk.cksize < t_BufSize ){ t_BufSize = dataChunk.cksize; *t_pReadSize = dataChunk.cksize; }else{ *t_pReadSize = t_BufSize; } } if( *t_ppData == NULL ){ *t_ppData = new char[ t_BufSize ]; if( dataChunk.cksize > t_OffSet + t_BufSize ){ DWORD t_Dif = t_OffSet % t_BufSize; mmioSeek( hMmio, t_OffSet, SEEK_SET ); size = mmioRead( hMmio, (HPSTR)( *t_ppData ), t_BufSize ); t_OffSet += t_BufSize; }else{ DWORD t_BufSize1 = t_BufSize - ( dataChunk.cksize - t_OffSet ); DWORD t_BufSize2 = dataChunk.cksize - t_OffSet; DWORD t_Dif = t_OffSet % t_BufSize; char* t_Buf1 = new char[ t_BufSize1 ]; char* t_Buf2 = new char[ t_BufSize2 ]; size = mmioRead( hMmio, (HPSTR)t_Buf1, t_BufSize1 ); mmioSeek( hMmio, t_BufSize2, SEEK_END ); size = mmioRead( hMmio, (HPSTR)t_Buf2, t_BufSize2 ); //バッファコピー(Offset〜音声データの最後まで) for( DWORD i = 0; i < t_BufSize2; i++ ) { *(*t_ppData + i) = t_Buf2[ i ]; } //バッファコピー(音声データの先頭〜(セカンダリバッファサイズ - t_Buf2)分の位置まで) for( DWORD i = 0; i < t_BufSize1; i++ ) { *(*t_ppData + t_BufSize2 + i ) = t_Buf1[ i ]; } t_OffSet = t_BufSize1; delete[] t_Buf1; delete[] t_Buf2; } } // ハンドルクローズ mmioClose( hMmio, 0 ); return( 0 ); } LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg){ case WM_DESTROY: PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hWnd); } break; default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } unsigned __stdcall ThereadUpdateData( void* pArguments ) { // イベント通知を待つ while( g_ThreadLoopFlag ){ if( WaitForSingleObject( g_Event[ g_EventNum ], 1000 ) == WAIT_OBJECT_0 ){ LPVOID t_pWrite = NULL; DWORD t_Length = 0; if ( DS_OK == g_pDSBuffer->Lock( g_EventNum * UPDATE_BUF_SIZE, UPDATE_BUF_SIZE, &t_pWrite, &t_Length, NULL, NULL, 0 ) ) { char* t_pWaveData = NULL; UpDateWave( ( TCHAR* )g_FileName, &t_pWaveData, UPDATE_BUF_SIZE, g_OffSet ); if( t_pWaveData != NULL ){ //セカンダリバッファにコピー memcpy( t_pWrite, &t_pWaveData[ 0 ], t_Length); //セカンダリバッファをアンロック g_pDSBuffer->Unlock( t_pWrite, t_Length, NULL, 0); delete[] t_pWaveData; g_EventNum = ( g_EventNum + 1 ) % NOTIFYEVENTS; } } } } _endthreadex( 0 ); return( 0 ); } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG t_msg; HWND t_hWnd; WNDCLASSEX wcex ={ sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL, (HBRUSH)(COLOR_WINDOW+1), NULL, (TCHAR*)g_WindowName, NULL}; if( !RegisterClassEx( &wcex ) ){ return 0; } t_hWnd = CreateWindow( g_WindowName, g_WindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, g_WindowSizeX, g_WindowSizeY, NULL, NULL, hInstance, NULL ); if( t_hWnd == 0 ){ return( 0 ); } //イベント数がマイナスの場合、バッファがマイナスの場合、イベント数が奇数の場合、またはイベントが多すぎる場合、 if( NOTIFYEVENTS <= 0 || BUF_SIZE <= 0 || NOTIFYEVENTS % 2 != 0 || ( ( BUF_SIZE >> ( NOTIFYEVENTS >> 1 ) ) < 1000000 ) || BUF_SIZE < 1000000 ){ return( 0 ); } // Waveファイルオープン WAVEFORMATEX t_wFmt; char* t_pWaveData = NULL; DWORD t_WaveSize = 0; if( UpDateWave( ( TCHAR* )g_FileName, &t_pWaveData, BUF_SIZE, g_OffSet, &t_wFmt, &t_WaveSize ) == -1 ){ return( 0 ); } // サウンドデバイス作成 DirectSoundCreate8( NULL, &g_pDS8, NULL ); g_pDS8->SetCooperativeLevel( t_hWnd, DSSCL_PRIORITY ); DSBUFFERDESC DSBufferDesc; DSBufferDesc.dwSize = sizeof(DSBUFFERDESC); DSBufferDesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 // 再生位置の取得をできるようにします | DSBCAPS_CTRLPOSITIONNOTIFY // 再生位置通知イベントを有効にします | DSBCAPS_GLOBALFOCUS // グローバルフォーカスで作ります(これを入れないと非アクティブ状態になったとき再生が止まってしまう) | DSBCAPS_CTRLVOLUME // ボリュームを変更可能にする | DSBCAPS_CTRLFREQUENCY // 周波数を変更可能にする | DSBCAPS_CTRLPAN; // パンを変更可能にする DSBufferDesc.dwBufferBytes = t_WaveSize; DSBufferDesc.dwReserved = 0; DSBufferDesc.lpwfxFormat = &t_wFmt; DSBufferDesc.guid3DAlgorithm = GUID_NULL; IDirectSoundBuffer *t_pSoundBuf = 0; g_pDS8->CreateSoundBuffer( &DSBufferDesc, &t_pSoundBuf, NULL ); t_pSoundBuf->QueryInterface( IID_IDirectSoundBuffer8 ,(void**)&g_pDSBuffer ); //IDirectSoundBuffer8を作成したら、IDirectSoundBufferは不要 t_pSoundBuf->Release(); t_pSoundBuf = NULL; //正常にバッファが確保できなければ終了 if ( g_pDSBuffer == 0 ) { g_pDS8->Release(); g_pDS8 = NULL; return( 0 ); } HANDLE t_ThreadHandle = NULL; //ストリーミング再生する場合の処理 if( t_WaveSize == BUF_SIZE ){ // イベントの設定 if( FAILED( g_pDSBuffer->QueryInterface( IID_IDirectSoundNotify, (void**)&g_pDSNotify ) ) ) { return -1; } // イベントとシグナルになる位置を取得 for( int i = 0; i < NOTIFYEVENTS; i++ ) { g_Event[ i ] = CreateEvent( NULL, FALSE, FALSE, NULL ); g_dspn[ i ].dwOffset = ( BUF_SIZE >> ( NOTIFYEVENTS >> 1 ) ) + ( ( BUF_SIZE >> ( NOTIFYEVENTS >> 1 ) ) * i ) - 1; g_dspn[ i ].hEventNotify = g_Event[ i ]; } // イベントをセット if( FAILED( g_pDSNotify->SetNotificationPositions( NOTIFYEVENTS, g_dspn ) ) ) { return( -1 ); } //スレッド作成 unsigned threadID; t_ThreadHandle = (HANDLE)_beginthreadex( NULL, 0, &ThereadUpdateData, NULL, 0, &threadID ); } // セカンダリバッファにWaveデータ書き込み LPVOID t_pWrite = NULL; DWORD t_Length = 0; if ( DS_OK == g_pDSBuffer->Lock( 0, t_WaveSize, &t_pWrite, &t_Length, NULL, NULL, 0 ) ) { memcpy( t_pWrite, t_pWaveData, t_Length ); g_pDSBuffer->Unlock( t_pWrite, t_Length, NULL, 0); } delete[] t_pWaveData; t_pWaveData = NULL; ShowWindow( t_hWnd, nCmdShow ); // 再生 g_pDSBuffer->Play( 0, 0, DSBPLAY_LOOPING ); // メッセージ ループ do{ if( PeekMessage( &t_msg, NULL, 0, 0, PM_REMOVE ) ) { DispatchMessage( &t_msg ); } }while( t_msg.message != WM_QUIT ); //バッファを解放する前に、再生を停止しておく g_pDSBuffer->Stop(); //解放処理 if( g_pDSNotify != NULL ){ g_pDSNotify->Release(); g_pDSNotify = NULL; } if( g_pDSBuffer != NULL ){ g_pDSBuffer->Release(); g_pDSBuffer = NULL; } if( g_pDS8 != NULL ){ g_pDS8->Release(); g_pDS8 = NULL; } //ストリーミング再生した場合の処理 if( BUF_SIZE == t_WaveSize ){ //スレッドで行われている無限ループを解除、 //スレッドが完全に終了するまで待つ。 g_ThreadLoopFlag = 0; WaitForSingleObject( t_ThreadHandle , INFINITE ); //スレッドが終了したら、スレッドとイベントをClose CloseHandle( t_ThreadHandle ); for( int i = 0; i < NOTIFYEVENTS; i++ ) { CloseHandle( g_Event[ i ] ); } } return( 0 ); }