1 /***********************************************************************************
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4 (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
5 Jerremy Koot (jkoot@snes9x.com)
6
7 (c) Copyright 2002 - 2004 Matthew Kendora
8
9 (c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
10
11 (c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
12
13 (c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
14
15 (c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
16 Kris Bleakley (codeviolation@hotmail.com)
17
18 (c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
19 Nach (n-a-c-h@users.sourceforge.net),
20
21 (c) Copyright 2002 - 2011 zones (kasumitokoduck@yahoo.com)
22
23 (c) Copyright 2006 - 2007 nitsuja
24
25 (c) Copyright 2009 - 2016 BearOso,
26 OV2
27
28
29 BS-X C emulator code
30 (c) Copyright 2005 - 2006 Dreamer Nom,
31 zones
32
33 C4 x86 assembler and some C emulation code
34 (c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
35 Nach,
36 zsKnight (zsknight@zsnes.com)
37
38 C4 C++ code
39 (c) Copyright 2003 - 2006 Brad Jorsch,
40 Nach
41
42 DSP-1 emulator code
43 (c) Copyright 1998 - 2006 _Demo_,
44 Andreas Naive (andreasnaive@gmail.com),
45 Gary Henderson,
46 Ivar (ivar@snes9x.com),
47 John Weidman,
48 Kris Bleakley,
49 Matthew Kendora,
50 Nach,
51 neviksti (neviksti@hotmail.com)
52
53 DSP-2 emulator code
54 (c) Copyright 2003 John Weidman,
55 Kris Bleakley,
56 Lord Nightmare (lord_nightmare@users.sourceforge.net),
57 Matthew Kendora,
58 neviksti
59
60 DSP-3 emulator code
61 (c) Copyright 2003 - 2006 John Weidman,
62 Kris Bleakley,
63 Lancer,
64 z80 gaiden
65
66 DSP-4 emulator code
67 (c) Copyright 2004 - 2006 Dreamer Nom,
68 John Weidman,
69 Kris Bleakley,
70 Nach,
71 z80 gaiden
72
73 OBC1 emulator code
74 (c) Copyright 2001 - 2004 zsKnight,
75 pagefault (pagefault@zsnes.com),
76 Kris Bleakley
77 Ported from x86 assembler to C by sanmaiwashi
78
79 SPC7110 and RTC C++ emulator code used in 1.39-1.51
80 (c) Copyright 2002 Matthew Kendora with research by
81 zsKnight,
82 John Weidman,
83 Dark Force
84
85 SPC7110 and RTC C++ emulator code used in 1.52+
86 (c) Copyright 2009 byuu,
87 neviksti
88
89 S-DD1 C emulator code
90 (c) Copyright 2003 Brad Jorsch with research by
91 Andreas Naive,
92 John Weidman
93
94 S-RTC C emulator code
95 (c) Copyright 2001 - 2006 byuu,
96 John Weidman
97
98 ST010 C++ emulator code
99 (c) Copyright 2003 Feather,
100 John Weidman,
101 Kris Bleakley,
102 Matthew Kendora
103
104 Super FX x86 assembler emulator code
105 (c) Copyright 1998 - 2003 _Demo_,
106 pagefault,
107 zsKnight
108
109 Super FX C emulator code
110 (c) Copyright 1997 - 1999 Ivar,
111 Gary Henderson,
112 John Weidman
113
114 Sound emulator code used in 1.5-1.51
115 (c) Copyright 1998 - 2003 Brad Martin
116 (c) Copyright 1998 - 2006 Charles Bilyue'
117
118 Sound emulator code used in 1.52+
119 (c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
120
121 S-SMP emulator code used in 1.54+
122 (c) Copyright 2016 byuu
123
124 SH assembler code partly based on x86 assembler code
125 (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
126
127 2xSaI filter
128 (c) Copyright 1999 - 2001 Derek Liauw Kie Fa
129
130 HQ2x, HQ3x, HQ4x filters
131 (c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
132
133 NTSC filter
134 (c) Copyright 2006 - 2007 Shay Green
135
136 GTK+ GUI code
137 (c) Copyright 2004 - 2016 BearOso
138
139 Win32 GUI code
140 (c) Copyright 2003 - 2006 blip,
141 funkyass,
142 Matthew Kendora,
143 Nach,
144 nitsuja
145 (c) Copyright 2009 - 2016 OV2
146
147 Mac OS GUI code
148 (c) Copyright 1998 - 2001 John Stiles
149 (c) Copyright 2001 - 2011 zones
150
151
152 Specific ports contains the works of other authors. See headers in
153 individual files.
154
155
156 Snes9x homepage: http://www.snes9x.com/
157
158 Permission to use, copy, modify and/or distribute Snes9x in both binary
159 and source form, for non-commercial purposes, is hereby granted without
160 fee, providing that this license information and copyright notice appear
161 with all copies and any derived work.
162
163 This software is provided 'as-is', without any express or implied
164 warranty. In no event shall the authors be held liable for any damages
165 arising from the use of this software or it's derivatives.
166
167 Snes9x is freeware for PERSONAL USE only. Commercial users should
168 seek permission of the copyright holders first. Commercial use includes,
169 but is not limited to, charging money for Snes9x or software derived from
170 Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171 using Snes9x as a promotion for your commercial product.
172
173 The copyright holders request that bug fixes and improvements to the code
174 should be forwarded to them so everyone can benefit from the modifications
175 in future versions.
176
177 Super NES and Super Nintendo Entertainment System are trademarks of
178 Nintendo Co., Limited and its subsidiary companies.
179 ***********************************************************************************/
180
181
182
183
184 // CDirectSound.cpp: implementation of the CDirectSound class.
185 //
186 //////////////////////////////////////////////////////////////////////
187
188 #include "wsnes9x.h"
189 #include "../snes9x.h"
190 #include "../apu/apu.h"
191 #include "CDirectSound.h"
192 #include <process.h>
193
194 //////////////////////////////////////////////////////////////////////
195 // Construction/Destruction
196 //////////////////////////////////////////////////////////////////////
CDirectSound()197 CDirectSound::CDirectSound()
198 {
199 lpDS = NULL;
200 lpDSB = NULL;
201 lpDSBPrimary = NULL;
202
203 initDone = NULL;
204 blockCount = 0;
205 blockSize = 0;
206 bufferSize = 0;
207 blockSamples = 0;
208 hTimer = NULL;
209 }
210
~CDirectSound()211 CDirectSound::~CDirectSound()
212 {
213 DeInitDirectSound();
214 }
215
216 /* CDirectSound::InitDirectSound
217 initializes the DirectSound object, the timer queue for the mixing timer, and sets the cooperation level
218 -----
219 returns true if successful, false otherwise
220 */
InitDirectSound()221 bool CDirectSound::InitDirectSound ()
222 {
223 HRESULT dErr;
224
225 if(initDone)
226 return true;
227
228 if (!lpDS)
229 {
230 dErr = DirectSoundCreate (NULL, &lpDS, NULL);
231 if (dErr != DS_OK)
232 {
233 MessageBox (GUI.hWnd, TEXT("\
234 Unable to initialise DirectSound. You will not be able to hear any\n\
235 sound effects or music while playing.\n\n\
236 It is usually caused by not having DirectX installed, another\n\
237 application that has already opened DirectSound in exclusive\n\
238 mode or the Windows WAVE device has been opened."),
239 TEXT("Snes9X - Unable to Open DirectSound"),
240 MB_OK | MB_ICONWARNING);
241 return (false);
242 }
243 }
244 initDone = true;
245 dErr = lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_PRIORITY | DSSCL_EXCLUSIVE);
246 if (!SUCCEEDED(dErr))
247 {
248 dErr = lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_PRIORITY);
249 if (!SUCCEEDED(dErr))
250 {
251 if (!SUCCEEDED(lpDS -> SetCooperativeLevel (GUI.hWnd, DSSCL_NORMAL)))
252 {
253 DeInitDirectSound();
254 initDone = false;
255 }
256 if (initDone)
257 MessageBox (GUI.hWnd, TEXT("\
258 Unable to set DirectSound's priority cooperative level.\n\
259 Another application is dicating the sound playback rate,\n\
260 sample size and mono/stereo setting."),
261 TEXT("Snes9X - Unable to Set DirectSound priority"),
262 MB_OK | MB_ICONWARNING);
263 else
264 MessageBox (GUI.hWnd, TEXT("\
265 Unable to set any DirectSound cooperative level. You will\n\
266 not be able to hear any sound effects or music while playing.\n\n\
267 It is usually caused by another application that has already\n\
268 opened DirectSound in exclusive mode."),
269 TEXT("Snes9X - Unable to DirectSound"),
270 MB_OK | MB_ICONWARNING);
271 }
272 }
273
274 return (initDone);
275 }
276
277 /* CDirectSound::DeInitDirectSound
278 releases all DirectSound objects and buffers
279 */
DeInitDirectSound()280 void CDirectSound::DeInitDirectSound()
281 {
282 initDone = false;
283
284 DeInitSoundBuffer();
285
286 if( lpDS != NULL)
287 {
288 lpDS->SetCooperativeLevel (GUI.hWnd, DSSCL_NORMAL);
289 lpDS->Release ();
290 lpDS = NULL;
291 }
292 }
293
294 /* CDirectSound::InitSoundBuffer
295 creates the DirectSound buffers and allocates the temp buffer for SoundSync
296 -----
297 returns true if successful, false otherwise
298 */
InitSoundBuffer()299 bool CDirectSound::InitSoundBuffer()
300 {
301 DSBUFFERDESC dsbd;
302 WAVEFORMATEX wfx,wfx_actual;
303 HRESULT dErr;
304
305 blockCount = 4;
306 blockTime = GUI.SoundBufferSize / blockCount;
307
308 blockSamples = (Settings.SoundPlaybackRate * blockTime * (Settings.Stereo ? 2 : 1)) / 1000;
309 blockSize = blockSamples * (Settings.SixteenBitSound ? 2 : 1);
310 bufferSize = blockSize * blockCount;
311
312 wfx.wFormatTag = WAVE_FORMAT_PCM;
313 wfx.nChannels = Settings.Stereo ? 2 : 1;
314 wfx.nSamplesPerSec = Settings.SoundPlaybackRate;
315 wfx.nBlockAlign = (Settings.SixteenBitSound ? 2 : 1) * (Settings.Stereo ? 2 : 1);
316 wfx.wBitsPerSample = Settings.SixteenBitSound ? 16 : 8;
317 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
318 wfx.cbSize = 0;
319
320 ZeroMemory (&dsbd, sizeof(DSBUFFERDESC) );
321 dsbd.dwSize = sizeof(dsbd);
322 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_STICKYFOCUS;
323
324 dErr = lpDS->CreateSoundBuffer (&dsbd, &lpDSBPrimary, NULL);
325 if (dErr != DS_OK)
326 {
327 lpDSB = NULL;
328 return (false);
329 }
330
331 lpDSBPrimary->SetFormat (&wfx);
332 if (lpDSBPrimary->GetFormat (&wfx_actual, sizeof (wfx_actual), NULL) == DS_OK)
333 {
334 if(wfx.nSamplesPerSec!=wfx_actual.nSamplesPerSec ||
335 wfx_actual.nChannels != wfx.nChannels || wfx.wBitsPerSample != wfx_actual.wBitsPerSample) {
336 return false;
337 }
338 }
339
340 lpDSBPrimary->Play (0, 0, DSBPLAY_LOOPING);
341
342 ZeroMemory (&dsbd, sizeof (dsbd));
343 dsbd.dwSize = sizeof( dsbd);
344 dsbd.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME |
345 DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
346 dsbd.dwBufferBytes = bufferSize;
347 dsbd.lpwfxFormat = &wfx;
348
349 if (lpDS->CreateSoundBuffer (&dsbd, &lpDSB, NULL) != DS_OK)
350 {
351 lpDSBPrimary->Release ();
352 lpDSBPrimary = NULL;
353 lpDSB->Release();
354 lpDSB = NULL;
355 return (false);
356 }
357
358 return true;
359 }
360
361 /* CDirectSound::DeInitSoundBuffer
362 deinitializes the DirectSound/temp buffers and stops the mixing timer
363 */
DeInitSoundBuffer()364 void CDirectSound::DeInitSoundBuffer()
365 {
366 if(hTimer) {
367 timeKillEvent(hTimer);
368 hTimer = NULL;
369 }
370 if( lpDSB != NULL)
371 {
372 lpDSB->Stop ();
373 lpDSB->Release();
374 lpDSB = NULL;
375 }
376 if( lpDSBPrimary != NULL)
377 {
378 lpDSBPrimary->Stop ();
379 lpDSBPrimary->Release();
380 lpDSBPrimary = NULL;
381 }
382 }
383
384 /* CDirectSound::SetupSound
385 applies sound setting changes by recreating the buffers and starting a new mixing timer
386 it fills the buffer before starting playback
387 -----
388 returns true if successful, false otherwise
389 */
SetupSound()390 bool CDirectSound::SetupSound()
391 {
392 HRESULT hResult;
393
394 if(!initDone)
395 return false;
396
397 DeInitSoundBuffer();
398 InitSoundBuffer();
399
400 BYTE *B1;
401 DWORD S1;
402 hResult = lpDSB->Lock (0, 0, (void **)&B1,
403 &S1, NULL, NULL, DSBLOCK_ENTIREBUFFER);
404 if (hResult == DSERR_BUFFERLOST)
405 {
406 lpDSB->Restore ();
407 hResult = lpDSB->Lock (0, 0, (void **)&B1,
408 &S1, NULL, NULL, DSBLOCK_ENTIREBUFFER);
409 }
410 if (!SUCCEEDED(hResult))
411 {
412 hResult = lpDSB -> Unlock (B1, S1, NULL, NULL);
413 return true;
414 }
415
416 S9xMixSamples(B1,blockSamples * blockCount);
417 lpDSB->Unlock(B1,S1,NULL,NULL);
418
419 lpDSB->Play (0, 0, DSBPLAY_LOOPING);
420
421 last_block = blockCount - 1;
422
423 hTimer = timeSetEvent (blockTime/2, blockTime/2, SoundTimerCallback, (DWORD_PTR)this, TIME_PERIODIC);
424 if(!hTimer) {
425 DeInitSoundBuffer();
426 return false;
427 }
428
429 return (true);
430 }
431
432 /* CDirectSound::ProcessSound
433 Finishes core sample creation, syncronizes the buffer access.
434 */
ProcessSound()435 void CDirectSound::ProcessSound()
436 {
437 EnterCriticalSection(&GUI.SoundCritSect);
438
439 S9xFinalizeSamples();
440
441 LeaveCriticalSection(&GUI.SoundCritSect);
442 }
443
444 /* CDirectSound::MixSound
445 the mixing function called by the mix timer
446 uses the current play position to decide if a new block can be filled with audio data
447 synchronizes the core buffer access with a critical section
448 */
MixSound()449 void CDirectSound::MixSound()
450 {
451 DWORD play_pos = 0, write_pos = 0;
452 HRESULT hResult;
453 DWORD curr_block;
454
455 if(!initDone)
456 return;
457
458 lpDSB->GetCurrentPosition (&play_pos, NULL);
459
460 curr_block = ((play_pos / blockSize) + blockCount) % blockCount;
461
462 if (curr_block != last_block)
463 {
464 BYTE *B1, *B2;
465 DWORD S1, S2;
466
467 write_pos = curr_block * blockSize;
468 last_block = curr_block;
469
470 hResult = lpDSB->Lock (write_pos, blockSize, (void **)&B1,
471 &S1, (void **)&B2, &S2, 0);
472 if (hResult == DSERR_BUFFERLOST)
473 {
474 lpDSB->Restore ();
475 hResult = lpDSB->Lock (write_pos, blockSize,
476 (void **)&B1, &S1, (void **)&B2,
477 &S2, 0);
478 }
479 if (!SUCCEEDED(hResult))
480 {
481 hResult = lpDSB -> Unlock (B1, S1, B2, S2);
482 return;
483 }
484
485 EnterCriticalSection(&GUI.SoundCritSect);
486 if (B1)
487 {
488 S9xMixSamples(B1,(Settings.SixteenBitSound?S1>>1:S1));
489 }
490 if (B2)
491 {
492 S9xMixSamples(B2,(Settings.SixteenBitSound?S2>>1:S2));
493 }
494 LeaveCriticalSection(&GUI.SoundCritSect);
495
496 SetEvent(GUI.SoundSyncEvent);
497
498 hResult = lpDSB -> Unlock (B1, S1, B2, S2);
499 if (!SUCCEEDED(hResult))
500 return;
501 }
502 }
503
504 /* CDirectSound::SoundTimerCallback
505 Timer callback that tries to mix a new block. Called twice each block.
506 */
507
SoundTimerCallback(UINT uTimerID,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dw1,DWORD_PTR dw2)508 VOID CALLBACK CDirectSound::SoundTimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) {
509 CDirectSound *S9xDirectSound = (CDirectSound *)dwUser;
510 S9xDirectSound->MixSound();
511 }
512
513