change PAD_ScanPads()s behaviour. the return value now contains a bitmask of the...
[libogc.git] / libmodplay / gcmodplay.c
blob831aadd563fafc2f457b11564ee03d1fa894aee7
1 // Modified by Francisco Mu�oz 'Hermes' MAY 2008
2 // Changed to use libAESND
3 // linke aginst libaesnd.a - -laesnd
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <gccore.h>
9 #include <aesndlib.h>
10 #include "gcmodplay.h"
12 //#define _GCMOD_DEBUG
14 #define STACKSIZE 8192
15 #define SNDBUFFERSIZE (5760) //that's the maximum buffer size
17 static BOOL thr_running = FALSE;
18 static BOOL sndPlaying = FALSE;
19 static MODSNDBUF sndBuffer;
21 static u32 shiftVal = 0;
22 static vu32 curr_audio = 0;
23 static u8 audioBuf[2][SNDBUFFERSIZE] ATTRIBUTE_ALIGN(32);
25 static lwpq_t player_queue;
26 static lwp_t hplayer;
27 static u8 player_stack[STACKSIZE] ATTRIBUTE_ALIGN(8);
28 static void* player(void *);
30 #ifdef __AESNDLIB_H__
31 static s32 mod_freq = 48000;
32 static AESNDPB *modvoice = NULL;
33 #endif
35 #ifdef _GCMOD_DEBUG
36 static u64 mixtime = 0;
37 static u64 reqcbtime = 0;
38 extern u32 diff_usec(unsigned long long start,unsigned long long end);
39 extern u32 diff_msec(unsigned long long start,unsigned long long end);
40 #endif
42 static void* player(void *arg)
44 #ifdef _GCMOD_DEBUG
45 u64 start;
46 #endif
48 thr_running = TRUE;
49 while(sndPlaying==TRUE) {
50 LWP_ThreadSleep(player_queue);
51 if(sndPlaying==TRUE) {
52 #ifdef _GCMOD_DEBUG
53 start = gettime();
54 #endif
55 sndBuffer.callback(sndBuffer.usr_data,((u8*)audioBuf[curr_audio]),SNDBUFFERSIZE);
56 #ifdef _GCMOD_DEBUG
57 mixtime = gettime();
58 #endif
61 thr_running = FALSE;
63 return 0;
66 #ifdef __AESNDLIB_H__
67 static void __aesndvoicecallback(AESNDPB *pb,u32 state)
69 #ifdef _GCMOD_DEBUG
70 static u64 rqstart = 0,rqend = 0;
72 rqend = gettime();
73 if(rqstart) reqcbtime = rqend - rqstart;
74 #endif
75 switch(state) {
76 case VOICE_STATE_STOPPED:
77 case VOICE_STATE_RUNNING:
78 break;
79 case VOICE_STATE_STREAM:
80 AESND_SetVoiceBuffer(pb,(void*)audioBuf[curr_audio],SNDBUFFERSIZE);
81 LWP_ThreadSignal(player_queue);
82 curr_audio ^= 1;
83 break;
85 #ifdef _GCMOD_DEBUG
86 rqstart = gettime();
87 #endif
90 #else
92 static void dmaCallback()
94 #ifdef _GCMOD_DEBUG
95 static u64 adstart = 0,adend = 0;
97 adend = gettime();
98 if(adstart) printf("dmaCallback(%d us)\n",diff_usec(adstart,adend));
99 #endif
101 curr_audio ^= 1;
102 AUDIO_InitDMA((u32)audioBuf[curr_audio],SNDBUFFERSIZE);
103 LWP_ThreadSignal(player_queue);
105 #ifdef _GCMOD_DEBUG
106 adstart = gettime();
107 #endif
109 #endif
111 static void mixCallback(void *usrdata,u8 *stream,u32 len)
113 u32 i;
114 MODPlay *mp = (MODPlay*)usrdata;
115 MOD *mod = &mp->mod;
117 if(mp->manual_polling)
118 mod->notify = &mp->paused;
119 else
120 mod->notify = NULL;
122 mod->mixingbuf = stream;
123 mod->mixingbuflen = len;
125 if(mp->paused) {
126 for(i=0;i<(len>>1);i++)
127 ((u16*)stream)[i] = 0;
128 } else
129 MOD_Player(mod);
131 #ifndef __AESNDLIB_H__
132 DCFlushRange(stream,len);
133 #endif
136 static s32 SndBufStart(MODSNDBUF *sndbuf)
138 if(sndPlaying) return -1;
140 memcpy(&sndBuffer,sndbuf,sizeof(MODSNDBUF));
142 shiftVal = 0;
143 if(sndBuffer.chans==2)
144 shiftVal++;
145 if(sndBuffer.fmt==16)
146 shiftVal++;
148 memset(audioBuf[0],0,SNDBUFFERSIZE);
149 memset(audioBuf[1],0,SNDBUFFERSIZE);
151 DCFlushRange(audioBuf[0],SNDBUFFERSIZE);
152 DCFlushRange(audioBuf[1],SNDBUFFERSIZE);
154 while(thr_running);
156 curr_audio = 0;
157 sndPlaying = TRUE;
158 if(LWP_CreateThread(&hplayer,player,NULL,player_stack,STACKSIZE,80)!=-1) {
159 #ifndef __AESNDLIB_H__
160 AUDIO_RegisterDMACallback(dmaCallback);
161 AUDIO_InitDMA((u32)audioBuf[curr_audio],SNDBUFFERSIZE);
162 AUDIO_StartDMA();
163 #else
164 AESND_SetVoiceStop(modvoice,false);
165 #endif
166 return 1;
168 sndPlaying = FALSE;
170 return -1;
173 static void SndBufStop()
175 if(!sndPlaying) return;
176 #ifndef __AESNDLIB_H__
177 AUDIO_StopDMA();
178 AUDIO_RegisterDMACallback(NULL);
179 #else
180 AESND_SetVoiceStop(modvoice,true);
181 #endif
182 curr_audio = 0;
183 sndPlaying = FALSE;
184 LWP_ThreadSignal(player_queue);
185 LWP_JoinThread(hplayer,NULL);
188 static s32 updateWaveFormat(MODPlay *mod)
190 BOOL p = mod->playing;
192 if(p)
193 SndBufStop();
195 if(mod->stereo) {
196 mod->soundBuf.chans = 2;
197 mod->mod.channels = 2;
198 } else {
199 mod->soundBuf.chans = 1;
200 mod->mod.channels = 1;
203 mod->soundBuf.freq = mod->playfreq;
204 mod->mod.freq = mod->playfreq;
205 mod->mod.bits = 16;
207 if(p) {
208 mod->mod.samplescounter = 0;
209 mod->mod.samplespertick = mod->mod.bpmtab[mod->mod.bpm-32];
212 mod->soundBuf.fmt = 16;
213 mod->soundBuf.usr_data = mod;
214 mod->soundBuf.callback = mixCallback;
215 mod->soundBuf.samples = (f32)mod->playfreq/50.0F;
217 if(p)
218 SndBufStart(&mod->soundBuf);
220 return 0;
223 void MODPlay_Init(MODPlay *mod)
225 memset(mod,0,sizeof(MODPlay));
227 #ifndef __AESNDLIB_H__
228 AUDIO_Init(NULL);
229 #else
230 modvoice = AESND_AllocateVoice(__aesndvoicecallback);
231 if(modvoice) {
232 AESND_SetVoiceFormat(modvoice,VOICE_STEREO16);
233 AESND_SetVoiceFrequency(modvoice,mod_freq);
234 AESND_SetVoiceVolume(modvoice,255,255);
235 AESND_SetVoiceStream(modvoice,true);
237 #endif
238 MODPlay_SetFrequency(mod,48000);
239 MODPlay_SetStereo(mod,TRUE);
241 LWP_InitQueue(&player_queue);
243 sndPlaying = FALSE;
244 thr_running = FALSE;
246 mod->paused = FALSE;
247 mod->bits = TRUE;
248 mod->numSFXChans = 0;
249 mod->manual_polling = FALSE;
252 s32 MODPlay_SetFrequency(MODPlay *mod,u32 freq)
254 if(freq==mod->playfreq) return 0;
255 if(freq==32000 || freq==48000) {
256 #ifndef __AESNDLIB_H__
257 if(freq==32000)
258 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_32KHZ);
259 else
260 AUDIO_SetDSPSampleRate(AI_SAMPLERATE_48KHZ);
261 #else
262 mod_freq = 48000;
263 #endif
264 mod->playfreq = freq;
265 updateWaveFormat(mod);
266 return 0;
268 return -1;
271 void MODPlay_SetStereo(MODPlay *mod,BOOL stereo)
273 if(stereo==mod->stereo) return;
275 mod->stereo = stereo;
276 updateWaveFormat(mod);
279 void MODPlay_Unload(MODPlay *mod)
281 MODPlay_Stop(mod);
282 MOD_Free(&mod->mod);
285 s32 MODPlay_SetMOD(MODPlay *mod,const void *mem)
287 MODPlay_Unload(mod);
289 if(MOD_SetMOD(&mod->mod,(u8*)mem)==0) {
290 MODPlay_AllocSFXChannels(mod,mod->numSFXChans);
291 return 0;
293 return -1;
296 s32 MODPlay_Start(MODPlay *mod)
298 if(mod->playing) return -1;
299 if(mod->mod.modraw==NULL) return -1;
301 updateWaveFormat(mod);
302 MOD_Start(&mod->mod);
303 if(SndBufStart(&mod->soundBuf)<0) return -1;
304 mod->playing = TRUE;
305 return 0;
308 s32 MODPlay_Stop(MODPlay *mod)
310 if(!mod->playing) return -1;
312 SndBufStop();
313 mod->playing = FALSE;
314 return 0;
317 s32 MODPlay_AllocSFXChannels(MODPlay *mod,u32 sfxchans)
319 if(mod->mod.modraw==NULL) return -1;
321 if(MOD_AllocSFXChannels(&mod->mod,sfxchans)==0) {
322 mod->numSFXChans = sfxchans;
323 return 0;
325 return -1;
328 s32 MODPlay_Pause(MODPlay *mod,BOOL pause)
330 if(!mod->playing) return -1;
331 mod->paused = pause;
332 return 0;
335 s32 MODPlay_TriggerNote(MODPlay *mod,u32 chan,u8 inst,u16 freq,u8 vol)
337 if(mod->mod.modraw==0) return -1;
338 return MOD_TriggerNote(&mod->mod,chan,inst,freq,vol);
341 // add by Hermes
343 /* void MODPlay_SetVolume(MODPlay *mod, s32 musicvolume, s32 sfxvolume)
345 Set the volume levels for the MOD music (call it after MODPlay_SetMOD())
347 mod: the MODPlay pointer
349 musicvolume: in range 0 to 64
350 sfxvolume: in range 0 to 64
354 void MODPlay_SetVolume(MODPlay *mod, s32 musicvolume, s32 sfxvolume)
356 if(musicvolume<0) musicvolume=0;
357 if(musicvolume>64) musicvolume=64;
359 if(sfxvolume<0) sfxvolume=0;
360 if(sfxvolume>64) sfxvolume=64;
362 mod->mod.musicvolume= musicvolume;
363 mod->mod.sfxvolume = sfxvolume;
366 #ifdef _GCMOD_DEBUG
367 u32 MODPlay_MixingTime()
369 return ticks_to_microsecs(mixtime);
372 u32 MODPlay_RequestTime()
374 return ticks_to_microsecs(reqcbtime);
376 #endif