SDL-mirror/src/audio/mint/SDL_mintaudio.c

629 lines
14 KiB
C
Raw Normal View History

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