Merge branch 'dhewg'
[libogc.git] / libmodplay / gcmodplay.c
blob5a16d644926f1c536aef3ae253db0d18c509180a
1 // Modified by Francisco Mu�oz 'Hermes' MAY 2008
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <gccore.h>
7 #include <asndlib.h>
8 #include "gcmodplay.h"
10 //#define _GCMOD_DEBUG
12 #define STACKSIZE 8192
13 #define SNDBUFFERSIZE (3840<<2) //that's the maximum buffer size for one VBlank we need.
15 static BOOL thr_running = FALSE;
16 static BOOL sndPlaying = FALSE;
17 static MODSNDBUF sndBuffer;
19 static s32 have_samples = 0;
20 static s32 mod_freq = 48000;
22 static u32 shiftVal = 0;
23 static vu32 curr_audio = 0;
24 static u32 curr_datalen[2] = {0,0};
25 static u8 audioBuf[2][SNDBUFFERSIZE] ATTRIBUTE_ALIGN(32);
27 static lwpq_t player_queue;
28 static lwp_t hplayer;
29 static u8 player_stack[STACKSIZE];
30 static void* player(void *);
32 #ifdef _GCMOD_DEBUG
33 extern long long gettime();
34 extern u32 diff_usec(unsigned long long start,unsigned long long end);
35 extern u32 diff_msec(unsigned long long start,unsigned long long end);
36 #endif
38 static void* player(void *arg)
40 #ifdef _GCMOD_DEBUG
41 long long start,end;
42 #endif
44 thr_running = TRUE;
45 while(sndPlaying==TRUE) {
46 LWP_ThreadSleep(player_queue);
48 if(curr_datalen[curr_audio]>0 && sndPlaying==TRUE) {
49 #ifdef _GCMOD_DEBUG
50 printf("player(run callback)\n\n");
51 start = gettime();
52 #endif
53 sndBuffer.callback(sndBuffer.usr_data,((u8*)audioBuf[curr_audio]),curr_datalen[curr_audio]);
54 have_samples = 2;
55 #ifdef _GCMOD_DEBUG
56 end = gettime();
57 printf("player(end callback,%d - %d us)\n\n",curr_audio,diff_usec(start,end));
58 #endif
61 thr_running = FALSE;
62 #ifdef _GCMOD_DEBUG
63 printf("player stopped %d\n",thr_running);
64 #endif
65 return 0;
68 static void dmaCallback()
70 #ifndef __SNDLIB_H__
71 MODPlay *mp = (MODPlay*)sndBuffer.usr_data;
72 MOD *mod = &mp->mod;
73 #endif
75 #ifdef _GCMOD_DEBUG
76 static long long start = 0,end = 0;
78 end = gettime();
79 if(start) printf("dmaCallback(%p,%d,%d - after %d ms)\n",(void*)audioBuf[curr_audio],curr_datalen,curr_audio,diff_msec(start,end));
80 #endif
82 #ifndef __SNDLIB_H__
83 AUDIO_StopDMA();
84 AUDIO_InitDMA((u32)audioBuf[curr_audio],curr_datalen[curr_audio]);
85 AUDIO_StartDMA();
87 curr_audio ^= 1;
88 curr_datalen[curr_audio] = (mod->samplespertick<<shiftVal);
89 LWP_ThreadSignal(player_queue);
90 #else
91 if(have_samples==0) {
92 have_samples = 1;
93 LWP_ThreadSignal(player_queue);
94 return;
96 if(have_samples==1) return;
97 if(have_samples==2) {
98 if(SND_AddVoice(0,audioBuf[curr_audio], curr_datalen[curr_audio])!=0) return; // Sorry I am busy: try again
100 curr_datalen[curr_audio]=0;
101 have_samples=0;
102 curr_audio ^= 1;
103 curr_datalen[curr_audio]=SNDBUFFERSIZE;
105 #endif
107 #ifdef _GCMOD_DEBUG
108 start = gettime();
109 printf("dmaCallback(%p,%d,%d,%d us) leave\n",(void*)audioBuf[curr_audio],curr_datalen,curr_audio,diff_usec(end,start));
110 #endif
113 static void mixCallback(void *usrdata,u8 *stream,u32 len)
115 u32 i;
116 MODPlay *mp = (MODPlay*)usrdata;
117 MOD *mod = &mp->mod;
118 #ifdef _GCMOD_DEBUG
119 printf("mixCallback(%p,%p,%d) enter\n",stream,usrdata,len);
120 #endif
121 if(mp->manual_polling)
122 mod->notify = &mp->paused;
123 else
124 mod->notify = NULL;
126 mod->mixingbuf = stream;
127 mod->mixingbuflen = len;
129 if(mp->paused) {
130 for(i=0;i<(len>>1);i++)
131 ((u16*)stream)[i] = 0;
132 } else
133 MOD_Player(mod);
135 DCFlushRange(stream,len);
136 #ifdef _GCMOD_DEBUG
137 printf("mixCallback(%p,%p,%d,%d) leave\n",stream,usrdata,len,mp->paused);
138 #endif
141 static s32 SndBufStart(MODSNDBUF *sndbuf)
143 if(sndPlaying) return -1;
144 #ifdef _GCMOD_DEBUG
145 printf("SndBufStart(%p) enter\n",sndbuf);
146 #endif
147 memcpy(&sndBuffer,sndbuf,sizeof(MODSNDBUF));
149 shiftVal = 0;
150 if(sndBuffer.chans==2)
151 shiftVal++;
152 if(sndBuffer.fmt==16)
153 shiftVal++;
155 memset(audioBuf[0],0,SNDBUFFERSIZE);
156 memset(audioBuf[1],0,SNDBUFFERSIZE);
158 DCFlushRange(audioBuf[0],SNDBUFFERSIZE);
159 DCFlushRange(audioBuf[1],SNDBUFFERSIZE);
161 while(thr_running);
163 curr_audio = 0;
164 sndPlaying = TRUE;
165 curr_datalen[0] = SNDBUFFERSIZE;
166 curr_datalen[1] = SNDBUFFERSIZE;
167 if(LWP_CreateThread(&hplayer,player,NULL,player_stack,STACKSIZE,80)!=-1) {
168 #ifndef __SNDLIB_H__
169 AUDIO_RegisterDMACallback(dmaCallback);
170 AUDIO_InitDMA((u32)audioBuf[curr_audio],curr_datalen[curr_audio]);
171 AUDIO_StartDMA();
172 curr_audio ^= 1;
173 #else
174 SND_SetVoice(0, VOICE_STEREO_16BIT, mod_freq,0, audioBuf[curr_audio], curr_datalen[curr_audio], 255, 255, dmaCallback);
175 have_samples=0;
177 curr_audio ^= 1;
178 SND_Pause(0);
179 #endif
180 return 1;
182 sndPlaying = FALSE;
184 return -1;
187 static void SndBufStop()
189 if(!sndPlaying) return;
190 #ifndef __SNDLIB_H__
191 AUDIO_StopDMA();
192 AUDIO_RegisterDMACallback(NULL);
193 #else
194 SND_StopVoice(0);
195 #endif
196 curr_audio = 0;
197 sndPlaying = FALSE;
198 curr_datalen[0] = 0;
199 curr_datalen[1] = 0;
200 LWP_ThreadSignal(player_queue);
201 LWP_JoinThread(hplayer,NULL);
204 static s32 updateWaveFormat(MODPlay *mod)
206 BOOL p = mod->playing;
208 if(p)
209 SndBufStop();
211 if(mod->stereo) {
212 mod->soundBuf.chans = 2;
213 mod->mod.channels = 2;
214 } else {
215 mod->soundBuf.chans = 1;
216 mod->mod.channels = 1;
219 mod->soundBuf.freq = mod->playfreq;
220 mod->mod.freq = mod->playfreq;
221 mod->mod.bits = 16;
223 if(p) {
224 mod->mod.samplescounter = 0;
225 mod->mod.samplespertick = mod->mod.bpmtab[mod->mod.bpm-32];
228 mod->soundBuf.fmt = 16;
229 mod->soundBuf.usr_data = mod;
230 mod->soundBuf.callback = mixCallback;
231 mod->soundBuf.samples = (f32)mod->playfreq/50.0F;
233 if(p)
234 SndBufStart(&mod->soundBuf);
236 return 0;
239 void MODPlay_Init(MODPlay *mod)
241 memset(mod,0,sizeof(MODPlay));
243 #ifndef __SNDLIB_H__
244 AUDIO_Init(NULL);
245 #else
246 SND_Pause(0);
247 SND_StopVoice(0);
248 #endif
249 MODPlay_SetFrequency(mod,48000);
250 MODPlay_SetStereo(mod,TRUE);
252 LWP_InitQueue(&player_queue);
254 sndPlaying = FALSE;
255 thr_running = FALSE;
257 mod->paused = FALSE;
258 mod->bits = TRUE;
259 mod->numSFXChans = 0;
260 mod->manual_polling = FALSE;
263 s32 MODPlay_SetFrequency(MODPlay *mod,u32 freq)
265 if(freq==mod->playfreq) return 0;
266 if(freq==32000 || freq==48000) {
267 #ifndef __SNDLIB_H__
268 if(freq==32000)
269 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_32KHZ);
270 else
271 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
272 #else
273 mod_freq = 48000;
274 #endif
275 mod->playfreq = freq;
276 updateWaveFormat(mod);
277 return 0;
279 return -1;
282 void MODPlay_SetStereo(MODPlay *mod,BOOL stereo)
284 if(stereo==mod->stereo) return;
286 mod->stereo = stereo;
287 updateWaveFormat(mod);
290 void MODPlay_Unload(MODPlay *mod)
292 MODPlay_Stop(mod);
293 MOD_Free(&mod->mod);
296 s32 MODPlay_SetMOD(MODPlay *mod,const void *mem)
298 MODPlay_Unload(mod);
300 if(MOD_SetMOD(&mod->mod,(u8*)mem)==0) {
301 MODPlay_AllocSFXChannels(mod,mod->numSFXChans);
302 return 0;
304 return -1;
307 s32 MODPlay_Start(MODPlay *mod)
309 if(mod->playing) return -1;
310 if(mod->mod.modraw==NULL) return -1;
312 updateWaveFormat(mod);
313 MOD_Start(&mod->mod);
314 if(SndBufStart(&mod->soundBuf)<0) return -1;
315 mod->playing = TRUE;
316 return 0;
319 s32 MODPlay_Stop(MODPlay *mod)
321 if(!mod->playing) return -1;
323 SndBufStop();
324 mod->playing = FALSE;
325 return 0;
328 s32 MODPlay_AllocSFXChannels(MODPlay *mod,u32 sfxchans)
330 if(mod->mod.modraw==NULL) return -1;
332 if(MOD_AllocSFXChannels(&mod->mod,sfxchans)==0) {
333 mod->numSFXChans = sfxchans;
334 return 0;
336 return -1;
339 s32 MODPlay_Pause(MODPlay *mod,BOOL pause)
341 if(!mod->playing) return -1;
342 mod->paused = pause;
343 return 0;
346 s32 MODPlay_TriggerNote(MODPlay *mod,u32 chan,u8 inst,u16 freq,u8 vol)
348 if(mod->mod.modraw==0) return -1;
349 return MOD_TriggerNote(&mod->mod,chan,inst,freq,vol);
352 // add by Hermes
354 /* void MODPlay_SetVolume(MODPlay *mod, s32 musicvolume, s32 sfxvolume)
356 Set the volume levels for the MOD music (call it after MODPlay_SetMOD())
358 mod: the MODPlay pointer
360 musicvolume: in range 0 to 64
361 sfxvolume: in range 0 to 64
365 void MODPlay_SetVolume(MODPlay *mod, s32 musicvolume, s32 sfxvolume)
367 if(musicvolume<0) musicvolume=0;
368 if(musicvolume>64) musicvolume=64;
370 if(sfxvolume<0) sfxvolume=0;
371 if(sfxvolume>64) sfxvolume=64;
373 mod->mod.musicvolume= musicvolume;
374 mod->mod.sfxvolume = sfxvolume;