Home | History | Annotate | Download | only in mint
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 (at) 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 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 = 0;
    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 }
    437