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