Merge branch 'feature/handle-quit-event'
[jumpnbump.git] / sdl / sound.c
blobc6d129f3921aba140809378a6a4571d2e7d39551
1 /*
2 * sound.c
3 * Copyright (C) 1998 Brainchild Design - http://brainchilddesign.com/
4 *
5 * Copyright (C) 2001 Chuck Mason <cemason@users.sourceforge.net>
7 * Copyright (C) 2002 Florian Schulze <crow@icculus.org>
9 * This file is part of Jump'n'Bump.
11 * Jump'n'Bump is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * Jump'n'Bump is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <stdlib.h>
27 #include <string.h>
28 #include "globals.h"
29 #include <limits.h>
30 #ifndef _MSC_VER
31 #include <unistd.h>
32 #endif
33 #include "SDL.h"
35 #ifndef NO_SDL_MIXER
36 #include "SDL_mixer.h"
38 static Mix_Music *current_music = (Mix_Music *) NULL;
39 #endif
41 sfx_data sounds[NUM_SFX];
43 static int SAMPLECOUNT = 512;
45 #define MAX_CHANNELS 32
47 typedef struct {
48 /* loop flag */
49 int loop;
50 /* The channel step amount... */
51 unsigned int step;
52 /* ... and a 0.16 bit remainder of last step. */
53 unsigned int stepremainder;
54 unsigned int samplerate;
55 /* The channel data pointers, start and end. */
56 signed short* data;
57 signed short* startdata;
58 signed short* enddata;
59 /* Hardware left and right channel volume lookup. */
60 int leftvol;
61 int rightvol;
62 } channel_info_t;
64 channel_info_t channelinfo[MAX_CHANNELS];
66 /* Sample rate in samples/second */
67 int audio_rate = 44100;
68 int global_sfx_volume = 0;
70 // This function loops all active (internal) sound
71 // channels, retrieves a given number of samples
72 // from the raw sound data, modifies it according
73 // to the current (internal) channel parameters,
74 // mixes the per channel samples into the given
75 // mixing buffer, and clamping it to the allowed
76 // range.
78 // This function currently supports only 16bit.
81 static void stopchan(int i)
83 if (channelinfo[i].data) {
84 memset(&channelinfo[i], 0, sizeof(channel_info_t));
90 // This function adds a sound to the
91 // list of currently active sounds,
92 // which is maintained as a given number
93 // (eight, usually) of internal channels.
94 // Returns a handle.
96 int addsfx(signed short *data, int len, int loop, int samplerate, int channel)
98 stopchan(channel);
100 /* We will handle the new SFX. */
101 /* Set pointer to raw data. */
102 channelinfo[channel].data = data;
103 channelinfo[channel].startdata = data;
105 /* Set pointer to end of raw data. */
106 channelinfo[channel].enddata = channelinfo[channel].data + len - 1;
107 channelinfo[channel].samplerate = samplerate;
109 channelinfo[channel].loop = loop;
110 channelinfo[channel].stepremainder = 0;
112 return channel;
116 static void updateSoundParams(int slot, int volume)
118 int rightvol;
119 int leftvol;
122 // Set stepping
123 // MWM 2000-12-24: Calculates proportion of channel samplerate
124 // to global samplerate for mixing purposes.
125 // Patched to shift left *then* divide, to minimize roundoff errors
126 // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz
128 channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/audio_rate);
130 leftvol = volume;
131 rightvol= volume;
133 /* Sanity check, clamp volume. */
134 if (rightvol < 0)
135 rightvol = 0;
136 if (rightvol > 127)
137 rightvol = 127;
139 if (leftvol < 0)
140 leftvol = 0;
141 if (leftvol > 127)
142 leftvol = 127;
144 channelinfo[slot].leftvol = leftvol;
145 channelinfo[slot].rightvol = rightvol;
149 void mix_sound(void *unused, Uint8 *stream, int len)
151 /* Mix current sound data. */
152 /* Data, from raw sound, for right and left. */
153 register int sample;
154 register int dl;
155 register int dr;
157 /* Pointers in audio stream, left, right, end. */
158 signed short* leftout;
159 signed short* rightout;
160 signed short* leftend;
161 /* Step in stream, left and right, thus two. */
162 int step;
164 /* Mixing channel index. */
165 int chan;
167 /* Left and right channel */
168 /* are in audio stream, alternating. */
169 leftout = (signed short *)stream;
170 rightout = ((signed short *)stream)+1;
171 step = 2;
173 /* Determine end, for left channel only */
174 /* (right channel is implicit). */
175 leftend = leftout + (len/4)*step;
177 /* Mix sounds into the mixing buffer. */
178 /* Loop over step*SAMPLECOUNT, */
179 /* that is 512 values for two channels. */
180 while (leftout != leftend) {
181 /* Reset left/right value. */
182 dl = *leftout * 256;
183 dr = *rightout * 256;
185 /* Love thy L2 chache - made this a loop. */
186 /* Now more channels could be set at compile time */
187 /* as well. Thus loop those channels. */
188 for ( chan = 0; chan < MAX_CHANNELS; chan++ ) {
189 /* Check channel, if active. */
190 if (channelinfo[chan].data) {
191 /* Get the raw data from the channel. */
192 /* no filtering */
193 /* sample = *channelinfo[chan].data; */
194 /* linear filtering */
195 sample = (int)(((int)channelinfo[chan].data[0] * (int)(0x10000 - channelinfo[chan].stepremainder))
196 + ((int)channelinfo[chan].data[1] * (int)(channelinfo[chan].stepremainder))) >> 16;
198 /* Add left and right part */
199 /* for this channel (sound) */
200 /* to the current data. */
201 /* Adjust volume accordingly. */
202 dl += sample * (channelinfo[chan].leftvol * global_sfx_volume) / 128;
203 dr += sample * (channelinfo[chan].rightvol * global_sfx_volume) / 128;
204 /* Increment index ??? */
205 channelinfo[chan].stepremainder += channelinfo[chan].step;
206 /* MSB is next sample??? */
207 channelinfo[chan].data += channelinfo[chan].stepremainder >> 16;
208 /* Limit to LSB??? */
209 channelinfo[chan].stepremainder &= 0xffff;
211 /* Check whether we are done. */
212 if (channelinfo[chan].data >= channelinfo[chan].enddata) {
213 if (channelinfo[chan].loop) {
214 channelinfo[chan].data = channelinfo[chan].startdata;
215 } else {
216 stopchan(chan);
222 /* Clamp to range. Left hardware channel. */
223 /* Has been char instead of short. */
224 /* if (dl > 127) *leftout = 127; */
225 /* else if (dl < -128) *leftout = -128; */
226 /* else *leftout = dl; */
228 dl = dl / 256;
229 dr = dr / 256;
231 if (dl > SHRT_MAX)
232 *leftout = SHRT_MAX;
233 else if (dl < SHRT_MIN)
234 *leftout = SHRT_MIN;
235 else
236 *leftout = (signed short)dl;
238 /* Same for right hardware channel. */
239 if (dr > SHRT_MAX)
240 *rightout = SHRT_MAX;
241 else if (dr < SHRT_MIN)
242 *rightout = SHRT_MIN;
243 else
244 *rightout = (signed short)dr;
246 /* Increment current pointers in stream */
247 leftout += step;
248 rightout += step;
252 /* misc handling */
254 char dj_init(void)
256 Uint16 audio_format = MIX_DEFAULT_FORMAT;
257 int audio_channels = 2;
258 int audio_buffers = 4096;
260 open_screen();
262 if (main_info.no_sound)
263 return 0;
265 audio_buffers = SAMPLECOUNT*audio_rate/11025;
267 memset(channelinfo, 0, sizeof(channelinfo));
268 memset(sounds, 0, sizeof(sounds));
270 #ifndef NO_SDL_MIXER
271 if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) {
272 fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError());
273 main_info.no_sound = 1;
274 return 1;
277 Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
278 printf("Opened audio at %dHz %dbit %s, %d bytes audio buffer\n", audio_rate, (audio_format & 0xFF), (audio_channels > 1) ? "stereo" : "mono", audio_buffers);
280 Mix_SetMusicCMD(getenv("MUSIC_CMD"));
282 Mix_SetPostMix(mix_sound, NULL);
283 #else
284 main_info.no_sound = 1;
285 return 1;
286 #endif
288 return 0;
291 void dj_deinit(void)
293 if (main_info.no_sound)
294 return;
296 #ifndef NO_SDL_MIXER
297 Mix_HaltMusic();
298 if (current_music)
299 Mix_FreeMusic(current_music);
300 current_music = NULL;
302 Mix_CloseAudio();
303 #endif
305 SDL_Quit();
308 void dj_start(void)
312 void dj_stop(void)
316 char dj_autodetect_sd(void)
318 return 0;
321 char dj_set_stereo(char flag)
323 return 0;
326 void dj_set_auto_mix(char flag)
330 unsigned short dj_set_mixing_freq(unsigned short freq)
332 return freq;
335 void dj_set_dma_time(unsigned short time)
339 void dj_set_nosound(char flag)
343 /* mix handling */
345 void dj_mix(void)
349 /* sfx handling */
351 char dj_set_num_sfx_channels(char num_channels)
353 return num_channels;
356 void dj_set_sfx_volume(char volume)
358 if (main_info.no_sound)
359 return;
361 SDL_LockAudio();
362 global_sfx_volume = volume*2;
363 SDL_UnlockAudio();
366 void dj_play_sfx(unsigned char sfx_num, unsigned short freq, char volume, char panning, unsigned short delay, char channel)
368 int slot;
370 if (main_info.music_no_sound || main_info.no_sound)
371 return;
373 if (channel<0) {
374 for (slot=0; slot<MAX_CHANNELS; slot++)
375 if (channelinfo[slot].data==NULL)
376 break;
377 if (slot>=MAX_CHANNELS)
378 return;
379 } else
380 slot = channel;
382 SDL_LockAudio();
383 addsfx((short *)sounds[sfx_num].buf, sounds[sfx_num].length, sounds[sfx_num].loop, freq, slot);
384 updateSoundParams(slot, volume*2);
385 SDL_UnlockAudio();
388 char dj_get_sfx_settings(unsigned char sfx_num, sfx_data *data)
390 if (main_info.no_sound)
391 return 0;
393 memcpy(data, &sounds[sfx_num], sizeof(sfx_data));
394 return 0;
397 char dj_set_sfx_settings(unsigned char sfx_num, sfx_data *data)
399 if (main_info.no_sound)
400 return 0;
402 memcpy(&sounds[sfx_num], data, sizeof(sfx_data));
403 return 0;
406 void dj_set_sfx_channel_volume(char channel_num, char volume)
408 if (main_info.no_sound)
409 return;
411 SDL_LockAudio();
412 updateSoundParams(channel_num, volume*2);
413 SDL_UnlockAudio();
416 void dj_stop_sfx_channel(char channel_num)
418 if (main_info.no_sound)
419 return;
421 SDL_LockAudio();
422 stopchan(channel_num);
423 SDL_UnlockAudio();
426 char dj_load_sfx(unsigned char * file_handle, char *filename, int file_length, char sfx_type, unsigned char sfx_num)
428 unsigned int i;
429 unsigned char *src;
430 unsigned short *dest;
432 if (main_info.no_sound)
433 return 0;
435 sounds[sfx_num].buf = malloc(file_length);
437 memcpy(sounds[sfx_num].buf, file_handle, file_length);
439 sounds[sfx_num].length = file_length / 2;
440 src = sounds[sfx_num].buf;
441 dest = (unsigned short *)sounds[sfx_num].buf;
442 for (i=0; i<sounds[sfx_num].length; i++)
444 unsigned short temp;
445 temp = src[0] + (src[1] << 8);
446 *dest = temp;
447 src += 2;
448 dest++;
450 return 0;
453 void dj_free_sfx(unsigned char sfx_num)
455 if (main_info.no_sound)
456 return;
458 free(sounds[sfx_num].buf);
459 memset(&sounds[sfx_num], 0, sizeof(sfx_data));
462 /* mod handling */
464 char dj_ready_mod(char mod_num)
466 #ifndef NO_SDL_MIXER
467 FILE *tmp;
468 int tmp_fd;
469 char* filename;
470 unsigned char *fp;
471 int len;
473 if (main_info.no_sound)
474 return 0;
476 switch (mod_num) {
477 case MOD_MENU:
478 fp = dat_open("jump.mod");
479 len = dat_filelen("jump.mod");
480 break;
481 case MOD_GAME:
482 fp = dat_open("bump.mod");
483 len = dat_filelen("bump.mod");
484 break;
485 case MOD_SCORES:
486 fp = dat_open("scores.mod");
487 len = dat_filelen("scores.mod");
488 break;
489 default:
490 fprintf(stderr, "bogus parameter to dj_ready_mod()\n");
491 fp = NULL;
492 len = 0;
493 break;
496 if (Mix_PlayingMusic())
497 Mix_FadeOutMusic(1500);
499 if (current_music) {
500 Mix_FreeMusic(current_music);
501 current_music = NULL;
504 if (fp == NULL) {
505 return 0;
508 filename = strdup("/tmp/jumpnbump.mod.XXXXXX");
509 tmp_fd = mkstemp(filename);
510 if (tmp_fd == -1) {
511 free(filename);
512 return 0;
514 tmp = fdopen(tmp_fd, "wb");
515 if (!tmp) {
516 free(filename);
517 return 0;
519 fwrite(fp, len, 1, tmp);
520 fflush(tmp);
521 fclose(tmp);
523 current_music = Mix_LoadMUS(filename);
524 unlink(filename);
525 free(filename);
526 if (current_music == NULL) {
527 fprintf(stderr, "Couldn't load music: %s\n", SDL_GetError());
528 return 0;
531 #endif
533 return 0;
536 char dj_start_mod(void)
538 #ifndef NO_SDL_MIXER
539 if (main_info.no_sound)
540 return 0;
542 Mix_VolumeMusic(0);
543 Mix_PlayMusic(current_music, -1);
544 #endif
546 return 0;
549 void dj_stop_mod(void)
551 #ifndef NO_SDL_MIXER
552 if (main_info.no_sound)
553 return;
555 Mix_HaltMusic();
556 #endif
559 void dj_set_mod_volume(char volume)
561 #ifndef NO_SDL_MIXER
562 if (main_info.no_sound)
563 return;
565 Mix_VolumeMusic(volume);
566 #endif
569 char dj_load_mod(unsigned char * file_handle, char *filename, char mod_num)
571 return 0;
574 void dj_free_mod(char mod_num)