1 /*
2  * ux_audio.c - Unix interface, sound support
3  *
4  * This file is part of Frotz.
5  *
6  * Frotz is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Frotz is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  * Or visit http://www.fsf.org/
20  *
21  * This file and only this file is dual licensed under the MIT license.
22  *
23  * Copyright (c) 2019 Mark McCurry
24  */
25 
26 #define __UNIX_PORT_FILE
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <pthread.h>
32 #include <assert.h>
33 #include <unistd.h> //pread
34 
35 #include "ux_defines.h"
36 
37 #ifdef USE_NCURSES_H
38 #include <ncurses.h>
39 #else
40 #include <curses.h>
41 #endif
42 
43 #include "ux_sema.h"
44 #include "ux_frotz.h"
45 #include "ux_blorb.h"
46 #include "ux_audio.h"
47 
48 extern f_setup_t f_setup;
49 extern z_header_t z_header;
50 
51 #ifndef NO_SOUND
52 
53 ux_sem_t sound_done;	/* 1 if the sound is done */
54 
55 #include <ao/ao.h>
56 #include <sndfile.h>
57 #include <samplerate.h>
58 #include <libmodplug/modplug.h>
59 
60 /* Exports
61  * void  os_init_sound(void);                     startup system
62  * void  os_beep(int);                            enqueue a beep sample
63  * void  os_prepare_sample(int);                  put a sample into memory
64  * void  os_start_sample(int, int, int, zword);   queue up a sample
65  * void  os_stop_sample(int);                     terminate sample
66  * void  os_finish_with_sample(int);              remove from memory
67  */
68 
69 #define EVENT_START_STREAM  1
70 #define EVENT_STOP_STREAM   2
71 
72 typedef struct {
73 	SRC_STATE *src_state;
74 	SRC_DATA   src_data;
75 	float	  *scratch;
76 	float	  *input;
77 	float	  *output;
78 } resampler_t;
79 
80 
81 typedef struct {
82 	bool active; /* If a voice is actively outputting sound*/
83 	int  src;    /* The source sound ID*/
84 	int  type;   /* The voice type 0, 1, 2..N*/
85 	int  pos;    /* The current position*/
86 	int  repid;  /* The current number of repetitions*/
87 } sound_state_t;
88 
89 
90 typedef struct {
91 	float  *samples;
92 	int     nsamples;
93 } sound_buffer_t;
94 
95 typedef enum {
96 	FORM,
97 	OGGV,
98 	MOD
99 } sound_type_t;
100 
101 typedef struct sound_stream {
102 	/* returns 1 if process can continue */
103 	int (*process)(struct sound_stream *self, float *outl, float *outr, unsigned samples);
104 	void (*cleanup)(struct sound_stream *self);
105 	sound_type_t sound_type;
106 	int id;
107 } sound_stream_t;
108 
109 typedef struct {
110 	int (*process)(sound_stream_t *self, float *outl, float *outr, unsigned samples);
111 	void (*cleanup)(sound_stream_t *self);
112 	sound_type_t sound_type;
113 	int id;
114 } sound_stream_dummy_t;
115 
116 typedef struct {
117 	uint8_t *data;
118 	size_t   len;
119 	size_t   pos;
120 } buf_t;
121 
122 typedef struct {
123 	FILE    *file;
124 	size_t   base_offset;
125 	size_t   len;
126 	size_t   pos;
127 } file_reader_t;
128 
129 typedef struct {
130 	int (*process)(sound_stream_t *self, float *outl, float *outr, unsigned samples);
131 	void (*cleanup)(sound_stream_t *self);
132 	sound_type_t sound_type;
133 	int id;
134 
135 	resampler_t *rsmp;
136 	float  volume;
137 	float *floatbuffer;
138 
139 	SNDFILE *sndfile;
140 	SF_INFO  sf_info;
141 	size_t length;     /* Number of samples (= smpsl.length == smpsr.length) */
142 	int    repeats;    /* Total times to play the sample 1..n */
143 	int    pos;
144 
145 	file_reader_t freader;
146 } sound_stream_aiff_t;
147 
148 typedef struct {
149 	int (*process)(sound_stream_t *self, float *outl, float *outr, unsigned samples);
150 	void (*cleanup)(sound_stream_t *self);
151 	sound_type_t sound_type;
152 	int id;
153 
154 	char *filedata;
155 	short *shortbuffer;
156 	ModPlugFile *mod;
157 	ModPlug_Settings settings;
158 } sound_stream_mod_t;
159 
160 typedef struct {
161 	uint8_t type;
162 	union {
163 		sound_stream_t *e;
164 		int            i;
165 	};
166 } sound_event_t;
167 
168 #define NUM_VOICES 8
169 
170 typedef struct {
171 	/* Audio driver parameters */
172 	size_t buffer_size;
173 	float  sample_rate;
174 
175 	/* Output buffers */
176 	float *outl;
177 	float *outr;
178 
179 	/* Sound parameters */
180 	sound_stream_t *streams[NUM_VOICES]; /* Active streams */
181 	sound_state_t   voices[NUM_VOICES];  /* Max concurrent sound effects/music */
182 
183 	/* Event (one is process per frame of audio) */
184 	ux_sem_t ev_free;    /* 1 if an event can be submitted */
185 	ux_sem_t ev_pending; /* 1 if there's an event ready to be processed */
186 	sound_event_t event;
187 } sound_engine_t;
188 
189 static sound_engine_t frotz_audio;
190 /* FILE *audio_log; */
191 
192 /**********************************************************************
193  *                         Utilities                                  *
194  *                                                                    *
195  * getfiledata          - get all bytes of a file after               *
196  *                        the start point                             *
197  * make_id              - Create BLORB identifier                     *
198  * get_type             - Get OGG/FORM/AIFF type                      *
199  * limit                - x -> a <= x <= b                            *
200  *                                                                    *
201  **********************************************************************/
202 static char *
getfiledata(FILE * fp,long * size)203 getfiledata(FILE *fp, long *size)
204 {
205 	long offset = ftell(fp);
206 	fseek(fp, 0L, SEEK_END);
207 	(*size) = ftell(fp);
208 	fseek(fp, offset, SEEK_SET);
209 	char *data = (char*)malloc(*size);
210 	fread(data, *size, sizeof(char), fp);
211 	fseek(fp, offset, SEEK_SET);
212 	return(data);
213 }
214 
215 
216 static
make_id(uint8_t a,uint8_t b,uint8_t c,uint8_t d)217 int32_t make_id(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
218 {
219 	return (a << 24) | (b << 16) | (c << 8) | d;
220 }
221 
222 
223 static int
get_type(int magic)224 get_type(int magic)
225 {
226 	/*fprintf(audio_log, "magic = %x\n", magic);*/
227 	if (magic == make_id('F','O','R','M'))
228 		return FORM;
229 	if (magic == make_id('M','O','D',' '))
230 		return MOD;
231 	if (magic == make_id('O','G','G','V'))
232 		return OGGV;
233 	return -1;
234 }
235 
236 
237 static float
limit(float mn,float mx,float v)238 limit(float mn, float mx, float v)
239 {
240 	if (v<mn) return mn;
241 	if (v>mx) return mx;
242 	return v;
243 }
244 
245 
246 /**********************************************************************
247  *                         Resampler                                  *
248  *                                                                    *
249  * Processes data input at one sampling rate and converts to another. *
250  * Used on ogg and aiff streams.                                      *
251  *                                                                    *
252  * NOTE: Additional code may be needed to smoothly handle repeat loop *
253  *       conditions                                                   *
254  *                                                                    *
255  * resampler_init    - Create resampler                               *
256  * resampler_cleanup - Deallocate resampler resources
257  * resampler_step    - Add data to resampler                          *
258  * resampler_consume - Remove data from resampler                     *
259  **********************************************************************/
260 
261 static resampler_t*
resampler_init(int sample_rate_input)262 resampler_init(int sample_rate_input)
263 {
264 	resampler_t *rsmp = (resampler_t*)calloc(sizeof(resampler_t), 1);
265 	int error;
266 	rsmp->src_state = src_new(SRC_SINC_FASTEST, 2, &error);
267 	rsmp->input   = (float*)calloc(frotz_audio.buffer_size, sizeof(float)*2);
268 	rsmp->output  = (float*)calloc(frotz_audio.buffer_size, sizeof(float)*2);
269 	rsmp->scratch = (float*)calloc(frotz_audio.buffer_size, sizeof(float)*2);
270 	rsmp->src_data.src_ratio     = frotz_audio.sample_rate*1.0f/sample_rate_input;
271 	rsmp->src_data.input_frames  = 0;
272 	rsmp->src_data.output_frames = frotz_audio.buffer_size;
273 	rsmp->src_data.data_in       = rsmp->input;
274 	rsmp->src_data.data_out      = rsmp->output;
275 	rsmp->src_data.end_of_input  = 0;
276 
277 	return rsmp;
278 }
279 static void
resampler_cleanup(resampler_t * rsmp)280 resampler_cleanup(resampler_t *rsmp)
281 {
282 	src_delete(rsmp->src_state);
283 	free(rsmp->input);
284 	free(rsmp->output);
285 	free(rsmp->scratch);
286 }
287 
288 
289 /*0 done running, 1 run again with more data*/
290 static int
resampler_step(resampler_t * rsmp,float * block)291 resampler_step(resampler_t *rsmp, float *block)
292 {
293 	/*Always stereo*/
294 	const int channels = 2;
295 	const int smps     = frotz_audio.buffer_size;
296 	if (block) {
297 		assert(rsmp->src_data.input_frames == 0);
298 		memcpy(rsmp->input, block, channels*smps*sizeof(float));
299 		rsmp->src_data.data_in      = rsmp->input;
300 		rsmp->src_data.input_frames = smps;
301 	}
302 	int err = src_process(rsmp->src_state, &rsmp->src_data);
303 	assert(err == 0);
304 
305 	int u_in = rsmp->src_data.input_frames_used;
306 	rsmp->src_data.data_in      += 2*u_in;
307 	rsmp->src_data.input_frames -= u_in;
308 	/*
309 	 * If input buffer is empty, reset data_in pointer just in case
310 	 * the output buffer is also full.
311 	 */
312 	if (rsmp->src_data.input_frames == 0)
313 		rsmp->src_data.data_in      = rsmp->input;
314 	int g_out = rsmp->src_data.output_frames_gen;
315 	rsmp->src_data.data_out      += 2*g_out;
316 	rsmp->src_data.output_frames -= g_out;
317 
318 	if (rsmp->src_data.output_frames == 0)
319 		return 0;
320 	return 1;
321 }
322 
323 
324 static void
resampler_consume(resampler_t * rsmp)325 resampler_consume(resampler_t *rsmp)
326 {
327 	rsmp->src_data.data_out      = rsmp->output;
328 	rsmp->src_data.output_frames = frotz_audio.buffer_size;
329 }
330 
331 
332 /**********************************************************************
333  *                           MOD                                      *
334  *                                                                    *
335  * Processes MOD data via libmodplug                                  *
336  *                                                                    *
337  * process_mod - Generate MOD samples                                 *
338  * cleanup_mod - Free MOD resources                                   *
339  * load_mod    - Create MOD stream                                    *
340  **********************************************************************/
341 
342 static int
process_mod(sound_stream_t * self_,float * outl,float * outr,unsigned samples)343 process_mod(sound_stream_t *self_, float *outl, float *outr, unsigned samples)
344 {
345 	sound_stream_mod_t *self = (sound_stream_mod_t*)self_;
346 
347 	int n = ModPlug_Read(self->mod, self->shortbuffer, samples*4) / 4;
348 	const float scale = (1.0f/32768.0f);/*volfactor;*/
349 	int i;
350 	for (i=0; i<n; ++i) {
351 		outl[i] += scale*self->shortbuffer[i*2+0];
352 		outr[i] += scale*self->shortbuffer[i*2+1];
353 	}
354 
355 	if (n <= 0)
356 		return 0;
357 
358     return 1;
359 }
360 
361 static void
cleanup_mod(sound_stream_t * s)362 cleanup_mod(sound_stream_t *s)
363 {
364 	sound_stream_mod_t *self = (sound_stream_mod_t*)s;
365 	ModPlug_Unload(self->mod);
366 	free(self->shortbuffer);
367 	free(self->filedata);
368 }
369 
370 
371 /*file, data start, id, volume*/
372 static sound_stream_t *
load_mod(FILE * fp,long startpos,int id,float volume)373 load_mod(FILE *fp, long startpos, int id, float volume)
374 {
375 	sound_stream_mod_t *stream = (sound_stream_mod_t*)calloc(sizeof(sound_stream_mod_t), 1);
376 	long size;
377 	long filestart = ftell(fp);
378 	fseek(fp, startpos, SEEK_SET);
379 
380 	stream->id         = id;
381 	stream->sound_type = MOD;
382 	stream->process    = process_mod;
383 	stream->cleanup    = cleanup_mod;
384 
385 	ModPlug_GetSettings(&stream->settings);
386 
387 	/* Note: All "Basic Settings" must be set before ModPlug_Load. */
388 	stream->settings.mResamplingMode   = MODPLUG_RESAMPLE_FIR; /* RESAMP */
389 	stream->settings.mChannels         = 2;
390 	stream->settings.mBits             = 16;
391 	stream->settings.mFrequency        = frotz_audio.sample_rate;
392 	stream->settings.mStereoSeparation = 128;
393 	stream->settings.mMaxMixChannels   = 256;
394 
395 	/* insert more setting changes here */
396 	ModPlug_SetSettings(&stream->settings);
397 
398 	/* remember to free() filedata later */
399 	stream->filedata = getfiledata(fp, &size);
400 
401 	stream->mod = ModPlug_Load(stream->filedata, size);
402 	fseek(fp, filestart, SEEK_SET);
403 	if (!stream->mod) {
404 		fprintf(stderr, "Unable to load MOD chunk.\n\r");
405 		return 0;
406 	}
407 
408 	ModPlug_SetMasterVolume(stream->mod, volume * 256);/*powf(2.0f, 8.0f));*/
409 
410 	stream->shortbuffer = (int16_t*)calloc(frotz_audio.buffer_size, sizeof(short) * 2);
411 
412 	return (sound_stream_t*)stream;
413 }
414 
415 /**********************************************************************
416  *                         AIFF/OGG                                   *
417  *                                                                    *
418  * Processes OGG/AIFF data via sndfile + resampler                    *
419  *                                                                    *
420  * process_aiff     - Create OGG/AIFF samples                         *
421  * cleanup_aiff     - Free   OGG/AIFF resources                       *
422  * mem_snd_read     - In memory read                                  *
423  * mem_snd_seek     - In memory seek                                  *
424  * mem_tell         - In memory tell                                  *
425  * mem_get_filelen  - In memory filelen                               *
426  * load_aiff        - Create OGG/AIFF stream                          *
427  *                                                                    *
428  **********************************************************************/
429 
430 static int
process_aiff(sound_stream_t * self_,float * outl,float * outr,unsigned samples)431 process_aiff(sound_stream_t *self_, float *outl, float *outr, unsigned samples)
432 {
433 	sound_stream_aiff_t *self = (sound_stream_aiff_t*)self_;
434 
435 	int needs_data = resampler_step(self->rsmp, 0);
436 	int i;
437 	unsigned remaining_samples = samples;
438 
439 	while(needs_data) {
440 		int inf = sf_readf_float(self->sndfile,
441 			self->floatbuffer, remaining_samples);
442 		if (self->sf_info.channels == 1) {
443 			for (i=0; i<inf; ++i) {
444 				self->rsmp->scratch[2*i+0+2*(samples-remaining_samples)] =
445 					self->floatbuffer[i];
446 				self->rsmp->scratch[2*i+1+2*(samples-remaining_samples)] =
447 					self->floatbuffer[i];
448 			}
449 		} else if (self->sf_info.channels == 2) {
450 			for (i=0; i<inf; ++i) {
451 				self->rsmp->scratch[2*i+0+2*(samples-remaining_samples)] =
452 					self->floatbuffer[2*i+0];
453 				self->rsmp->scratch[2*i+1+2*(samples-remaining_samples)] =
454 					self->floatbuffer[2*i+1];
455 			}
456 		}
457 		/*
458         	 * If the read function didn't fill the scratch buffer, see if
459 		 * there are more repeats and if so, continue filling the scratch
460 		 * buffer, a repeat value of 255 means repeat forever
461 		 */
462 		if (inf < (int)remaining_samples) {
463 			if (self->repeats<255)
464 				self->repeats--;
465 			if (self->repeats > 0){
466 				/*
467 				 * Repeating... Seek back to the beginning of the sound
468 				 * and allow the read to get enough samples to fill the
469 				 * scratch buffer, and continue with the while loop
470 				 */
471 				sf_seek(self->sndfile,0, SEEK_SET);
472 				if ( inf == (int)remaining_samples)
473 					remaining_samples = samples;
474 				else
475 					remaining_samples = remaining_samples - inf;
476 				continue;
477 			} else if (inf <= 0) {
478 				/*
479 				 * No repeats and no data left in the sound file,
480 				 * return 0 to tell the next level up that the sound
481 				 * is done
482 				 */
483 				return 0;
484 			} else {
485 				/*
486 				 * No repeats but there was data read, set things back
487 				 * up so that the maximum buffer size can be read, but
488 				 * fall through to make sure the data is resampled
489 				 */
490 				remaining_samples = samples;
491 			}
492 		}
493         	needs_data = resampler_step(self->rsmp, self->rsmp->scratch);
494     	}
495 	resampler_consume(self->rsmp);
496 
497 	for (i=0; i<(int)samples; ++i) {
498 		outl[i] += self->rsmp->output[2*i+0]*self->volume;
499 		outr[i] += self->rsmp->output[2*i+1]*self->volume;
500 	}
501 
502 	return 1;
503 }
504 
505 
506 static void
cleanup_aiff(sound_stream_t * s)507 cleanup_aiff(sound_stream_t *s)
508 {
509 	sound_stream_aiff_t *self = (sound_stream_aiff_t*)s;
510 
511 	/* Cleanup frame */
512 	resampler_cleanup(self->rsmp);
513 	free(self->rsmp);
514 	sf_close(self->sndfile);
515 	free(self->floatbuffer);
516 }
517 
518 
519 static sf_count_t
mem_snd_read(void * ptr_,sf_count_t size,void * datasource)520 mem_snd_read(void *ptr_, sf_count_t size, void* datasource)
521 {
522 	uint8_t *ptr = (uint8_t*)ptr_;
523 	file_reader_t *fr = (file_reader_t *)datasource;
524 	size_t to_read = size;
525 	size_t read_total = 0;
526 	ssize_t did_read = 0;
527 	while (to_read > 0) {
528 		did_read = pread(fileno(fr->file), ptr, size, fr->pos+fr->base_offset);
529 		if (did_read < 0)
530 			return did_read;
531 		else if (did_read == 0)
532 			return read_total;
533 		read_total += did_read;
534 		fr->pos    += did_read;
535 		ptr        += did_read;
536 		to_read    -= did_read;
537 	}
538 	return read_total;
539 }
540 
541 
542 static sf_count_t
mem_snd_seek(sf_count_t offset,int whence,void * datasource)543 mem_snd_seek(sf_count_t offset, int whence, void *datasource) {
544 	file_reader_t *fr = (file_reader_t *)datasource;
545 	int64_t pos = 0;
546 	if (whence == SEEK_SET)
547 		pos = offset;
548 	if (whence == SEEK_CUR)
549 		pos += offset;
550 	if (whence == SEEK_END)
551 		pos = fr->len-offset;
552 	if (pos >= (int64_t)fr->len)
553 		pos = fr->len-1;
554 	if (pos < 0)
555 		pos = 0;
556 	fr->pos = pos;
557 
558 	return 0;
559 }
560 
561 
562 static long
mem_tell(void * datasource)563 mem_tell(void *datasource) {
564 	file_reader_t *fr = (file_reader_t*)datasource;
565 	return fr->pos;
566 }
567 
568 
569 static sf_count_t
mem_get_filelen(void * datasource)570 mem_get_filelen(void *datasource)
571 {
572 	file_reader_t *fr = (file_reader_t*)datasource;
573 	return fr->len;
574 }
575 
576 
577 static sound_stream_t *
load_aiff(FILE * fp,long startpos,long length,int id,float volume,int repeats)578 load_aiff(FILE *fp, long startpos, long length, int id, float volume, int repeats)
579 {
580 	sound_stream_aiff_t *aiff =
581 		(sound_stream_aiff_t*)calloc(sizeof(sound_stream_aiff_t), 1);
582 	aiff->sound_type = FORM;
583 	aiff->id         = id;
584 	aiff->process    = process_aiff;
585 	aiff->cleanup    = cleanup_aiff;
586 	aiff->repeats    = repeats;
587 
588 	aiff->volume = volume;
589 	aiff->sf_info.format = 0;
590 
591 	fseek(fp, startpos, SEEK_SET);
592 	aiff->freader.file        = fp;
593 	aiff->freader.pos         = 0;
594 	aiff->freader.len         = length;
595 	aiff->freader.base_offset = startpos;
596 
597 	SF_VIRTUAL_IO mem_cb = {
598 		.seek        = mem_snd_seek,
599 		.read        = mem_snd_read,
600 		.tell        = (sf_vio_tell)mem_tell,
601 		.get_filelen = mem_get_filelen,
602 		.write       = NULL
603 	};
604 
605 	aiff->sndfile = sf_open_virtual(&mem_cb, SFM_READ,
606 		&aiff->sf_info, &aiff->freader);
607 	aiff->rsmp = resampler_init(aiff->sf_info.samplerate);
608 
609 	aiff->floatbuffer = (float*)malloc(frotz_audio.buffer_size *
610 		aiff->sf_info.channels * sizeof(float));
611 
612 	return (sound_stream_t*) aiff;
613 }
614 
615 
616 
617 
618 /**********************************************************************
619  *                       Sound Engine                                 *
620  *                                                                    *
621  * Processes OGG/AIFF data via sndfile + resampler                    *
622  *                                                                    *
623  * process_engine     - Create a frame of output                      *
624  * audio_loop         - Stream audio to sound device                  *
625  * sound_halt_aiff    - Stop all AIFF voices                          *
626  * sound_halt_mod     - Stop all MOD voices                           *
627  * sound_halt_ogg     - Stop all OGG voices                           *
628  * sound_stop_id      - Proxy to stop an id                           *
629  * sound_stop_id_real - Stop a given stream id                        *
630  * sound_enqueue      - Proxy to start a stream obj                   *
631  * sound_enqueue_real - Start a stream obj                            *
632  * volume_factor      - Convert volume to scalar multiplier           *
633  **********************************************************************/
634 
635 static void
636 sound_enqueue_real(sound_engine_t *e, sound_stream_t *s);
637 static void
638 sound_stop_id_real(sound_engine_t *e, int id);
639 
640 static void
process_engine(sound_engine_t * e)641 process_engine(sound_engine_t *e)
642 {
643 	int i;
644 	/* Handle event */
645 	if (ux_sem_trywait(&e->ev_pending) == 0) {
646 		if (e->event.type == EVENT_START_STREAM)
647 			sound_enqueue_real(e,e->event.e);
648 		else if (e->event.type == EVENT_STOP_STREAM)
649 			sound_stop_id_real(e,e->event.i);
650 		ux_sem_post(&e->ev_free);
651 	}
652 
653 	/* Start out with an empty buffer */
654 	memset(e->outl, 0, sizeof(float)*e->buffer_size);
655 	memset(e->outr, 0, sizeof(float)*e->buffer_size);
656 
657 	for (i=0; i<8; ++i) {
658 		sound_state_t *state = &e->voices[i];
659 
660 		/* Only process active voices */
661 		if (!state->active)
662 			continue;
663 
664 		sound_stream_t *sound = e->streams[i];
665 
666 		if (sound) {
667 			int ret = sound->process(sound, e->outl, e->outr, e->buffer_size);
668 			if (ret == 0) {
669 				/* fprintf(audio_log, "stream #%d is complete\n", i); */
670 				state->active = false;
671 				sound->cleanup(sound);
672 				free(sound);
673 				e->streams[i] = NULL;
674 				ux_sem_post(&sound_done);
675 			}
676 		}
677 	}
678 }
679 
680 
681 static void*
audio_loop(void * v)682 audio_loop(void*v)
683 {
684 	(void) v;
685 	size_t outsize = frotz_audio.buffer_size*2*sizeof(int16_t);
686 	int16_t *buf  = (int16_t*)calloc(outsize,1);
687 	int i;
688 	ao_device *device;
689 	ao_sample_format format;
690 	ao_initialize();
691 	int default_driver = ao_default_driver_id();
692 
693 	memset(&format, 0, sizeof(ao_sample_format));
694 
695 	format.byte_format = AO_FMT_NATIVE;
696 	format.bits = 16;
697 	format.channels = 2;
698 	format.rate = 48000.0f;
699 	device = ao_open_live(default_driver, &format, NULL);
700 
701 	while (1) {
702 		process_engine(&frotz_audio);
703 
704 		const float mul = (32768.0f);
705 		for (i=0; i<(int)frotz_audio.buffer_size; ++i) {
706 			buf[2*i+0] = limit(-32764,32767,mul*0.8*frotz_audio.outl[i]);
707 			buf[2*i+1] = limit(-32764,32767,mul*0.8*frotz_audio.outr[i]);
708 		}
709 		ao_play(device, (char*)buf, outsize);
710 	}
711 	return 0;
712 }
713 
714 
715 static void
sound_halt_aiff(void)716 sound_halt_aiff(void)
717 {
718 	int i;
719 	for (i=0; i<NUM_VOICES; ++i) {
720 		if (frotz_audio.streams[i] && frotz_audio.streams[i]->sound_type == FORM) {
721 			/* fprintf(audio_log, "killing aiff stream #%d\n", i); */
722 			sound_stream_t *s = frotz_audio.streams[i];
723 			frotz_audio.streams[i] = 0;
724 			s->cleanup(s);
725 			free(s);
726 		}
727 	}
728 }
729 
730 
731 static void
sound_halt_mod(void)732 sound_halt_mod(void)
733 {
734 	int i;
735 	for (i=0; i<NUM_VOICES; ++i) {
736 		if (frotz_audio.streams[i] && frotz_audio.streams[i]->sound_type == MOD) {
737 			/* fprintf(audio_log, "killing mod stream #%d\n", i); */
738 			sound_stream_t *s = frotz_audio.streams[i];
739 			frotz_audio.streams[i] = 0;
740 			s->cleanup(s);
741 			free(s);
742 		}
743 	}
744 }
745 
746 
747 static void
sound_halt_ogg(void)748 sound_halt_ogg(void)
749 {
750 	int i;
751 	for(i=0; i<NUM_VOICES; ++i) {
752 		if(frotz_audio.streams[i] && frotz_audio.streams[i]->sound_type == OGGV) {
753 			/* fprintf(audio_log, "killing ogg stream #%d\n", i); */
754 			sound_stream_t *s = frotz_audio.streams[i];
755 			frotz_audio.streams[i] = 0;
756 			s->cleanup(s);
757 			free(s);
758 		}
759 	}
760 }
761 
762 
763 static sound_stream_t *load_mod(FILE *fp, long startpos, int id, float volume);
764 static sound_stream_t *load_aiff(FILE *fp, long startpos, long length, int id, float volume, int repeats);
765 
766 
767 static void
sound_stop_id(int id)768 sound_stop_id(int id)
769 {
770 	ux_sem_wait(&frotz_audio.ev_free);
771 	frotz_audio.event.type = EVENT_STOP_STREAM;
772 	frotz_audio.event.i    = id;
773 	ux_sem_post(&frotz_audio.ev_pending);
774 }
775 
776 
777 static void
sound_stop_id_real(sound_engine_t * e,int id)778 sound_stop_id_real(sound_engine_t *e, int id)
779 {
780 	int i;
781 	for (i=0; i<NUM_VOICES; ++i) {
782 		sound_stream_t *s = e->streams[i];
783 		if (s && s->id == id) {
784 			/*fprintf(audio_log, "killing stream #%d\n", i);*/
785 			e->streams[i] = 0;
786 			s->cleanup(s);
787 			free(s);
788 		}
789 	}
790 }
791 
792 
793 static void
sound_enqueue(sound_stream_t * s)794 sound_enqueue(sound_stream_t *s)
795 {
796 	ux_sem_wait(&frotz_audio.ev_free);
797 	frotz_audio.event.type = EVENT_START_STREAM;
798 	frotz_audio.event.e    = s;
799 	ux_sem_post(&frotz_audio.ev_pending);
800 }
801 
802 
803 static void
sound_enqueue_real(sound_engine_t * e,sound_stream_t * s)804 sound_enqueue_real(sound_engine_t *e, sound_stream_t *s)
805 {
806 	assert(e);
807 	assert(s);
808 	int i;
809 
810 	if (s->sound_type == FORM) {
811 		sound_halt_aiff();
812 	} else if (s->sound_type == MOD) {
813 		sound_halt_mod();
814 		sound_halt_ogg();
815 	} else if (s->sound_type == OGGV) {
816 		sound_halt_mod();
817 		sound_halt_ogg();
818 	}
819 
820 	for (i=0; i<NUM_VOICES; ++i) {
821 		if (e->streams[i]) /*only use free voices*/
822 			continue;
823 		/* fprintf(audio_log, "Enqueue %p to %d\n", s, i);*/
824 		e->streams[i]       = s;
825 		e->voices[i].active = true;
826 		e->voices[i].src    = 0;
827 		e->voices[i].pos    = 0;
828 		e->voices[i].repid  = 0;
829 		break;
830 	}
831 }
832 
833 
834 static float
volume_factor(int vol)835 volume_factor(int vol)
836 {
837 	static float lut[8] = {0.0078125f, 0.015625f, 0.03125f, 0.0625f, 0.125f, 0.25f, 0.5f, 1.0f};
838 
839 	if (vol < 1) vol = 1;
840 	if (vol > 8) vol = 8;
841 	return lut[vol-1];
842 	/* return powf(2, vol - 8); */
843 }
844 
845 
846 /**********************************************************************
847  *                       Public API                                   *
848  *                                                                    *
849  **********************************************************************/
850 
851 void
os_init_sound(void)852 os_init_sound(void)
853 {
854 	int i;
855 	int err;
856 	static pthread_attr_t attr;
857 
858 	if (!f_setup.sound) return;
859 
860 	/* Initialize sound engine */
861 	/* audio_log = fopen("audio_log.txt", "w"); */
862 	/* fprintf(audio_log, "os_init_sound...\n"); */
863 	frotz_audio.buffer_size = 1024;
864 	frotz_audio.sample_rate = 48000;
865 	frotz_audio.outl        = (float*)calloc(frotz_audio.buffer_size, sizeof(float));
866 	frotz_audio.outr        = (float*)calloc(frotz_audio.buffer_size, sizeof(float));
867 
868 	for(i=0; i<NUM_VOICES; ++i)
869 		frotz_audio.voices[i].active = 0;
870 
871 	/* No events registered on startup */
872 	ux_sem_init(&frotz_audio.ev_free,    0, 1);
873 	ux_sem_init(&frotz_audio.ev_pending, 0, 0);
874 	ux_sem_init(&sound_done, 0, 0);
875 	frotz_audio.event.type = 0;
876 
877 	/* Start audio thread */
878 	pthread_attr_init(&attr);
879 	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
880 
881 	pthread_t unused_id;
882 	err = pthread_create(&unused_id, &attr, &audio_loop, NULL);
883 	if (err != 0) {
884 		fprintf(stderr, "Can't create audio thread :[%s]", strerror(err));
885 		os_quit(EXIT_FAILURE);
886 	}
887 }
888 
889 
890 /*
891  * os_start_sample
892  *
893  * Play the given sample at the given volume (ranging from 1 to 8 and
894  * 255 meaning a default volume). The sound is played once or several
895  * times in the background (255 meaning forever). In Z-code 3 the
896  * repeats value is always 0 and the number of repeats is taken from
897  * the sound file itself. The end_of_sound function is called as soon
898  * as the sound finishes.
899  *
900  * The end_of_sound function is called in os_tick();
901  *
902  */
903 void
os_start_sample(int number,int volume,int repeats,zword eos)904 os_start_sample(int number, int volume, int repeats, zword eos)
905 {
906 	(void) eos;
907 	/* fprintf(audio_log, "os_start_sample(%d,%d,%d,%d)...\n",number,volume,repeats, eos); */
908 	/* fflush(audio_log); */
909 	extern bb_map_t     *blorb_map;
910 	extern FILE         *blorb_fp;
911 
912 	bb_result_t resource;
913 	int type;
914 	const float vol = volume_factor(volume);
915 	sound_stream_t *s = 0;
916 
917 	if (!f_setup.sound) return;
918 
919 	/* Load resource from BLORB data */
920 	if (blorb_map == NULL) return;
921 
922 	if (bb_err_None != bb_load_resource(blorb_map, bb_method_FilePos, &resource, bb_ID_Snd, number))
923 		return;
924 
925 	type = get_type(blorb_map->chunks[resource.chunknum].type);
926 
927 	if (type == FORM) {
928 		s = load_aiff(blorb_fp,
929 			resource.data.startpos,
930 			resource.length,
931 			number,
932 			vol,
933 			repeats);
934 	} else if (type == MOD) {
935 		s = load_mod(blorb_fp, resource.data.startpos, number, vol);
936 	} else if (type == OGGV) {
937 		s = load_aiff(blorb_fp,
938 			resource.data.startpos,
939 			resource.length,
940 			number,
941 			vol,
942 			repeats);
943 		s->sound_type = OGGV;
944 	}
945 
946 	if (s)
947 		sound_enqueue(s);
948 }
949 
950 
os_beep(int bv)951 void os_beep(int bv)
952 {
953 	(void) bv;
954 	/* Currently implemented as a simple terminal bell */
955 	/* To implement generate a high frequency beep for bv=1, */
956 	/* low frequency for bv=2 */
957 	/* fprintf(audio_log, "os_beep(%d)...\n", bv); */
958 	beep();
959 }
960 
961 
os_prepare_sample(int id)962 void os_prepare_sample(int id)
963 {
964 	(void) id;
965 	/* Currently not implemented */
966 	/* fprintf(audio_log, "os_prepare_sample(%d)...\n", id); */
967 }
968 
969 
os_stop_sample(int id)970 void os_stop_sample(int id)
971 {
972 	/* fprintf(audio_log, "os_stop_sample(%d)...\n", id); */
973 	if (!f_setup.sound) return;
974 	sound_stop_id(id);
975 }
976 
os_finish_with_sample(int id)977 void os_finish_with_sample(int id)
978 {
979 	/* fprintf(audio_log, "os_finish_with_sample(%d)...\n", id); */
980 	os_stop_sample(id);
981 }
982 
983 #endif /* NO_SOUND */
984