1 /*
2 * sound.c - Win32 port specific code
3 *
4 * Copyright (C) 2000 Krzysztof Nikiel
5 * Copyright (C) 2000-2003 Atari800 development team (see DOC/CREDITS)
6 *
7 * This file is part of the Atari800 emulator project which emulates
8 * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
9 *
10 * Atari800 is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * Atari800 is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Atari800; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "config.h"
26
27 #ifdef SOUND
28
29 #include <windows.h>
30 #ifdef DIRECTX
31 # define DIRECTSOUND_VERSION 0x0500
32 # include <dsound.h>
33 #endif
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include "sound.h"
37 #include "main.h"
38 #include "pokeysnd.h"
39 #include "atari.h"
40 #include "log.h"
41
42 #define MIXBUFSIZE 0x1000
43 #define WAVSHIFT 9
44 #define WAVSIZE (1 << WAVSHIFT)
45 int buffers = 0;
46
47 enum {SOUND_NONE, SOUND_DX, SOUND_WAV};
48
49 static int usesound = TRUE;
50 static int issound = SOUND_NONE;
51 static int dsprate = 44100;
52 static int snddelay = 40; /* delay in milliseconds */
53 static int snddelaywav = 100;
54 static int bit16 = FALSE;
55
56 static HANDLE event;
57 static HWAVEOUT wout;
58 static WAVEHDR *waves;
59
60 #ifdef DIRECTX
61 static int wavonly = FALSE;
62 static DWORD sbufsize = 0;
63 static int samples = 0; /* #samples to be in play buffer */
64 static UBYTE mixbuf[MIXBUFSIZE];
65 static DWORD bufpos = 0;
66
67 static LPDIRECTSOUND lpDS = NULL;
68 static LPDIRECTSOUNDBUFFER pDSB = NULL;
69
initsound_dx(void)70 static int initsound_dx(void)
71 {
72 DWORD i;
73 int err;
74 DSBUFFERDESC dsBD =
75 {0};
76 WAVEFORMATEX wfx;
77 DSBCAPS bc;
78
79 LPVOID pMem1, pMem2;
80 DWORD dwSize1, dwSize2;
81
82 if (issound != SOUND_NONE)
83 return 0;
84
85 if ((err = DirectSoundCreate(NULL, &lpDS, NULL)) < 0)
86 return err;
87 if ((err = IDirectSound_SetCooperativeLevel(lpDS, hWndMain,
88 DSSCL_EXCLUSIVE)) < 0)
89 goto end;
90
91 dsBD.dwSize = sizeof(dsBD);
92 dsBD.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
93 dsBD.dwBufferBytes = 0x4000;
94 dsBD.lpwfxFormat = &wfx;
95
96 wfx.wFormatTag = WAVE_FORMAT_PCM;
97 #ifdef STEREO_SOUND
98 wfx.nChannels = POKEYSND_stereo_enabled ? 2 : 1;
99 #else
100 wfx.nChannels = 1;
101 #endif /* STEREO_SOUND */
102 wfx.nSamplesPerSec = dsprate;
103 wfx.nAvgBytesPerSec = dsprate * wfx.nChannels * (bit16 ? 2 : 1);
104 wfx.nBlockAlign = wfx.nChannels * (bit16 ? 2 : 1);
105 wfx.wBitsPerSample = bit16 ? 16 : 8;
106 wfx.cbSize = 0;
107
108 if ((err = IDirectSound_CreateSoundBuffer(lpDS, &dsBD, &pDSB, NULL)) < 0)
109 goto end;
110
111 bc.dwSize = sizeof(bc);
112 IDirectSoundBuffer_GetCaps(pDSB, &bc);
113 sbufsize = bc.dwBufferBytes;
114
115 if ((err = IDirectSoundBuffer_Lock(pDSB, 0, sbufsize,
116 &pMem1, &dwSize1, &pMem2, &dwSize2,
117 DSBLOCK_FROMWRITECURSOR)) < 0)
118 goto end;
119
120 for (i = 0; i < dwSize1 >> 1; i++)
121 *((short *) pMem1 + i) = 0;
122 for (i = 0; i < dwSize2 >> 1; i++)
123 *((short *) pMem2 + i) = 0;
124 IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
125
126 IDirectSoundBuffer_Play(pDSB, 0, 0, DSBPLAY_LOOPING);
127
128 POKEYSND_Init(POKEYSND_FREQ_17_EXACT, (UWORD) dsprate, (UBYTE) wfx.nChannels, (bit16 ? POKEYSND_BIT16 : 0));
129
130 samples = dsprate * snddelay / 1000;
131
132 IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &bufpos);
133
134 issound = SOUND_DX;
135 return 0;
136
137 end:
138 MessageBox(hWndMain, "DirectSound Init FAILED", myname, MB_OK);
139 Sound_Exit();
140 return err;
141 }
142
uninitsound_dx(void)143 static void uninitsound_dx(void)
144 {
145 if (issound != SOUND_DX)
146 return;
147
148 if (lpDS)
149 {
150 if (pDSB)
151 {
152 IDirectSoundBuffer_Stop(pDSB);
153 IDirectSoundBuffer_Release(pDSB);
154 pDSB = NULL;
155 }
156 IDirectSound_Release(lpDS);
157 lpDS = NULL;
158 }
159 issound = SOUND_NONE;
160 }
161
sound_update_dx(void)162 static void sound_update_dx(void)
163 {
164 DWORD wc;
165 int d1;
166 LPVOID pMem1, pMem2;
167 DWORD dwSize1, dwSize2;
168 signed short *p1s;
169 signed short *p2s;
170 UBYTE *p1b;
171 UBYTE *p2b;
172 signed short *pbufs;
173 UBYTE *pbufb;
174 int s1, s2;
175 int err;
176 int i;
177 int samplesize = bit16 ? 2 : 1;
178
179 #ifdef STEREO_SOUND
180 if (POKEYSND_stereo_enabled) samplesize *= 2;
181 #endif
182
183 if (issound != SOUND_DX)
184 return;
185
186 IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &wc);
187
188 d1 = (wc - bufpos);
189 if ((DWORD) abs(d1) > (sbufsize >> 1)) {
190 if (d1 < 0)
191 d1 += sbufsize;
192 else
193 d1 -= sbufsize;
194 }
195 if (d1 < (-samples * samplesize)) // there is more than necessary bytes filled?
196 return;
197
198 d1 = (samples * samplesize) + d1; // bytes to fill
199 d1 = (d1 / samplesize) * samplesize; //round to a sample pair
200
201 if (d1 > (sizeof(mixbuf) / sizeof(mixbuf[0])))
202 {
203 d1 = (sizeof(mixbuf) / sizeof(mixbuf[0]));
204 }
205
206 if ((err = IDirectSoundBuffer_Lock(pDSB,
207 bufpos,
208 d1,
209 &pMem1, &dwSize1,
210 &pMem2, &dwSize2,
211 0)) < 0)
212 {
213 if (err == DSERR_BUFFERLOST)
214 Sound_Continue();
215 return;
216 }
217
218 s1 = dwSize1;
219 s2 = dwSize2;
220 p1s = (signed short *)pMem1;
221 p2s = (signed short *)pMem2;
222 p1b = (UBYTE *)pMem1;
223 p2b = (UBYTE *)pMem2;
224 bufpos += (s1 + s2);
225 if (bufpos >= sbufsize)
226 bufpos -= sbufsize;
227
228 if (bit16)
229 {
230 s1 /= 2;
231 s2 /= 2;
232 }
233 i = s1 + s2;
234
235 POKEYSND_Process(mixbuf, i);
236
237 pbufs = (signed short *)mixbuf;
238 pbufb = (UBYTE *)mixbuf;
239 if (s1)
240 {
241 if (bit16)
242 for (; s1; s1--)
243 *p1s++ = *pbufs++;
244 else
245 for (; s1; s1--)
246 *p1b++ = *pbufb++;
247 }
248 if (s2)
249 {
250 if (bit16)
251 for (; s2; s2--)
252 *p2s++ = *pbufs++;
253 else
254 for (; s2; s2--)
255 *p2b++ = *pbufb++;
256 }
257
258 IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
259
260 return;
261 }
262 #endif /* DIRECTX */
263
uninitsound_wav(void)264 static void uninitsound_wav(void)
265 {
266 int i;
267 MMRESULT err;
268
269 if (issound != SOUND_WAV)
270 return;
271
272 l0:
273 for (i = 0; i < buffers; i++)
274 {
275 if (!(waves[i].dwFlags & WHDR_DONE))
276 {
277 WaitForSingleObject(event, 5000);
278 ResetEvent(event);
279 goto l0;
280 }
281 }
282
283 waveOutReset (wout);
284
285 for (i = 0; i < buffers; i++)
286 {
287 err = waveOutUnprepareHeader(wout, &waves[i], sizeof (waves[i]));
288 if (err != MMSYSERR_NOERROR)
289 {
290 fprintf(stderr, "warning: cannot unprepare wave header (%x)\n", err);
291 }
292 free(waves[i].lpData);
293 }
294 free(waves);
295
296 waveOutClose(wout);
297 CloseHandle(event);
298
299 issound = SOUND_NONE;
300 }
301
initsound_wav(void)302 static int initsound_wav(void)
303 {
304 int i;
305 WAVEFORMATEX wfx;
306 MMRESULT err;
307
308 event = CreateEvent(NULL, TRUE, FALSE, NULL);
309
310 memset(&wfx, 0, sizeof(wfx));
311
312 wfx.wFormatTag = WAVE_FORMAT_PCM;
313 #ifdef STEREO_SOUND
314 wfx.nChannels = POKEYSND_stereo_enabled ? 2 : 1;
315 #else
316 wfx.nChannels = 1;
317 #endif /* STEREO_SOUND */
318 wfx.nSamplesPerSec = dsprate;
319 wfx.nAvgBytesPerSec = dsprate * wfx.nChannels * (bit16 ? 2 : 1);
320 wfx.nBlockAlign = wfx.nChannels * (bit16 ? 2 : 1);
321 wfx.wBitsPerSample = bit16 ? 16 : 8;
322 wfx.cbSize = 0;
323
324 err = waveOutOpen(&wout, WAVE_MAPPER, &wfx, (int)event, 0, CALLBACK_EVENT);
325 if (err == WAVERR_BADFORMAT)
326 {
327 Log_print("wave output parameters unsupported\n");
328 exit(1);
329 }
330 if (err != MMSYSERR_NOERROR)
331 {
332 Log_print("cannot open wave output (%x)\n", err);
333 exit(1);
334 }
335
336 buffers = ((wfx.nAvgBytesPerSec * snddelaywav / 1000) >> WAVSHIFT) + 1;
337 waves = malloc(buffers * sizeof(*waves));
338 for (i = 0; i < buffers; i++)
339 {
340 memset(&waves[i], 0, sizeof (waves[i]));
341 if (!(waves[i].lpData = (UBYTE *)malloc(WAVSIZE)))
342 {
343 Log_print("could not get wave buffer memory\n");
344 exit(1);
345 }
346 waves[i].dwBufferLength = WAVSIZE;
347 err = waveOutPrepareHeader(wout, &waves[i], sizeof(waves[i]));
348 if (err != MMSYSERR_NOERROR)
349 {
350 Log_print("cannot prepare wave header (%x)\n", err);
351 exit(1);
352 }
353 waves[i].dwFlags |= WHDR_DONE;
354 }
355
356 POKEYSND_Init(POKEYSND_FREQ_17_EXACT, (UWORD) dsprate, (UBYTE) wfx.nChannels, (bit16 ? POKEYSND_BIT16 : 0));
357
358 issound = SOUND_WAV;
359 return 0;
360 }
361
Sound_Initialise(int * argc,char * argv[])362 int Sound_Initialise(int *argc, char *argv[])
363 {
364 int i, j;
365 int help = FALSE;
366
367 if (issound != SOUND_NONE)
368 return TRUE;
369
370 for (i = j = 1; i < *argc; i++)
371 {
372 int i_a = (i + 1 < *argc); /* is argument available? */
373 int a_m = FALSE; /* error, argument missing! */
374
375 if (strcmp(argv[i], "-sound") == 0)
376 usesound = TRUE;
377 else if (strcmp(argv[i], "-nosound") == 0)
378 usesound = FALSE;
379 else if (strcmp(argv[i], "-audio16") == 0)
380 bit16 = TRUE;
381 else if (strcmp(argv[i], "-dsprate") == 0)
382 {
383 if (i_a)
384 sscanf(argv[++i], "%d", &dsprate);
385 else a_m = TRUE;
386 }
387 else if (strcmp(argv[i], "-snddelay") == 0)
388 {
389 if (i_a)
390 {
391 sscanf(argv[++i], "%d", &snddelay);
392 snddelaywav = snddelay;
393 }
394 else a_m = TRUE;
395 }
396 else if (strcmp(argv[i], "-quality") == 0) {
397 if (i_a) {
398 int quality;
399 sscanf(argv[++i], "%d", &quality);
400 if (quality > 1) {
401 POKEYSND_SetMzQuality(quality - 1);
402 POKEYSND_enable_new_pokey = 1;
403 }
404 else
405 POKEYSND_enable_new_pokey = 0;
406 }
407 else a_m = TRUE;
408 }
409 #ifdef DIRECTX
410 else if (strcmp(argv[i], "-wavonly") == 0)
411 wavonly = TRUE;
412 #endif
413 else
414 {
415 if (strcmp(argv[i], "-help") == 0)
416 {
417 Log_print("\t-sound Enable sound\n"
418 "\t-nosound Disable sound\n"
419 #ifdef DIRECTX
420 "\t-wavonly Disable direct sound\n"
421 #endif
422 "\t-dsprate <rate> Set dsp rate\n"
423 "\t-snddelay <ms> Set sound delay\n"
424 "\t-audio16 Use 16 bit mixing\n"
425 "\t-quality <level> Set sound quality"
426 );
427 help = TRUE;
428 }
429 argv[j++] = argv[i];
430 }
431
432 if (a_m) {
433 Log_print("Missing argument for '%s'", argv[i]);
434 usesound = FALSE;
435 return FALSE;
436 }
437 }
438 *argc = j;
439
440 if (help || !usesound) {
441 usesound = FALSE;
442 return TRUE;
443 }
444
445 #ifdef DIRECTX
446 if (!wavonly)
447 {
448 i = initsound_dx();
449 if (!i)
450 return TRUE;
451 }
452 #endif
453 initsound_wav();
454 return TRUE;
455 }
456
Sound_Reinit(void)457 void Sound_Reinit(void)
458 {
459 if (usesound)
460 {
461 Sound_Exit();
462 #ifdef DIRECTX
463 if (!wavonly)
464 {
465 int i = initsound_dx();
466 if (!i)
467 return;
468 }
469 #endif
470 initsound_wav();
471 }
472 return;
473 }
474
Sound_Exit(void)475 void Sound_Exit(void)
476 {
477 #ifdef DIRECTX
478 if (issound == SOUND_DX)
479 uninitsound_dx();
480 #endif
481 if (issound == SOUND_WAV)
482 uninitsound_wav();
483
484 issound = SOUND_NONE;
485 }
486
getwave(void)487 static WAVEHDR *getwave(void)
488 {
489 int i;
490
491 for (i = 0; i < buffers; i++)
492 {
493 if (waves[i].dwFlags & WHDR_DONE)
494 {
495 waves[i].dwFlags &= ~WHDR_DONE;
496 return &waves[i];
497 }
498 }
499
500 return NULL;
501 }
502
Sound_Update(void)503 void Sound_Update(void)
504 {
505 MMRESULT err;
506 WAVEHDR *wh;
507
508 switch (issound)
509 {
510 case SOUND_WAV:
511 while ((wh = getwave()))
512 {
513 POKEYSND_Process(wh->lpData, wh->dwBufferLength >> (bit16 ? 1 : 0));
514 err = waveOutWrite(wout, wh, sizeof(*wh));
515 if (err != MMSYSERR_NOERROR)
516 {
517 Log_print("cannot write wave output (%x)\n", err);
518 return;
519 }
520 }
521 break;
522 #ifdef DIRECTX
523 case SOUND_DX:
524 sound_update_dx();
525 #endif
526 }
527 }
528
Sound_Pause(void)529 void Sound_Pause(void)
530 {
531 #ifdef DIRECTX
532 if (issound != SOUND_DX)
533 return;
534 if (!pDSB)
535 return;
536 IDirectSoundBuffer_Stop(pDSB);
537 #endif
538 }
539
Sound_Continue(void)540 void Sound_Continue(void)
541 {
542 #ifdef DIRECTX
543 if (issound != SOUND_DX)
544 return;
545 if (!pDSB)
546 return;
547 IDirectSoundBuffer_Restore(pDSB);
548 IDirectSoundBuffer_Play(pDSB, 0, 0, DSBPLAY_LOOPING);
549 IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &bufpos);
550 #endif
551 }
552
553 #endif /* SOUND */
554