7da8b2b2d7085ca4fc3d6e8062a8fa9691432b1b
[crow/jumpnbump.git] / sdl / sound.c
1 #include "globals.h"
2 #include <limits.h>
3
4 static Mix_Music *current_music = (Mix_Music *) NULL;
5
6 sfx_data sounds[NUM_SFX];
7
8 static int SAMPLECOUNT = 512;
9
10 #define MAX_CHANNELS    32
11
12 typedef struct {
13         // loop flag
14         int loop;
15         // The channel step amount...
16         unsigned int step;
17         // ... and a 0.16 bit remainder of last step.
18         unsigned int stepremainder;
19         unsigned int samplerate;
20         // The channel data pointers, start and end.
21         signed short* data;
22         signed short* startdata;
23         signed short* enddata;
24         // Hardware left and right channel volume lookup.
25         int leftvol;
26         int rightvol;
27 } channel_info_t;
28
29 channel_info_t channelinfo[MAX_CHANNELS];
30
31 // Sample rate in samples/second
32 int audio_rate = 44100;
33 int global_sfx_volume = 0;
34 //
35 // This function loops all active (internal) sound
36 //  channels, retrieves a given number of samples
37 //  from the raw sound data, modifies it according
38 //  to the current (internal) channel parameters,
39 //  mixes the per channel samples into the given
40 //  mixing buffer, and clamping it to the allowed
41 //  range.
42 //
43 // This function currently supports only 16bit.
44 //
45
46 static void stopchan(int i)
47 {
48         if (channelinfo[i].data) {
49                 memset(&channelinfo[i], 0, sizeof(channel_info_t));
50         }
51 }
52
53
54 //
55 // This function adds a sound to the
56 //  list of currently active sounds,
57 //  which is maintained as a given number
58 //  (eight, usually) of internal channels.
59 // Returns a handle.
60 //
61 int addsfx(signed short *data, int len, int loop, int samplerate, int channel)
62 {
63         stopchan(channel);
64
65         // We will handle the new SFX.
66         // Set pointer to raw data.
67         channelinfo[channel].data = data;
68         channelinfo[channel].startdata = data;
69       
70         /* Set pointer to end of raw data. */
71         channelinfo[channel].enddata = channelinfo[channel].data + len - 1;
72         channelinfo[channel].samplerate = samplerate;
73
74         channelinfo[channel].loop = loop;
75         channelinfo[channel].stepremainder = 0;
76
77         return channel;
78 }
79
80
81 static void updateSoundParams(int slot, int volume)
82 {
83         int rightvol;
84         int leftvol;
85
86         // Set stepping
87         // MWM 2000-12-24: Calculates proportion of channel samplerate
88         // to global samplerate for mixing purposes.
89         // Patched to shift left *then* divide, to minimize roundoff errors
90         // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz
91         channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/audio_rate);
92
93         leftvol = volume;
94         rightvol= volume;  
95
96         // Sanity check, clamp volume.
97         if (rightvol < 0)
98                 rightvol = 0;
99         if (rightvol > 127)
100                 rightvol = 127;
101     
102         if (leftvol < 0)
103                 leftvol = 0;
104         if (leftvol > 127)
105                 leftvol = 127;
106     
107         channelinfo[slot].leftvol = leftvol;
108         channelinfo[slot].rightvol = rightvol;
109 }
110
111
112 void mix_sound(void *unused, Uint8 *stream, int len)
113 {
114         // Mix current sound data.
115         // Data, from raw sound, for right and left.
116         register int sample;
117         register int    dl;
118         register int    dr;
119
120         // Pointers in audio stream, left, right, end.
121         signed short*   leftout;
122         signed short*   rightout;
123         signed short*   leftend;
124         // Step in stream, left and right, thus two.
125         int       step;
126
127         // Mixing channel index.
128         int       chan;
129
130         // Left and right channel
131         //  are in audio stream, alternating.
132         leftout = (signed short *)stream;
133         rightout = ((signed short *)stream)+1;
134         step = 2;
135
136         // Determine end, for left channel only
137         //  (right channel is implicit).
138         leftend = leftout + (len/4)*step;
139
140         // Mix sounds into the mixing buffer.
141         // Loop over step*SAMPLECOUNT,
142         //  that is 512 values for two channels.
143         while (leftout != leftend) {
144                 // Reset left/right value. 
145                 //dl = 0;
146                 //dr = 0;
147                 dl = *leftout * 256;
148                 dr = *rightout * 256;
149
150                 // Love thy L2 chache - made this a loop.
151                 // Now more channels could be set at compile time
152                 //  as well. Thus loop those  channels.
153                 for ( chan = 0; chan < MAX_CHANNELS; chan++ ) {
154                         // Check channel, if active.
155                         if (channelinfo[chan].data) {
156                                 // Get the raw data from the channel.
157                                 // no filtering
158                                 // sample = *channelinfo[chan].data;
159                                 // linear filtering
160                                 sample = (int)(((int)channelinfo[chan].data[0] * (int)(0x10000 - channelinfo[chan].stepremainder))
161                                         + ((int)channelinfo[chan].data[1] * (int)(channelinfo[chan].stepremainder))) >> 16;
162
163                                 // Add left and right part
164                                 //  for this channel (sound)
165                                 //  to the current data.
166                                 // Adjust volume accordingly.
167                                 dl += sample * (channelinfo[chan].leftvol * global_sfx_volume) / 128;
168                                 dr += sample * (channelinfo[chan].rightvol * global_sfx_volume) / 128;
169                                 // Increment index ???
170                                 channelinfo[chan].stepremainder += channelinfo[chan].step;
171                                 // MSB is next sample???
172                                 channelinfo[chan].data += channelinfo[chan].stepremainder >> 16;
173                                 // Limit to LSB???
174                                 channelinfo[chan].stepremainder &= 0xffff;
175
176                                 // Check whether we are done.
177                                 if (channelinfo[chan].data >= channelinfo[chan].enddata)
178                                         if (channelinfo[chan].loop) {
179                                                 channelinfo[chan].data = channelinfo[chan].startdata;
180                                         } else {
181                                                 stopchan(chan);
182                                         }
183                         }
184                 }
185   
186                 // Clamp to range. Left hardware channel.
187                 // Has been char instead of short.
188                 // if (dl > 127) *leftout = 127;
189                 // else if (dl < -128) *leftout = -128;
190                 // else *leftout = dl;
191
192                 dl = dl / 256;
193                 dr = dr / 256;
194
195                 if (dl > SHRT_MAX)
196                         *leftout = SHRT_MAX;
197                 else if (dl < SHRT_MIN)
198                         *leftout = SHRT_MIN;
199                 else
200                         *leftout = (signed short)dl;
201
202                 // Same for right hardware channel.
203                 if (dr > SHRT_MAX)
204                         *rightout = SHRT_MAX;
205                 else if (dr < SHRT_MIN)
206                         *rightout = SHRT_MIN;
207                 else
208                         *rightout = (signed short)dr;
209
210                 // Increment current pointers in stream
211                 leftout += step;
212                 rightout += step;
213         }
214 }
215
216 /* misc handling */
217
218 char dj_init(void)
219 {
220         Uint16 audio_format = AUDIO_U16;
221         int audio_channels = 2;
222         int audio_buffers = 4096;
223
224         open_screen();
225
226         audio_buffers = SAMPLECOUNT*audio_rate/11025;
227
228         if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) {
229                 fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
230                 return 0;
231         }
232
233         Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
234         printf("Opened audio at %dHz %dbit %s, %d bytes audio buffer\n", audio_rate, (audio_format & 0xFF), (audio_channels > 1) ? "stereo" : "mono", audio_buffers);
235
236         Mix_SetMusicCMD(getenv("MUSIC_CMD"));
237
238         Mix_SetPostMix(mix_sound, NULL);
239
240         memset(channelinfo, 0, sizeof(channelinfo));
241         memset(sounds, 0, sizeof(sounds));
242
243         return 0;
244 }
245
246 void dj_deinit(void)
247 {
248         Mix_HaltMusic();
249         if (current_music)
250                 Mix_FreeMusic(current_music);
251         current_music = NULL;
252
253         Mix_CloseAudio();
254
255         SDL_Quit();
256 }
257
258 void dj_start(void)
259 {
260 }
261
262 void dj_stop(void)
263 {
264 }
265
266 char dj_autodetect_sd(void)
267 {
268         return 0;
269 }
270
271 char dj_set_stereo(char flag)
272 {
273         return 0;
274 }
275
276 void dj_set_auto_mix(char flag)
277 {
278 }
279
280 unsigned short dj_set_mixing_freq(unsigned short freq)
281 {
282         return freq;
283 }
284
285 void dj_set_dma_time(unsigned short time)
286 {
287 }
288
289 void dj_set_nosound(char flag)
290 {
291 }
292
293 /* mix handling */
294
295 void dj_mix(void)
296 {
297 }
298
299 /* sfx handling */
300
301 char dj_set_num_sfx_channels(char num_channels)
302 {
303         return num_channels;
304 }
305
306 void dj_set_sfx_volume(char volume)
307 {
308         SDL_LockAudio();
309         global_sfx_volume = volume*2;
310         SDL_UnlockAudio();
311 }
312
313 void dj_play_sfx(unsigned char sfx_num, unsigned short freq, char volume, char panning, unsigned short delay, char channel)
314 {
315         int slot;
316
317         if (channel<0) {
318                 for (slot=0; slot<MAX_CHANNELS; slot++)
319                         if (channelinfo[slot].data==NULL)
320                                 break;
321                 if (slot>=MAX_CHANNELS)
322                         return;
323         } else
324                 slot = channel;
325
326         SDL_LockAudio();
327         addsfx((short *)sounds[sfx_num].buf, sounds[sfx_num].length, sounds[sfx_num].loop, freq, slot);
328         updateSoundParams(slot, volume*2);
329         SDL_UnlockAudio();
330 }
331
332 char dj_get_sfx_settings(unsigned char sfx_num, sfx_data *data)
333 {
334         memcpy(data, &sounds[sfx_num], sizeof(sfx_data));
335         return 0;
336 }
337
338 char dj_set_sfx_settings(unsigned char sfx_num, sfx_data *data)
339 {
340         memcpy(&sounds[sfx_num], data, sizeof(sfx_data));
341         return 0;
342 }
343
344 void dj_set_sfx_channel_volume(char channel_num, char volume)
345 {
346         SDL_LockAudio();
347         updateSoundParams(channel_num, volume*2);
348         SDL_UnlockAudio();
349 }
350
351 void dj_stop_sfx_channel(char channel_num)
352 {
353         SDL_LockAudio();
354         stopchan(channel_num);
355         SDL_UnlockAudio();
356 }
357
358 char dj_load_sfx(FILE * file_handle, char *filename, int file_length, char sfx_type, unsigned char sfx_num)
359 {
360         sounds[sfx_num].buf = malloc(file_length);
361         fread(sounds[sfx_num].buf, 1, file_length, file_handle);
362         sounds[sfx_num].length = file_length / 2;
363         return 0;
364 }
365
366 void dj_free_sfx(unsigned char sfx_num)
367 {
368         free(sounds[sfx_num].buf);
369         memset(&sounds[sfx_num], 0, sizeof(sfx_data));
370 }
371
372 /* mod handling */
373
374 char dj_ready_mod(char mod_num)
375 {
376         FILE *tmp;
377 #ifdef _MSC_VER
378         char filename[] = "jnb.tmpmusic.mod";
379 #else
380         char filename[] = "/tmp/jnb.tmpmusic.mod";
381 #endif
382         FILE *fp;
383         int len;
384         switch (mod_num) {
385         case MOD_MENU:
386                 fp = dat_open("jump.mod", datfile_name, "rb");
387                 len = dat_filelen("jump.mod", datfile_name);
388                 break;
389         case MOD_GAME:
390                 fp = dat_open("bump.mod", datfile_name, "rb");
391                 len = dat_filelen("bump.mod", datfile_name);
392                 break;
393         case MOD_SCORES:
394                 fp = dat_open("scores.mod", datfile_name, "rb");
395                 len = dat_filelen("scores.mod", datfile_name);
396                 break;
397         default:
398                 break;
399         }
400
401         if (Mix_PlayingMusic())
402                 Mix_FadeOutMusic(1500);
403
404         if (current_music) {
405                 Mix_FreeMusic(current_music);
406                 current_music = NULL;
407         }
408         tmp = fopen(filename, "wb");
409         for (; len > 0; len--)
410                 fputc(fgetc(fp), tmp);
411         fflush(tmp);
412         fclose(tmp);
413         fclose(fp);
414
415         current_music = Mix_LoadMUS(filename);
416         if (current_music == NULL) {
417                 fprintf(stderr, "Couldn't load music: %s\n", SDL_GetError());
418                 return 0;
419         }
420
421         return 0;
422 }
423
424 char dj_start_mod(void)
425 {
426         Mix_VolumeMusic(0);
427         Mix_PlayMusic(current_music, -1);
428
429         return 0;
430 }
431
432 void dj_stop_mod(void)
433 {
434         Mix_HaltMusic();
435 }
436
437 void dj_set_mod_volume(char volume)
438 {
439         Mix_VolumeMusic(volume);
440 }
441
442 char dj_load_mod(FILE * file_handle, char *filename, char mod_num)
443 {
444         return 0;
445 }
446
447 void dj_free_mod(char mod_num)
448 {
449 }