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