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 | } |