diff src/audio/mint/SDL_mintaudio.c @ 398:d219b0e02f5f

Added Atari audio support (thanks Patrice!)
author Sam Lantinga <slouken@libsdl.org>
date Mon, 10 Jun 2002 20:42:53 +0000
parents
children 0ce5a68278fd
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audio/mint/SDL_mintaudio.c	Mon Jun 10 20:42:53 2002 +0000
@@ -0,0 +1,628 @@
+/*
+ * MiNT audio driver
+ * 
+ * Patrice Mandin
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Mint includes */
+#include <mint/osbind.h>
+#include <mint/falcon.h>
+#include <sys/cookie.h>
+
+#include "SDL_endian.h"
+#include "SDL_audio.h"
+#include "SDL_audio_c.h"
+#include "SDL_audiomem.h"
+#include "SDL_sysaudio.h"
+
+#include "SDL_mintaudio.h"
+#include "SDL_mintaudiodma.h"
+#include "SDL_mintaudiogsxb.h"
+#include "SDL_mintaudiointerrupt_s.h"
+
+#include "SDL_atarimxalloc_c.h"
+
+/*--- Defines ---*/
+
+#define MINT_AUDIO_DRIVER_NAME "mint"
+
+/* Master clocks for replay frequencies */
+#define MASTERCLOCK_STE		8010666		/* Not sure of this one */
+#define MASTERCLOCK_TT		16107953	/* Not sure of this one */
+#define MASTERCLOCK_FALCON1	25175000
+#define MASTERCLOCK_FALCON2	32000000	/* Only usable for DSP56K */
+#define MASTERCLOCK_FALCONEXT	-1		/* Clock on DSP56K port, unknown */
+#define MASTERCLOCK_MILAN1	22579200	/* Standard clock for 44.1 Khz */
+#define MASTERCLOCK_MILAN2	24576000	/* Standard clock for 48 Khz */
+
+/* Master clock predivisors */
+#define MASTERPREDIV_STE	160
+#define MASTERPREDIV_TT		320
+#define MASTERPREDIV_FALCON	256
+#define MASTERPREDIV_MILAN	256
+
+/* Values>>16 in _MCH cookie */
+enum {
+	MCH_ST=0,
+	MCH_STE,
+	MCH_TT,
+	MCH_F30
+};
+
+/* MFP 68901 interrupt sources */
+enum {
+	MFP_PARALLEL=0,
+	MFP_DCD,
+	MFP_CTS,
+	MFP_BITBLT,
+	MFP_TIMERD,
+	MFP_BAUDRATE=MFP_TIMERD,
+	MFP_TIMERC,
+	MFP_200HZ=MFP_TIMERC,
+	MFP_ACIA,
+	MFP_DISK,
+	MFP_TIMERB,
+	MFP_HBLANK=MFP_TIMERB,
+	MFP_TERR,
+	MFP_TBE,
+	MFP_RERR,
+	MFP_RBF,
+	MFP_TIMERA,
+	MFP_DMASOUND=MFP_TIMERA,
+	MFP_RING,
+	MFP_MONODETECT
+};
+
+/* Xbtimer() timers */
+enum {
+	XB_TIMERA=0,
+	XB_TIMERB,
+	XB_TIMERC,
+	XB_TIMERD
+};
+
+/*--- Static variables ---*/
+
+static unsigned long cookie_snd, cookie_mch, cookie_gsxb;
+static Uint16 hardfreq[16];
+static Uint16 numfreq;
+static SDL_AudioDevice *SDL_MintAudio_device;
+
+/*--- Audio driver functions ---*/
+
+static void Mint_CloseAudio(_THIS);
+static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
+static void Mint_LockAudio(_THIS);
+static void Mint_UnlockAudio(_THIS);
+
+/*--- Audio driver bootstrap functions ---*/
+
+static int Audio_Available(void)
+{
+	const char *envr = getenv("SDL_AUDIODRIVER");
+
+	/* Check if user asked a different audio driver */
+	if ((envr) && (strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
+		return 0;
+	}
+
+	/* Cookie _SND present ? if not, assume ST machine */
+	if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
+		cookie_snd = SND_PSG;
+	}
+
+	/* Cookie _MCH present ? if not, assume ST machine */
+	if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
+		cookie_mch = MCH_ST << 16;
+	}
+
+	/* Cookie GSXB present ? */
+	cookie_gsxb = (Getcookie(C_GSXB, &cookie_gsxb) == C_FOUND);
+
+	/* Check if we have xbios functions (Falcon, clones) */
+	if ((cookie_snd & SND_16BIT)!=0) {
+		/* Check if audio is lockable */
+		if (Locksnd()==1) {
+			Unlocksnd();
+		} else {
+			/* Already in use */
+			return(0);
+		}
+
+		return(1);
+	}
+
+	/* Check if we have 8 bits DMA audio (STE, TT) */
+	if ((cookie_snd & SND_8BIT)!=0) {
+		return(1);
+	}
+
+    return(0);
+}
+
+static void Audio_DeleteDevice(SDL_AudioDevice *device)
+{
+    free(device->hidden);
+    free(device);
+}
+
+static SDL_AudioDevice *Audio_CreateDevice(int devindex)
+{
+    SDL_AudioDevice *this;
+
+    /* Initialize all variables that we clean on shutdown */
+    this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
+    if ( this ) {
+        memset(this, 0, (sizeof *this));
+    }
+    if ( this == NULL ) {
+        SDL_OutOfMemory();
+        if ( this ) {
+            free(this);
+        }
+        return(0);
+    }
+
+    /* Set the function pointers */
+    this->OpenAudio   = Mint_OpenAudio;
+    this->CloseAudio  = Mint_CloseAudio;
+    this->LockAudio   = Mint_LockAudio;
+    this->UnlockAudio = Mint_UnlockAudio;
+    this->free        = Audio_DeleteDevice;
+
+    return this;
+}
+
+AudioBootStrap MINTAUDIO_bootstrap = {
+	MINT_AUDIO_DRIVER_NAME, "MiNT audio driver",
+	Audio_Available, Audio_CreateDevice
+};
+
+static void Mint_LockAudio(_THIS)
+{
+	void *oldpile;
+
+	/* Stop replay */
+	if ((cookie_snd & SND_16BIT)!=0) {
+		Buffoper(0);
+	} else if ((cookie_snd & SND_8BIT)!=0) {
+		oldpile=(void *)Super(0);
+		DMAAUDIO_IO.control=0;
+		Super(oldpile);
+	}
+}
+
+static void Mint_UnlockAudio(_THIS)
+{
+	void *oldpile;
+
+	/* Restart replay */
+	if ((cookie_snd & SND_16BIT)!=0) {
+		Buffoper(SB_PLA_ENA|SB_PLA_RPT);
+	} else if ((cookie_snd & SND_8BIT)!=0) {
+		oldpile=(void *)Super(0);
+		DMAAUDIO_IO.control=3;
+		Super(oldpile);
+	}
+}
+
+/* This is called from the interrupt routine */
+void SDL_MintAudio_Callback(void)
+{
+	SDL_AudioDevice *audio;
+	Uint8 *buffer;
+
+	audio = SDL_MintAudio_device;
+ 	buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
+
+	if ( ! audio->paused ) {
+		if ( audio->convert.needed ) {
+			audio->spec.callback(audio->spec.userdata,
+				(Uint8 *)audio->convert.buf,audio->convert.len);
+			SDL_ConvertAudio(&audio->convert);
+			memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
+		} else {
+			audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
+		}
+	}
+}
+
+static void Mint_StopAudio_Dma8(void)
+{
+	void *oldpile;
+	
+	oldpile=(void *)Super(0);
+	DMAAUDIO_IO.control=0;
+	Super(oldpile);
+
+	Jdisint(MFP_DMASOUND);
+}
+
+static void Mint_StopAudio_Xbios(void)
+{
+	Buffoper(0);
+	Jdisint(MFP_DMASOUND);
+}
+
+static void Mint_StopAudio_Gsxb(void)
+{
+	Buffoper(0);
+}
+
+static void Mint_CloseAudio(_THIS)
+{
+	if (cookie_gsxb && ((cookie_snd & (SND_GSXB|SND_16BIT))==(SND_GSXB|SND_16BIT)) ) {
+		Mint_StopAudio_Gsxb();
+	} else if ((cookie_snd & SND_16BIT)!=0) {
+		Mint_StopAudio_Xbios();
+	} else if ((cookie_snd & SND_8BIT)!=0) {
+		Mint_StopAudio_Dma8();
+	}
+
+	/* Clear buffers */
+	if (SDL_MintAudio_audiobuf[0]) {
+		Mfree(SDL_MintAudio_audiobuf[0]);
+		SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
+	}
+
+	/* Unlock sound system */
+	if ((cookie_snd & SND_16BIT)!=0) {
+		Unlocksnd();
+	}
+}
+
+static void Mint_CheckAudio_Dma8(SDL_AudioSpec *spec)
+{
+	int i;
+
+	spec->format = AUDIO_S8;
+
+	switch(cookie_mch>>16) {
+		case MCH_STE:
+			/* STE replay frequencies */
+			for (i=0;i<4;i++) {
+				hardfreq[i]=MASTERCLOCK_STE/(MASTERPREDIV_STE*(i+1));
+			}
+
+			if (spec->freq>=(hardfreq[0]+hardfreq[1])>>1) {
+				numfreq=3;		/* 50066 */
+			} else if (spec->freq>=(hardfreq[1]+hardfreq[2])>>1) {
+				numfreq=2;		/* 25033 */
+			} else if (spec->freq>=(hardfreq[2]+hardfreq[3])>>1) {
+				numfreq=1;		/* 12517 */
+			} else {
+				numfreq=0;		/* 6258 */
+			}
+
+			spec->freq=hardfreq[numfreq];
+			break;
+		case MCH_TT:
+			/* TT replay frequencies */
+			for (i=0;i<4;i++) {
+				hardfreq[i]=MASTERCLOCK_TT/(MASTERPREDIV_TT*(i+1));
+			}
+
+			if (spec->freq>=(hardfreq[0]+hardfreq[1])>>1) {
+				numfreq=3;		/* 50337 */
+			} else if (spec->freq>=(hardfreq[1]+hardfreq[2])>>1) {
+				numfreq=2;		/* 25169 */
+			} else if (spec->freq>=(hardfreq[2]+hardfreq[3])>>1) {
+				numfreq=1;		/* 12584 */
+			} else {
+				numfreq=0;		/* 6292 */
+			}
+			spec->freq=hardfreq[numfreq];
+			break;
+	}
+}
+
+static void Mint_CheckAudio_Xbios(SDL_AudioSpec *spec)
+{
+	int i;
+
+	/* Check conversions needed */
+	switch (spec->format & 0xff) {
+		case 8:
+			spec->format = AUDIO_S8;
+			break;
+		case 16:
+			spec->format = AUDIO_S16MSB;
+			break;
+	}
+	
+	/* Check hardware channels */
+	if ((spec->channels==1) && ((spec->format & 0xff)==16)) {
+		spec->channels=2;
+	}
+
+	/* Falcon replay frequencies */
+	for (i=0;i<16;i++) {
+		hardfreq[i]=MASTERCLOCK_FALCON1/(MASTERPREDIV_FALCON*(i+1));
+	}
+
+	/* The Falcon CODEC only support some frequencies */
+	if (spec->freq>=(hardfreq[CLK50K]+hardfreq[CLK33K])>>1) {
+		numfreq=CLK50K;		/* 49170 */
+	} else if (spec->freq>=(hardfreq[CLK33K]+hardfreq[CLK25K])>>1) {
+		numfreq=CLK33K;		/* 32780 */
+	} else if (spec->freq>=(hardfreq[CLK25K]+hardfreq[CLK20K])>>1) {
+		numfreq=CLK25K;		/* 24585 */
+	} else if (spec->freq>=(hardfreq[CLK20K]+hardfreq[CLK16K])>>1) {
+		numfreq=CLK20K;		/* 19668 */
+	} else if (spec->freq>=(hardfreq[CLK16K]+hardfreq[CLK12K])>>1) {
+		numfreq=CLK16K;		/* 16390 */
+	} else if (spec->freq>=(hardfreq[CLK12K]+hardfreq[CLK10K])>>1) {
+		numfreq=CLK12K;		/* 12292 */
+	} else if (spec->freq>=(hardfreq[CLK10K]+hardfreq[CLK8K])>>1) {
+		numfreq=CLK10K;		/* 9834 */
+	} else {
+		numfreq=CLK8K;		/* 8195 */
+	}				
+
+	spec->freq=hardfreq[numfreq];
+}
+
+static int Mint_CheckAudio_Gsxb(SDL_AudioSpec *spec)
+{
+	long snd_format;
+	int i, resolution, format_signed, format_bigendian;
+
+	resolution = spec->format & 0x00ff;
+	format_signed = ((spec->format & 0x8000)!=0);
+	format_bigendian = ((spec->format & 0x1000)!=0);
+
+	/* Check formats available */
+	snd_format = Sndstatus(SND_QUERYFORMATS);
+	switch (resolution) {
+		case 8:
+			if ((snd_format & SND_FORMAT8)==0) {
+				SDL_SetError("Mint_CheckAudio: 8 bits samples not supported");
+				return -1;
+			}
+			snd_format = Sndstatus(SND_QUERY8BIT);
+			break;
+		case 16:
+			if ((snd_format & SND_FORMAT16)==0) {
+				SDL_SetError("Mint_CheckAudio: 16 bits samples not supported");
+				return -1;
+			}
+			snd_format = Sndstatus(SND_QUERY16BIT);
+			break;
+		default:
+			SDL_SetError("Mint_CheckAudio: Unsupported sample resolution");
+			return -1;
+			break;
+	}
+
+	/* Check signed/unsigned format */
+	if (format_signed) {
+		if (snd_format & SND_FORMATSIGNED) {
+			/* Ok */
+		} else if (snd_format & SND_FORMATUNSIGNED) {
+			/* Give unsigned format */
+			spec->format = spec->format & (~0x8000);
+		}
+	} else {
+		if (snd_format & SND_FORMATUNSIGNED) {
+			/* Ok */
+		} else if (snd_format & SND_FORMATSIGNED) {
+			/* Give signed format */
+			spec->format |= 0x8000;
+		}
+	}
+
+	if (format_bigendian) {
+		if (snd_format & SND_FORMATBIGENDIAN) {
+			/* Ok */
+		} else if (snd_format & SND_FORMATLITTLEENDIAN) {
+			/* Give little endian format */
+			spec->format = spec->format & (~0x1000);
+		}
+	} else {
+		if (snd_format & SND_FORMATBIGENDIAN) {
+			/* Ok */
+		} else if (snd_format & SND_FORMATLITTLEENDIAN) {
+			/* Give big endian format */
+			spec->format |= 0x1000;
+		}
+	}
+	
+	/* Only xbios functions available = clone with PC board */
+	for (i=0;i<8;i++) {
+		hardfreq[i]=MASTERCLOCK_MILAN1/(MASTERPREDIV_MILAN*(i+1));
+	}
+
+	if (spec->freq>=(hardfreq[CLK_44K]+hardfreq[CLK_22K])>>1) {
+		numfreq = CLK_44K;	/* 44100 */
+	} else if (spec->freq>=(hardfreq[CLK_22K]+hardfreq[CLK_11K])>>1) {
+		numfreq = CLK_22K;	/* 22050 */
+	} else {
+		numfreq = CLK_11K;	/* 11025 */
+	}				
+
+	spec->freq=hardfreq[numfreq];
+
+	return 0;
+}
+
+static void Mint_InitAudio_Dma8(SDL_AudioSpec *spec)
+{
+	void *oldpile;
+	unsigned long buffer;
+	unsigned char mode;
+	
+	oldpile=(void *)Super(0);
+
+	/* Stop currently playing sound */
+	DMAAUDIO_IO.control=0;
+
+	/* Set buffer */
+	buffer = (unsigned long) SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
+	DMAAUDIO_IO.start_high = (buffer>>16) & 255;
+	DMAAUDIO_IO.start_mid = (buffer>>8) & 255;
+	DMAAUDIO_IO.start_low = buffer & 255;
+
+	buffer += SDL_MintAudio_audiosize;
+	DMAAUDIO_IO.end_high = (buffer>>16) & 255;
+	DMAAUDIO_IO.end_mid = (buffer>>8) & 255;
+	DMAAUDIO_IO.end_low = buffer & 255;
+
+	mode = numfreq;
+	if (spec->channels==1) {
+		mode |= 1<<7;
+	}
+	DMAAUDIO_IO.mode = mode;	
+
+	/* Set interrupt */
+	Jdisint(MFP_DMASOUND);
+	Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_IntDma);
+	Jenabint(MFP_DMASOUND);
+
+	/* Go */
+	DMAAUDIO_IO.control = 3;	/* playback + repeat */
+
+	Super(oldpile);
+}
+
+static void Mint_InitAudio_Xbios(SDL_AudioSpec *spec)
+{
+	int channels_mode;
+	void *buffer;
+
+	/* Stop currently playing sound */
+	Buffoper(0);
+
+	Settracks(0,0);
+	Setmontracks(0);
+
+	switch (spec->format & 0xff) {
+		case 8:
+			if (spec->channels==2) {
+				channels_mode=STEREO8;
+			} else {
+				channels_mode=MONO8;
+			}
+			break;
+		case 16:
+		default:
+			channels_mode=STEREO16;
+			break;
+	}
+	Setmode(channels_mode);
+
+	Devconnect(DMAPLAY, DAC, CLK25M, numfreq, 1);
+
+	/* Set buffer */
+	buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
+	Setbuffer(0, buffer, buffer+SDL_MintAudio_audiosize);
+	
+	/* Install interrupt */
+	Setinterrupt(SI_TIMERA, SI_PLAY);
+
+	Jdisint(MFP_DMASOUND);
+	Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_IntXbios);
+	Jenabint(MFP_DMASOUND);
+
+	/* Go */
+	Buffoper(SB_PLA_ENA|SB_PLA_RPT);
+}
+
+static void Mint_InitAudio_Gsxb(SDL_AudioSpec *spec)
+{
+	int channels_mode;
+	void *buffer;
+
+	/* Stop currently playing sound */
+	Buffoper(0);
+
+	switch (spec->format & 0xff) {
+		case 8:
+			if (spec->channels==2) {
+				channels_mode=STEREO8;
+			} else {
+				channels_mode=MONO8;
+			}
+			break;
+		case 16:
+			if (spec->channels==2) {
+				channels_mode=STEREO16;
+			} else {
+				channels_mode=MONO16;
+			}
+			break;
+		default:
+			channels_mode=STEREO16;
+			break;
+	}
+	Setmode(channels_mode);
+
+	Devconnect(0, 0, CLKEXT, numfreq, 1);
+
+	/* Set buffer */
+	buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
+	Setbuffer(0, buffer, buffer+SDL_MintAudio_audiosize);
+	
+	/* Install interrupt */
+	NSetinterrupt(2, SI_PLAY, SDL_MintAudio_IntGsxb);
+		
+	/* Go */
+	Buffoper(SB_PLA_ENA|SB_PLA_RPT);
+}
+
+static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
+{
+	/* Lock sound system */
+	if ((cookie_snd & SND_16BIT)!=0) {
+		if (Locksnd()!=1) {
+    	    SDL_SetError("Mint_OpenAudio: Audio system already in use");
+	        return(-1);
+		}
+	}
+
+	/* Check audio capabilities */
+	if (cookie_gsxb && ((cookie_snd & (SND_GSXB|SND_16BIT))==(SND_GSXB|SND_16BIT)) ) {
+		if (Mint_CheckAudio_Gsxb(spec)==-1) {
+			return -1;
+		}
+	} else if ((cookie_snd & SND_16BIT)!=0) {
+		Mint_CheckAudio_Xbios(spec);
+	} else if ((cookie_snd & SND_8BIT)!=0) {
+		Mint_CheckAudio_Dma8(spec);
+	}
+
+	SDL_CalculateAudioSpec(spec);
+
+	/* Allocate memory for audio buffers in DMA-able RAM */
+	spec->size = spec->samples;
+	spec->size *= spec->channels;
+	spec->size *= (spec->format & 0xFF)/8;
+
+	SDL_MintAudio_audiosize = spec->size;
+
+	SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(SDL_MintAudio_audiosize *2, MX_STRAM);
+	if (SDL_MintAudio_audiobuf[0]==NULL) {
+		SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
+		return (-1);
+	}
+	SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + SDL_MintAudio_audiosize;
+	SDL_MintAudio_numbuf=0;
+	memset(SDL_MintAudio_audiobuf[0], 0, SDL_MintAudio_audiosize * 2);
+	SDL_MintAudio_mutex = 0;
+
+	SDL_MintAudio_device = this;
+
+	/* Setup audio hardware */
+	if (cookie_gsxb && ((cookie_snd & (SND_GSXB|SND_16BIT))==(SND_GSXB|SND_16BIT)) ) {
+		Mint_InitAudio_Gsxb(spec);
+	} else if ((cookie_snd & SND_16BIT)!=0) {
+		Mint_InitAudio_Xbios(spec);
+	} else if ((cookie_snd & SND_8BIT)!=0) {
+		Mint_InitAudio_Dma8(spec);
+	}
+
+    return 1;
+}