view src/audio/mint/SDL_mintaudio.c @ 571:8e3ce997621c

Date: Thu, 16 Jan 2003 13:48:31 +0200 From: "Mike Gorchak" Subject: All QNX patches whole patches concerning QNX. Almost all code has been rewritten by Julian and me. Added initial support for hw overlays in QNX and many many others fixes. P.S. This patches has been reviewed by Dave Rempel from QSSL and included in SDL 1.2.5 distribution, which coming on 3rd party CD for newest 6.2.1 version of QNX, which will be available soon.
author Sam Lantinga <slouken@libsdl.org>
date Mon, 20 Jan 2003 01:38:37 +0000
parents 0ce5a68278fd
children 594422ab8f9f
line wrap: on
line source

/*
 * 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 <mint/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;
}