1 /*
2  * Copyright 2009-2018 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 static int cur_vol = 0;
29 
game_change_vol(int d,int val)30 int game_change_vol(int d, int val)
31 {
32 	int v = snd_volume_mus(-1);
33 	int pc = snd_vol_to_pcn(v);
34 	int opc = pc;
35 	if (d) {
36 		pc += d;
37 		if (pc < 0)
38 			pc = 0;
39 		if (pc > 100)
40 			pc = 100;
41 		while (snd_vol_to_pcn(v) != pc)
42 			v += (d<0)?-1:1;
43 	} else {
44 		v = val;
45 		pc = snd_vol_to_pcn(v);
46 	}
47 	if (!pc)
48 		v = 0;
49 	snd_volume_mus(v);
50 	if (opc && !pc) {
51 		game_stop_mus(0);
52 	}
53 	if (!opc && pc) {
54 		game_music_player();
55 	}
56 	cur_vol = snd_volume_mus(-1);
57 	if (!nosound_sw)
58 		opt_vol = cur_vol;
59 	return 0;
60 }
61 
62 #define MAX_WAVS SND_CHANNELS * 2
63 
64 static LIST_HEAD(sounds);
65 static int sounds_nr = 0;
66 
67 typedef struct {
68 	struct list_node list;
69 	char	*fname;
70 	wav_t	wav;
71 	int	loaded;
72 	int	system;
73 	int	fmt;
74 	short	*buf;
75 	size_t	len;
76 } _snd_t;
77 typedef struct {
78 	_snd_t *snd;
79 	int	loop;
80 	int	channel;
81 } _snd_req_t;
82 static _snd_t *channels[SND_CHANNELS];
83 static _snd_req_t sound_reqs[SND_CHANNELS];
84 static void sound_play(_snd_t *sn, int chan, int loop);
85 
sound_play_click(void)86 void sound_play_click(void)
87 {
88 	sound_play(game_theme.click, -1, 1);
89 }
90 
sound_callback(void * aux)91 static void sound_callback(void *aux)
92 {
93 	_snd_req_t *r;
94 	int channel = *((int *)aux);
95 	int c = channel % SND_CHANNELS;
96 	free(aux);
97 /*	fprintf(stderr, "finished: %d\n", channel); */
98 	channels[c] = NULL;
99 	r = &sound_reqs[c];
100 	if (r->snd) {
101 		_snd_t *s = r->snd;
102 		r->snd = NULL;
103 		sound_play(s, channel, r->loop);
104 	} else {
105 		snd_halt_chan(channel, 0); /* to avoid races */
106 	}
107 }
108 
game_channel_finished(int channel)109 void game_channel_finished(int channel) /* SDL callback */
110 {
111 	int *i = malloc(sizeof(channel));
112 	if (!i) {
113 		fprintf(stderr, "game_channel_finished: No memory\n");
114 		return;
115 	}
116 	*i = channel;
117 	push_user_event(sound_callback, i);
118 }
119 
sound_playing(_snd_t * snd)120 static int  sound_playing(_snd_t *snd)
121 {
122 	int i;
123 	for (i = 0; i < SND_CHANNELS; i++) {
124 		if (channels[i] == snd)
125 			return i;
126 		if (sound_reqs[i].snd == snd)
127 			return i;
128 	}
129 	return -1;
130 }
131 
sound_channel(int i)132 static const char *sound_channel(int i)
133 {
134 	_snd_t *sn;
135 	if (i >= SND_CHANNELS)
136 		i = i % SND_CHANNELS;
137 	if (i == -1) {
138 		for (i = 0; i < SND_CHANNELS; i++) {
139 			sn = channels[i];
140 			if (sn && !sn->system)
141 				return sn->fname;
142 		}
143 		return NULL;
144 	}
145 	sn = channels[i];
146 	if (!sn)
147 		return NULL;
148 	if (sn->system)
149 		return NULL; /* hidden system sound */
150 	return sn->fname;
151 }
152 
sound_free(_snd_t * sn)153 static void sound_free(_snd_t *sn)
154 {
155 	if (!sn)
156 		return;
157 	list_del(&sn->list);
158 	sounds_nr --;
159 	free(sn->fname);
160 	snd_free_wav(sn->wav);
161 	if (sn->buf)
162 		free(sn->buf);
163 	free(sn);
164 }
165 
sounds_shrink(void)166 static void sounds_shrink(void)
167 {
168 	_snd_t *pos, *pos2;
169 	_snd_t *sn;
170 	pos = list_top(&sounds, _snd_t, list);
171 /*	fprintf(stderr,"shrink try\n"); */
172 	while (pos && sounds_nr > MAX_WAVS) {
173 		sn = (_snd_t*)pos;
174 		if (sound_playing(sn) != -1 || sn->loaded) {
175 			pos = list_next(&sounds, pos, list);
176 			continue;
177 		}
178 		pos2 = list_next(&sounds, pos, list);
179 		sound_free(sn);
180 		pos = pos2;
181 /*		fprintf(stderr,"shrink by 1\n"); */
182 	}
183 }
184 
sounds_free(void)185 static void sounds_free(void)
186 {
187 	int i = 0;
188 	_snd_t *pos, *pos2;
189 	_snd_t *sn;
190 	pos = list_top(&sounds, _snd_t, list);
191 
192 	snd_halt_chan(-1, 0); /* halt sounds */
193 	while (pos) {
194 		sn = (_snd_t*)pos;
195 		pos2 = list_next(&sounds, pos, list);
196 		if (sn->system)
197 			sn->loaded = 1; /* ref by system only */
198 		else
199 			sound_free(sn);
200 		pos = pos2;
201 	}
202 	for (i = 0; i < SND_CHANNELS; i++) {
203 		channels[i] = NULL;
204 		sound_reqs[i].snd = NULL;
205 	}
206 /*	sounds_nr = 0;
207 	fprintf(stderr, "%d\n", sounds_nr); */
208 	input_uevents(); /* all callbacks */
209 }
210 
sound_find(const char * fname)211 static _snd_t *sound_find(const char *fname)
212 {
213 	_snd_t *pos = NULL;
214 	_snd_t *sn;
215 	if (!fname)
216 		return NULL;
217 	list_for_each(&sounds, pos, list) {
218 		sn = (_snd_t*)pos;
219 		if (!strcmp(fname, sn->fname)) {
220 			list_del(&sn->list);
221 			list_add(&sounds, &sn->list); /* move it on head */
222 			return sn;
223 		}
224 	}
225 	return NULL;
226 }
227 
sound_find_channel(void)228 static int sound_find_channel(void)
229 {
230 	int i;
231 	for (i = 0; i < SND_CHANNELS; i ++) {
232 		if (!channels[i] && !sound_reqs[i].snd)
233 			return i;
234 	}
235 	return -1;
236 }
237 
sound_play(_snd_t * sn,int chan,int loop)238 static void sound_play(_snd_t *sn, int chan, int loop)
239 {
240 	int c;
241 	if (!sn)
242 		return;
243 	if (chan == -1) {
244 		c = sound_find_channel();
245 		if (c == -1)
246 			return; /* all channels are busy */
247 	} else
248 		c = chan;
249 	if (channels[c]) {
250 		sound_reqs[c].snd = sn;
251 		sound_reqs[c].loop = loop;
252 		sound_reqs[c].channel = chan;
253 		snd_halt_chan(chan, 0); /* work in callback */
254 		input_uevents(); /* all callbacks */
255 		return;
256 	}
257 	c = snd_play(sn->wav, c, loop - 1);
258 /*	fprintf(stderr, "added: %d\n", c); */
259 	if (c == -1)
260 		return;
261 	channels[c] = sn;
262 }
263 
sound_add(const char * fname,int fmt,short * buf,int len)264 static _snd_t *sound_add(const char *fname, int fmt, short *buf, int len)
265 {
266 	wav_t w;
267 	_snd_t *sn;
268 	if (!fname || !*fname)
269 		return NULL;
270 	sn = malloc(sizeof(_snd_t));
271 	if (!sn)
272 		return NULL;
273 	memset(sn, 0, sizeof(*sn));
274 /*	LIST_HEAD_INIT(&sn->list); */
275 	sn->fname = strdup(fname);
276 	sn->loaded = 0;
277 	sn->system = 0;
278 	sn->buf = buf;
279 	sn->len = len;
280 	sn->fmt = fmt;
281 	if (!sn->fname) {
282 		free(sn);
283 		return NULL;
284 	}
285 	if (buf)
286 		w = snd_load_mem(fmt, buf, len);
287 	else
288 		w = snd_load_wav(fname);
289 	if (!w) {
290 		if (snd_enabled())
291 			game_res_err_msg(fname, debug_sw);
292 		goto err;
293 	}
294 	sn->wav = w;
295 
296 	sounds_shrink();
297 
298 	list_add(&sounds, &sn->list);
299 	sounds_nr ++;
300 	return sn;
301 err:
302 	free(sn->fname);
303 	free(sn);
304 	return NULL;
305 }
306 
sounds_reload(void)307 static void sounds_reload(void)
308 {
309 	_snd_t *pos = NULL;
310 	_snd_t *sn;
311 	int i;
312 	snd_halt_chan(-1, 0); /* stop all sound */
313 	list_for_each(&sounds, pos, list) {
314 		sn = (_snd_t*)pos;
315 		snd_free_wav(sn->wav);
316 		if (sn->buf)
317 			sn->wav = snd_load_mem(sn->fmt, sn->buf, sn->len);
318 		else
319 			sn->wav = snd_load_wav(sn->fname);
320 	}
321 	for (i = 0; i < SND_CHANNELS; i++) {
322 		channels[i] = NULL;
323 		sound_reqs[i].snd = NULL;
324 	}
325 	input_uevents(); /* all callbacks */
326 }
327 
_sound_get(const char * fname,int fmt,short * buff,size_t len)328 static void *_sound_get(const char *fname, int fmt, short *buff, size_t len)
329 {
330 	_snd_t *sn = NULL;
331 	if (fname) {
332 		sn = sound_find(fname);
333 		if (sn) {
334 			sn->loaded ++; /* to pin */
335 			return sn;
336 		}
337 		sn = sound_add(fname, fmt, buff, len);
338 	} else if (buff) {
339 		char *name = malloc(64);
340 		if (!name)
341 			return NULL;
342 		snprintf(name, 64, "snd:%p", buff); name[64 - 1] = 0;
343 		sn = sound_add(name, fmt, buff, len);
344 		if (!sn)
345 			free(name);
346 		else {
347 			snprintf(name, 64, "snd:%p", sn); name[64 - 1] = 0;
348 			free(sn->fname);
349 			sn->fname = name;
350 		}
351 	}
352 	if (!sn)
353 		return NULL;
354 	sn->loaded = 1;
355 	return sn;
356 }
357 
_sound_put(void * s)358 static void _sound_put(void *s)
359 {
360 	_snd_t *sn = (_snd_t *)s;
361 	if (!sn || !sn->loaded)
362 		return;
363 	if (!sn->system || sn->loaded > 1)
364 		sn->loaded --;
365 	if (!sn->loaded && sound_playing(sn) == -1)
366 		sound_free(sn);
367 	return;
368 }
369 
sound_get(const char * fname)370 void *sound_get(const char *fname)
371 {
372 	_snd_t *sn = _sound_get(fname, 0, NULL, 0);
373 	if (!sn)
374 		return NULL;
375 	sn->system = 1;
376 	return sn;
377 }
378 
sound_put(void * s)379 void sound_put(void *s)
380 {
381 	_snd_t *sn = (_snd_t *)s;
382 	if (!sn)
383 		return;
384 	sn->system = 0;
385 	_sound_put(sn);
386 }
387 
sound_load(const char * fname)388 static int sound_load(const char *fname)
389 {
390 	_snd_t *sn = _sound_get(fname, 0, NULL, 0);
391 	if (!sn)
392 		return -1;
393 	return 0;
394 }
395 
sound_load_mem(int fmt,short * buff,size_t len)396 static char *sound_load_mem(int fmt, short *buff, size_t len)
397 {
398 	_snd_t *sn = _sound_get(NULL, fmt, buff, len);
399 	if (!sn)
400 		return NULL;
401 	return sn->fname;
402 }
403 
sound_unload(const char * fname)404 static void sound_unload(const char *fname)
405 {
406 	_snd_t *sn;
407 	sn = sound_find(fname);
408 	_sound_put(sn);
409 	return;
410 }
411 
_play_combined_snd(char * filename,int chan,int loop)412 static int _play_combined_snd(char *filename, int chan, int loop)
413 {
414 	char *str;
415 	char *p, *ep;
416 	_snd_t *sn;
417 	p = str = strdup(filename);
418 	if (!str)
419 		return -1;
420 
421 	p = strip(p);
422 	while (*p) {
423 		int c = chan, l = loop;
424 		int at = 0;
425 		ep = p + strcspn(p, ";@");
426 
427 		if (*ep == '@') {
428 			at = 1;
429 			*ep = 0; ep ++;
430 			if (sscanf(ep, "%d,%d", &c, &l) > 1)
431 				at ++;
432 			ep += strcspn(ep, ";");
433 			if (*ep)
434 				ep ++;
435 		} else if (*ep == ';') {
436 			*ep = 0; ep ++;
437 		} else if (*ep) {
438 			goto err;
439 		}
440 		p = strip(p);
441 		sn = sound_find(p);
442 		if (!sn)
443 			sn = sound_add(p, 0, NULL, 0);
444 		if (sn)
445 			sound_play(sn, c, l);
446 		else if (at || c != -1) { /* if @ or specific channel */
447 			snd_halt_chan(c, (at == 2)?l:500);
448 		}
449 		p = ep;
450 	}
451 	free(str);
452 	return 0;
453 err:
454 	free(str);
455 	return -1;
456 }
457 
game_sound_player(void)458 static void game_sound_player(void)
459 {
460 	char		*snd;
461 	int		chan = -1;
462 	int		loop = 1;
463 
464 	struct instead_args args[] = {
465 			{ .val = "nil", .type = INSTEAD_NIL },
466 			{ .val = "-1", .type = INSTEAD_NUM },
467 			{ .val = NULL }
468 	};
469 
470 	if (!snd_volume_mus(-1))
471 		return;
472 
473 	instead_lock();
474 	instead_function("instead.get_sound", NULL);
475 
476 	loop = instead_iretval(2);
477 	chan = instead_iretval(1);
478 	snd = instead_retval(0);
479 	instead_clear();
480 	if (!snd) {
481 		if (chan != -1) {
482 			/* halt channel */
483 			snd_halt_chan(chan, 500);
484 			instead_function("instead.set_sound", args); instead_clear();
485 		}
486 		instead_unlock();
487 		return;
488 	}
489 	instead_function("instead.set_sound", args); instead_clear();
490 	instead_unlock();
491 	unix_path(snd);
492 	_play_combined_snd(snd, chan, loop);
493 	free(snd);
494 }
495 
496 static	char *last_music = NULL;
497 
free_last_music(void)498 static void free_last_music(void)
499 {
500 	if (last_music)
501 		free(last_music);
502 	last_music = NULL;
503 }
504 
game_change_hz(int hz)505 int game_change_hz(int hz)
506 {
507 	if (!hz)
508 		return -1;
509 #ifndef __EMSCRIPTEN__
510 	snd_close();
511 	free_last_music();
512 	snd_open(hz);
513 	snd_volume_mus(cur_vol);
514 	sounds_reload();
515 	game_music_player();
516 #endif
517 	opt_hz = snd_hz();
518 	return 0;
519 }
520 
game_stop_mus(int ms)521 void game_stop_mus(int ms)
522 {
523 	snd_stop_mus(ms);
524 	free_last_music();
525 }
526 
finish_music(void * data)527 static void finish_music(void *data)
528 {
529 	int rc;
530 	if (!curgame_dir)
531 		return;
532 	instead_lock();
533 	instead_function("instead.finish_music", NULL);
534 	rc = instead_bretval(0);
535 	instead_clear();
536 	instead_unlock();
537 	if (rc)
538 		free_last_music();
539 	snd_volume_mus(cur_vol); /* reset volume */
540 }
541 
game_music_finished(void)542 void game_music_finished(void) /* SDL callback */
543 {
544 	push_user_event(&finish_music, NULL);
545 }
546 
game_music_player(void)547 void game_music_player(void)
548 {
549 	int	loop;
550 	char		*mus;
551 
552 	int cf_out = 0;
553 	int cf_in = 0;
554 
555 	if (!snd_volume_mus(-1))
556 		return;
557 	if (!opt_music || !curgame_dir)
558 		return;
559 	instead_lock();
560 	instead_function("instead.get_music", NULL);
561 	mus = instead_retval(0);
562 	loop = instead_iretval(1);
563 	unix_path(mus);
564 	instead_clear();
565 
566 	instead_function("instead.get_music_fading", NULL);
567 	cf_out = instead_iretval(0);
568 	cf_in = instead_iretval(1);
569 	instead_clear();
570 	instead_unlock();
571 	if (mus && loop == -1) { /* disabled, 0 - forever, 1-n - loops */
572 		free(mus);
573 		mus = NULL;
574 	}
575 
576 	if (loop == 0)
577 		loop = -1;
578 
579 	if (cf_out == 0)
580 		cf_out = 500;
581 	else if (cf_out < 0)
582 		cf_out = 0;
583 
584 	if (cf_in < 0)
585 		cf_in = 0;
586 
587 	if (!mus) {
588 		if (last_music) {
589 			game_stop_mus(cf_out);
590 		}
591 	} else if (!last_music || strcmp(last_music, mus)) {
592 		game_stop_mus(cf_out);
593 		last_music = mus;
594 		if (snd_play_mus(mus, cf_in, loop) < 0)
595 			game_res_err_msg(mus, debug_sw);
596 	} else
597 		free(mus);
598 }
599 
luaB_is_sound(lua_State * L)600 static int luaB_is_sound(lua_State *L) {
601 	const char *chan = luaL_optstring(L, 1, NULL);
602 	int c, r;
603 	if (!chan)
604 		c = -1;
605 	else
606 		c = atoi(chan);
607 	r = (sound_channel(c) != NULL);
608 	lua_pushboolean(L, r);  /* else not a number */
609 	return 1;
610 }
611 
luaB_load_sound(lua_State * L)612 static int luaB_load_sound(lua_State *L) {
613 	int rc;
614 	const char *fname = luaL_optstring(L, 1, NULL);
615 	if (!fname)
616 		return 0;
617 	rc = sound_load(fname);
618 	if (rc)
619 		return 0;
620 	lua_pushstring(L, fname);
621 	return 1;
622 }
623 #define SND_F2S(v) ((short)((float)(v) * 16383.0) * 2)
624 
luaB_load_sound_mem(lua_State * L)625 static int luaB_load_sound_mem(lua_State *L) {
626 	int hz = luaL_optinteger(L, 1, -1);
627 	int channels = luaL_optinteger(L, 2, -1);
628 	int len; int i;
629 	short *buf = NULL;
630 	const char *name;
631 	int fmt = 0;
632 	luaL_checktype(L, 3, LUA_TTABLE);
633 
634 	if (hz < 0 || channels < 0)
635 		return 0;
636 #if LUA_VERSION_NUM >= 502
637 	len = lua_rawlen(L, 3);
638 #else
639 	len = lua_objlen(L, 3);
640 #endif
641 	if (len <= 0)
642 		return 0;
643 	buf = malloc(sizeof(short) * len);
644 	if (!buf)
645 		return 0;
646 
647 	lua_pushvalue(L, 3);
648 
649 	for (i = 0; i < len; i++) {
650 		float v;
651 		lua_pushinteger(L, i + 1);
652 		lua_gettable(L, -2);
653 
654 		if (!lua_isnumber(L, -1)) {
655 			v = 0;
656 		} else {
657 			v = (float)lua_tonumber(L, -1);
658 		}
659 		buf[i] = SND_F2S(v);
660 		lua_pop(L, 1);
661 	}
662 	lua_pop(L, 1);
663 	/* here we got the sample */
664 	if (channels == 2)
665 		fmt |= SND_FMT_STEREO;
666 
667 	if (hz == 11025)
668 		fmt |= SND_FMT_11;
669 	else if (hz == 22050)
670 		fmt |= SND_FMT_22;
671 	else
672 		fmt |= SND_FMT_44;
673 	name = sound_load_mem(fmt, buf, len);
674 /*	free(buf); */
675 	if (!name)
676 		return 0;
677 	lua_pushstring(L, name);
678 	return 1;
679 }
680 
luaB_free_sound(lua_State * L)681 static int luaB_free_sound(lua_State *L) {
682 	const char *fname = luaL_optstring(L, 1, NULL);
683 	if (!fname)
684 		return 0;
685 	sound_unload(fname);
686 	return 0;
687 }
688 
luaB_free_sounds(lua_State * L)689 static int luaB_free_sounds(lua_State *L) {
690 	sounds_free();
691 	return 0;
692 }
693 
luaB_panning_sound(lua_State * L)694 static int luaB_panning_sound(lua_State *L) {
695 	int chan = luaL_optinteger(L, 1, -1);
696 	int left = luaL_optnumber(L, 2, 255);
697 	int right = luaL_optnumber(L, 3, 255);
698 	snd_panning(chan, left, right);
699 	return 0;
700 }
701 
luaB_volume_sound(lua_State * L)702 static int luaB_volume_sound(lua_State *L) {
703 	int vol = luaL_optnumber(L, 1, -1);
704 	vol = snd_volume_mus(vol);
705 	lua_pushinteger(L, vol);
706 	return 1;
707 }
708 
luaB_channel_sound(lua_State * L)709 static int luaB_channel_sound(lua_State *L) {
710 	const char *s;
711 	int ch = luaL_optinteger(L, 1, 0);
712 	ch = ch % SND_CHANNELS;
713 	s = sound_channel(ch);
714 	if (s) {
715 		lua_pushstring(L, s);
716 		return 1;
717 	}
718 	return 0;
719 }
720 static int callback_ref = 0;
721 static int sound_inited = 0;
722 
723 #define SOUND_MAGIC 0x2004
724 struct lua_sound {
725 	int type;
726 	short *buf;
727 	int len;
728 };
mus_callback(void * udata,unsigned char * stream,int len)729 static void mus_callback(void *udata, unsigned char *stream, int len)
730 {
731 	lua_State *L = (lua_State *) udata;
732 	struct lua_sound *hdr;
733 	if (!callback_ref)
734 		return;
735 	instead_lock();
736 	lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);
737 	lua_pushinteger(L, snd_hz());
738 	lua_pushinteger(L, len >> 1);
739 	hdr = lua_newuserdata(L, sizeof(*hdr));
740 	if (!hdr)
741 		goto err;
742 	hdr->type = SOUND_MAGIC;
743 	hdr->len = len >> 1; /* 16bits */
744 	hdr->buf = (short *)stream;
745 	luaL_getmetatable(L, "soundbuffer metatable");
746 	lua_setmetatable(L, -2);
747 	if (instead_pcall(L, 3)) { /* on any error */
748 	err:
749 		snd_mus_callback(NULL, NULL);
750 		luaL_unref(L, LUA_REGISTRYINDEX, callback_ref);
751 		callback_ref = 0;
752 	}
753 	instead_clear();
754 	instead_unlock();
755 	return;
756 }
757 
luaB_music_callback(lua_State * L)758 static int luaB_music_callback(lua_State *L) {
759 	if (!sound_inited)
760 		return 0;
761 
762 	snd_mus_callback(NULL, NULL);
763 	if (callback_ref)
764 		luaL_unref(L, LUA_REGISTRYINDEX, callback_ref);
765 	callback_ref = 0;
766 
767 	if (lua_isfunction(L, 1)) {
768 		game_stop_mus(0);
769 		callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);
770 		snd_mus_callback(mus_callback, L);
771 	}
772 
773 	return 0;
774 }
775 
776 static const luaL_Reg sound_funcs[] = {
777 	{"instead_sound", luaB_is_sound},
778 	{"instead_sound_load", luaB_load_sound},
779 	{"instead_sound_load_mem", luaB_load_sound_mem},
780 	{"instead_sound_free", luaB_free_sound},
781 	{"instead_sound_channel", luaB_channel_sound},
782 	{"instead_sound_panning", luaB_panning_sound},
783 	{"instead_sound_volume", luaB_volume_sound},
784 	{"instead_sounds_free", luaB_free_sounds},
785 	{"instead_music_callback", luaB_music_callback},
786 	{NULL, NULL}
787 };
788 
789 
sound_done(void)790 static int sound_done(void)
791 {
792 	if (!sound_inited)
793 		return 0;
794 	if (callback_ref) {
795 		snd_mus_callback(NULL, NULL);
796 		luaL_unref(instead_lua(), LUA_REGISTRYINDEX, callback_ref);
797 		callback_ref = 0;
798 	}
799 	game_stop_mus(0);
800 	sounds_free();
801 	snd_close();
802 	sound_inited = 0;
803 	return 0;
804 }
805 /*
806 static int sound_size(lua_State *L) {
807 	struct lua_sound *hdr = (struct lua_sound*)lua_touserdata(L, 1);
808 	if (!hdr || hdr->type != SOUND_MAGIC)
809 		return 0;
810 	lua_pushnumber(L, hdr->len);
811 	return 1;
812 }
813 */
sound_value(lua_State * L)814 static int sound_value(lua_State *L) {
815 	struct lua_sound *hdr = (struct lua_sound*)lua_touserdata(L, 1);
816 	int pos = luaL_optinteger(L, 2, -1);
817 	float v = luaL_optnumber(L, 3, 0.0f);
818 	if (pos <= 0)
819 		return 0;
820 	if (pos > hdr->len)
821 		return 0;
822 	pos --;
823 	if (lua_isnoneornil(L, 3)) {
824 		lua_pushinteger(L, hdr->buf[pos]);
825 		return 1;
826 	}
827 	hdr->buf[pos] = SND_F2S(v);
828 	return 0;
829 }
830 
831 /*
832 ** Creates chunk metatable.
833 */
chunk_create_meta(lua_State * L)834 static int chunk_create_meta (lua_State *L) {
835 	luaL_newmetatable (L, "soundbuffer metatable");
836 	lua_pushstring (L, "__index");
837 	lua_pushcfunction(L, sound_value);
838 	lua_settable(L, -3);
839 
840 	lua_pushstring (L, "__newindex");
841 	lua_pushcfunction(L, sound_value);
842 	lua_settable(L, -3);
843 /*
844 	lua_pushstring (L, "size");
845 	lua_pushcfunction (L, sound_size);
846 	lua_settable(L, -3);
847 */
848 	return 0;
849 }
850 
sound_init(void)851 static int sound_init(void)
852 {
853 	int rc;
854 	char path[PATH_MAX];
855 
856 	instead_api_register(sound_funcs);
857 	chunk_create_meta(instead_lua());
858 
859 	snprintf(path, sizeof(path), "%s/%s", instead_stead_path(), "/ext/sound.lua");
860 
861 	rc = instead_loadfile(dirpath(path));
862 	if (rc)
863 		return rc;
864 	snd_open(opt_hz);
865 	if (!nosound_sw)
866 		game_change_vol(0, opt_vol);
867 	sound_inited = 1;
868 	return 0;
869 }
870 
sound_cmd(void)871 static int sound_cmd(void)
872 {
873 	game_music_player();
874 	game_sound_player();
875 	return 0;
876 }
877 
878 static struct instead_ext ext = {
879 	.init = sound_init,
880 	.done = sound_done,
881 	.cmd = sound_cmd,
882 };
883 
instead_sound_init(void)884 int instead_sound_init(void)
885 {
886 	return instead_extension(&ext);
887 }
888