Mercurial > vba-linux
view src/win32/DirectSound.cpp @ 2:3549bbe597ed
adding makefile.am
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 03 Mar 2012 10:31:47 -0600 |
parents | f9f4f1b99eed |
children |
line wrap: on
line source
1 #include "stdafx.h"2 #include <mmreg.h>3 #include <dsound.h>5 #include "resource.h"6 #include "AVIWrite.h"7 #include "Sound.h"8 #include "WavWriter.h"9 #include "VBA.h"11 #include "../gba/GBAGlobals.h"12 #include "../gba/GBASound.h"13 #include "../common/nesvideos-piece.h"15 extern void directXMessage(const char *);17 class DirectSound : public ISound18 {19 private:20 HINSTANCE dsoundDLL;21 LPDIRECTSOUND pDirectSound;22 LPDIRECTSOUNDBUFFER dsbPrimary;23 LPDIRECTSOUNDBUFFER dsbSecondary;24 LPDIRECTSOUNDNOTIFY dsbNotify;25 HANDLE dsbEvent;26 WAVEFORMATEX wfx;27 float curRate;28 public:29 DirectSound();30 virtual ~DirectSound();32 bool init();33 void pause();34 void reset();35 void resume();36 void write();37 void setSpeed(float rate);38 bool isPlaying();39 void clearAudioBuffer();40 };42 DirectSound::DirectSound()43 {44 dsoundDLL = NULL;45 pDirectSound = NULL;46 dsbPrimary = NULL;47 dsbSecondary = NULL;48 dsbNotify = NULL;49 dsbEvent = NULL;50 }52 DirectSound::~DirectSound()53 {54 if (theApp.aviRecorder != NULL)55 {56 delete theApp.aviRecorder;57 theApp.aviRecorder = NULL;58 theApp.aviRecording = false;59 }61 if (theApp.soundRecording)62 {63 if (theApp.soundRecorder != NULL)64 {65 delete theApp.soundRecorder;66 theApp.soundRecorder = NULL;67 }68 theApp.soundRecording = false;69 }71 if (dsbNotify != NULL)72 {73 dsbNotify->Release();74 dsbNotify = NULL;75 }77 if (dsbEvent != NULL)78 {79 CloseHandle(dsbEvent);80 dsbEvent = NULL;81 }83 if (pDirectSound != NULL)84 {85 if (dsbPrimary != NULL)86 {87 dsbPrimary->Release();88 dsbPrimary = NULL;89 }91 if (dsbSecondary != NULL)92 {93 dsbSecondary->Release();94 dsbSecondary = NULL;95 }97 pDirectSound->Release();98 pDirectSound = NULL;99 }101 if (dsoundDLL != NULL)102 {103 FreeLibrary(dsoundDLL);104 dsoundDLL = NULL;105 }106 }108 bool DirectSound::init()109 {110 HRESULT hr;112 dsoundDLL = LoadLibrary("DSOUND.DLL");113 HRESULT (WINAPI *DSoundCreate)(LPCGUID, LPDIRECTSOUND *, IUnknown *);114 if (dsoundDLL != NULL)115 {116 DSoundCreate = (HRESULT (WINAPI *)(LPCGUID, LPDIRECTSOUND *, IUnknown *))117 GetProcAddress(dsoundDLL, "DirectSoundCreate");119 if (DSoundCreate == NULL)120 {121 directXMessage("DirectSoundCreate");122 return false;123 }124 }125 else126 {127 directXMessage("DSOUND.DLL");128 return false;129 }131 if (FAILED(hr = DSoundCreate(NULL, &pDirectSound, NULL)))132 {133 // errorMessage(myLoadString(IDS_ERROR_SOUND_CREATE), hr);134 systemMessage(IDS_CANNOT_CREATE_DIRECTSOUND,135 "Cannot create DirectSound %08x", hr);136 pDirectSound = NULL;137 dsbSecondary = NULL;138 return false;139 }141 if (FAILED(hr = pDirectSound->SetCooperativeLevel((HWND)*theApp.m_pMainWnd, DSSCL_EXCLUSIVE)))142 {143 // errorMessage(myLoadString(IDS_ERROR_SOUND_LEVEL), hr);144 systemMessage(IDS_CANNOT_SETCOOPERATIVELEVEL,145 "Cannot SetCooperativeLevel %08x", hr);146 return false;147 }149 DSBUFFERDESC dsbdesc;150 ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));151 dsbdesc.dwSize = sizeof(DSBUFFERDESC);152 dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;154 if (FAILED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbPrimary, NULL)))155 {156 // errorMessage(myLoadString(IDS_ERROR_SOUND_BUFFER),hr);157 systemMessage(IDS_CANNOT_CREATESOUNDBUFFER,158 "Cannot CreateSoundBuffer %08x", hr);159 return false;160 }162 // Set primary buffer format164 memset(&wfx, 0, sizeof(WAVEFORMATEX));165 wfx.wFormatTag = WAVE_FORMAT_PCM;166 wfx.nChannels = 2;167 switch (soundQuality)168 {169 case 2:170 wfx.nSamplesPerSec = 22050;171 soundBufferLen = 736 * 2;172 soundBufferTotalLen = 7360 * 2;173 break;174 case 4:175 wfx.nSamplesPerSec = 11025;176 soundBufferLen = 368 * 2;177 soundBufferTotalLen = 3680 * 2;178 break;179 default:180 soundQuality = 1;181 wfx.nSamplesPerSec = 44100;182 soundBufferLen = 1470 * 2;183 soundBufferTotalLen = 14700 * 2;184 }185 wfx.wBitsPerSample = 16;186 wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;187 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;189 if (FAILED(hr = dsbPrimary->SetFormat(&wfx)))190 {191 // errorMessage(myLoadString(IDS_ERROR_SOUND_PRIMARY),hr);192 systemMessage(IDS_CANNOT_SETFORMAT_PRIMARY,193 "Cannot SetFormat for primary %08x", hr);194 return false;195 }197 ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));198 dsbdesc.dwSize = sizeof(DSBUFFERDESC);199 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS;200 dsbdesc.dwBufferBytes = soundBufferTotalLen;201 dsbdesc.lpwfxFormat = &wfx;203 if (FAILED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL)))204 {205 bool ok = false;206 while (dsbdesc.dwFlags != DSBCAPS_GETCURRENTPOSITION2)207 {208 if (dsbdesc.dwFlags & DSBCAPS_CTRLFREQUENCY)209 dsbdesc.dwFlags ^= DSBCAPS_CTRLFREQUENCY;210 else if (dsbdesc.dwFlags & DSBCAPS_GLOBALFOCUS)211 dsbdesc.dwFlags ^= DSBCAPS_GLOBALFOCUS;212 else if (dsbdesc.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)213 dsbdesc.dwFlags ^= DSBCAPS_CTRLPOSITIONNOTIFY;214 if (SUCCEEDED(hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL)))215 {216 ok = true;217 break;218 }219 }220 if (!ok)221 {222 systemMessage(IDS_CANNOT_CREATESOUNDBUFFER_SEC, "Cannot CreateSoundBuffer secondary %08x", hr);223 return false;224 }226 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2;227 }229 dsbSecondary->SetCurrentPosition(0);231 if (!theApp.useOldSync)232 {233 hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify,234 (void * *)&dsbNotify);235 if (!FAILED(hr))236 {237 dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL);239 DSBPOSITIONNOTIFY notify[10];241 for (int i = 0; i < 10; i++)242 {243 notify[i].dwOffset = i * soundBufferLen;244 notify[i].hEventNotify = dsbEvent;245 }246 if (FAILED(dsbNotify->SetNotificationPositions(10, notify)))247 {248 dsbNotify->Release();249 dsbNotify = NULL;250 CloseHandle(dsbEvent);251 dsbEvent = NULL;252 }253 }254 }256 hr = dsbPrimary->Play(0, 0, DSBPLAY_LOOPING);258 if (FAILED(hr))259 {260 // errorMessage(myLoadString(IDS_ERROR_SOUND_PLAYPRIM), hr);261 systemMessage(IDS_CANNOT_PLAY_PRIMARY, "Cannot Play primary %08x", hr);262 return false;263 }265 systemSoundOn = true;267 return true;268 }270 void DirectSound::setSpeed(float rate)271 {272 if (dsbSecondary == NULL || wfx.nSamplesPerSec <= 0)273 return;275 if (rate != curRate)276 {277 curRate = rate;279 if (rate > 4.0f)280 rate = 4.0f;281 if (rate < 0.06f)282 rate = 0.06f;284 dsbSecondary->SetFrequency((DWORD)((float)wfx.nSamplesPerSec * rate));285 }286 }288 void DirectSound::pause()289 {290 if (dsbSecondary != NULL)291 {292 DWORD status = 0;293 dsbSecondary->GetStatus(&status);295 if (status & DSBSTATUS_PLAYING)296 {297 //systemScreenMessage("sound stopped (pause)!", 3);298 dsbSecondary->Stop();299 }300 }301 }303 bool DirectSound::isPlaying()304 {305 if (dsbSecondary != NULL)306 {307 DWORD status = 0;308 dsbSecondary->GetStatus(&status);310 if (status & DSBSTATUS_PLAYING)311 {312 return true;313 }314 }315 return false;316 }318 void DirectSound::reset()319 {320 if (dsbSecondary)321 {322 //systemScreenMessage("sound stopped (reset)!", 3);323 dsbSecondary->Stop();324 dsbSecondary->SetCurrentPosition(0);325 }326 }328 void DirectSound::resume()329 {330 if (dsbSecondary != NULL)331 {332 dsbSecondary->Play(0, 0, DSBPLAY_LOOPING);333 }334 }336 long linearFrameCount = 0;337 long linearSoundByteCount = 0;338 long linearSoundFrameCount = 0;340 void DirectSound::write()341 {342 int len = soundBufferLen;343 LPVOID lpvPtr1;344 DWORD dwBytes1;345 LPVOID lpvPtr2;346 DWORD dwBytes2;348 do349 {350 linearSoundByteCount += len;351 if (wfx.nAvgBytesPerSec)352 linearSoundFrameCount = 60 * linearSoundByteCount / wfx.nAvgBytesPerSec;354 if (pDirectSound != NULL)355 {356 if (theApp.soundRecording)357 {358 if (dsbSecondary)359 {360 if (theApp.soundRecorder == NULL)361 {362 theApp.soundRecorder = new WavWriter;363 WAVEFORMATEX format;364 dsbSecondary->GetFormat(&format, sizeof(format), NULL);365 if (theApp.soundRecorder->Open(theApp.soundRecordName))366 theApp.soundRecorder->SetFormat(&format);367 }368 }370 if (theApp.soundRecorder)371 {372 theApp.soundRecorder->AddSound((u8 *)soundFinalWave, len);373 }374 }376 if (theApp.nvAudioLog)377 {378 NESVideoLoggingAudio((u8 *)soundFinalWave, wfx.nSamplesPerSec, wfx.wBitsPerSample, wfx.nChannels, len /379 (wfx.nChannels * (wfx.wBitsPerSample / 8)));380 }382 // alternate avi record routine has been added in VBA.cpp383 if (!theApp.altAviRecordMethod && theApp.aviRecording)384 {385 if (theApp.aviRecorder && !theApp.aviRecorder->IsPaused())386 {387 if (dsbSecondary)388 {389 if (!theApp.aviRecorder->IsSoundAdded())390 {391 WAVEFORMATEX format;392 dsbSecondary->GetFormat(&format, sizeof(format), NULL);393 theApp.aviRecorder->SetSoundFormat(&format);394 }395 }397 theApp.aviRecorder->AddSound((u8 *)soundFinalWave, len);398 }399 }400 }401 }402 while (linearSoundFrameCount <= linearFrameCount);404 // arbitrarily wrap counters at 10000 frames to avoid mismatching wrap-around freeze405 if (linearSoundFrameCount > 10000 && linearFrameCount > 10000)406 {407 linearFrameCount -= 10000;408 linearSoundByteCount -= wfx.nAvgBytesPerSec * 10000 / 60;409 linearSoundFrameCount = 60 * linearSoundByteCount / wfx.nAvgBytesPerSec;410 }412 if (!pDirectSound)413 return;415 HRESULT hr;417 bool fastForward = speedup;418 #if (defined(WIN32) && !defined(SDL))419 fastForward |= theApp.frameSearchSkipping;420 #endif422 // slows down emulator to match up with the sound speed423 if (!fastForward && synchronize && !(theApp.throttle > 100 && theApp.accuratePitchThrottle)424 && theApp.throttle >= 6 && theApp.throttle <= 400)425 {426 DWORD status = 0;427 hr = dsbSecondary->GetStatus(&status);428 if (status & DSBSTATUS_PLAYING)429 {430 if (!soundPaused)431 {432 DWORD play;433 while (true)434 {435 dsbSecondary->GetCurrentPosition(&play, NULL);437 if (soundNextPosition + soundBufferLen < soundBufferTotalLen)438 {439 if (play < soundNextPosition440 || play > soundNextPosition + soundBufferLen)441 break;442 }443 else444 {445 if (play < soundNextPosition446 && play > (soundNextPosition + soundBufferLen) % soundBufferTotalLen)447 break;448 }450 if (dsbEvent)451 {452 WaitForSingleObject(dsbEvent, 50);453 }454 }455 }456 }457 else458 {459 soundPaused = 1;460 }461 }463 // Obtain memory address of write block. This will be in two parts464 // if the block wraps around.465 hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen,466 &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2,467 0);469 if (FAILED(hr))470 {471 char str [256];472 sprintf(str, "Locking secondary failed with %d", hr);473 systemScreenMessage(str);474 }476 // If DSERR_BUFFERLOST is returned, restore and retry lock.477 if (DSERR_BUFFERLOST == hr)478 {479 dsbSecondary->Restore();480 hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen,481 &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2,482 0);483 }485 if (SUCCEEDED(hr))486 {487 if (theApp.muteFrameAdvance && theApp.winPauseNextFrame || theApp.winMuteForNow)488 {489 // Write 0 to pointers.490 if (NULL != lpvPtr1)491 ZeroMemory(lpvPtr1, dwBytes1);492 if (NULL != lpvPtr2)493 ZeroMemory(lpvPtr2, dwBytes2);494 }495 else496 {497 // Write to pointers.498 if (NULL != lpvPtr1)499 CopyMemory(lpvPtr1, soundFinalWave, dwBytes1);500 if (NULL != lpvPtr2)501 CopyMemory(lpvPtr2, soundFinalWave + dwBytes1, dwBytes2);502 }504 // Release the data back to DirectSound.505 hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2,506 dwBytes2);507 }509 soundNextPosition += soundBufferLen;510 soundNextPosition %= soundBufferTotalLen;511 }513 void DirectSound::clearAudioBuffer()514 {515 LPVOID lpvPtr1;516 DWORD dwBytes1;517 LPVOID lpvPtr2;518 DWORD dwBytes2;519 HRESULT hr = dsbSecondary->Lock(0, soundBufferTotalLen, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);520 if (!FAILED(hr))521 {522 if (lpvPtr1)523 memset(lpvPtr1, 0, dwBytes1);524 if (lpvPtr2)525 memset(lpvPtr2, 0, dwBytes2);526 hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);527 }528 }530 ISound *newDirectSound()531 {532 return new DirectSound();533 }