//this code is sloppily ripped from an unfinished sound system written for internal use by m.gambrell //it is released into the public domain and its stability is not warranted #include "oakra.h" #include "dsound.h" #include #define LATENCY_MS (100) class DSVoice : public OAKRA_Voice { public: OAKRA_Module_OutputDS *driver; OAKRA_Format format; int formatShift; IDirectSoundBuffer *ds_buf; int buflen; unsigned int cPlay; int vol,pan; virtual void setPan(int pan) { //removed for FCEU } virtual void setVol(int vol) { //removed for FCEU } virtual int getVol() { return vol; } virtual int getPan() { return pan; } void setSource(OAKRA_Module *source) { this->source = source; } virtual ~DSVoice() { driver->freeVoiceInternal(this,true); ds_buf->Release(); } DSVoice(OAKRA_Module_OutputDS *driver, OAKRA_Format &format, IDirectSound *ds_dev, bool global) { this->driver = driver; vol = 255; pan = 0; source = 0; this->format = format; formatShift = getFormatShift(format); buflen = (format.rate * LATENCY_MS / 1000); WAVEFORMATEX wfx; memset(&wfx, 0, sizeof(wfx)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = format.channels; wfx.nSamplesPerSec = format.rate; wfx.nAvgBytesPerSec = format.rate * format.size; wfx.nBlockAlign = format.size; wfx.wBitsPerSample = (format.format==OAKRA_S16?16:8); wfx.cbSize = sizeof(wfx); DSBUFFERDESC dsbd; memset(&dsbd, 0, sizeof(dsbd)); dsbd.dwSize = sizeof(dsbd); //commented out for FCEU dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2;// | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME ; if(global) dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS ; dsbd.dwBufferBytes = buflen * format.size; dsbd.lpwfxFormat = &wfx; HRESULT hr = ds_dev->CreateSoundBuffer(&dsbd,&ds_buf,0); cPlay = 0; hr = ds_buf->Play(0,0,DSBPLAY_LOOPING); } //not supported virtual void volFade(int start, int end, int ms) {} void update() { DWORD play, write; HRESULT hr = ds_buf->GetCurrentPosition(&play, &write); play >>= formatShift; write >>= formatShift; int todo; if(playLock( cPlay<>= formatShift; buffer2_length >>= formatShift; int done = 0; if(source) { done = source->generate(buffer1_length,buffer1); if(done != buffer1_length) { generateSilence(buffer1_length - done,(char *)buffer1 + (done<generate(buffer2_length,buffer2); if(done != buffer2_length) { generateSilence(buffer2_length - done,(char *)buffer2 + (done<Unlock( buffer1, buffer1_length, buffer2, buffer2_length); cPlay = play; } }; class Data { public: bool global; IDirectSound* ds_dev; std::vector voices; CRITICAL_SECTION criticalSection; }; class ThreadData { public: ThreadData() { kill = dead = false; } OAKRA_Module_OutputDS *ds; bool kill,dead; }; OAKRA_Module_OutputDS::OAKRA_Module_OutputDS() { data = new Data(); ((Data *)data)->global = false; InitializeCriticalSection(&((Data *)data)->criticalSection); } OAKRA_Module_OutputDS::~OAKRA_Module_OutputDS() { //ask the driver to shutdown, and wait for it to do so ((ThreadData *)threadData)->kill = true; while(!((ThreadData *)threadData)->dead) Sleep(1); ////kill all the voices std::vector voicesCopy = ((Data *)data)->voices; int voices = (int)voicesCopy.size(); for(int i=0;icriticalSection); ((Data *)data)->ds_dev->Release(); delete (Data *)data; delete (ThreadData *)threadData; } OAKRA_Voice *OAKRA_Module_OutputDS::getVoice(OAKRA_Format &format, OAKRA_Module *source) { DSVoice *dsv = (DSVoice *)getVoice(format); dsv->setSource(source); return dsv; } OAKRA_Voice *OAKRA_Module_OutputDS::getVoice(OAKRA_Format &format) { DSVoice *voice = new DSVoice(this,format,((Data *)data)->ds_dev,((Data *)data)->global); ((Data *)data)->voices.push_back(voice); return voice; } void OAKRA_Module_OutputDS::freeVoice(OAKRA_Voice *voice) { freeVoiceInternal(voice,false); } void OAKRA_Module_OutputDS::freeVoiceInternal(OAKRA_Voice *voice, bool internal) { lock(); Data *data = (Data *)this->data; int j = -1; for(int i=0;i<(int)data->voices.size();i++) if(data->voices[i] == voice) j = i; if(j!=-1) data->voices.erase(data->voices.begin()+j); if(!internal) delete voice; unlock(); } void OAKRA_Module_OutputDS::start(void *hwnd) { HRESULT hr = CoInitialize(NULL); IDirectSound* ds_dev; hr = CoCreateInstance(CLSID_DirectSound,0,CLSCTX_INPROC_SERVER,IID_IDirectSound,(void**)&ds_dev); if(!hwnd) { hwnd = GetDesktopWindow(); ((Data *)data)->global = true; } //use default device hr = ds_dev->Initialize(0); hr = ds_dev->SetCooperativeLevel((HWND)hwnd, DSSCL_NORMAL); ((Data *)data)->ds_dev = ds_dev; } DWORD WINAPI updateProc(LPVOID lpParameter) { ThreadData *data = (ThreadData *)lpParameter; for(;;) { if(data->kill) break; data->ds->update(); Sleep(1); } data->dead = true; return 0; } void OAKRA_Module_OutputDS::beginThread() { DWORD updateThreadId; threadData = new ThreadData(); ((ThreadData *)threadData)->ds = this; HANDLE updateThread = CreateThread(0,0,updateProc,threadData,0,&updateThreadId); SetThreadPriority(updateThread,THREAD_PRIORITY_TIME_CRITICAL); //SetThreadPriority(updateThread,THREAD_PRIORITY_HIGHEST); } void OAKRA_Module_OutputDS::endThread() { ((ThreadData *)threadData)->kill = true; } void OAKRA_Module_OutputDS::update() { lock(); int voices = (int)((Data *)data)->voices.size(); //render all the voices for(int i=0;ivoices[i]->update(); //look for voices that are dead std::vector deaders; for(int i=0;ivoices[i]->dead) deaders.push_back(((Data *)data)->voices[i]); //unlock the driver before killing voices! //that way, the voice's death callback won't occur within the driver lock unlock(); //kill those voices for(int i=0;i<(int)deaders.size();i++) { deaders[i]->callbackDied(); freeVoice(deaders[i]); } } void OAKRA_Module_OutputDS::lock() { EnterCriticalSection( &((Data *)this->data)->criticalSection ); } void OAKRA_Module_OutputDS::unlock() { LeaveCriticalSection( &((Data *)this->data)->criticalSection ); }