view 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 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 ISound
18 {
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 else
126 {
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 format
164 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 do
349 {
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.cpp
383 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 freeze
405 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 #endif
422 // slows down emulator to match up with the sound speed
423 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 < soundNextPosition
440 || play > soundNextPosition + soundBufferLen)
441 break;
442 }
443 else
444 {
445 if (play < soundNextPosition
446 && play > (soundNextPosition + soundBufferLen) % soundBufferTotalLen)
447 break;
448 }
450 if (dsbEvent)
451 {
452 WaitForSingleObject(dsbEvent, 50);
453 }
454 }
455 }
456 }
457 else
458 {
459 soundPaused = 1;
460 }
461 }
463 // Obtain memory address of write block. This will be in two parts
464 // 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 else
496 {
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 }