Rev 5 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 1 | pmbaty | 1 | /* | 
| 2 |  * src/syssnd.c | ||
| 3 |  * | ||
| 4 |  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved. | ||
| 5 |  * | ||
| 6 |  * The use and distribution terms for this software are contained in the file | ||
| 7 |  * named README, which can be found in the root of this distribution. By | ||
| 8 |  * using this software in any fashion, you are agreeing to be bound by the | ||
| 9 |  * terms of this license. | ||
| 10 |  * | ||
| 11 |  * You must not remove this notice, or any other, from this software. | ||
| 12 |  */ | ||
| 13 | |||
| 11 | pmbaty | 14 | #include <SDL2/SDL.h> | 
| 1 | pmbaty | 15 | #include <stdlib.h> | 
| 16 | #include <memory.h> | ||
| 17 | |||
| 18 | #include "system.h" | ||
| 19 | #include "game.h" | ||
| 20 | #include "syssnd.h" | ||
| 21 | |||
| 22 | |||
| 23 | #define ADJVOL(S) (((S) *sndVol) / SDL_MIX_MAXVOLUME) | ||
| 24 | |||
| 25 | |||
| 26 | static U8 isAudioActive = FALSE; | ||
| 5 | pmbaty | 27 | static channel_t channels[SYSSND_MIXCHANNELS]; | 
| 1 | pmbaty | 28 | |
| 29 | static U8 sndVol = SDL_MIX_MAXVOLUME; /* internal volume */ | ||
| 30 | static U8 sndUVol = SYSSND_MAXVOL; /* user-selected volume */ | ||
| 31 | static U8 sndMute = FALSE; /* mute flag */ | ||
| 32 | |||
| 33 | |||
| 34 | /* | ||
| 35 |  * prototypes | ||
| 36 |  */ | ||
| 5 | pmbaty | 37 | static void end_channel (channel_t *); | 
| 1 | pmbaty | 38 | |
| 39 | |||
| 40 | /* | ||
| 41 |  * Callback -- this is also where all sound mixing is done | ||
| 42 |  * | ||
| 43 |  * Note: it may not be that much a good idea to do all the mixing here ; it | ||
| 44 |  * may be more efficient to mix samples every frame, or maybe everytime a | ||
| 45 |  * new sound is sent to be played. I don't know. | ||
| 46 |  */ | ||
| 47 | void syssnd_callback (void *userdata, U8 *stream, int len) | ||
| 48 | { | ||
| 5 | pmbaty | 49 | channel_t *channel; | 
| 50 |    U8 channel_index; | ||
| 51 |    S16 final_sample; | ||
| 1 | pmbaty | 52 |    U32 i; | 
| 53 | |||
| 5 | pmbaty | 54 |    //  | 
| 55 | for (i = 0; i < (U32)len; i += 2) | ||
| 56 |    { | ||
| 57 | final_sample = 0; | ||
| 1 | pmbaty | 58 | |
| 5 | pmbaty | 59 | for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++) | 
| 1 | pmbaty | 60 |       { | 
| 5 | pmbaty | 61 | channel = &channels[channel_index]; // quick access to channel | 
| 62 | |||
| 63 | if (channel->loop == 0) | ||
| 64 | continue; | ||
| 65 | |||
| 66 |          // channel is active | ||
| 67 | if (channel->len > 0) | ||
| 1 | pmbaty | 68 |          { | 
| 5 | pmbaty | 69 |             // not ending | 
| 70 | final_sample += ADJVOL (*((U16 *) channel->buf)); | ||
| 71 | channel->buf += 2; | ||
| 72 | channel->len -= 2; | ||
| 73 |          } | ||
| 74 |          else | ||
| 75 |          { | ||
| 76 |             // ending | ||
| 77 | if (channel->loop > 0) | ||
| 78 | channel->loop--; | ||
| 79 | |||
| 80 | if (channel->loop) | ||
| 1 | pmbaty | 81 |             { | 
| 5 | pmbaty | 82 |                // just loop | 
| 83 | channel->buf = channel->snd->buf; | ||
| 84 | channel->len = channel->snd->len; | ||
| 85 | |||
| 86 | final_sample += ADJVOL (*((U16 *) channel->buf)); | ||
| 87 | channel->buf += 2; | ||
| 88 | channel->len -= 2; | ||
| 1 | pmbaty | 89 |             } | 
| 90 |             else | ||
| 5 | pmbaty | 91 | end_channel (channel); // end for real | 
| 1 | pmbaty | 92 |          } | 
| 93 |       } | ||
| 94 | |||
| 5 | pmbaty | 95 | *((S16 *) &stream[i]) = (sndMute ? 0 : final_sample); | 
| 1 | pmbaty | 96 |    } | 
| 97 | } | ||
| 98 | |||
| 99 | |||
| 5 | pmbaty | 100 | static void end_channel (channel_t *channel) | 
| 1 | pmbaty | 101 | { | 
| 5 | pmbaty | 102 | channel->loop = 0; | 
| 103 | if (channel->snd->dispose) | ||
| 104 | syssnd_free (channel->snd); | ||
| 105 | channel->snd = NULL; | ||
| 1 | pmbaty | 106 | } | 
| 107 | |||
| 108 | |||
| 109 | void syssnd_init (void) | ||
| 110 | { | ||
| 5 | pmbaty | 111 |    SDL_AudioSpec desired; | 
| 1 | pmbaty | 112 |    U16 c; | 
| 113 | |||
| 114 | if (SDL_InitSubSystem (SDL_INIT_AUDIO) < 0) | ||
| 115 | return; | ||
| 116 | |||
| 117 | desired.freq = SYSSND_FREQ; | ||
| 5 | pmbaty | 118 | desired.format = AUDIO_S16; | 
| 1 | pmbaty | 119 | desired.channels = SYSSND_CHANNELS; | 
| 120 | desired.samples = SYSSND_MIXSAMPLES; | ||
| 121 | desired.callback = syssnd_callback; | ||
| 122 | desired.userdata = NULL; | ||
| 123 | |||
| 5 | pmbaty | 124 | if (SDL_OpenAudio (&desired, NULL) < 0) | 
| 1 | pmbaty | 125 | return; | 
| 126 | |||
| 127 | if (sysarg_args_vol != 0) | ||
| 128 |    { | ||
| 129 | sndUVol = sysarg_args_vol; | ||
| 130 | sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL; | ||
| 131 |    } | ||
| 132 | |||
| 133 | for (c = 0; c < SYSSND_MIXCHANNELS; c++) | ||
| 5 | pmbaty | 134 | channels[c].loop = 0; /* deactivate */ | 
| 1 | pmbaty | 135 | |
| 136 | isAudioActive = TRUE; | ||
| 137 | SDL_PauseAudio (0); | ||
| 138 | } | ||
| 139 | |||
| 140 | |||
| 141 | /* | ||
| 142 |  * Shutdown | ||
| 143 |  */ | ||
| 144 | void syssnd_shutdown (void) | ||
| 145 | { | ||
| 146 | if (!isAudioActive) | ||
| 147 | return; | ||
| 148 | |||
| 149 | SDL_CloseAudio (); | ||
| 150 | isAudioActive = FALSE; | ||
| 151 | } | ||
| 152 | |||
| 153 | |||
| 154 | /* | ||
| 155 |  * Toggle mute | ||
| 156 |  * | ||
| 157 |  * When muted, sounds are still managed but not sent to the dsp, hence | ||
| 158 |  * it is possible to un-mute at any time. | ||
| 159 |  */ | ||
| 160 | void syssnd_toggleMute (void) | ||
| 161 | { | ||
| 162 | sndMute = !sndMute; | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | void syssnd_vol (S8 d) | ||
| 167 | { | ||
| 168 | if ((d < 0 && sndUVol > 0) || (d > 0 && sndUVol < SYSSND_MAXVOL)) | ||
| 169 |    { | ||
| 170 | sndUVol += d; | ||
| 171 | sndVol = SDL_MIX_MAXVOLUME * sndUVol / SYSSND_MAXVOL; | ||
| 172 |    } | ||
| 173 | } | ||
| 174 | |||
| 175 | |||
| 176 | /* | ||
| 177 |  * Play a sound | ||
| 178 |  * | ||
| 179 |  * loop: number of times the sound should be played, -1 to loop forever | ||
| 180 |  * returns: channel number, or -1 if none was available | ||
| 181 |  * | ||
| 182 |  * NOTE if sound is already playing, simply reset it (i.e. can not have | ||
| 183 |  * twice the same sound playing -- tends to become noisy when too many | ||
| 184 |  * bad guys die at the same time). | ||
| 185 |  */ | ||
| 186 | S8 syssnd_play (sound_t *sound, S8 loop) | ||
| 187 | { | ||
| 5 | pmbaty | 188 |    S8 channel_index; | 
| 1 | pmbaty | 189 | |
| 5 | pmbaty | 190 | if ((sound == NULL) || !isAudioActive) | 
| 191 | return (-1); | ||
| 1 | pmbaty | 192 | |
| 5 | pmbaty | 193 | for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++) | 
| 194 | if ((channels[channel_index].snd == sound) || (channels[channel_index].loop == 0)) | ||
| 195 | break; // look for a free channel slot | ||
| 196 | if (channel_index == SYSSND_MIXCHANNELS) | ||
| 197 | return (-1); | ||
| 1 | pmbaty | 198 | |
| 5 | pmbaty | 199 | channels[channel_index].loop = loop; | 
| 200 | channels[channel_index].snd = sound; | ||
| 201 | channels[channel_index].buf = sound->buf; | ||
| 202 | channels[channel_index].len = sound->len; | ||
| 1 | pmbaty | 203 | |
| 5 | pmbaty | 204 | return (channel_index); | 
| 1 | pmbaty | 205 | } | 
| 206 | |||
| 207 | |||
| 208 | /* | ||
| 209 |  * Pause | ||
| 210 |  * | ||
| 211 |  * pause: TRUE or FALSE | ||
| 212 |  * clear: TRUE to cleanup all sounds and make sure we start from scratch | ||
| 213 |  */ | ||
| 5 | pmbaty | 214 | void syssnd_pause (U8 want_pause, U8 want_clear) | 
| 1 | pmbaty | 215 | { | 
| 5 | pmbaty | 216 |    U8 channel_index; | 
| 1 | pmbaty | 217 | |
| 218 | if (!isAudioActive) | ||
| 219 | return; | ||
| 220 | |||
| 5 | pmbaty | 221 | if (want_clear) | 
| 222 | for (channel_index = 0; channel_index < SYSSND_MIXCHANNELS; channel_index++) | ||
| 223 | channels[channel_index].loop = 0; | ||
| 1 | pmbaty | 224 | |
| 5 | pmbaty | 225 | SDL_PauseAudio (want_pause ? 1 : 0); | 
| 1 | pmbaty | 226 | } | 
| 227 | |||
| 228 | |||
| 229 | /* | ||
| 230 |  * Stop a channel | ||
| 231 |  */ | ||
| 232 | void syssnd_stopchan (S8 chan) | ||
| 233 | { | ||
| 234 | if (chan < 0 || chan > SYSSND_MIXCHANNELS) | ||
| 235 | return; | ||
| 236 | |||
| 5 | pmbaty | 237 | if (channels[chan].snd) | 
| 238 | end_channel (&channels[chan]); | ||
| 1 | pmbaty | 239 | } | 
| 240 | |||
| 241 | |||
| 242 | /* | ||
| 243 |  * Stop a sound | ||
| 244 |  */ | ||
| 245 | void syssnd_stopsound (sound_t *sound) | ||
| 246 | { | ||
| 247 |    U8 i; | ||
| 248 | |||
| 249 | if (!sound) | ||
| 250 | return; | ||
| 251 | |||
| 252 | for (i = 0; i < SYSSND_MIXCHANNELS; i++) | ||
| 5 | pmbaty | 253 | if (channels[i].snd == sound) | 
| 254 | end_channel (&channels[i]); | ||
| 1 | pmbaty | 255 | } | 
| 256 | |||
| 257 | |||
| 258 | /* | ||
| 259 |  * See if a sound is playing | ||
| 260 |  */ | ||
| 261 | int syssnd_isplaying (sound_t *sound) | ||
| 262 | { | ||
| 5 | pmbaty | 263 |    U8 i; | 
| 1 | pmbaty | 264 | |
| 265 | for (i = 0; i < SYSSND_MIXCHANNELS; i++) | ||
| 5 | pmbaty | 266 | if (channels[i].snd == sound) | 
| 267 | return (1); | ||
| 1 | pmbaty | 268 | |
| 5 | pmbaty | 269 | return (0); | 
| 1 | pmbaty | 270 | } | 
| 271 | |||
| 272 | |||
| 273 | /* | ||
| 274 |  * Stops all channels. | ||
| 275 |  */ | ||
| 276 | void syssnd_stopall (void) | ||
| 277 | { | ||
| 278 |    U8 i; | ||
| 279 | |||
| 280 | for (i = 0; i < SYSSND_MIXCHANNELS; i++) | ||
| 5 | pmbaty | 281 | if (channels[i].snd) | 
| 282 | end_channel (&channels[i]); | ||
| 1 | pmbaty | 283 | } | 
| 284 | |||
| 285 | |||
| 286 | /* | ||
| 287 |  * Load a sound. | ||
| 288 |  */ | ||
| 289 | sound_t *syssnd_load (char *name) | ||
| 290 | { | ||
| 2 | pmbaty | 291 | char *soundfile_fullpathname; | 
| 1 | pmbaty | 292 | sound_t *s; | 
| 293 |    SDL_AudioSpec audiospec; | ||
| 294 | |||
| 2 | pmbaty | 295 |    /* alloc space for sound file's full pathname and build it */ | 
| 296 | soundfile_fullpathname = (char *) malloc (1024); | ||
| 297 | sprintf_s (soundfile_fullpathname, 1024, "%s/%s", sys_getbasepath (), name); | ||
| 298 | |||
| 1 | pmbaty | 299 |    /* alloc sound */ | 
| 300 | s = malloc (sizeof (sound_t)); | ||
| 301 | |||
| 302 |    /* read */ | ||
| 303 |    /* second param == 1 -> close source once read */ | ||
| 5 | pmbaty | 304 | if (!SDL_LoadWAV_RW (SDL_RWFromFile (soundfile_fullpathname, "rb"), 1, &audiospec, &(s->buf), &(s->len))) | 
| 1 | pmbaty | 305 |    { | 
| 306 | free (s); | ||
| 307 | return NULL; | ||
| 308 |    } | ||
| 5 | pmbaty | 309 | free (soundfile_fullpathname); | 
| 1 | pmbaty | 310 | |
| 311 | s->dispose = FALSE; | ||
| 312 | |||
| 313 | return s; | ||
| 314 | } | ||
| 315 | |||
| 316 | |||
| 317 | /* | ||
| 318 |  * | ||
| 319 |  */ | ||
| 320 | void syssnd_free (sound_t *s) | ||
| 321 | { | ||
| 322 | if (!s) | ||
| 323 | return; | ||
| 324 | |||
| 325 | if (s->buf) | ||
| 326 | SDL_FreeWAV (s->buf); | ||
| 327 | |||
| 328 | s->buf = NULL; | ||
| 329 | s->len = 0; | ||
| 330 | } |