Home | History | Annotate | Download | only in dc
      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 Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 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     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 
     22 */
     23 #include "SDL_config.h"
     24 
     25 /* Output dreamcast aica */
     26 
     27 #include "SDL_timer.h"
     28 #include "SDL_audio.h"
     29 #include "../SDL_audiomem.h"
     30 #include "../SDL_audio_c.h"
     31 #include "../SDL_audiodev_c.h"
     32 #include "SDL_dcaudio.h"
     33 
     34 #include "aica.h"
     35 #include <dc/spu.h>
     36 
     37 /* Audio driver functions */
     38 static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec);
     39 static void DCAUD_WaitAudio(_THIS);
     40 static void DCAUD_PlayAudio(_THIS);
     41 static Uint8 *DCAUD_GetAudioBuf(_THIS);
     42 static void DCAUD_CloseAudio(_THIS);
     43 
     44 /* Audio driver bootstrap functions */
     45 static int DCAUD_Available(void)
     46 {
     47 	return 1;
     48 }
     49 
     50 static void DCAUD_DeleteDevice(SDL_AudioDevice *device)
     51 {
     52 	SDL_free(device->hidden);
     53 	SDL_free(device);
     54 }
     55 
     56 static SDL_AudioDevice *DCAUD_CreateDevice(int devindex)
     57 {
     58 	SDL_AudioDevice *this;
     59 
     60 	/* Initialize all variables that we clean on shutdown */
     61 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
     62 	if ( this ) {
     63 		SDL_memset(this, 0, (sizeof *this));
     64 		this->hidden = (struct SDL_PrivateAudioData *)
     65 				SDL_malloc((sizeof *this->hidden));
     66 	}
     67 	if ( (this == NULL) || (this->hidden == NULL) ) {
     68 		SDL_OutOfMemory();
     69 		if ( this ) {
     70 			SDL_free(this);
     71 		}
     72 		return(0);
     73 	}
     74 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
     75 
     76 	/* Set the function pointers */
     77 	this->OpenAudio = DCAUD_OpenAudio;
     78 	this->WaitAudio = DCAUD_WaitAudio;
     79 	this->PlayAudio = DCAUD_PlayAudio;
     80 	this->GetAudioBuf = DCAUD_GetAudioBuf;
     81 	this->CloseAudio = DCAUD_CloseAudio;
     82 
     83 	this->free = DCAUD_DeleteDevice;
     84 
     85 	spu_init();
     86 
     87 	return this;
     88 }
     89 
     90 AudioBootStrap DCAUD_bootstrap = {
     91 	"dcaudio", "Dreamcast AICA audio",
     92 	DCAUD_Available, DCAUD_CreateDevice
     93 };
     94 
     95 /* This function waits until it is possible to write a full sound buffer */
     96 static void DCAUD_WaitAudio(_THIS)
     97 {
     98 	if (this->hidden->playing) {
     99 		/* wait */
    100 		while(aica_get_pos(0)/this->spec.samples == this->hidden->nextbuf) {
    101 			thd_pass();
    102 		}
    103 	}
    104 }
    105 
    106 #define	SPU_RAM_BASE	0xa0800000
    107 
    108 static void spu_memload_stereo8(int leftpos,int rightpos,void *src0,size_t size)
    109 {
    110 	uint8 *src = src0;
    111 	uint32 *left  = (uint32*)(leftpos +SPU_RAM_BASE);
    112 	uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE);
    113 	size = (size+7)/8;
    114 	while(size--) {
    115 		unsigned lval,rval;
    116 		lval = *src++;
    117 		rval = *src++;
    118 		lval|= (*src++)<<8;
    119 		rval|= (*src++)<<8;
    120 		lval|= (*src++)<<16;
    121 		rval|= (*src++)<<16;
    122 		lval|= (*src++)<<24;
    123 		rval|= (*src++)<<24;
    124 		g2_write_32(left++,lval);
    125 		g2_write_32(right++,rval);
    126 		g2_fifo_wait();
    127 	}
    128 }
    129 
    130 static void spu_memload_stereo16(int leftpos,int rightpos,void *src0,size_t size)
    131 {
    132 	uint16 *src = src0;
    133 	uint32 *left  = (uint32*)(leftpos +SPU_RAM_BASE);
    134 	uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE);
    135 	size = (size+7)/8;
    136 	while(size--) {
    137 		unsigned lval,rval;
    138 		lval = *src++;
    139 		rval = *src++;
    140 		lval|= (*src++)<<16;
    141 		rval|= (*src++)<<16;
    142 		g2_write_32(left++,lval);
    143 		g2_write_32(right++,rval);
    144 		g2_fifo_wait();
    145 	}
    146 }
    147 
    148 static void DCAUD_PlayAudio(_THIS)
    149 {
    150 	SDL_AudioSpec *spec = &this->spec;
    151 	unsigned int offset;
    152 
    153 	if (this->hidden->playing) {
    154 		/* wait */
    155 		while(aica_get_pos(0)/spec->samples == this->hidden->nextbuf) {
    156 			thd_pass();
    157 		}
    158 	}
    159 
    160 	offset = this->hidden->nextbuf*spec->size;
    161 	this->hidden->nextbuf^=1;
    162 	/* Write the audio data, checking for EAGAIN on broken audio drivers */
    163 	if (spec->channels==1) {
    164 		spu_memload(this->hidden->leftpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
    165 	} else {
    166 		offset/=2;
    167 		if ((this->spec.format&255)==8) {
    168 			spu_memload_stereo8(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
    169 		} else {
    170 			spu_memload_stereo16(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
    171 		}
    172 	}
    173 
    174 	if (!this->hidden->playing) {
    175 		int mode;
    176 		this->hidden->playing = 1;
    177 		mode = (spec->format==AUDIO_S8)?SM_8BIT:SM_16BIT;
    178 		if (spec->channels==1) {
    179 			aica_play(0,mode,this->hidden->leftpos,0,spec->samples*2,spec->freq,255,128,1);
    180 		} else {
    181 			aica_play(0,mode,this->hidden->leftpos ,0,spec->samples*2,spec->freq,255,0,1);
    182 			aica_play(1,mode,this->hidden->rightpos,0,spec->samples*2,spec->freq,255,255,1);
    183 		}
    184 	}
    185 }
    186 
    187 static Uint8 *DCAUD_GetAudioBuf(_THIS)
    188 {
    189 	return(this->hidden->mixbuf);
    190 }
    191 
    192 static void DCAUD_CloseAudio(_THIS)
    193 {
    194 	aica_stop(0);
    195 	if (this->spec.channels==2) aica_stop(1);
    196 	if ( this->hidden->mixbuf != NULL ) {
    197 		SDL_FreeAudioMem(this->hidden->mixbuf);
    198 		this->hidden->mixbuf = NULL;
    199 	}
    200 }
    201 
    202 static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
    203 {
    204     Uint16 test_format = SDL_FirstAudioFormat(spec->format);
    205     int valid_datatype = 0;
    206     while ((!valid_datatype) && (test_format)) {
    207         spec->format = test_format;
    208         switch (test_format) {
    209             /* only formats Dreamcast accepts... */
    210             case AUDIO_S8:
    211             case AUDIO_S16LSB:
    212                 valid_datatype = 1;
    213                 break;
    214 
    215             default:
    216                 test_format = SDL_NextAudioFormat();
    217                 break;
    218         }
    219     }
    220 
    221     if (!valid_datatype) {  /* shouldn't happen, but just in case... */
    222         SDL_SetError("Unsupported audio format");
    223         return (-1);
    224     }
    225 
    226     if (spec->channels > 2)
    227         spec->channels = 2;  /* no more than stereo on the Dreamcast. */
    228 
    229 	/* Update the fragment size as size in bytes */
    230 	SDL_CalculateAudioSpec(spec);
    231 
    232 	/* Allocate mixing buffer */
    233 	this->hidden->mixlen = spec->size;
    234 	this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    235 	if ( this->hidden->mixbuf == NULL ) {
    236 		return(-1);
    237 	}
    238 	SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
    239 	this->hidden->leftpos = 0x11000;
    240 	this->hidden->rightpos = 0x11000+spec->size;
    241 	this->hidden->playing = 0;
    242 	this->hidden->nextbuf = 0;
    243 
    244 	/* We're ready to rock and roll. :-) */
    245 	return(0);
    246 }
    247