// VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator. // Copyright (C) 1999-2003 Forgotten // Copyright (C) 2004 Forgotten and the VBA development team // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or(at your option) // any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "stdafx.h" #include "VBA.h" #include "AVIWrite.h" #include "Sound.h" #include "WavWriter.h" #include "../System.h" #include "../GBA.h" #include "../Globals.h" #include "../Sound.h" #include #include class DirectSound : public ISound { private: HINSTANCE dsoundDLL; LPDIRECTSOUND pDirectSound; LPDIRECTSOUNDBUFFER dsbPrimary; LPDIRECTSOUNDBUFFER dsbSecondary; LPDIRECTSOUNDNOTIFY dsbNotify; HANDLE dsbEvent; WAVEFORMATEX wfx; public: DirectSound(); virtual ~DirectSound(); bool init(); void pause(); void reset(); void resume(); void write(); }; DirectSound::DirectSound() { dsoundDLL = NULL; pDirectSound = NULL; dsbPrimary = NULL; dsbSecondary = NULL; dsbNotify = NULL; dsbEvent = NULL; } DirectSound::~DirectSound() { if(theApp.aviRecorder != NULL) { delete theApp.aviRecorder; theApp.aviRecorder = NULL; theApp.aviFrameNumber = 0; } if(theApp.soundRecording) { if(theApp.soundRecorder != NULL) { delete theApp.soundRecorder; theApp.soundRecorder = NULL; } theApp.soundRecording = false; } if(dsbNotify != NULL) { dsbNotify->Release(); dsbNotify = NULL; } if(dsbEvent != NULL) { CloseHandle(dsbEvent); dsbEvent = NULL; } if(pDirectSound != NULL) { if(dsbPrimary != NULL) { dsbPrimary->Release(); dsbPrimary = NULL; } if(dsbSecondary != NULL) { dsbSecondary->Release(); dsbSecondary = NULL; } pDirectSound->Release(); pDirectSound = NULL; } if(dsoundDLL != NULL) { FreeLibrary(dsoundDLL); dsoundDLL = NULL; } } bool DirectSound::init() { HRESULT hr; dsoundDLL = LoadLibrary("DSOUND.DLL"); HRESULT (WINAPI *DSoundCreate)(LPCGUID,LPDIRECTSOUND *,IUnknown *); if(dsoundDLL != NULL) { DSoundCreate = (HRESULT (WINAPI *)(LPCGUID,LPDIRECTSOUND *,IUnknown *)) GetProcAddress(dsoundDLL, "DirectSoundCreate"); if(DSoundCreate == NULL) { theApp.directXMessage("DirectSoundCreate"); return false; } } else { theApp.directXMessage("DSOUND.DLL"); return false; } if((hr = DSoundCreate(NULL,&pDirectSound,NULL) != DS_OK)) { // errorMessage(myLoadString(IDS_ERROR_SOUND_CREATE), hr); systemMessage(IDS_CANNOT_CREATE_DIRECTSOUND, "Cannot create DirectSound %08x", hr); pDirectSound = NULL; dsbSecondary = NULL; return false; } if((hr=pDirectSound->SetCooperativeLevel((HWND)*theApp.m_pMainWnd, DSSCL_EXCLUSIVE)) != DS_OK) { // errorMessage(myLoadString(IDS_ERROR_SOUND_LEVEL), hr); systemMessage(IDS_CANNOT_SETCOOPERATIVELEVEL, "Cannot SetCooperativeLevel %08x", hr); return false; } DSBUFFERDESC dsbdesc; ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC)); dsbdesc.dwSize=sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; if((hr=pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbPrimary, NULL) != DS_OK)) { // errorMessage(myLoadString(IDS_ERROR_SOUND_BUFFER),hr); systemMessage(IDS_CANNOT_CREATESOUNDBUFFER, "Cannot CreateSoundBuffer %08x", hr); return false; } // Set primary buffer format memset(&wfx, 0, sizeof(WAVEFORMATEX)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; switch(soundQuality) { case 2: wfx.nSamplesPerSec = 22050; soundBufferLen = 736*2; soundBufferTotalLen = 7360*2; break; case 4: wfx.nSamplesPerSec = 11025; soundBufferLen = 368*2; soundBufferTotalLen = 3680*2; break; default: soundQuality = 1; wfx.nSamplesPerSec = 44100; soundBufferLen = 1470*2; soundBufferTotalLen = 14700*2; } wfx.wBitsPerSample = 16; wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if((hr = dsbPrimary->SetFormat(&wfx)) != DS_OK) { // errorMessage(myLoadString(IDS_ERROR_SOUND_PRIMARY),hr); systemMessage(IDS_CANNOT_SETFORMAT_PRIMARY, "Cannot SetFormat for primary %08x", hr); return false; } ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY; dsbdesc.dwBufferBytes = soundBufferTotalLen; dsbdesc.lpwfxFormat = &wfx; if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL)) != DS_OK) { dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2; if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL)) != DS_OK) { systemMessage(IDS_CANNOT_CREATESOUNDBUFFER_SEC, "Cannot CreateSoundBuffer secondary %08x", hr); return false; } } dsbSecondary->SetCurrentPosition(0); if(!theApp.useOldSync) { hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify, (void **)&dsbNotify); if(!FAILED(hr)) { dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL); DSBPOSITIONNOTIFY notify[10]; for(int i = 0; i < 10; i++) { notify[i].dwOffset = i*soundBufferLen; notify[i].hEventNotify = dsbEvent; } if(FAILED(dsbNotify->SetNotificationPositions(10, notify))) { dsbNotify->Release(); dsbNotify = NULL; CloseHandle(dsbEvent); dsbEvent = NULL; } } } hr = dsbPrimary->Play(0,0,DSBPLAY_LOOPING); if(hr != DS_OK) { // errorMessage(myLoadString(IDS_ERROR_SOUND_PLAYPRIM), hr); systemMessage(IDS_CANNOT_PLAY_PRIMARY, "Cannot Play primary %08x", hr); return false; } systemSoundOn = true; return true; } void DirectSound::pause() { if(dsbSecondary != NULL) { DWORD status = 0; dsbSecondary->GetStatus(&status); if(status & DSBSTATUS_PLAYING) { dsbSecondary->Stop(); } } } void DirectSound::reset() { if(dsbSecondary) { dsbSecondary->Stop(); dsbSecondary->SetCurrentPosition(0); } } void DirectSound::resume() { if(dsbSecondary != NULL) { dsbSecondary->Play(0,0,DSBPLAY_LOOPING); } } void DirectSound::write() { int len = soundBufferLen; LPVOID lpvPtr1; DWORD dwBytes1; LPVOID lpvPtr2; DWORD dwBytes2; if(!pDirectSound) return; if(theApp.soundRecording) { if(dsbSecondary) { if(theApp.soundRecorder == NULL) { theApp.soundRecorder = new WavWriter; WAVEFORMATEX format; dsbSecondary->GetFormat(&format, sizeof(format), NULL); if(theApp.soundRecorder->Open(theApp.soundRecordName)) theApp.soundRecorder->SetFormat(&format); } } if(theApp.soundRecorder) { theApp.soundRecorder->AddSound((u8 *)soundFinalWave, len); } } if(theApp.aviRecording) { if(theApp.aviRecorder) { if(dsbSecondary) { if(!theApp.aviRecorder->IsSoundAdded()) { WAVEFORMATEX format; dsbSecondary->GetFormat(&format, sizeof(format), NULL); theApp.aviRecorder->SetSoundFormat(&format); } } theApp.aviRecorder->AddSound((const char *)soundFinalWave, len); } } HRESULT hr; if(!speedup && synchronize && !theApp.throttle) { DWORD status=0; hr = dsbSecondary->GetStatus(&status); if(status && DSBSTATUS_PLAYING) { if(!soundPaused) { DWORD play; while(true) { dsbSecondary->GetCurrentPosition(&play, NULL); if(play < soundNextPosition || play > soundNextPosition+soundBufferLen) { break; } if(dsbEvent) { WaitForSingleObject(dsbEvent, 50); } } } } else { soundPaused = 1; } } // Obtain memory address of write block. This will be in two parts // if the block wraps around. hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); // If DSERR_BUFFERLOST is returned, restore and retry lock. if (DSERR_BUFFERLOST == hr) { dsbSecondary->Restore(); hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen,&lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); } soundNextPosition += soundBufferLen; soundNextPosition = soundNextPosition % soundBufferTotalLen; if SUCCEEDED(hr) { // Write to pointers. CopyMemory(lpvPtr1, soundFinalWave, dwBytes1); if (NULL != lpvPtr2) { CopyMemory(lpvPtr2, soundFinalWave+dwBytes1, dwBytes2); } // Release the data back to DirectSound. hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); } } ISound *newDirectSound() { return new DirectSound(); }