1 /*
2 * Copyright 2009-2017 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "externals.h"
26 #include "internals.h"
27
28 #ifdef SAILFISHOS
29 #include <audioresource.h>
30 #include <glib.h>
31 #endif
32
33 #include <SDL.h>
34 #include <SDL_mixer.h>
35
36 #ifdef S60
37 int audio_rate = 11025;
38 #else
39 int audio_rate = 22050;
40 #endif
41
42 Uint16 audio_format = MIX_DEFAULT_FORMAT;
43 int audio_channels = 2;
44 int audio_buffers = 8192;
45
46 static mus_t mus;
47 static char *next_mus = NULL;
48 static int next_fadein = 0;
49 static int next_loop = -1;
50 static SDL_TimerID timer_id = NULL_TIMER;
51
52 static int sound_on = 0;
53
54 struct _mus_t {
55 Mix_Music *mus;
56 SDL_RWops *rw;
57 };
58
snd_enabled(void)59 int snd_enabled(void)
60 {
61 return sound_on;
62 }
63
mus_callback(void * aux)64 static void mus_callback(void *aux)
65 {
66 if (!timer_id)
67 return;
68 if (snd_playing_mus())
69 return;
70 if (mus)
71 snd_free_mus(mus);
72 mus = NULL;
73 if (next_mus) {
74 if (snd_play_mus(next_mus, next_fadein, next_loop) < 0)
75 game_res_err_msg(next_mus, debug_sw);
76 free(next_mus);
77 next_mus = NULL;
78 }
79 SDL_RemoveTimer(timer_id);
80 timer_id = NULL_TIMER;
81 }
82
callback(Uint32 interval,void * aux)83 static Uint32 callback(Uint32 interval, void *aux)
84 {
85 push_user_event(mus_callback, aux);
86 return interval;
87 }
88
snd_hz(void)89 int snd_hz(void)
90 {
91 int freq = 0;
92 if (sound_on)
93 Mix_QuerySpec(&freq, NULL, NULL);
94 return freq;
95 }
96
97 int nosound_sw = 0;
snd_pause(int on)98 void snd_pause(int on)
99 {
100 if (!sound_on)
101 return;
102 if (on) {
103 Mix_Pause(-1);
104 Mix_PauseMusic();
105 } else {
106 Mix_Resume(-1);
107 Mix_ResumeMusic();
108 }
109 return;
110 }
111
_snd_open(int hz)112 static int _snd_open(int hz)
113 {
114 int chunk;
115 if (!hz)
116 hz = audio_rate;
117 else
118 audio_rate = hz;
119 chunk = (chunksize_sw>0)?chunksize_sw:DEFAULT_CHUNKSIZE;
120 audio_buffers = (audio_rate / 11025) * chunk;
121 if (audio_buffers <= 0) /* wrong parameter? */
122 audio_buffers = DEFAULT_CHUNKSIZE;
123 #ifdef __EMSCRIPTEN__
124 if (Mix_OpenAudioDevice(44100, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, 4096, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) {
125 #else
126 if (Mix_OpenAudio(hz, audio_format, audio_channels, audio_buffers)) {
127 #endif
128 fprintf(stderr, "Unable to open audio!\n");
129 return -1;
130 }
131 sound_on = 1;
132 Mix_ChannelFinished(game_channel_finished);
133 return 0;
134 }
135
136 #ifdef SAILFISHOS
137 static audioresource_t *audio_resource = NULL;
138
139 static void on_audio_resource_acquired(audioresource_t *ar, bool acquired, void *phz)
140 {
141 if (acquired && !sound_on)
142 _snd_open(*(int *)phz);
143 }
144
145 int snd_open(int hz)
146 {
147 if (nosound_sw)
148 return -1;
149 if (sound_on)
150 snd_close(); /* reopen */
151 if (!audio_resource) {
152 audio_resource = audioresource_init(AUDIO_RESOURCE_GAME,
153 on_audio_resource_acquired, &hz);
154 if (!audio_resource)
155 return -1;
156 audioresource_acquire(audio_resource);
157 }
158 while (!sound_on) {
159 fprintf(stderr, "Waiting for audio resource to be acquired...\n");
160 g_main_context_iteration(NULL, true);
161 }
162 return 0;
163 }
164 #else
165 int snd_open(int hz)
166 {
167 if (nosound_sw)
168 return -1;
169 if (sound_on)
170 snd_close(); /* reopen */
171 return _snd_open(hz);
172 }
173 #endif
174
175 int snd_init(int hz)
176 {
177 if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
178 fprintf(stderr, "Unable to init audio!\n");
179 return -1;
180 }
181 return snd_open(hz);
182 }
183
184 int snd_volume_mus(int vol)
185 {
186 if (!sound_on)
187 return 0;
188 Mix_Volume(-1, vol);
189 return Mix_VolumeMusic(vol);
190 }
191
192 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
193 #define SND_DEFAULT_FORMAT AUDIO_S16LSB
194 #else
195 #define SND_DEFAULT_FORMAT AUDIO_S16MSB
196 #endif
197
198 #define MIXER_VERSION_ATLEAST(a,b,c) (SDL_VERSIONNUM(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION,SDL_MIXER_PATCHLEVEL) >= SDL_VERSIONNUM(a, b, c))
199
200 wav_t snd_load_mem(int fmt, const short *data, size_t len)
201 {
202 int freq = 22050, ffreq;
203 SDL_AudioCVT wavecvt;
204 Mix_Chunk *chunk;
205 size_t size = len * sizeof(short);
206
207 freq = snd_hz();
208
209 if (fmt & SND_FMT_11)
210 ffreq = 11025;
211 else if (fmt & SND_FMT_22)
212 ffreq = 22050;
213 else
214 ffreq = 44100;
215
216 if (audio_format != SND_DEFAULT_FORMAT ||
217 audio_channels != ((fmt & SND_FMT_STEREO) ? 2:1) ||
218 ffreq != freq) {
219 if (SDL_BuildAudioCVT(&wavecvt,
220 SND_DEFAULT_FORMAT, (fmt & SND_FMT_STEREO) ? 2:1, ffreq,
221 audio_format, audio_channels, freq) < 0)
222 return NULL;
223
224 wavecvt.len = size;
225 wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len * wavecvt.len_mult);
226
227 if (!wavecvt.buf)
228 return NULL;
229
230 SDL_memcpy(wavecvt.buf, data, size);
231
232 if (SDL_ConvertAudio(&wavecvt) < 0) {
233 SDL_free(wavecvt.buf);
234 return NULL;
235 }
236 chunk = Mix_QuickLoad_RAW(wavecvt.buf, wavecvt.len_cvt);
237 } else {
238 Uint8 *b = (Uint8 *)SDL_calloc(1, size);
239 if (!b)
240 return NULL;
241 SDL_memcpy(b, data, size);
242 chunk = Mix_QuickLoad_RAW(b, size);
243 }
244 if (!chunk)
245 return NULL;
246 chunk->allocated = 1;
247 return (wav_t)chunk;
248 }
249
250 wav_t snd_load_wav(const char *fname)
251 {
252 SDL_RWops *rw;
253 wav_t r;
254 if (!sound_on)
255 return NULL;
256 if (!fname || !*fname)
257 return NULL;
258 rw = RWFromIdf(instead_idf(), fname);
259 if (!rw || !(r = (wav_t)Mix_LoadWAV_RW(rw, 1))) {
260 return NULL;
261 }
262 return r;
263 }
264
265 void snd_free_wav(wav_t w)
266 {
267 if (!w)
268 return;
269 /* Mix_HaltChannel(-1); */
270 Mix_FreeChunk((Mix_Chunk*)w);
271 }
272
273 static int mix_fn = 0;
274
275 void snd_halt_chan(int han, int ms)
276 {
277 if (han >= MIX_CHANNELS)
278 han %= MIX_CHANNELS;
279 if (han == -1 && mix_fn) /* forever wait */
280 return;
281 if (ms)
282 Mix_FadeOutChannel(han, ms);
283 else {
284 Mix_HaltChannel(han);
285 }
286 }
287
288 mus_t snd_load_mus(const char *fname)
289 {
290 mus_t mus = NULL;
291 if (!sound_on)
292 return NULL;
293 mus = malloc(sizeof(struct _mus_t));
294 if (!mus)
295 return NULL;
296 mus->rw = RWFromIdf(instead_idf(), fname);
297 if (!mus->rw)
298 goto err;
299 #if MIXER_VERSION_ATLEAST(2,0,0)
300 mus->mus = Mix_LoadMUS_RW(mus->rw, SDL_FALSE);
301 #else
302 mus->mus = Mix_LoadMUS_RW(mus->rw);
303 #endif
304 if (!mus->mus)
305 goto err1;
306 return mus;
307 err1:
308 SDL_RWclose(mus->rw);
309 err:
310 free(mus);
311 return NULL;
312 }
313
314 extern void game_music_finished(void);
315
316 int snd_play_mus(char *fname, int ms, int loop)
317 {
318 if (!sound_on)
319 return 0;
320 if (snd_playing_mus()) {
321 if (next_mus) {
322 free(next_mus);
323 }
324 next_mus = strdup(fname);
325 next_fadein = ms;
326 next_loop = loop;
327 if (!timer_id)
328 timer_id = SDL_AddTimer(200, callback, NULL);
329 return 1;
330 }
331 if (mus)
332 snd_free_mus(mus);
333
334 mus = snd_load_mus(fname);
335 if (!mus)
336 return -1;
337 if (loop >= 0)
338 Mix_HookMusicFinished(game_music_finished);
339 else
340 Mix_HookMusicFinished(NULL);
341 if (ms)
342 Mix_FadeInMusic(mus->mus, loop, ms);
343 else
344 Mix_PlayMusic(mus->mus, loop);
345 snd_volume_mus(snd_volume_mus(-1)); /* SDL hack? */
346 return 0;
347 }
348
349 void snd_stop_mus(int ms)
350 {
351 if (!sound_on)
352 return;
353 if (mix_fn)
354 return;
355 Mix_HookMusicFinished(NULL);
356 if (ms)
357 Mix_FadeOutMusic(ms);
358 else
359 Mix_HaltMusic();
360 }
361
362 int snd_playing_mus(void)
363 {
364 if (!sound_on)
365 return 0;
366 if (Mix_PlayingMusic() | Mix_FadingMusic())
367 return 1;
368 return 0;
369 }
370
371 int snd_playing(int channel)
372 {
373 if (!sound_on)
374 return 0;
375 if (channel >= MIX_CHANNELS)
376 channel %= MIX_CHANNELS;
377 if (channel < 0)
378 channel = -1;
379 return Mix_Playing(channel);
380 }
381
382 int snd_panning(int channel, int left, int right)
383 {
384 if (channel >= MIX_CHANNELS)
385 channel %= MIX_CHANNELS;
386 if (channel < 0)
387 channel = -1;
388 return Mix_SetPanning(channel, left, right);
389 }
390
391
392 void snd_free_mus(mus_t mus)
393 {
394 int to_close = 0;
395 if (!sound_on)
396 return;
397 if (!mus)
398 return;
399 Mix_HaltMusic();
400 if (mus->mus) {
401 #ifdef _SDL_MOD_BUG
402 if ((Mix_GetMusicType(mus->mus) == MUS_MOD) && !MIXER_VERSION_ATLEAST(1, 2, 12))
403 SDL_RWclose(mus->rw);
404 #endif
405 if (MIXER_VERSION_ATLEAST(1, 2, 12) && Mix_GetMusicType(mus->mus) != MUS_MP3) {
406 to_close = 1;
407 }
408 Mix_FreeMusic((Mix_Music*) mus->mus);
409 if (to_close)
410 SDL_RWclose(mus->rw);
411 }
412 free(mus);
413 }
414
415 int snd_play(void *chunk, int channel, int loop)
416 {
417 if (!sound_on)
418 return -1;
419 if (!chunk)
420 return -1;
421 if (channel >= MIX_CHANNELS)
422 channel %= MIX_CHANNELS;
423 if (channel < 0)
424 channel = -1;
425 if (channel != -1)
426 snd_halt_chan(channel, 0);
427 snd_volume_mus(snd_volume_mus(-1)); /* SDL hack? */
428 return Mix_PlayChannel(channel, (Mix_Chunk*)chunk, loop);
429 }
430
431 void snd_mus_callback(void (*fn)(void *udata, unsigned char *stream, int len), void *arg)
432 {
433 mix_fn = !!fn;
434 Mix_HookMusic(fn, arg);
435 }
436
437 void snd_close(void)
438 {
439 if (!sound_on)
440 return;
441 Mix_HookMusic(NULL, NULL);
442 Mix_ChannelFinished(NULL);
443 if (timer_id) {
444 SDL_RemoveTimer(timer_id);
445 timer_id = NULL_TIMER;
446 }
447 Mix_HaltChannel(-1);
448 Mix_HookMusicFinished(NULL);
449 Mix_HaltMusic();
450 if (mus)
451 snd_free_mus(mus);
452 mus = NULL;
453 if (next_mus)
454 free(next_mus);
455 next_mus = NULL;
456 #ifndef __EMSCRIPTEN__
457 Mix_CloseAudio();
458 #endif
459 sound_on = 0;
460 #ifdef SAILFISHOS
461 audioresource_release(audio_resource);
462 audioresource_free(audio_resource);
463 audio_resource = NULL;
464 #endif
465 }
466
467 void snd_done(void)
468 {
469 if (sound_on)
470 snd_close();
471 SDL_QuitSubSystem(SDL_INIT_AUDIO);
472 }
473
474 int snd_vol_from_pcn(int v)
475 {
476 return (v * 127) / 100;
477 }
478
479 int snd_vol_to_pcn(int v)
480 {
481 return (v * 100) / 127;
482 }
483