1 //
2 // GOATTRACKER sound routines
3 //
4
5 #define GSOUND_C
6
7 #ifdef __WIN32__
8 #include <windows.h>
9 #endif
10
11 #include "goattrk2.h"
12
13 // General / reSID output
14 int playspeed;
15 int usehardsid = 0;
16 int usecatweasel = 0;
17 int initted = 0;
18 int firsttimeinit = 1;
19 unsigned framerate = PALFRAMERATE;
20 Sint16 *buffer = NULL;
21 FILE *writehandle = NULL;
22
23 void sound_playrout(void);
24 void sound_mixer(Sint32 *dest, unsigned samples);
25 Uint32 sound_timer(Uint32 interval);
26
27 #ifdef __WIN32__
28
29 // Win32 HardSID output
30 typedef void (CALLBACK* lpWriteToHardSID)(Uint8 DeviceID, Uint8 SID_reg, Uint8 Data);
31 typedef Uint8 (CALLBACK* lpReadFromHardSID)(Uint8 DeviceID, Uint8 SID_reg);
32 typedef void (CALLBACK* lpInitHardSID_Mapper)(void);
33 typedef void (CALLBACK* lpMuteHardSID_Line)(int Mute);
34 typedef void (CALLBACK* lpHardSID_Delay)(Uint8 DeviceID, Uint16 Cycles);
35 typedef void (CALLBACK* lpHardSID_Write)(Uint8 DeviceID, Uint16 Cycles, Uint8 SID_reg, Uint8 Data);
36 typedef void (CALLBACK* lpHardSID_Flush)(Uint8 DeviceID);
37 typedef void (CALLBACK* lpHardSID_SoftFlush)(Uint8 DeviceID);
38 typedef boolean (CALLBACK* lpHardSID_Lock)(Uint8 DeviceID);
39 lpWriteToHardSID WriteToHardSID = NULL;
40 lpReadFromHardSID ReadFromHardSID = NULL;
41 lpInitHardSID_Mapper InitHardSID_Mapper = NULL;
42 lpMuteHardSID_Line MuteHardSID_Line = NULL;
43 lpHardSID_Delay HardSID_Delay = NULL;
44 lpHardSID_Write HardSID_Write = NULL;
45 lpHardSID_Flush HardSID_Flush = NULL;
46 lpHardSID_SoftFlush HardSID_SoftFlush = NULL;
47 lpHardSID_Lock HardSID_Lock = NULL;
48 HINSTANCE hardsiddll = 0;
49 int dll_initialized = FALSE;
50 // Cycle-exact HardSID support
51 int cycleexacthardsid = FALSE;
52 SDL_Thread* playerthread = NULL;
53 SDL_mutex* flushmutex = NULL;
54 volatile int runplayerthread = FALSE;
55 volatile int flushplayerthread = FALSE;
56 volatile int suspendplayroutine = FALSE;
57 int sound_thread(void *userdata);
58
59 void InitHardDLL(void);
60
61 // Win32 CatWeasel MK3 PCI output
62 #define SID_SID_PEEK_POKE CTL_CODE(FILE_DEVICE_SOUND,0x0800UL + 1,METHOD_BUFFERED,FILE_ANY_ACCESS)
63 HANDLE catweaselhandle;
64
65 #else
66
67 // Unix HardSID & CatWeasel output
68 int hardsidfd = -1;
69 int catweaselfd = -1;
70
71 #endif
72
sound_init(unsigned b,unsigned mr,unsigned writer,unsigned hardsid,unsigned m,unsigned ntsc,unsigned multiplier,unsigned catweasel,unsigned interpolate,unsigned customclockrate)73 int sound_init(unsigned b, unsigned mr, unsigned writer, unsigned hardsid, unsigned m, unsigned ntsc, unsigned multiplier, unsigned catweasel, unsigned interpolate, unsigned customclockrate)
74 {
75 int c;
76
77 #ifdef __WIN32__
78 if (!flushmutex)
79 flushmutex = SDL_CreateMutex();
80 #endif
81
82 sound_uninit();
83
84 if (multiplier)
85 {
86 if (ntsc)
87 {
88 framerate = NTSCFRAMERATE * multiplier;
89 snd_bpmtempo = 150 * multiplier;
90 }
91 else
92 {
93 framerate = PALFRAMERATE * multiplier;
94 snd_bpmtempo = 125 * multiplier;
95 }
96 }
97 else
98 {
99 if (ntsc)
100 {
101 framerate = NTSCFRAMERATE / 2;
102 snd_bpmtempo = 150 / 2;
103 }
104 else
105 {
106 framerate = PALFRAMERATE / 2;
107 snd_bpmtempo = 125 / 2;
108 }
109 }
110
111 if (hardsid)
112 {
113 #ifdef __WIN32__
114 InitHardDLL();
115 if (dll_initialized)
116 {
117 usehardsid = hardsid;
118 if (cycleexacthardsid) {
119 HardSID_Lock(usehardsid-1);
120 HardSID_Flush(usehardsid-1);
121 HardSID_Write(usehardsid-1, SIDWRITEDELAY, 0, 0x00);
122 Sleep(300);
123 }
124 for (c = 0; c < NUMSIDREGS; c++)
125 {
126 sidreg[c] = 0;
127 if (cycleexacthardsid) {
128 HardSID_Write(usehardsid-1, SIDWRITEDELAY, c, 0x00);
129 }
130 else {
131 WriteToHardSID(usehardsid-1, c, 0x00);
132 }
133 }
134 if (cycleexacthardsid) {
135 HardSID_SoftFlush(usehardsid-1);
136 }
137 else {
138 MuteHardSID_Line(FALSE);
139 }
140 }
141 else return 0;
142 if (!cycleexacthardsid)
143 {
144 SDL_SetTimer(1000 / framerate, sound_timer);
145 }
146 else
147 {
148 runplayerthread = TRUE;
149 playerthread = SDL_CreateThread(sound_thread, NULL);
150 if (!playerthread) return 0;
151 }
152 #else
153 char filename[80];
154 sprintf(filename, "/dev/sid%d", hardsid-1);
155 hardsidfd = open(filename, O_WRONLY, S_IREAD|S_IWRITE);
156 if (hardsidfd >= 0)
157 {
158 usehardsid = hardsid;
159 for (c = 0; c < NUMSIDREGS; c++)
160 {
161 Uint32 dataword = c << 8;
162 write(hardsidfd, &dataword, 4);
163 }
164 }
165 else return 0;
166 SDL_SetTimer(1000 / framerate, sound_timer);
167 #endif
168
169 goto SOUNDOK;
170 }
171
172 if (catweasel)
173 {
174 #ifdef __WIN32__
175 catweaselhandle = CreateFile("\\\\.\\SID6581_1", GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0L,
176 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0L);
177 if (catweaselhandle == INVALID_HANDLE_VALUE)
178 return 0;
179 #else
180 catweaselfd = open("/dev/sid", O_WRONLY);
181 if (catweaselfd < 0)
182 catweaselfd = open("/dev/misc/sid", O_WRONLY);
183 if (catweaselfd < 0)
184 return 0;
185 #ifndef __amigaos__
186 if (ntsc)
187 ioctl(catweaselfd, CWSID_IOCTL_NTSC);
188 else
189 ioctl(catweaselfd, CWSID_IOCTL_PAL);
190 #endif
191 #endif
192
193 usecatweasel = 1;
194 SDL_SetTimer(1000 / framerate, sound_timer);
195 goto SOUNDOK;
196 }
197
198 if (!buffer) buffer = (Sint16*)malloc(MIXBUFFERSIZE * sizeof(Sint16));
199 if (!buffer) return 0;
200
201 if (writer)
202 writehandle = fopen("sidaudio.raw", "wb");
203
204 playspeed = mr;
205 if (playspeed < MINMIXRATE) playspeed = MINMIXRATE;
206 if (playspeed > MAXMIXRATE) playspeed = MAXMIXRATE;
207 if (b < MINBUF) b = MINBUF;
208 if (b > MAXBUF) b = MAXBUF;
209
210 if (firsttimeinit)
211 {
212 if (!snd_init(mr, SIXTEENBIT|MONO, b, 1, 0)) return 0;
213 firsttimeinit = 0;
214 }
215 playspeed = snd_mixrate;
216 sid_init(playspeed, m, ntsc, interpolate & 1, customclockrate, interpolate >> 1);
217
218 snd_player = &sound_playrout;
219 snd_setcustommixer(sound_mixer);
220
221 SOUNDOK:
222 initted = 1;
223 return 1;
224 }
225
sound_uninit(void)226 void sound_uninit(void)
227 {
228 int c;
229
230 if (!initted) return;
231 initted = 0;
232
233 // Apparently a delay is needed to make sure the sound timer thread is
234 // not mixing stuff anymore, and we can safely delete related structures
235 SDL_Delay(50);
236
237 if (usehardsid || usecatweasel)
238 {
239 #ifdef __WIN32__
240 if (!playerthread)
241 {
242 SDL_SetTimer(0, NULL);
243 }
244 else
245 {
246 runplayerthread = FALSE;
247 SDL_WaitThread(playerthread, NULL);
248 playerthread = NULL;
249 }
250 #else
251 SDL_SetTimer(0, NULL);
252 #endif
253 }
254 else
255 {
256 snd_setcustommixer(NULL);
257 snd_player = NULL;
258 }
259
260 if (writehandle)
261 {
262 fclose(writehandle);
263 writehandle = NULL;
264 }
265
266 if (buffer)
267 {
268 free(buffer);
269 buffer = NULL;
270 }
271
272 if (usehardsid)
273 {
274 #ifdef __WIN32__
275 for (c = 0; c < NUMSIDREGS; c++)
276 {
277 if (cycleexacthardsid) {
278 HardSID_Write(usehardsid-1, SIDWRITEDELAY, c, 0x00);
279 }
280 else {
281 WriteToHardSID(usehardsid-1, c, 0x00);
282 }
283 }
284 MuteHardSID_Line(TRUE);
285 #else
286 if (hardsidfd >= 0)
287 {
288 for (c = 0; c < NUMSIDREGS; c++)
289 {
290 Uint32 dataword = c << 8;
291 write(hardsidfd, &dataword, 4);
292 }
293 close(hardsidfd);
294 hardsidfd = -1;
295 }
296 #endif
297 }
298
299 if (usecatweasel)
300 {
301 #ifdef __WIN32__
302 DWORD w;
303 unsigned char buf[NUMSIDREGS * 2];
304 for (w = 0; w < NUMSIDREGS; w++)
305 {
306 buf[w*2] = 0x18 - w;
307 buf[w*2+1] = 0;
308 }
309 DeviceIoControl(catweaselhandle, SID_SID_PEEK_POKE, buf, sizeof(buf), 0L, 0UL, &w, 0L);
310 CloseHandle(catweaselhandle);
311 catweaselhandle = INVALID_HANDLE_VALUE;
312 #else
313 if (catweaselfd >= 0)
314 {
315 unsigned char buf[NUMSIDREGS];
316 memset(buf, 0, sizeof(buf));
317 lseek(catweaselfd, 0, SEEK_SET);
318 write(catweaselfd, buf, sizeof(buf));
319 close(catweaselfd);
320 catweaselfd = -1;
321 }
322 #endif
323 }
324 }
325
sound_suspend(void)326 void sound_suspend(void)
327 {
328 #ifdef __WIN32__
329 SDL_LockMutex(flushmutex);
330 suspendplayroutine = TRUE;
331 SDL_UnlockMutex(flushmutex);
332 #endif
333 }
334
sound_flush(void)335 void sound_flush(void)
336 {
337 #ifdef __WIN32__
338 SDL_LockMutex(flushmutex);
339 flushplayerthread = TRUE;
340 SDL_UnlockMutex(flushmutex);
341 #endif
342 }
343
sound_timer(Uint32 interval)344 Uint32 sound_timer(Uint32 interval)
345 {
346 if (!initted) return interval;
347 sound_playrout();
348 return interval;
349 }
350
351 #ifdef __WIN32__
sound_thread(void * userdata)352 int sound_thread(void *userdata)
353 {
354 unsigned long flush_cycles_interactive = hardsidbufinteractive * 1000; /* 0 = flush off for interactive mode*/
355 unsigned long flush_cycles_playback = hardsidbufplayback * 1000; /* 0 = flush off for playback mode*/
356 unsigned long cycles_after_flush = 0;
357 boolean interactive;
358
359 while (runplayerthread)
360 {
361 unsigned cycles = 1000000 / framerate; // HardSID should be clocked at 1MHz
362 int c;
363
364 if (flush_cycles_interactive > 0 || flush_cycles_playback > 0)
365 {
366 cycles_after_flush += cycles;
367 }
368
369 // Do flush if starting playback, stopping playback, starting an interactive note etc.
370 if (flushplayerthread)
371 {
372 SDL_LockMutex(flushmutex);
373 if (HardSID_Flush)
374 {
375 HardSID_Flush(usehardsid-1);
376 }
377 // Can clear player suspend now (if set)
378 suspendplayroutine = FALSE;
379 flushplayerthread = FALSE;
380 SDL_UnlockMutex(flushmutex);
381
382 SDL_Delay(0);
383 }
384
385 if (!suspendplayroutine) playroutine();
386
387 interactive = !(boolean)recordmode /* jam mode */ || !(boolean)isplaying();
388
389 for (c = 0; c < NUMSIDREGS; c++)
390 {
391 unsigned o = sid_getorder(c);
392
393 HardSID_Write(usehardsid-1, SIDWRITEDELAY, o, sidreg[o]);
394 cycles -= SIDWRITEDELAY;
395 }
396
397 // Now wait the rest of frame
398 while (cycles)
399 {
400 unsigned runnow = cycles;
401 if (runnow > 65535) runnow = 65535;
402 HardSID_Delay(usehardsid-1, runnow);
403 cycles -= runnow;
404 }
405
406 if ((flush_cycles_interactive>0 && interactive && cycles_after_flush>=flush_cycles_interactive) ||
407 (flush_cycles_playback>0 && !interactive && cycles_after_flush>=flush_cycles_playback))
408 {
409 if (HardSID_SoftFlush)
410 HardSID_SoftFlush(usehardsid-1);
411 cycles_after_flush = 0;
412 }
413 }
414
415 unsigned r;
416
417 for (r = 0; r < NUMSIDREGS; r++)
418 {
419 HardSID_Write(usehardsid-1, SIDWRITEDELAY, r, 0);
420 }
421 if (HardSID_SoftFlush)
422 HardSID_SoftFlush(usehardsid-1);
423
424 return 0;
425 }
426 #endif
427
sound_playrout(void)428 void sound_playrout(void)
429 {
430 int c;
431
432 playroutine();
433 if (usehardsid)
434 {
435 #ifdef __WIN32__
436 for (c = 0; c < NUMSIDREGS; c++)
437 {
438 unsigned o = sid_getorder(c);
439 if (cycleexacthardsid) {
440 HardSID_Write(usehardsid-1, SIDWRITEDELAY, o, sidreg[o]);
441 }
442 else {
443 WriteToHardSID(usehardsid-1, o, sidreg[o]);
444 }
445 }
446 #else
447 for (c = 0; c < NUMSIDREGS; c++)
448 {
449 unsigned o = sid_getorder(c);
450 Uint32 dataword = (o << 8) | sidreg[o];
451 write(hardsidfd, &dataword, 4);
452 }
453 #endif
454 }
455 else if (usecatweasel)
456 {
457 #ifdef __WIN32__
458 DWORD w;
459 unsigned char buf[NUMSIDREGS * 2];
460
461 for(w = 0; w < NUMSIDREGS; w++)
462 {
463 unsigned o = sid_getorder(w);
464
465 buf[w*2] = o;
466 buf[w*2+1] = sidreg[o];
467 }
468 DeviceIoControl(catweaselhandle, SID_SID_PEEK_POKE, buf, sizeof(buf), 0L, 0UL, &w, 0L);
469 #else
470 for (c = 0; c < NUMSIDREGS; c++)
471 {
472 unsigned o = sid_getorder(c);
473
474 lseek(catweaselfd, o, SEEK_SET);
475 write(catweaselfd, &sidreg[o], 1);
476 }
477 #endif
478 }
479 }
480
sound_mixer(Sint32 * dest,unsigned samples)481 void sound_mixer(Sint32 *dest, unsigned samples)
482 {
483 int c;
484
485 if (!initted) return;
486 if (samples > MIXBUFFERSIZE) return;
487 if (!buffer) return;
488
489 sid_fillbuffer(buffer, samples);
490 if (writehandle)
491 fwrite(buffer, samples * sizeof(Uint16), 1, writehandle);
492
493 for (c = 0; c < samples; c++)
494 dest[c] = buffer[c];
495 }
496
497 #ifdef __WIN32__
InitHardDLL()498 void InitHardDLL()
499 {
500 if (!(hardsiddll=LoadLibrary("HARDSID.DLL"))) return;
501
502 WriteToHardSID = (lpWriteToHardSID) GetProcAddress(hardsiddll, "WriteToHardSID");
503 ReadFromHardSID = (lpReadFromHardSID) GetProcAddress(hardsiddll, "ReadFromHardSID");
504 InitHardSID_Mapper = (lpInitHardSID_Mapper) GetProcAddress(hardsiddll, "InitHardSID_Mapper");
505 MuteHardSID_Line = (lpMuteHardSID_Line) GetProcAddress(hardsiddll, "MuteHardSID_Line");
506
507 if (!WriteToHardSID) return;
508
509 // Try to get cycle-exact interface
510 HardSID_Delay = (lpHardSID_Delay) GetProcAddress(hardsiddll, "HardSID_Delay");
511 HardSID_Write = (lpHardSID_Write) GetProcAddress(hardsiddll, "HardSID_Write");
512 HardSID_Flush = (lpHardSID_Flush) GetProcAddress(hardsiddll, "HardSID_Flush");
513 HardSID_SoftFlush = (lpHardSID_SoftFlush) GetProcAddress(hardsiddll, "HardSID_SoftFlush");
514 HardSID_Lock = (lpHardSID_Lock) GetProcAddress(hardsiddll, "HardSID_Lock");
515 if ((HardSID_Delay) && (HardSID_Write) && (HardSID_Flush) && (HardSID_SoftFlush) && (HardSID_Lock))
516 cycleexacthardsid = TRUE;
517
518 InitHardSID_Mapper();
519 dll_initialized = TRUE;
520 }
521 #endif
522