fix deadlock if ts_cb is not set
[sdl_omap.git] / src / audio / dc / SDL_dcaudio.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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 */
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 }