Mercurial > vba-linux
diff src/win32/DirectSound.cpp @ 1:f9f4f1b99eed
importing src directory
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:27 -0600 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/win32/DirectSound.cpp Sat Mar 03 10:31:27 2012 -0600 1.3 @@ -0,0 +1,534 @@ 1.4 +#include "stdafx.h" 1.5 +#include <mmreg.h> 1.6 +#include <dsound.h> 1.7 + 1.8 +#include "resource.h" 1.9 +#include "AVIWrite.h" 1.10 +#include "Sound.h" 1.11 +#include "WavWriter.h" 1.12 +#include "VBA.h" 1.13 + 1.14 +#include "../gba/GBAGlobals.h" 1.15 +#include "../gba/GBASound.h" 1.16 +#include "../common/nesvideos-piece.h" 1.17 + 1.18 +extern void directXMessage(const char *); 1.19 + 1.20 +class DirectSound : public ISound 1.21 +{ 1.22 +private: 1.23 + HINSTANCE dsoundDLL; 1.24 + LPDIRECTSOUND pDirectSound; 1.25 + LPDIRECTSOUNDBUFFER dsbPrimary; 1.26 + LPDIRECTSOUNDBUFFER dsbSecondary; 1.27 + LPDIRECTSOUNDNOTIFY dsbNotify; 1.28 + HANDLE dsbEvent; 1.29 + WAVEFORMATEX wfx; 1.30 + float curRate; 1.31 +public: 1.32 + DirectSound(); 1.33 + virtual ~DirectSound(); 1.34 + 1.35 + bool init(); 1.36 + void pause(); 1.37 + void reset(); 1.38 + void resume(); 1.39 + void write(); 1.40 + void setSpeed(float rate); 1.41 + bool isPlaying(); 1.42 + void clearAudioBuffer(); 1.43 +}; 1.44 + 1.45 +DirectSound::DirectSound() 1.46 +{ 1.47 + dsoundDLL = NULL; 1.48 + pDirectSound = NULL; 1.49 + dsbPrimary = NULL; 1.50 + dsbSecondary = NULL; 1.51 + dsbNotify = NULL; 1.52 + dsbEvent = NULL; 1.53 +} 1.54 + 1.55 +DirectSound::~DirectSound() 1.56 +{ 1.57 + if (theApp.aviRecorder != NULL) 1.58 + { 1.59 + delete theApp.aviRecorder; 1.60 + theApp.aviRecorder = NULL; 1.61 + theApp.aviRecording = false; 1.62 + } 1.63 + 1.64 + if (theApp.soundRecording) 1.65 + { 1.66 + if (theApp.soundRecorder != NULL) 1.67 + { 1.68 + delete theApp.soundRecorder; 1.69 + theApp.soundRecorder = NULL; 1.70 + } 1.71 + theApp.soundRecording = false; 1.72 + } 1.73 + 1.74 + if (dsbNotify != NULL) 1.75 + { 1.76 + dsbNotify->Release(); 1.77 + dsbNotify = NULL; 1.78 + } 1.79 + 1.80 + if (dsbEvent != NULL) 1.81 + { 1.82 + CloseHandle(dsbEvent); 1.83 + dsbEvent = NULL; 1.84 + } 1.85 + 1.86 + if (pDirectSound != NULL) 1.87 + { 1.88 + if (dsbPrimary != NULL) 1.89 + { 1.90 + dsbPrimary->Release(); 1.91 + dsbPrimary = NULL; 1.92 + } 1.93 + 1.94 + if (dsbSecondary != NULL) 1.95 + { 1.96 + dsbSecondary->Release(); 1.97 + dsbSecondary = NULL; 1.98 + } 1.99 + 1.100 + pDirectSound->Release(); 1.101 + pDirectSound = NULL; 1.102 + } 1.103 + 1.104 + if (dsoundDLL != NULL) 1.105 + { 1.106 + FreeLibrary(dsoundDLL); 1.107 + dsoundDLL = NULL; 1.108 + } 1.109 +} 1.110 + 1.111 +bool DirectSound::init() 1.112 +{ 1.113 + HRESULT hr; 1.114 + 1.115 + dsoundDLL = LoadLibrary("DSOUND.DLL"); 1.116 + HRESULT (WINAPI *DSoundCreate)(LPCGUID, LPDIRECTSOUND *, IUnknown *); 1.117 + if (dsoundDLL != NULL) 1.118 + { 1.119 + DSoundCreate = (HRESULT (WINAPI *)(LPCGUID, LPDIRECTSOUND *, IUnknown *)) 1.120 + GetProcAddress(dsoundDLL, "DirectSoundCreate"); 1.121 + 1.122 + if (DSoundCreate == NULL) 1.123 + { 1.124 + directXMessage("DirectSoundCreate"); 1.125 + return false; 1.126 + } 1.127 + } 1.128 + else 1.129 + { 1.130 + directXMessage("DSOUND.DLL"); 1.131 + return false; 1.132 + } 1.133 + 1.134 + if (FAILED(hr = DSoundCreate(NULL, &pDirectSound, NULL))) 1.135 + { 1.136 + // errorMessage(myLoadString(IDS_ERROR_SOUND_CREATE), hr); 1.137 + systemMessage(IDS_CANNOT_CREATE_DIRECTSOUND, 1.138 + "Cannot create DirectSound %08x", hr); 1.139 + pDirectSound = NULL; 1.140 + dsbSecondary = NULL; 1.141 + return false; 1.142 + } 1.143 + 1.144 + if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)*theApp.m_pMainWnd, DSSCL_EXCLUSIVE))) 1.145 + { 1.146 + // errorMessage(myLoadString(IDS_ERROR_SOUND_LEVEL), hr); 1.147 + systemMessage(IDS_CANNOT_SETCOOPERATIVELEVEL, 1.148 + "Cannot SetCooperativeLevel %08x", hr); 1.149 + return false; 1.150 + } 1.151 + 1.152 + DSBUFFERDESC dsbdesc; 1.153 + ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); 1.154 + dsbdesc.dwSize = sizeof(DSBUFFERDESC); 1.155 + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; 1.156 + 1.157 + if (FAILED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbPrimary, NULL))) 1.158 + { 1.159 + // errorMessage(myLoadString(IDS_ERROR_SOUND_BUFFER),hr); 1.160 + systemMessage(IDS_CANNOT_CREATESOUNDBUFFER, 1.161 + "Cannot CreateSoundBuffer %08x", hr); 1.162 + return false; 1.163 + } 1.164 + 1.165 + // Set primary buffer format 1.166 + 1.167 + memset(&wfx, 0, sizeof(WAVEFORMATEX)); 1.168 + wfx.wFormatTag = WAVE_FORMAT_PCM; 1.169 + wfx.nChannels = 2; 1.170 + switch (soundQuality) 1.171 + { 1.172 + case 2: 1.173 + wfx.nSamplesPerSec = 22050; 1.174 + soundBufferLen = 736 * 2; 1.175 + soundBufferTotalLen = 7360 * 2; 1.176 + break; 1.177 + case 4: 1.178 + wfx.nSamplesPerSec = 11025; 1.179 + soundBufferLen = 368 * 2; 1.180 + soundBufferTotalLen = 3680 * 2; 1.181 + break; 1.182 + default: 1.183 + soundQuality = 1; 1.184 + wfx.nSamplesPerSec = 44100; 1.185 + soundBufferLen = 1470 * 2; 1.186 + soundBufferTotalLen = 14700 * 2; 1.187 + } 1.188 + wfx.wBitsPerSample = 16; 1.189 + wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; 1.190 + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; 1.191 + 1.192 + if (FAILED(hr = dsbPrimary->SetFormat(&wfx))) 1.193 + { 1.194 + // errorMessage(myLoadString(IDS_ERROR_SOUND_PRIMARY),hr); 1.195 + systemMessage(IDS_CANNOT_SETFORMAT_PRIMARY, 1.196 + "Cannot SetFormat for primary %08x", hr); 1.197 + return false; 1.198 + } 1.199 + 1.200 + ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC)); 1.201 + dsbdesc.dwSize = sizeof(DSBUFFERDESC); 1.202 + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS; 1.203 + dsbdesc.dwBufferBytes = soundBufferTotalLen; 1.204 + dsbdesc.lpwfxFormat = &wfx; 1.205 + 1.206 + if (FAILED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))) 1.207 + { 1.208 + bool ok = false; 1.209 + while (dsbdesc.dwFlags != DSBCAPS_GETCURRENTPOSITION2) 1.210 + { 1.211 + if (dsbdesc.dwFlags & DSBCAPS_CTRLFREQUENCY) 1.212 + dsbdesc.dwFlags ^= DSBCAPS_CTRLFREQUENCY; 1.213 + else if (dsbdesc.dwFlags & DSBCAPS_GLOBALFOCUS) 1.214 + dsbdesc.dwFlags ^= DSBCAPS_GLOBALFOCUS; 1.215 + else if (dsbdesc.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY) 1.216 + dsbdesc.dwFlags ^= DSBCAPS_CTRLPOSITIONNOTIFY; 1.217 + if (SUCCEEDED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))) 1.218 + { 1.219 + ok = true; 1.220 + break; 1.221 + } 1.222 + } 1.223 + if (!ok) 1.224 + { 1.225 + systemMessage(IDS_CANNOT_CREATESOUNDBUFFER_SEC, "Cannot CreateSoundBuffer secondary %08x", hr); 1.226 + return false; 1.227 + } 1.228 + 1.229 + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2; 1.230 + } 1.231 + 1.232 + dsbSecondary->SetCurrentPosition(0); 1.233 + 1.234 + if (!theApp.useOldSync) 1.235 + { 1.236 + hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify, 1.237 + (void * *)&dsbNotify); 1.238 + if (!FAILED(hr)) 1.239 + { 1.240 + dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 1.241 + 1.242 + DSBPOSITIONNOTIFY notify[10]; 1.243 + 1.244 + for (int i = 0; i < 10; i++) 1.245 + { 1.246 + notify[i].dwOffset = i * soundBufferLen; 1.247 + notify[i].hEventNotify = dsbEvent; 1.248 + } 1.249 + if (FAILED(dsbNotify->SetNotificationPositions(10, notify))) 1.250 + { 1.251 + dsbNotify->Release(); 1.252 + dsbNotify = NULL; 1.253 + CloseHandle(dsbEvent); 1.254 + dsbEvent = NULL; 1.255 + } 1.256 + } 1.257 + } 1.258 + 1.259 + hr = dsbPrimary->Play(0, 0, DSBPLAY_LOOPING); 1.260 + 1.261 + if (FAILED(hr)) 1.262 + { 1.263 + // errorMessage(myLoadString(IDS_ERROR_SOUND_PLAYPRIM), hr); 1.264 + systemMessage(IDS_CANNOT_PLAY_PRIMARY, "Cannot Play primary %08x", hr); 1.265 + return false; 1.266 + } 1.267 + 1.268 + systemSoundOn = true; 1.269 + 1.270 + return true; 1.271 +} 1.272 + 1.273 +void DirectSound::setSpeed(float rate) 1.274 +{ 1.275 + if (dsbSecondary == NULL || wfx.nSamplesPerSec <= 0) 1.276 + return; 1.277 + 1.278 + if (rate != curRate) 1.279 + { 1.280 + curRate = rate; 1.281 + 1.282 + if (rate > 4.0f) 1.283 + rate = 4.0f; 1.284 + if (rate < 0.06f) 1.285 + rate = 0.06f; 1.286 + 1.287 + dsbSecondary->SetFrequency((DWORD)((float)wfx.nSamplesPerSec * rate)); 1.288 + } 1.289 +} 1.290 + 1.291 +void DirectSound::pause() 1.292 +{ 1.293 + if (dsbSecondary != NULL) 1.294 + { 1.295 + DWORD status = 0; 1.296 + dsbSecondary->GetStatus(&status); 1.297 + 1.298 + if (status & DSBSTATUS_PLAYING) 1.299 + { 1.300 + //systemScreenMessage("sound stopped (pause)!", 3); 1.301 + dsbSecondary->Stop(); 1.302 + } 1.303 + } 1.304 +} 1.305 + 1.306 +bool DirectSound::isPlaying() 1.307 +{ 1.308 + if (dsbSecondary != NULL) 1.309 + { 1.310 + DWORD status = 0; 1.311 + dsbSecondary->GetStatus(&status); 1.312 + 1.313 + if (status & DSBSTATUS_PLAYING) 1.314 + { 1.315 + return true; 1.316 + } 1.317 + } 1.318 + return false; 1.319 +} 1.320 + 1.321 +void DirectSound::reset() 1.322 +{ 1.323 + if (dsbSecondary) 1.324 + { 1.325 + //systemScreenMessage("sound stopped (reset)!", 3); 1.326 + dsbSecondary->Stop(); 1.327 + dsbSecondary->SetCurrentPosition(0); 1.328 + } 1.329 +} 1.330 + 1.331 +void DirectSound::resume() 1.332 +{ 1.333 + if (dsbSecondary != NULL) 1.334 + { 1.335 + dsbSecondary->Play(0, 0, DSBPLAY_LOOPING); 1.336 + } 1.337 +} 1.338 + 1.339 +long linearFrameCount = 0; 1.340 +long linearSoundByteCount = 0; 1.341 +long linearSoundFrameCount = 0; 1.342 + 1.343 +void DirectSound::write() 1.344 +{ 1.345 + int len = soundBufferLen; 1.346 + LPVOID lpvPtr1; 1.347 + DWORD dwBytes1; 1.348 + LPVOID lpvPtr2; 1.349 + DWORD dwBytes2; 1.350 + 1.351 + do 1.352 + { 1.353 + linearSoundByteCount += len; 1.354 + if (wfx.nAvgBytesPerSec) 1.355 + linearSoundFrameCount = 60 * linearSoundByteCount / wfx.nAvgBytesPerSec; 1.356 + 1.357 + if (pDirectSound != NULL) 1.358 + { 1.359 + if (theApp.soundRecording) 1.360 + { 1.361 + if (dsbSecondary) 1.362 + { 1.363 + if (theApp.soundRecorder == NULL) 1.364 + { 1.365 + theApp.soundRecorder = new WavWriter; 1.366 + WAVEFORMATEX format; 1.367 + dsbSecondary->GetFormat(&format, sizeof(format), NULL); 1.368 + if (theApp.soundRecorder->Open(theApp.soundRecordName)) 1.369 + theApp.soundRecorder->SetFormat(&format); 1.370 + } 1.371 + } 1.372 + 1.373 + if (theApp.soundRecorder) 1.374 + { 1.375 + theApp.soundRecorder->AddSound((u8 *)soundFinalWave, len); 1.376 + } 1.377 + } 1.378 + 1.379 + if (theApp.nvAudioLog) 1.380 + { 1.381 + NESVideoLoggingAudio((u8 *)soundFinalWave, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels, len / 1.382 + (wfx.nChannels * (wfx.wBitsPerSample / 8))); 1.383 + } 1.384 + 1.385 + // alternate avi record routine has been added in VBA.cpp 1.386 + if (!theApp.altAviRecordMethod && theApp.aviRecording) 1.387 + { 1.388 + if (theApp.aviRecorder && !theApp.aviRecorder->IsPaused()) 1.389 + { 1.390 + if (dsbSecondary) 1.391 + { 1.392 + if (!theApp.aviRecorder->IsSoundAdded()) 1.393 + { 1.394 + WAVEFORMATEX format; 1.395 + dsbSecondary->GetFormat(&format, sizeof(format), NULL); 1.396 + theApp.aviRecorder->SetSoundFormat(&format); 1.397 + } 1.398 + } 1.399 + 1.400 + theApp.aviRecorder->AddSound((u8 *)soundFinalWave, len); 1.401 + } 1.402 + } 1.403 + } 1.404 + } 1.405 + while (linearSoundFrameCount <= linearFrameCount); 1.406 + 1.407 + // arbitrarily wrap counters at 10000 frames to avoid mismatching wrap-around freeze 1.408 + if (linearSoundFrameCount > 10000 && linearFrameCount > 10000) 1.409 + { 1.410 + linearFrameCount -= 10000; 1.411 + linearSoundByteCount -= wfx.nAvgBytesPerSec * 10000 / 60; 1.412 + linearSoundFrameCount = 60 * linearSoundByteCount / wfx.nAvgBytesPerSec; 1.413 + } 1.414 + 1.415 + if (!pDirectSound) 1.416 + return; 1.417 + 1.418 + HRESULT hr; 1.419 + 1.420 + bool fastForward = speedup; 1.421 +#if (defined(WIN32) && !defined(SDL)) 1.422 + fastForward |= theApp.frameSearchSkipping; 1.423 +#endif 1.424 + 1.425 + // slows down emulator to match up with the sound speed 1.426 + if (!fastForward && synchronize && !(theApp.throttle > 100 && theApp.accuratePitchThrottle) 1.427 + && theApp.throttle >= 6 && theApp.throttle <= 400) 1.428 + { 1.429 + DWORD status = 0; 1.430 + hr = dsbSecondary->GetStatus(&status); 1.431 + if (status & DSBSTATUS_PLAYING) 1.432 + { 1.433 + if (!soundPaused) 1.434 + { 1.435 + DWORD play; 1.436 + while (true) 1.437 + { 1.438 + dsbSecondary->GetCurrentPosition(&play, NULL); 1.439 + 1.440 + if (soundNextPosition + soundBufferLen < soundBufferTotalLen) 1.441 + { 1.442 + if (play < soundNextPosition 1.443 + || play > soundNextPosition + soundBufferLen) 1.444 + break; 1.445 + } 1.446 + else 1.447 + { 1.448 + if (play < soundNextPosition 1.449 + && play > (soundNextPosition + soundBufferLen) % soundBufferTotalLen) 1.450 + break; 1.451 + } 1.452 + 1.453 + if (dsbEvent) 1.454 + { 1.455 + WaitForSingleObject(dsbEvent, 50); 1.456 + } 1.457 + } 1.458 + } 1.459 + } 1.460 + else 1.461 + { 1.462 + soundPaused = 1; 1.463 + } 1.464 + } 1.465 + 1.466 + // Obtain memory address of write block. This will be in two parts 1.467 + // if the block wraps around. 1.468 + hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, 1.469 + &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 1.470 + 0); 1.471 + 1.472 + if (FAILED(hr)) 1.473 + { 1.474 + char str [256]; 1.475 + sprintf(str, "Locking secondary failed with %d", hr); 1.476 + systemScreenMessage(str); 1.477 + } 1.478 + 1.479 + // If DSERR_BUFFERLOST is returned, restore and retry lock. 1.480 + if (DSERR_BUFFERLOST == hr) 1.481 + { 1.482 + dsbSecondary->Restore(); 1.483 + hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, 1.484 + &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 1.485 + 0); 1.486 + } 1.487 + 1.488 + if (SUCCEEDED(hr)) 1.489 + { 1.490 + if (theApp.muteFrameAdvance && theApp.winPauseNextFrame || theApp.winMuteForNow) 1.491 + { 1.492 + // Write 0 to pointers. 1.493 + if (NULL != lpvPtr1) 1.494 + ZeroMemory(lpvPtr1, dwBytes1); 1.495 + if (NULL != lpvPtr2) 1.496 + ZeroMemory(lpvPtr2, dwBytes2); 1.497 + } 1.498 + else 1.499 + { 1.500 + // Write to pointers. 1.501 + if (NULL != lpvPtr1) 1.502 + CopyMemory(lpvPtr1, soundFinalWave, dwBytes1); 1.503 + if (NULL != lpvPtr2) 1.504 + CopyMemory(lpvPtr2, soundFinalWave + dwBytes1, dwBytes2); 1.505 + } 1.506 + 1.507 + // Release the data back to DirectSound. 1.508 + hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2, 1.509 + dwBytes2); 1.510 + } 1.511 + 1.512 + soundNextPosition += soundBufferLen; 1.513 + soundNextPosition %= soundBufferTotalLen; 1.514 +} 1.515 + 1.516 +void DirectSound::clearAudioBuffer() 1.517 +{ 1.518 + LPVOID lpvPtr1; 1.519 + DWORD dwBytes1; 1.520 + LPVOID lpvPtr2; 1.521 + DWORD dwBytes2; 1.522 + HRESULT hr = dsbSecondary->Lock(0, soundBufferTotalLen, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); 1.523 + if (!FAILED(hr)) 1.524 + { 1.525 + if (lpvPtr1) 1.526 + memset(lpvPtr1, 0, dwBytes1); 1.527 + if (lpvPtr2) 1.528 + memset(lpvPtr2, 0, dwBytes2); 1.529 + hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2); 1.530 + } 1.531 +} 1.532 + 1.533 +ISound *newDirectSound() 1.534 +{ 1.535 + return new DirectSound(); 1.536 +} 1.537 +