From 344f7a7f24aa5eaa9855c05158995464b2909a93 Mon Sep 17 00:00:00 2001 From: David Griffith Date: Mon, 16 Dec 2013 00:22:01 -0800 Subject: [PATCH] This approach seems to work better. Just AIFF for now. --- Makefile | 2 +- src/curses/ux_audio.c | 567 +++++++++++++++++++++++---------------- src/curses/ux_blorb.c | 3 + src/curses/ux_blorb.h | 6 - src/curses/ux_frotz.h | 9 +- src/curses/ux_init.c | 2 + src/curses/ux_resource.c | 2 + 7 files changed, 345 insertions(+), 246 deletions(-) diff --git a/Makefile b/Makefile index 927424a..27ee5cb 100644 --- a/Makefile +++ b/Makefile @@ -193,7 +193,7 @@ CURSES_DEFS = $(OPT_DEFS) $(COLOR_DEFS) $(SOUND_DEFS) $(SOUNDCARD) \ FLAGS = $(OPTS) $(CURSES_DEFS) $(INCL) -SOUND_LIB = -lao -ldl -lm -lsndfile +SOUND_LIB = -lao -ldl -lm -lsndfile -lvorbisfile -lmodplug $(NAME): $(NAME)-curses curses: $(NAME)-curses diff --git a/src/curses/ux_audio.c b/src/curses/ux_audio.c index 4a702bc..f90db51 100644 --- a/src/curses/ux_audio.c +++ b/src/curses/ux_audio.c @@ -20,174 +20,220 @@ #define __UNIX_PORT_FILE +#include +#include +#include +#include +#include + #ifdef USE_NCURSES_H #include #else #include #endif -#include -#include #include #include -#include +#include +#include +#include #include "ux_frotz.h" -#include "ux_blorb.h" -enum {SFX_TYPE, MOD_TYPE}; +#define BUFFSIZE 4096 -typedef struct EFFECT { - void (*destroy)(struct EFFECT *); - int number; - int type; - int active; - int voice; - int *buffer; - int buflen; - int repeats; - int volume; - int ended; - zword eos; - ulong endtime; -} EFFECT; +static int playaiff(FILE *, bb_result_t, int, int); +static int playmod(FILE *, bb_result_t, int, int); +static int playogg(FILE *, bb_result_t, int, int); -/* no effects cache */ +static int mypower(int, int); +static char *getfiledata(FILE *, long *); +static void sigchld_handler(int); -static EFFECT *e_sfx = NULL; -static EFFECT *e_mod = NULL; +static pid_t sfx_pid; +static pid_t music_pid; +/* + * os_beep + * + * Play a beep sound. Ideally, the sound should be high- (number == 1) + * or low-pitched (number == 2). + * + */ -int audiorunning = 0; +void os_beep (int number) +{ -static ao_device *device; + beep(); -static EFFECT *getaiff(FILE *, size_t, int, int); -static EFFECT *geteffect(int); -static EFFECT *new_effect(int, int); +}/* os_beep */ /* -int ux_initsound(void) + * os_prepare_sample + * + * Load the sample from the disk. + * + */ + +void os_prepare_sample (int number) { - ao_sample_format format; - int default_driver; + /* Not implemented */ - if (audiorunning) return 1; - if (!f_setup.sound) return 0; - audiorunning = 1; +}/* os_prepare_sample */ - ao_initialize(); +/* + * os_start_sample + * + * Play the given sample at the given volume (ranging from 1 to 8 and + * 255 meaning a default volume). The sound is played once or several + * times in the background (255 meaning forever). In Z-code 3 the + * repeats value is always 0 and the number of repeats is taken from + * the sound file itself. The end_of_sound function is called as soon + * as the sound finishes. + * + */ - default_driver = ao_default_driver_id(); +void os_start_sample (int number, int volume, int repeats, zword eos) +{ + bb_result_t resource; - memset(&format, 0, sizeof(format)); - format.bits = 16; - format.channels = 2; - format.rate = 44100; - format.byte_format = AO_FMT_BIG; + if (bb_err_None != bb_load_resource(blorb_map, bb_method_FilePos, &resource, bb_ID_Snd, number)) + return; - device = ao_open_live(default_driver, &format, NULL); - if (device == NULL) { - printf("Error opening audio device.\n"); - exit(1); + if (blorb_map->chunks[resource.chunknum].type == bb_make_id('F','O','R','M')) { + playaiff(blorb_fp, resource, volume, repeats); } - printf("Audio ready.\n"); - return 1; -} -void ux_stopsound(void) -{ - ao_close(device); - ao_shutdown(); -} -*/ + return; +}/* os_start_sample */ + +/* + * os_stop_sample + * + * Turn off the current sample. + * + */ -static EFFECT *geteffect(int num) +void os_stop_sample (int number) { - myresource res; - EFFECT *result = NULL; - unsigned int id; - if ((e_sfx) && (e_sfx->number == num)) return e_sfx; - if ((e_mod) && (e_mod->number == num)) return e_mod; + /* Not implemented */ - if (ux_getresource(num,0,bb_method_FilePos,&res) != bb_err_None) - return NULL; +}/* os_stop_sample */ - /* Look for a recognized format. */ +/* + * os_finish_with_sample + * + * Remove the current sample from memory (if any). + * + */ - id = res.type; +void os_finish_with_sample (number) +{ - if (id == bb_make_id('F','O','R','M')) { - result = getaiff(res.fp, res.bbres.data.startpos, res.bbres.length, num); - } -/* - else if (id == bb_make_id('M','O','D',' ') { - result = getmodule(res.file, res.bbres.data.startpos, res.bbres.length, num); - } else { - result = getogg(res.file, res.bbres.data.startpos, res.bbres.length, num); - } -*/ - ux_freeresource(&res); - return result; -} + /* Not implemented */ +}/* os_finish_with_sample */ -static void baredestroy(EFFECT * r) +/* + * os_wait_sample + * + * Stop repeating the current sample and wait until it finishes. + * + */ + +void os_wait_sample (void) { - if (r) { - if (r->buffer != NULL) free(r->buffer); - free(r); - } -} + /* Not implemented */ -static EFFECT *new_effect(int type, int num) -{ - EFFECT * reader = (EFFECT *)calloc(1, sizeof(EFFECT)); +}/* os_wait_sample */ - if (reader) { - reader->type = type; - reader->number = num; - reader->destroy = baredestroy; + +/**************************************************/ + +static int mypower(int base, int exp) { + if (exp == 0) + return 1; + else if (exp % 2) + return base * mypower(base, exp - 1); + else { + int temp = mypower(base, exp / 2); + return temp * temp; } - return (EFFECT *)reader; } +static void sigchld_handler(int signal) { + int status; + struct sigaction sa; + int dead_child; + + dead_child = wait(&status); + if (dead_child == sfx_pid) + sfx_pid = 0; + else if (dead_child == music_pid) + music_pid = 0; + + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGCHLD, &sa, NULL); +} -static EFFECT *getaiff(FILE *fp, size_t pos, int len, int num) +/* + * This function should be able to play OGG chunks, but for some strange + * reason, libsndfile refuses to load them. Libsndfile is capable of + * loading and playing OGGs when they're naked file. I don't see what + * the big problem is. + * + */ +int playaiff(FILE *fp, bb_result_t result, int vol, int repeats) { + int default_driver; + int frames_read; + int count; + int toread; + int *buffer; + long filestart; + + int volcount; + int volfactor; + ao_device *device; ao_sample_format format; - int default_driver; + SNDFILE *sndfile; SF_INFO sf_info; - EFFECT *sample; - void *data; - int size; - int count; - - int frames_read; - - int *buffer; + sigset_t sigchld_mask; + struct sigaction sa; + sfx_pid = fork(); - sample = new_effect(SFX_TYPE, num); - if (sample == NULL || sample == 0) - return sample; + if (sfx_pid < 0) { + perror("fork"); + return 1; + } - if (fseek(fp, pos, SEEK_SET) != 0) - return NULL; + if (sfx_pid > 0) { + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGCHLD, &sa, NULL); + return 0; + } + sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); ao_initialize(); default_driver = ao_default_driver_id(); sf_info.format = 0; - sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 1); + filestart = ftell(fp); + lseek(fileno(fp), result.data.startpos, SEEK_SET); + sndfile = sf_open_fd(fileno(fp), SFM_READ, &sf_info, 0); memset(&format, 0, sizeof(ao_sample_format)); format.byte_format = AO_FMT_NATIVE; @@ -197,178 +243,229 @@ static EFFECT *getaiff(FILE *fp, size_t pos, int len, int num) device = ao_open_live(default_driver, &format, NULL /* no options */); if (device == NULL) { - printf("Error opening sound device.\n"); - exit(1); +// printf("Error opening sound device.\n"); + return 1; } - buffer = malloc(sizeof(int) * sf_info.frames * sf_info.channels); - - sf_read_int(sndfile, buffer, sf_info.frames); + if (vol < 1) vol = 1; + if (vol > 8) vol = 8; + volfactor = mypower(2, -vol + 8); + + buffer = malloc(BUFFSIZE * sf_info.channels * sizeof(int)); + frames_read = 0; + toread = sf_info.frames * sf_info.channels; + + while (toread > 0) { + if (toread < BUFFSIZE * sf_info.channels) + count = toread; + else + count = BUFFSIZE * sf_info.channels; + frames_read = sf_read_int(sndfile, buffer, count); + for (volcount = 0; volcount <= frames_read; volcount++) + buffer[volcount] /= volfactor; + ao_play(device, (char *)buffer, frames_read * sizeof(int)); + toread = toread - frames_read; + } - ao_play(device, (char *)buffer, sf_info.frames * sizeof(int)); + free(buffer); + fseek(fp, filestart, SEEK_SET); ao_close(device); - ao_shutdown(); - sf_close(sndfile); + ao_shutdown(); -/* - while (count <= len) { - fread(sample->buffer + count, 1, 1, f); - count++; - } - - sample->buflen = count; -*/ - return sample; + return 0; } -static void startsample() -{ -/* - if (e_sfx == NULL) return; - ao_play(device, e_sfx->buffer, e_sfx->buflen); -*/ -} -static void stopsample() +static int playmod(FILE *fp, bb_result_t result, int vol, int repeats) { - /* nothing yet */ -} + unsigned char *buffer; + int modlen; -/* - * os_beep - * - * Play a beep sound. Ideally, the sound should be high- (number == 1) - * or low-pitched (number == 2). - * - */ + int default_driver; + ao_device *device; + ao_sample_format format; -void os_beep (int number) -{ -// beep(); + char *filedata; + long size; + ModPlugFile *mod; + ModPlug_Settings settings; - ao_sample_format format; - char *buffer; - int buf_size; - int sample; - float freq; - int i; + long original_offset; + + original_offset = ftell(fp); + fseek(fp, result.data.startpos, SEEK_SET); + + ao_initialize(); + default_driver = ao_default_driver_id(); - if (number == 1) - freq = 880.0; - else - freq = 440.0; + ModPlug_GetSettings(&settings); + + memset(&format, 0, sizeof(ao_sample_format)); + format.byte_format = AO_FMT_NATIVE; format.bits = 16; format.channels = 2; format.rate = 44100; - format.byte_format = AO_FMT_LITTLE; - buf_size = format.bits/8 * format.channels * format.rate; - buffer = calloc(buf_size, sizeof(char)); + /* Note: All "Basic Settings" must be set before ModPlug_Load. */ + settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; /* RESAMP */ + settings.mChannels = 2; + settings.mBits = 16; + settings.mFrequency = 44100; + settings.mStereoSeparation = 128; + settings.mMaxMixChannels = 256; + + /* insert more setting changes here */ + ModPlug_SetSettings(&settings); + + /* remember to free() filedata later */ + filedata = getfiledata(fp, &size); + + mod = ModPlug_Load(filedata, size); + if (!mod) { + printf("Unable to load module\n"); + free(filedata); + return 1; + } + + device = ao_open_live(default_driver, &format, NULL /* no options */); + if (device == NULL) { + printf("Error opening sound device.\n"); + return 1; + } + + if (vol < 1) vol = 1; + if (vol > 8) vol = 8; - for (i = 0; i < format.rate; i++) { - sample = (int)(0.75 * 32768.0 * - sin(2 * M_PI * freq * ((float) i/format.rate))); + ModPlug_SetMasterVolume(mod, mypower(2, vol)); - /* Put the same stuff in left and right channel */ - buffer[4*i] = buffer[4*i+2] = sample & 0xff; - buffer[4*i+1] = buffer[4*i+3] = (sample >> 8) & 0xff; + buffer = malloc(BUFFSIZE * sizeof(char)); + modlen = 1; + while (modlen != 0) { + if (modlen == 0) break; + modlen = ModPlug_Read(mod, buffer, BUFFSIZE * sizeof(char)); + if (modlen > 0 && ao_play(device, (char *) buffer, modlen * sizeof(char)) == 0) { + perror("audio write"); + exit(1); + } } - ao_play(device, buffer, buf_size); + free(buffer); + ao_close(device); + ao_shutdown(); -}/* os_beep */ + fseek(fp, original_offset, SEEK_SET); + + printf("Finished\n"); + + return 0; +} /* - * os_prepare_sample - * - * Load the sample from the disk. - * + * libmodplug requires the whole file to be pulled into memory. + * This function does that and then closes the file. */ +static char *getfiledata(FILE *fp, long *size) +{ + char *data; + long offset; + + offset = ftell(fp); + fseek(fp, 0L, SEEK_END); + (*size) = ftell(fp); + fseek(fp, offset, SEEK_SET); + data = (char*)malloc(*size); + fread(data, *size, sizeof(char), fp); + fseek(fp, offset, SEEK_SET); + + return(data); +} -void os_prepare_sample (int number) + +static int playogg(FILE *fp, bb_result_t result, int vol, int repeats) { + ogg_int64_t toread; + ogg_int64_t frames_read; + ogg_int64_t count; - /* Not implemented */ + vorbis_info *info; -}/* os_prepare_sample */ + OggVorbis_File vf; + int current_section; + void *buffer; -/* - * os_start_sample - * - * Play the given sample at the given volume (ranging from 1 to 8 and - * 255 meaning a default volume). The sound is played once or several - * times in the background (255 meaning forever). In Z-code 3 the - * repeats value is always 0 and the number of repeats is taken from - * the sound file itself. The end_of_sound function is called as soon - * as the sound finishes. - * - */ + int default_driver; + ao_device *device; + ao_sample_format format; -void os_start_sample (int number, int volume, int repeats, zword eos) -{ - EFFECT *e; - -// if (!audiorunning) return; - e = geteffect(number); - if (e == NULL) return; -// if (e->type == SFX_TYPE) stopsample(); - stopsample(); - if (repeats < 1) repeats = 1; - if (repeats == 255) repeats = -1; - if (volume < 0) volume = 0; - if (volume > 8) volume = 8; - if (e->type == SFX_TYPE && repeats > 0) repeats--; - e->repeats = repeats; - e->volume = 32*volume; - e->eos = eos; - e->ended = 0; - if (e->type == SFX_TYPE) { - if ((e_sfx) && (e_sfx != e)) e_sfx->destroy(e_sfx); - e_sfx = e; - startsample(); + int volcount; + int volfactor; + + ao_initialize(); + default_driver = ao_default_driver_id(); + + fseek(fp, result.data.startpos, SEEK_SET); + + memset(&format, 0, sizeof(ao_sample_format)); + + if (ov_open_callbacks(fp, &vf, NULL, 0, OV_CALLBACKS_NOCLOSE) < 0) { + printf("Oops.\n"); + exit(1); } -}/* os_start_sample */ -/* - * os_stop_sample - * - * Turn off the current sample. - * - */ + info = ov_info(&vf, -1); -void os_stop_sample (int number) -{ + format.byte_format = AO_FMT_LITTLE; + format.bits = 16; + format.channels = info->channels; + format.rate = info->rate; - /* Not implemented */ + device = ao_open_live(default_driver, &format, NULL /* no options */); + if (device == NULL) { + printf("Error opening sound device.\n"); + ov_clear(&vf); + return 1; + } -}/* os_stop_sample */ + if (vol < 1) vol = 1; + if (vol > 8) vol = 8; + volfactor = mypower(2, -vol + 8); -/* - * os_finish_with_sample - * - * Remove the current sample from memory (if any). - * - */ + buffer = malloc(BUFFSIZE * format.channels * sizeof(int16_t)); -void os_finish_with_sample (number) -{ + frames_read = 0; + toread = ov_pcm_total(&vf, -1) * 2 * format.channels; + count = 0; - /* Not implemented */ + while (count < toread) { + frames_read = ov_read(&vf, (char *)buffer, BUFFSIZE, 0,2,1,¤t_section); + for (volcount = 0; volcount <= frames_read / 2; volcount++) + ((int16_t *) buffer)[volcount] /= volfactor; + ao_play(device, (char *)buffer, frames_read * sizeof(char)); + count += frames_read; + } -}/* os_finish_with_sample */ + ao_close(device); + ao_shutdown(); + ov_clear(&vf); -/* - * os_wait_sample - * - * Stop repeating the current sample and wait until it finishes. - * - */ + free(buffer); + printf("Finished\n"); -void os_wait_sample (void) -{ + return 0; +} - /* Not implemented */ -}/* os_wait_sample */ + + +/* ----------------------------------------- */ + + +/* + * This function should be able to play OGG chunks, but for some strange + * reason, libsndfile refuses to load them. Libsndfile is capable of + * loading and playing OGGs when they're naked file. I don't see what + * the big problem is. + * + */ diff --git a/src/curses/ux_blorb.c b/src/curses/ux_blorb.c index 671da51..4dea719 100644 --- a/src/curses/ux_blorb.c +++ b/src/curses/ux_blorb.c @@ -41,6 +41,9 @@ f_setup_t f_setup; u_setup_t u_setup; +FILE *blorb_fp; +bb_result_t blorb_res; +bb_map_t *blorb_map; /* * isblorb diff --git a/src/curses/ux_blorb.h b/src/curses/ux_blorb.h index 713ea9d..de3bc48 100644 --- a/src/curses/ux_blorb.h +++ b/src/curses/ux_blorb.h @@ -31,12 +31,6 @@ typedef struct { FILE *fp; } myresource; -//bb_err_t blorb_err; -bb_map_t *blorb_map; -bb_result_t blorb_res; - -FILE *blorb_fp; - int sf_getresource( int num, int ispic, int method, myresource * res); void sf_freeresource( myresource *res); diff --git a/src/curses/ux_frotz.h b/src/curses/ux_frotz.h index 19855eb..6ab1d08 100644 --- a/src/curses/ux_frotz.h +++ b/src/curses/ux_frotz.h @@ -7,6 +7,7 @@ #include "../common/frotz.h" #include "../blorb/blorb.h" +#include "../blorb/blorblow.h" #include "ux_setup.h" #define MASTER_CONFIG "frotz.conf" @@ -80,10 +81,10 @@ extern u_setup_t u_setup; /*** Blorb related stuff ***/ -bb_err_t blorb_err; -bb_map_t *blorb_map; -bb_result_t blorb_res; - +extern bb_err_t blorb_err; +extern bb_map_t *blorb_map; +extern bb_result_t blorb_res; +extern FILE *blorb_fp; /*** Functions specific to the Unix port of Frotz ***/ diff --git a/src/curses/ux_init.c b/src/curses/ux_init.c index 6b74f2f..7a1e0b1 100644 --- a/src/curses/ux_init.c +++ b/src/curses/ux_init.c @@ -544,6 +544,8 @@ FILE *os_load_story(void) } // ux_initsound(); +// os_start_sample(5, 8, 1, 0); +// os_start_sample(4, 8, 1, 0); // os_start_sample(3, 8, 1, 0); // exit(1); diff --git a/src/curses/ux_resource.c b/src/curses/ux_resource.c index 33245a4..bbb6a3e 100644 --- a/src/curses/ux_resource.c +++ b/src/curses/ux_resource.c @@ -6,6 +6,8 @@ #include "ux_frotz.h" #include "ux_blorb.h" +bb_map_t *blorb_map; + /* * ux_getresource * -- 2.34.1