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@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 */ 38static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); 39static void DCAUD_WaitAudio(_THIS); 40static void DCAUD_PlayAudio(_THIS); 41static Uint8 *DCAUD_GetAudioBuf(_THIS); 42static void DCAUD_CloseAudio(_THIS); 43 44/* Audio driver bootstrap functions */ 45static int DCAUD_Available(void) 46{ 47 return 1; 48} 49 50static void DCAUD_DeleteDevice(SDL_AudioDevice *device) 51{ 52 SDL_free(device->hidden); 53 SDL_free(device); 54} 55 56static 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 90AudioBootStrap 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 */ 96static 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 108static 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 130static 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 148static 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 187static Uint8 *DCAUD_GetAudioBuf(_THIS) 188{ 189 return(this->hidden->mixbuf); 190} 191 192static 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 202static 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