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