// TODO: Move the init into the thread, like WASAPI? int DSoundAudioBackend::RunThread() { setCurrentThreadName("DSound"); currentPos_ = 0; lastPos_ = 0; dsBuffer_->Play(0,0,DSBPLAY_LOOPING); while (!threadData_) { EnterCriticalSection(&soundCriticalSection); dsBuffer_->GetCurrentPosition((DWORD *)¤tPos_, 0); int numBytesToRender = RoundDown128(ModBufferSize(currentPos_ - lastPos_)); if (numBytesToRender >= 256) { int numBytesRendered = 4 * (*callback_)(realtimeBuffer_, numBytesToRender >> 2, 16, 44100, 2); //We need to copy the full buffer, regardless of what the mixer claims to have filled //If we don't do this then the sound will loop if the sound stops and the mixer writes only zeroes numBytesRendered = numBytesToRender; WriteDataToBuffer(lastPos_, (char *) realtimeBuffer_, numBytesRendered); currentPos_ = ModBufferSize(lastPos_ + numBytesRendered); totalRenderedBytes_ += numBytesRendered; lastPos_ = currentPos_; } LeaveCriticalSection(&soundCriticalSection); WaitForSingleObject(soundSyncEvent_, MAXWAIT); }
void LC3Sound::TimeProc( UINT IDEvent, /* identifies timer event */ UINT uReserved, /* not used */ DWORD_PTR dwUser, /* application-defined instance data */ DWORD_PTR dwReserved1, /* not used */ DWORD_PTR dwReserved2 /* not used */ ) { DWORD pos, posp; static volatile int timersema=0; /* use semaphore to prevent entering the mixing routines twice.. do we need this ? */ if(++timersema==1) { lpSwSamp->GetCurrentPosition( &pos, &posp); { if( !DirectSave( soundData)) { memset(soundData, 0x80, BUFSIZE); } WriteDataToBuffer( lpSwSamp, 0, (unsigned char*) soundData, BUFSIZE); } } timersema--; }
int DSoundAudioBackend::RunThread() { if (FAILED(DirectSoundCreate8(0, &ds_, 0))) { ds_ = NULL; threadData_ = 2; return 1; } ds_->SetCooperativeLevel(window_, DSSCL_PRIORITY); if (!CreateBuffer()) { ds_->Release(); ds_ = NULL; threadData_ = 2; return 1; } soundSyncEvent_ = CreateEvent(0, false, false, 0); InitializeCriticalSection(&soundCriticalSection); DWORD num1; short *p1; dsBuffer_->Lock(0, bufferSize_, (void **)&p1, &num1, 0, 0, 0); memset(p1, 0, num1); dsBuffer_->Unlock(p1, num1, 0, 0); totalRenderedBytes_ = -bufferSize_; setCurrentThreadName("DSound"); currentPos_ = 0; lastPos_ = 0; dsBuffer_->Play(0,0,DSBPLAY_LOOPING); while (!threadData_) { EnterCriticalSection(&soundCriticalSection); dsBuffer_->GetCurrentPosition((DWORD *)¤tPos_, 0); int numBytesToRender = RoundDown128(ModBufferSize(currentPos_ - lastPos_)); if (numBytesToRender >= 256) { int numBytesRendered = 4 * (*callback_)(realtimeBuffer_, numBytesToRender >> 2, 16, 44100, 2); //We need to copy the full buffer, regardless of what the mixer claims to have filled //If we don't do this then the sound will loop if the sound stops and the mixer writes only zeroes numBytesRendered = numBytesToRender; WriteDataToBuffer(lastPos_, (char *) realtimeBuffer_, numBytesRendered); currentPos_ = ModBufferSize(lastPos_ + numBytesRendered); totalRenderedBytes_ += numBytesRendered; lastPos_ = currentPos_; } LeaveCriticalSection(&soundCriticalSection); WaitForSingleObject(soundSyncEvent_, MAXWAIT); }
// The audio thread. void DSound::SoundLoop() { currentPos = 0; lastPos = 0; dsBuffer->Play(0, 0, DSBPLAY_LOOPING); while (!threadData) { // No blocking inside the csection dsBuffer->GetCurrentPosition((DWORD*)¤tPos, 0); int numBytesToRender = FIX128(ModBufferSize(currentPos - lastPos)); if (numBytesToRender >= 256) { if (numBytesToRender > sizeof(realtimeBuffer)) PanicAlert("soundThread: too big render call"); m_mixer->Mix(realtimeBuffer, numBytesToRender / 4); WriteDataToBuffer(lastPos, (char*)realtimeBuffer, numBytesToRender); lastPos = ModBufferSize(lastPos + numBytesToRender); } soundSyncEvent.Wait(); } }
//TODO: we should probably do something to prevent thread contention static void CALLBACK TimeProc( UINT IDEvent, /* identifies timer event */ UINT uReserved, /* not used */ DWORD_PTR dwUser, /* application-defined instance data */ DWORD_PTR dwReserved1, /* not used */ DWORD_PTR dwReserved2 /* not used */ ) { DWORD pos, posp; static volatile int timersema=0; /* use semaphore to prevent entering the mixing routines twice.. do we need this ? */ if(++timersema==1) { MADDriverRec *WinMADDriver = (MADDriverRec*) dwUser; if(WinMADDriver->Reading == false) { switch(WinMADDriver->DriverSettings.outPutBits) { case 8: memset(WinMADDriver->currentBuf, 0x80, WinMADDriver->WIN95BUFFERSIZE); break; case 16: memset(WinMADDriver->currentBuf, 0, WinMADDriver->WIN95BUFFERSIZE); break; } } WinMADDriver->lpSwSamp->lpVtbl->GetCurrentPosition(WinMADDriver->lpSwSamp, &pos, &posp); if(pos > WinMADDriver->WIN95BUFFERSIZE/2 && WinMADDriver->OnOff == true) { WinMADDriver->OnOff = false; if (!DirectSave(WinMADDriver->currentBuf, NULL, WinMADDriver)) { switch(WinMADDriver->DriverSettings.outPutBits) { case 8: memset(WinMADDriver->currentBuf, 0x80, WinMADDriver->WIN95BUFFERSIZE/2); break; case 16: memset(WinMADDriver->currentBuf, 0, WinMADDriver->WIN95BUFFERSIZE/2); break; } } if (!WriteDataToBuffer(WinMADDriver->lpSwSamp, 0, (unsigned char*) WinMADDriver->currentBuf, WinMADDriver->WIN95BUFFERSIZE/2)) { //DEBUG debugger("ERR"); } } else if (WinMADDriver->OnOff == false && (pos < WinMADDriver->WIN95BUFFERSIZE/2)) { WinMADDriver->OnOff = true; if (!DirectSave(WinMADDriver->currentBuf + WinMADDriver->WIN95BUFFERSIZE/2, NULL, WinMADDriver)) { switch(WinMADDriver->DriverSettings.outPutBits) { case 8: memset(WinMADDriver->currentBuf + WinMADDriver->WIN95BUFFERSIZE/2, 0x80, WinMADDriver->WIN95BUFFERSIZE/2); break; case 16: memset(WinMADDriver->currentBuf + WinMADDriver->WIN95BUFFERSIZE/2, 0, WinMADDriver->WIN95BUFFERSIZE/2); break; } } if (!WriteDataToBuffer(WinMADDriver->lpSwSamp, WinMADDriver->WIN95BUFFERSIZE/2, (unsigned char*) (WinMADDriver->currentBuf + WinMADDriver->WIN95BUFFERSIZE/2), WinMADDriver->WIN95BUFFERSIZE/2)) { //DEBUG debugger("ERR"); } } if (WinMADDriver->WIN95BUFFERSIZE - pos > 1700) WinMADDriver->OscilloWavePtr = WinMADDriver->currentBuf + pos; else WinMADDriver->OscilloWavePtr = WinMADDriver->currentBuf; } timersema--; }
int CSound::LoadInternal(const char* szFileName, DSBUFFERDESC* pDsbdesc, void* pData, unsigned long dwDataSize){ CSoundLoader* pLoader; char errmsg[512]; if(CS_E_OK!=this->GetLoaderInterface(&pLoader, szFileName, pData, dwDataSize)){ wsprintf(errmsg, "Sound::%sの読み取りインターフェイス取得に失敗.\nファイルが存在するかもしくは対応形式か確認して下さい.", szFileName); ::MessageBox(NULL, errmsg, "", MB_ICONEXCLAMATION|MB_OK|MB_TOPMOST); //FatalAppExit(0, errmsg); return CS_E_NOTFOUND; } if(CSL_E_OK != pLoader->QueryLoadFile(szFileName, pData, dwDataSize)){ wsprintf(errmsg, "Sound::%sの読み取りに失敗.", szFileName); ::MessageBox(NULL, errmsg, "", MB_ICONEXCLAMATION|MB_OK|MB_TOPMOST); //FatalAppExit(0, errmsg); return CS_E_UNEXP; } //初期化 this->AddRef(); this->UnInitialize(); m_Loader = pLoader; if(!m_pPrimaryBuffer){ //プライマリバッファを取得する if(CS_E_OK!=GetPrimaryBuffer(&m_pPrimaryBuffer, DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_PRIMARYBUFFER)) return CS_E_NULL_PRIMARY; } DSBUFFERDESC dsbdesc; zeroMem(&dsbdesc, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); //全体の長さとWFXの取得 DWORD dwDataLength = m_Loader->GetDecodedLength(); m_Loader->GetWaveFormatEx(&m_wfx); if(dwDataLength >= CS_LIMITLOADONMEMORY){//展開したときのサイズが1MB以上だったらストリーミング再生] //スレッド処理 this->CloseStreamThread(); m_hThreadMessageDispatchEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_hThread = CreateThread(NULL, 0, this->StreamThread, (void*)this, CREATE_SUSPENDED, &m_dwThreadId); //スレッド生成 // スレッド優先を変更 SetThreadPriority( m_hThread, THREAD_PRIORITY_NORMAL ); // スレッド開始 ResumeThread( m_hThread ); WaitForSingleObject(m_hThreadMessageDispatchEvent, INFINITE);// スレッドメッセージキューが作成されるのを待つ m_isStreamFile = TRUE; //セカンダリバッファ { SAFE_RELEASE(m_pSecondaryBuffer); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY| DSBCAPS_GLOBALFOCUS|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME| DSBCAPS_CTRLFREQUENCY|DSBCAPS_LOCSOFTWARE; if(pDsbdesc){ dsbdesc.dwFlags = pDsbdesc->dwFlags; dsbdesc.guid3DAlgorithm = dsbdesc.guid3DAlgorithm; } dsbdesc.lpwfxFormat = &m_wfx; DWORD dwSize = m_wfx.nAvgBytesPerSec * m_dwBufferLengthSec / m_dwNotificationNum; dwSize -= dwSize % m_wfx.nBlockAlign; dsbdesc.dwBufferBytes = dwSize * m_dwNotificationNum; if(!CreateBuffer(&m_pSecondaryBuffer, &dsbdesc, NULL)) return CS_E_NOCANDO; m_dwOneSplittedBufferSize = dwSize;//区切られたバッファの1つのサイズ(バッファ全体はこれ*m_dwNotificationNum } //通知インターフェイス #if !ENABLE_SOUND_POLLING { SAFE_RELEASE(m_pSoundNotify); if(FAILED(m_pSecondaryBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&m_pSoundNotify))){ return CS_E_NOCANDO; } SAFE_GLOBALFREE(m_pDsbnotify); if(!(m_pDsbnotify = (DSBPOSITIONNOTIFY*)GlobalAlloc(GPTR, m_dwNotificationNum * sizeof(DSBPOSITIONNOTIFY)))){ return CS_E_UNEXP; } m_pNotifyHandle = CreateEvent(NULL, FALSE, FALSE, NULL); //通知ハンドルの作成 for(DWORD i=0; i<m_dwNotificationNum; i++){ //OutputDebugStringFormatted("[%2lu]:%lu\n", i, (m_dwOneSplittedBufferSize*i) + 1); m_pDsbnotify[i].dwOffset = (m_dwOneSplittedBufferSize*i) + 1;// バッファを分割する。通知ポイントは、バッファの区切れ目から1バイト先。こうすることで、スペックの低いマシンでも249ms以内に次のバッファ区間を埋めればよいことになる。 m_pDsbnotify[i].hEventNotify = m_pNotifyHandle; } if(FAILED(m_pSoundNotify->SetNotificationPositions(m_dwNotificationNum, m_pDsbnotify))){ SAFE_GLOBALFREE(m_pDsbnotify); SAFE_RELEASE(m_pSoundNotify); SAFE_CLOSEHANDLE(m_pNotifyHandle); return CS_E_NOCANDO; } } #endif }else{ m_isStreamFile = FALSE; void* pdata = NULL; if(CSL_E_OK != m_Loader->GetDecodedData(&pdata, 0, 0, FALSE)){ } dsbdesc.dwFlags = DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME| DSBCAPS_CTRLFREQUENCY| DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY| DSBCAPS_GLOBALFOCUS; if(pDsbdesc){ dsbdesc.dwFlags = pDsbdesc->dwFlags; dsbdesc.guid3DAlgorithm = dsbdesc.guid3DAlgorithm; } dsbdesc.dwBufferBytes = dwDataLength; dsbdesc.lpwfxFormat = &m_wfx; SAFE_RELEASE(m_pSecondaryBuffer); if(!CreateBuffer(&m_pSecondaryBuffer, &dsbdesc, NULL)) return CS_E_NOCANDO; //セカンダリバッファの作成 if(!WriteDataToBuffer(&m_pSecondaryBuffer, pdata, 0, dwDataLength)) return CS_E_NOCANDO; //データをバッファに書き込む SAFE_GLOBALFREE(pdata); } SAFE_RELEASE(m_pPrimaryBuffer); return CS_E_OK; }