SDL 1.2.13
[crawl:crawl-sdl.git] / src / audio / mint / SDL_mintaudio_gsxb.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2004 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 #include "SDL_config.h"
23
24 /*
25         MiNT audio driver
26         using XBIOS functions (GSXB compatible driver)
27
28         Patrice Mandin
29 */
30
31 /* Mint includes */
32 #include <mint/osbind.h>
33 #include <mint/falcon.h>
34 #include <mint/cookie.h>
35
36 #include "SDL_audio.h"
37 #include "../SDL_audio_c.h"
38 #include "../SDL_sysaudio.h"
39
40 #include "../../video/ataricommon/SDL_atarimxalloc_c.h"
41
42 #include "SDL_mintaudio.h"
43 #include "SDL_mintaudio_gsxb.h"
44
45 /*--- Defines ---*/
46
47 #define MINT_AUDIO_DRIVER_NAME "mint_gsxb"
48
49 /* Debug print info */
50 #define DEBUG_NAME "audio:gsxb: "
51 #if 0
52 #define DEBUG_PRINT(what) \
53         { \
54                 printf what; \
55         }
56 #else
57 #define DEBUG_PRINT(what)
58 #endif
59
60 /*--- Static variables ---*/
61
62 static unsigned long cookie_snd, cookie_gsxb;
63
64 /*--- Audio driver functions ---*/
65
66 static void Mint_CloseAudio(_THIS);
67 static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
68 static void Mint_LockAudio(_THIS);
69 static void Mint_UnlockAudio(_THIS);
70
71 /* To check/init hardware audio */
72 static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
73 static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
74
75 /* GSXB callbacks */
76 static void Mint_GsxbInterrupt(void);
77 static void Mint_GsxbNullInterrupt(void);
78
79 /*--- Audio driver bootstrap functions ---*/
80
81 static int Audio_Available(void)
82 {
83         const char *envr = SDL_getenv("SDL_AUDIODRIVER");
84
85         /* Check if user asked a different audio driver */
86         if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
87                 DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
88                 return(0);
89         }
90
91         /* Cookie _SND present ? if not, assume ST machine */
92         if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
93                 cookie_snd = SND_PSG;
94         }
95
96         /* Check if we have 16 bits audio */
97         if ((cookie_snd & SND_16BIT)==0) {
98                 DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n"));
99             return(0);
100         }
101
102         /* Cookie GSXB present ? */
103         cookie_gsxb = (Getcookie(C_GSXB, &cookie_gsxb) == C_FOUND);
104
105         /* Is it GSXB ? */
106         if (((cookie_snd & SND_GSXB)==0) || (cookie_gsxb==0)) {
107                 DEBUG_PRINT((DEBUG_NAME "no GSXB audio\n"));
108                 return(0);
109         }
110
111         /* Check if audio is lockable */
112         if (Locksnd()!=1) {
113                 DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n"));
114                 return(0);
115         }
116
117         Unlocksnd();
118
119         DEBUG_PRINT((DEBUG_NAME "GSXB audio available!\n"));
120         return(1);
121 }
122
123 static void Audio_DeleteDevice(SDL_AudioDevice *device)
124 {
125     SDL_free(device->hidden);
126     SDL_free(device);
127 }
128
129 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
130 {
131         SDL_AudioDevice *this;
132
133         /* Initialize all variables that we clean on shutdown */
134         this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
135     if ( this ) {
136         SDL_memset(this, 0, (sizeof *this));
137         this->hidden = (struct SDL_PrivateAudioData *)
138                 SDL_malloc((sizeof *this->hidden));
139     }
140     if ( (this == NULL) || (this->hidden == NULL) ) {
141         SDL_OutOfMemory();
142         if ( this ) {
143             SDL_free(this);
144         }
145         return(0);
146     }
147     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
148
149     /* Set the function pointers */
150     this->OpenAudio   = Mint_OpenAudio;
151     this->CloseAudio  = Mint_CloseAudio;
152     this->LockAudio   = Mint_LockAudio;
153     this->UnlockAudio = Mint_UnlockAudio;
154     this->free        = Audio_DeleteDevice;
155
156     return this;
157 }
158
159 AudioBootStrap MINTAUDIO_GSXB_bootstrap = {
160         MINT_AUDIO_DRIVER_NAME, "MiNT GSXB audio driver",
161         Audio_Available, Audio_CreateDevice
162 };
163
164 static void Mint_LockAudio(_THIS)
165 {
166         /* Stop replay */
167         Buffoper(0);
168 }
169
170 static void Mint_UnlockAudio(_THIS)
171 {
172         /* Restart replay */
173         Buffoper(SB_PLA_ENA|SB_PLA_RPT);
174 }
175
176 static void Mint_CloseAudio(_THIS)
177 {
178         /* Stop replay */
179         Buffoper(0);
180
181         /* Uninstall interrupt */
182         if (NSetinterrupt(2, SI_NONE, Mint_GsxbNullInterrupt)<0) {
183                 DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed in close\n"));
184         }
185
186         /* Wait if currently playing sound */
187         while (SDL_MintAudio_mutex != 0) {
188         }
189
190         /* Clear buffers */
191         if (SDL_MintAudio_audiobuf[0]) {
192                 Mfree(SDL_MintAudio_audiobuf[0]);
193                 SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
194         }
195
196         /* Unlock sound system */
197         Unlocksnd();
198 }
199
200 static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
201 {
202         long snd_format;
203         int i, resolution, format_signed, format_bigendian;
204     Uint16 test_format = SDL_FirstAudioFormat(spec->format);
205     int valid_datatype = 0;
206
207         resolution = spec->format & 0x00ff;
208         format_signed = ((spec->format & 0x8000)!=0);
209         format_bigendian = ((spec->format & 0x1000)!=0);
210
211         DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
212         DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
213         DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
214         DEBUG_PRINT(("channels=%d, ", spec->channels));
215         DEBUG_PRINT(("freq=%d\n", spec->freq));
216
217     if (spec->channels > 2) {
218         spec->channels = 2;  /* no more than stereo! */
219     }
220
221     while ((!valid_datatype) && (test_format)) {
222         /* Check formats available */
223         snd_format = Sndstatus(SND_QUERYFORMATS);
224         spec->format = test_format;
225         resolution = spec->format & 0xff;
226         format_signed = (spec->format & (1<<15));
227         format_bigendian = (spec->format & (1<<12));
228         switch (test_format) {
229             case AUDIO_U8:
230             case AUDIO_S8:
231                 if (snd_format & SND_FORMAT8) {
232                     valid_datatype = 1;
233                     snd_format = Sndstatus(SND_QUERY8BIT);
234                 }
235                 break;
236
237             case AUDIO_U16LSB:
238             case AUDIO_S16LSB:
239             case AUDIO_U16MSB:
240             case AUDIO_S16MSB:
241                 if (snd_format & SND_FORMAT16) {
242                     valid_datatype = 1;
243                     snd_format = Sndstatus(SND_QUERY16BIT);
244                 }
245                 break;
246
247             default:
248                 test_format = SDL_NextAudioFormat();
249                 break;
250         }
251     }
252
253     if (!valid_datatype) {
254         SDL_SetError("Unsupported audio format");
255         return (-1);
256     }
257
258         /* Check signed/unsigned format */
259         if (format_signed) {
260                 if (snd_format & SND_FORMATSIGNED) {
261                         /* Ok */
262                 } else if (snd_format & SND_FORMATUNSIGNED) {
263                         /* Give unsigned format */
264                         spec->format = spec->format & (~0x8000);
265                 }
266         } else {
267                 if (snd_format & SND_FORMATUNSIGNED) {
268                         /* Ok */
269                 } else if (snd_format & SND_FORMATSIGNED) {
270                         /* Give signed format */
271                         spec->format |= 0x8000;
272                 }
273         }
274
275         if (format_bigendian) {
276                 if (snd_format & SND_FORMATBIGENDIAN) {
277                         /* Ok */
278                 } else if (snd_format & SND_FORMATLITTLEENDIAN) {
279                         /* Give little endian format */
280                         spec->format = spec->format & (~0x1000);
281                 }
282         } else {
283                 if (snd_format & SND_FORMATLITTLEENDIAN) {
284                         /* Ok */
285                 } else if (snd_format & SND_FORMATBIGENDIAN) {
286                         /* Give big endian format */
287                         spec->format |= 0x1000;
288                 }
289         }
290         
291         /* Calculate and select the closest frequency */
292         MINTAUDIO_freqcount=0;
293         for (i=1;i<4;i++) {
294                 SDL_MintAudio_AddFrequency(this,
295                         MASTERCLOCK_44K/(MASTERPREDIV_MILAN*(1<<i)), MASTERCLOCK_44K,
296                         (1<<i)-1, -1);
297         }
298
299 #if 1
300         for (i=0; i<MINTAUDIO_freqcount; i++) {
301                 DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
302                         i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
303                         MINTAUDIO_frequencies[i].predivisor
304                 ));
305         }
306 #endif
307
308         MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
309         spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
310
311         DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
312         DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
313         DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
314         DEBUG_PRINT(("channels=%d, ", spec->channels));
315         DEBUG_PRINT(("freq=%d\n", spec->freq));
316
317         return 0;
318 }
319
320 static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
321 {
322         int channels_mode, prediv;
323         void *buffer;
324
325         /* Stop currently playing sound */
326         Buffoper(0);
327
328         /* Set replay tracks */
329         Settracks(0,0);
330         Setmontracks(0);
331
332         /* Select replay format */
333         switch (spec->format & 0xff) {
334                 case 8:
335                         if (spec->channels==2) {
336                                 channels_mode=STEREO8;
337                         } else {
338                                 channels_mode=MONO8;
339                         }
340                         break;
341                 case 16:
342                         if (spec->channels==2) {
343                                 channels_mode=STEREO16;
344                         } else {
345                                 channels_mode=MONO16;
346                         }
347                         break;
348                 default:
349                         channels_mode=STEREO16;
350                         break;
351         }
352         if (Setmode(channels_mode)<0) {
353                 DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
354         }
355
356         prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
357         Devconnect(DMAPLAY, DAC, CLKEXT, prediv, 1);
358
359         /* Set buffer */
360         buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
361         if (Setbuffer(0, buffer, buffer + spec->size)<0) {
362                 DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
363         }
364         
365         /* Install interrupt */
366         if (NSetinterrupt(2, SI_PLAY, Mint_GsxbInterrupt)<0) {
367                 DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed\n"));
368         }
369
370         /* Go */
371         Buffoper(SB_PLA_ENA|SB_PLA_RPT);
372         DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
373 }
374
375 static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
376 {
377         /* Lock sound system */
378         if (Locksnd()!=1) {
379             SDL_SetError("Mint_OpenAudio: Audio system already in use");
380         return(-1);
381         }
382
383         SDL_MintAudio_device = this;
384
385         /* Check audio capabilities */
386         if (Mint_CheckAudio(this, spec)==-1) {
387                 return -1;
388         }
389
390         SDL_CalculateAudioSpec(spec);
391
392         /* Allocate memory for audio buffers in DMA-able RAM */
393         DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
394
395         SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
396         if (SDL_MintAudio_audiobuf[0]==NULL) {
397                 SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
398                 return (-1);
399         }
400         SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
401         SDL_MintAudio_numbuf=0;
402         SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
403         SDL_MintAudio_audiosize = spec->size;
404         SDL_MintAudio_mutex = 0;
405
406         DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
407         DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
408
409         SDL_MintAudio_CheckFpu();
410
411         /* Setup audio hardware */
412         Mint_InitAudio(this, spec);
413
414     return(1);  /* We don't use threaded audio */
415 }
416
417 static void Mint_GsxbInterrupt(void)
418 {
419         Uint8 *newbuf;
420
421         if (SDL_MintAudio_mutex)
422                 return;
423
424         SDL_MintAudio_mutex=1;
425
426         SDL_MintAudio_numbuf ^= 1;
427         SDL_MintAudio_Callback();
428         newbuf = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
429         Setbuffer(0, newbuf, newbuf + SDL_MintAudio_audiosize);
430
431         SDL_MintAudio_mutex=0;
432 }
433
434 static void Mint_GsxbNullInterrupt(void)
435 {
436 }