This approach seems to work better. Just AIFF for now.
authorDavid Griffith <dave@661.org>
Mon, 16 Dec 2013 08:22:01 +0000 (00:22 -0800)
committerDavid Griffith <dave@661.org>
Mon, 16 Dec 2013 08:23:59 +0000 (00:23 -0800)
Makefile
src/curses/ux_audio.c
src/curses/ux_blorb.c
src/curses/ux_blorb.h
src/curses/ux_frotz.h
src/curses/ux_init.c
src/curses/ux_resource.c

index 927424a20a57f47b3539d4247feb5f2cc356cb3c..27ee5cb4125dead450ea23420378531520b12e95 100644 (file)
--- 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
index 4a702bc6d4d4dd108ee069b53fce7878da49f9b2..f90db5105cfc87e81ab4bf5824368732c0865f45 100644 (file)
 
 #define __UNIX_PORT_FILE
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
 #ifdef USE_NCURSES_H
 #include <ncurses.h>
 #else
 #include <curses.h>
 #endif
 
-#include <stdio.h>
-#include <string.h>
 #include <ao/ao.h>
 #include <sndfile.h>
-#include <math.h>
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+#include <libmodplug/modplug.h>
 
 #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,&current_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.
+ *
+ */
index 671da511169da783fd9a2f4bd36a8ba953a6d57b..4dea7199ff2e2e29f69c71483f2a7f97bd30196c 100644 (file)
@@ -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
index 713ea9df04bd2b0fbf08d7c9ecd5ee38ee0f0b8e..de3bc480632c0df80b5584b60f5efd04c7c8e2d5 100644 (file)
@@ -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);
index 19855eb683ff7d6c3e650b0250f991606e709089..6ab1d08c0a43c21e47f03f3f9440d9c3f8fbb0b4 100644 (file)
@@ -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 ***/
 
index 6b74f2f9d3d66fdd4831dcb1678072f41a6bcf8f..7a1e0b12a9083a7d0120006180bf0698c65a1394 100644 (file)
@@ -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);
 
index 33245a4768b7de3f7171bc225306cba7dde03029..bbb6a3e93bb4c8c4ecded30086a696ceed3ddf45 100644 (file)
@@ -6,6 +6,8 @@
 #include "ux_frotz.h"
 #include "ux_blorb.h"
 
+bb_map_t *blorb_map;
+
 /*
  * ux_getresource
  *