summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoryo mama <pepper@scannerjammer.com>2015-01-25 23:46:43 -0800
committeryo mama <pepper@scannerjammer.com>2015-01-25 23:46:43 -0800
commite49ddd41e5549c1c6abab8005edd2e8b18ee0e09 (patch)
tree017a97ce4f17c9a4422e80f56f4e638c629438d4 /src
working copy...first push
Diffstat (limited to 'src')
-rw-r--r--src/.jalv_internal.h.swpbin0 -> 16384 bytes
-rw-r--r--src/LV2-render.c823
-rw-r--r--src/LV2-render_console.c73
-rw-r--r--src/LV2-render_internal.h280
-rw-r--r--src/log.c43
-rw-r--r--src/lv2_evbuf.c276
-rw-r--r--src/lv2_evbuf.h149
-rw-r--r--src/midi/Makefile645
-rw-r--r--src/midi/Makefile.am7
-rw-r--r--src/midi/Makefile.in645
-rw-r--r--src/midi/NOTES84
-rw-r--r--src/midi/example.midbin0 -> 93565 bytes
-rw-r--r--src/midi/example1.midbin0 -> 9285 bytes
-rw-r--r--src/midi/example2.midbin0 -> 21504 bytes
-rw-r--r--src/midi/fluid_list.c244
-rw-r--r--src/midi/fluid_list.h62
-rw-r--r--src/midi/fluid_list.lo12
-rw-r--r--src/midi/fluid_list.obin0 -> 17544 bytes
-rw-r--r--src/midi/fluid_midi.c1944
-rw-r--r--src/midi/fluid_midi.h389
-rw-r--r--src/midi/fluid_midi.lo12
-rw-r--r--src/midi/fluid_midi.obin0 -> 86056 bytes
-rw-r--r--src/midi/fluid_midi_custom.h23
-rw-r--r--src/midi/fluidsynth_priv.h281
-rw-r--r--src/midi/libfluidmidi.la41
-rw-r--r--src/midi/midi_loader.c71
-rw-r--r--src/midi/midi_loader.h46
-rw-r--r--src/midi/midi_loader_test.c240
-rw-r--r--src/state.c224
-rw-r--r--src/symap.c231
-rw-r--r--src/symap.h69
-rw-r--r--src/worker.c118
-rw-r--r--src/worker.h35
-rw-r--r--src/zix/common.h83
-rw-r--r--src/zix/sem.h227
-rw-r--r--src/zix/thread.h133
36 files changed, 7510 insertions, 0 deletions
diff --git a/src/.jalv_internal.h.swp b/src/.jalv_internal.h.swp
new file mode 100644
index 0000000..ec50f6b
--- /dev/null
+++ b/src/.jalv_internal.h.swp
Binary files differ
diff --git a/src/LV2-render.c b/src/LV2-render.c
new file mode 100644
index 0000000..d1be763
--- /dev/null
+++ b/src/LV2-render.c
@@ -0,0 +1,823 @@
+#define _POSIX_C_SOURCE 200809L /* for mkdtemp */
+#define _DARWIN_C_SOURCE /* for mkdtemp on OSX */
+#define _DEFAULT_SOURCE
+#include <assert.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __cplusplus
+# include <stdbool.h>
+#endif
+
+#ifdef _WIN32
+# include <io.h> /* for _mktemp */
+# define snprintf _snprintf
+#else
+# include <sys/stat.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#include "jalv_config.h"
+#include "LV2-render_internal.h"
+
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
+#include "lv2/lv2plug.in/ns/ext/data-access/data-access.h"
+#include "lv2/lv2plug.in/ns/ext/event/event.h"
+#include "lv2/lv2plug.in/ns/ext/options/options.h"
+#include "lv2/lv2plug.in/ns/ext/parameters/parameters.h"
+#include "lv2/lv2plug.in/ns/ext/patch/patch.h"
+#include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h"
+#include "lv2/lv2plug.in/ns/ext/presets/presets.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/time/time.h"
+#include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+#include "lilv/lilv.h"
+
+#include "lv2_evbuf.h"
+#include "worker.h"
+
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+
+#define USTR(str) ((const uint8_t*)str)
+
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifdef __clang__
+# define REALTIME __attribute__((annotate("realtime")))
+#else
+# define REALTIME
+#endif
+
+
+/* Size factor for UI ring buffers. The ring size is a few times the size of
+ an event output to give the UI a chance to keep up. Experiments with Ingen,
+ which can highly saturate its event output, led me to this value. It
+ really ought to be enough for anybody(TM).
+*/
+#define N_BUFFER_CYCLES 16
+
+#include <alsa/asoundlib.h>
+#include <sndfile.h>
+#include "midi/midi_loader.h"
+#include "midi/fluidsynth_priv.h"
+#define SAMPLE_RATE 48000 // here
+
+//so min is here, how come it doesn't see it? not sure, try remove inline
+int min(int x, int y) {
+ return (x < y) ? x : y;
+}
+
+typedef struct process_midi_ctx_t {
+ Jalv *jalv;
+ SNDFILE *outfile;
+ float sample_rate;
+} process_midi_ctx_t;
+
+
+void print_audio_to_terminal(float *sf_output, size_t nframes){
+ size_t i;
+ for(i = 0; i< nframes; i++){
+ printf("%04x ", * ((unsigned int *)sf_output) );
+ sf_output++;
+ }
+}
+
+int write_audio_to_file(SNDFILE *outfile, float *sf_output, size_t nframes){
+ size_t items_written = 0;
+ /* Write the audio */
+ if ((items_written = sf_writef_float(outfile,
+ sf_output,
+ nframes)) != nframes) {
+ fprintf(stderr, "Error: can't write data to output file\n");
+ fprintf(stderr, "%s: %s\n", "jalv", sf_strerror(outfile));
+ return 1;
+ }
+ return 0;
+}
+
+SNDFILE *open_wav_file(char *output_file, float sample_rate, int nchannels, size_t length){
+ /* prepare file */
+ SF_INFO outsfinfo;
+ SNDFILE *outfile;
+
+ outsfinfo.samplerate = sample_rate;
+ outsfinfo.channels = nchannels;
+ outsfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+
+ outsfinfo.frames = length;
+
+ outfile = sf_open(output_file, SFM_WRITE, &outsfinfo);
+ return outfile;
+}
+
+
+int process_midi_cb(fluid_midi_event_t *event, size_t msecs, process_midi_ctx_t *ctx)
+{
+ Jalv *jalv = ctx->jalv;
+ float **pluginOutputBuffers = (float **)calloc(jalv->num_ports, sizeof(float *));
+ float *pluginOutputBuffer_first = NULL;
+ size_t nframes;
+
+ /* convert msecs */
+ nframes = msecs * ctx->sample_rate / 1000;
+
+ /* Prepare port buffers */
+ for (uint32_t p = 0; p < jalv->num_ports; ++p) {
+ struct Port* port = &jalv->ports[p];
+ if (port->type == TYPE_AUDIO) {
+ pluginOutputBuffers[p] = (float *)calloc(nframes, sizeof(float));
+ if(!pluginOutputBuffer_first){ pluginOutputBuffer_first = pluginOutputBuffers[p]; };
+
+ lilv_instance_connect_port(
+ jalv->instance, p, //connect port p to this location
+ pluginOutputBuffers[p]
+ );
+ printf("buffer %x ptr: %8x\n", p, pluginOutputBuffers[p]);
+ } else if (port->type == TYPE_EVENT && port->flow == FLOW_INPUT) {
+ lv2_evbuf_reset(port->evbuf, true);
+
+ LV2_Evbuf_Iterator iter = lv2_evbuf_begin(port->evbuf);
+
+ uint8_t midi_event_buffer[3];
+ midi_event_buffer[0] = event->type;
+ midi_event_buffer[1] = event->param1;
+ midi_event_buffer[2] = event->param2;
+//atom_event: type: 19 frames: 1013 size: 3
+ lv2_evbuf_write(&iter,
+ 0, 0, //Doesn't care about these, not sure why
+ jalv->midi_event_id,
+ sizeof(midi_event_buffer), midi_event_buffer);
+ } else if (port->type == TYPE_EVENT) {
+ /* Clear event output for plugin to write to */
+// printf("CLEARING EVENT\n");
+ lv2_evbuf_reset(port->evbuf, false);
+ }
+ }
+
+
+
+ lilv_instance_run(jalv->instance, nframes);
+
+ write_audio_to_file(ctx->outfile, pluginOutputBuffers[3], nframes); //TODO ADD LATER
+// printf("total ports: %d\n", jalv->num_ports);
+ for(int i=0; i<jalv->num_ports; i++){
+ if(pluginOutputBuffers[i]){
+ //printf("buffer: %x content:", i);
+ //print_audio_to_terminal(pluginOutputBuffers[i], nframes);
+ free(pluginOutputBuffers[i]);
+ }
+ }
+
+
+
+
+ return 0;
+}
+
+
+ZixSem exit_sem; /**< Exit semaphore */
+
+static LV2_URID
+map_uri(LV2_URID_Map_Handle handle,
+ const char* uri)
+{
+ Jalv* jalv = (Jalv*)handle;
+ zix_sem_wait(&jalv->symap_lock);
+ const LV2_URID id = symap_map(jalv->symap, uri);
+ zix_sem_post(&jalv->symap_lock);
+ return id;
+}
+
+static const char*
+unmap_uri(LV2_URID_Unmap_Handle handle,
+ LV2_URID urid)
+{
+ Jalv* jalv = (Jalv*)handle;
+ zix_sem_wait(&jalv->symap_lock);
+ const char* uri = symap_unmap(jalv->symap, urid);
+ zix_sem_post(&jalv->symap_lock);
+ return uri;
+}
+
+/**
+ Map function for URI map extension.
+*/
+static uint32_t
+uri_to_id(LV2_URI_Map_Callback_Data callback_data,
+ const char* map,
+ const char* uri)
+{
+ Jalv* jalv = (Jalv*)callback_data;
+ zix_sem_wait(&jalv->symap_lock);
+ const LV2_URID id = symap_map(jalv->symap, uri);
+ zix_sem_post(&jalv->symap_lock);
+ return id;
+}
+
+
+
+//{{{ LV2 host features defined
+#define NS_EXT "http://lv2plug.in/ns/ext/"
+static LV2_URI_Map_Feature uri_map = { NULL, &uri_to_id };
+
+static LV2_Extension_Data_Feature ext_data = { NULL };
+
+
+static LV2_Feature uri_map_feature = { NS_EXT "uri-map", &uri_map };
+static LV2_Feature map_feature = { LV2_URID__map, NULL };
+static LV2_Feature unmap_feature = { LV2_URID__unmap, NULL };
+static LV2_Feature make_path_feature = { LV2_STATE__makePath, NULL };
+static LV2_Feature schedule_feature = { LV2_WORKER__schedule, NULL };
+static LV2_Feature log_feature = { LV2_LOG__log, NULL };
+static LV2_Feature options_feature = { LV2_OPTIONS__options, NULL };
+static LV2_Feature def_state_feature = { LV2_STATE__loadDefaultState, NULL };
+
+
+/** These features have no data */
+static LV2_Feature buf_size_features[3] = {
+ { LV2_BUF_SIZE__powerOf2BlockLength, NULL },
+ { LV2_BUF_SIZE__fixedBlockLength, NULL },
+ { LV2_BUF_SIZE__boundedBlockLength, NULL } };
+
+const LV2_Feature* features[13] = {
+ &uri_map_feature, &map_feature, &unmap_feature,
+ &make_path_feature,
+ &schedule_feature,
+ &log_feature,
+ &options_feature,
+ &def_state_feature,
+ &buf_size_features[0],
+ &buf_size_features[1],
+ &buf_size_features[2],
+ NULL
+};
+
+/** Return true iff Jalv supports the given feature. */
+static bool
+feature_is_supported(const char* uri)
+{
+ if (!strcmp(uri, "http://lv2plug.in/ns/lv2core#isLive")) {
+ return true;
+ }
+ for (const LV2_Feature*const* f = features; *f; ++f) {
+ if (!strcmp(uri, (*f)->URI)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+//}}}
+
+/** Abort and exit on error */
+static void
+die(const char* msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ exit(EXIT_FAILURE);
+}
+
+static void
+create_port(Jalv* jalv,
+ uint32_t port_index,
+ float default_value)
+{
+ struct Port* const port = &jalv->ports[port_index];
+
+ port->lilv_port = lilv_plugin_get_port_by_index(jalv->plugin, port_index);
+ port->evbuf = NULL;
+ port->buf_size = 0;
+ port->index = port_index;
+ port->control = 0.0f;
+ port->flow = FLOW_UNKNOWN;
+
+ const bool optional = lilv_port_has_property(
+ jalv->plugin, port->lilv_port, jalv->nodes.lv2_connectionOptional);
+
+ /* Set the port flow (input or output) */
+ if (lilv_port_is_a(jalv->plugin, port->lilv_port, jalv->nodes.lv2_InputPort)) {
+ port->flow = FLOW_INPUT;
+ } else if (lilv_port_is_a(jalv->plugin, port->lilv_port,
+ jalv->nodes.lv2_OutputPort)) {
+ port->flow = FLOW_OUTPUT;
+ } else if (!optional) {
+ die("Mandatory port has unknown type (neither input nor output)");
+ }
+
+ /* Set control values */
+ if (lilv_port_is_a(jalv->plugin, port->lilv_port, jalv->nodes.lv2_ControlPort)) {
+ port->type = TYPE_CONTROL;
+ port->control = isnan(default_value) ? 0.0f : default_value;
+ } else if (lilv_port_is_a(jalv->plugin, port->lilv_port,
+ jalv->nodes.lv2_AudioPort)) {
+ port->type = TYPE_AUDIO;
+ } else if (lilv_port_is_a(jalv->plugin, port->lilv_port,
+ jalv->nodes.ev_EventPort)) {
+ port->type = TYPE_EVENT;
+ port->old_api = true;
+ } else if (lilv_port_is_a(jalv->plugin, port->lilv_port,
+ jalv->nodes.atom_AtomPort)) {
+ port->type = TYPE_EVENT;
+ port->old_api = false;
+ } else if (!optional) {
+ die("Mandatory port has unknown data type");
+ }
+
+ LilvNode* min_size = lilv_port_get(
+ jalv->plugin, port->lilv_port, jalv->nodes.rsz_minimumSize);
+ if (min_size && lilv_node_is_int(min_size)) {
+ port->buf_size = lilv_node_as_int(min_size);
+ jalv->opts.buffer_size = MAX(
+ jalv->opts.buffer_size, port->buf_size * N_BUFFER_CYCLES);
+ }
+ lilv_node_free(min_size);
+
+ /* Update longest symbol for aligned console printing */
+ const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port);
+ const size_t len = strlen(lilv_node_as_string(sym));
+ if (len > jalv->longest_sym) {
+ jalv->longest_sym = len;
+ }
+}
+
+/**
+ Create port structures from data (via create_port()) for all ports.
+*/
+void
+jalv_create_ports(Jalv* jalv)
+{
+ jalv->num_ports = lilv_plugin_get_num_ports(jalv->plugin);
+ jalv->ports = (struct Port*)calloc(jalv->num_ports, sizeof(struct Port));
+ float* default_values = (float*)calloc(
+ lilv_plugin_get_num_ports(jalv->plugin), sizeof(float));
+ lilv_plugin_get_port_ranges_float(jalv->plugin, NULL, NULL, default_values);
+
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ create_port(jalv, i, default_values[i]);
+ }
+
+ const LilvPort* control_input = lilv_plugin_get_port_by_designation(
+ jalv->plugin, jalv->nodes.lv2_InputPort, jalv->nodes.lv2_control);
+ if (control_input) {
+ jalv->control_in = lilv_port_get_index(jalv->plugin, control_input);
+ }
+
+ free(default_values);
+}
+
+/**
+ Allocate port buffers (only necessary for MIDI).
+*/
+static void
+jalv_allocate_port_buffers(Jalv* jalv)
+{
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ struct Port* const port = &jalv->ports[i];
+ switch (port->type) {
+ case TYPE_EVENT:
+ lv2_evbuf_free(port->evbuf);
+ const size_t buf_size = (port->buf_size > 0)
+ ? port->buf_size
+ : jalv->midi_buf_size;
+ port->evbuf = lv2_evbuf_new(
+ buf_size,
+ port->old_api ? LV2_EVBUF_EVENT : LV2_EVBUF_ATOM,
+ jalv->map.map(jalv->map.handle,
+ lilv_node_as_string(jalv->nodes.atom_Chunk)),
+ jalv->map.map(jalv->map.handle,
+ lilv_node_as_string(jalv->nodes.atom_Sequence)));
+ //FIXME instance setup here
+ lilv_instance_connect_port(
+ jalv->instance, i, lv2_evbuf_get_buffer(port->evbuf));
+ default: break;
+ }
+ }
+}
+
+/**
+ Get a port structure by symbol.
+
+ TODO: Build an index to make this faster, currently O(n) which may be
+ a problem when restoring the state of plugins with many ports.
+*/
+struct Port*
+jalv_port_by_symbol(Jalv* jalv, const char* sym)
+{
+ for (uint32_t i = 0; i < jalv->num_ports; ++i) {
+ struct Port* const port = &jalv->ports[i];
+ const LilvNode* port_sym = lilv_port_get_symbol(jalv->plugin,
+ port->lilv_port);
+
+ if (!strcmp(lilv_node_as_string(port_sym), sym)) {
+ return port;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+print_control_value(Jalv* jalv, const struct Port* port, float value)
+{
+ const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port);
+ printf("%-*s = %f\n", jalv->longest_sym, lilv_node_as_string(sym), value);
+}
+//}}}
+
+static void
+activate_port(Jalv* jalv,
+ uint32_t port_index)
+{
+ struct Port* const port = &jalv->ports[port_index];
+
+ const LilvNode* sym = lilv_port_get_symbol(jalv->plugin, port->lilv_port);
+
+ /* Connect unsupported ports to NULL (known to be optional by this point) */
+ if (port->flow == FLOW_UNKNOWN || port->type == TYPE_UNKNOWN) {
+ lilv_instance_connect_port(jalv->instance, port_index, NULL);
+ return;
+ }
+
+ /* Connect the port based on its type */
+ switch (port->type) {
+ case TYPE_CONTROL:
+ print_control_value(jalv, port, port->control);
+ lilv_instance_connect_port(jalv->instance, port_index, &port->control);
+ break;
+ case TYPE_AUDIO:
+ //FIXME maybe connect the ports to the buffers here instead
+ break;
+ case TYPE_EVENT:
+ if (lilv_port_supports_event(
+ jalv->plugin, port->lilv_port, jalv->nodes.midi_MidiEvent)) {
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+static bool
+jalv_apply_control_arg(Jalv* jalv, const char* s)
+{
+ char sym[256];
+ float val = 0.0f;
+ if (sscanf(s, "%[^=]=%f", sym, &val) != 2) {
+ fprintf(stderr, "warning: Ignoring invalid value `%s'\n", s);
+ return false;
+ }
+
+ struct Port* port = jalv_port_by_symbol(jalv, sym);
+ if (!port) {
+ fprintf(stderr, "warning: Ignoring value for unknown port `%s'\n", sym);
+ return false;
+ }
+
+ port->control = val;
+ return true;
+}
+
+static void
+signal_handler(int ignored)
+{
+ zix_sem_post(&exit_sem);
+}
+
+int
+main(int argc, char** argv)
+{
+ Jalv jalv;
+ memset(&jalv, '\0', sizeof(Jalv));
+ jalv.prog_name = argv[0];
+ jalv.block_length = 4096;
+ jalv.midi_buf_size = 1024;
+ jalv.play_state = JALV_PAUSED;
+ jalv.bpm = 120.0f;
+
+ if (jalv_init(&argc, &argv, &jalv.opts)) {
+ return EXIT_FAILURE;
+ }
+
+ if (jalv.opts.uuid) {
+ printf("UUID: %s\n", jalv.opts.uuid);
+ }
+
+ jalv.symap = symap_new();
+ zix_sem_init(&jalv.symap_lock, 1);
+ uri_map.callback_data = &jalv;
+
+ jalv.map.handle = &jalv;
+ jalv.map.map = map_uri;
+ map_feature.data = &jalv.map;
+
+ jalv.unmap.handle = &jalv;
+ jalv.unmap.unmap = unmap_uri;
+ unmap_feature.data = &jalv.unmap;
+
+ lv2_atom_forge_init(&jalv.forge, &jalv.map);
+
+ jalv.sratom = sratom_new(&jalv.map);
+ jalv.ui_sratom = sratom_new(&jalv.map);
+
+ jalv.midi_event_id = uri_to_id(
+ &jalv, "http://lv2plug.in/ns/ext/event", LV2_MIDI__MidiEvent);
+
+ jalv.urids.atom_Float = symap_map(jalv.symap, LV2_ATOM__Float);
+ jalv.urids.atom_Int = symap_map(jalv.symap, LV2_ATOM__Int);
+ jalv.urids.atom_eventTransfer = symap_map(jalv.symap, LV2_ATOM__eventTransfer);
+ jalv.urids.bufsz_maxBlockLength = symap_map(jalv.symap, LV2_BUF_SIZE__maxBlockLength);
+ jalv.urids.bufsz_minBlockLength = symap_map(jalv.symap, LV2_BUF_SIZE__minBlockLength);
+ jalv.urids.bufsz_sequenceSize = symap_map(jalv.symap, LV2_BUF_SIZE__sequenceSize);
+ jalv.urids.log_Trace = symap_map(jalv.symap, LV2_LOG__Trace);
+ jalv.urids.midi_MidiEvent = symap_map(jalv.symap, LV2_MIDI__MidiEvent);
+ jalv.urids.param_sampleRate = symap_map(jalv.symap, LV2_PARAMETERS__sampleRate);
+ jalv.urids.patch_Set = symap_map(jalv.symap, LV2_PATCH__Set);
+ jalv.urids.patch_property = symap_map(jalv.symap, LV2_PATCH__property);
+ jalv.urids.patch_value = symap_map(jalv.symap, LV2_PATCH__value);
+ jalv.urids.time_Position = symap_map(jalv.symap, LV2_TIME__Position);
+ jalv.urids.time_bar = symap_map(jalv.symap, LV2_TIME__bar);
+ jalv.urids.time_barBeat = symap_map(jalv.symap, LV2_TIME__barBeat);
+ jalv.urids.time_beatUnit = symap_map(jalv.symap, LV2_TIME__beatUnit);
+ jalv.urids.time_beatsPerBar = symap_map(jalv.symap, LV2_TIME__beatsPerBar);
+ jalv.urids.time_beatsPerMinute = symap_map(jalv.symap, LV2_TIME__beatsPerMinute);
+ jalv.urids.time_frame = symap_map(jalv.symap, LV2_TIME__frame);
+ jalv.urids.time_speed = symap_map(jalv.symap, LV2_TIME__speed);
+ jalv.urids.ui_updateRate = symap_map(jalv.symap, LV2_UI__updateRate);
+
+
+
+#ifdef _WIN32
+ jalv.temp_dir = jalv_strdup("jalvXXXXXX");
+ _mktemp(jalv.temp_dir);
+#else
+ char* templ = jalv_strdup("/tmp/jalv-XXXXXX");
+ jalv.temp_dir = jalv_strjoin(mkdtemp(templ), "/");
+ free(templ);
+#endif
+
+ LV2_State_Make_Path make_path = { &jalv, jalv_make_path };
+ make_path_feature.data = &make_path;
+
+ LV2_Worker_Schedule schedule = { &jalv, jalv_worker_schedule };
+ schedule_feature.data = &schedule;
+
+ LV2_Log_Log llog = { &jalv, jalv_printf, jalv_vprintf };
+ log_feature.data = &llog;
+
+ zix_sem_init(&exit_sem, 0);
+ jalv.done = &exit_sem;
+
+ zix_sem_init(&jalv.paused, 0);
+// zix_sem_init(&jalv.worker.sem, 0);
+
+ signal(SIGINT, signal_handler);
+ signal(SIGTERM, signal_handler);
+
+ /* Find all installed plugins */
+ LilvWorld* world = lilv_world_new();
+ lilv_world_load_all(world);
+ jalv.world = world;
+ const LilvPlugins* plugins = lilv_world_get_all_plugins(world);
+
+ /* Cache URIs for concepts we'll use */
+ jalv.nodes.atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort);
+ jalv.nodes.atom_Chunk = lilv_new_uri(world, LV2_ATOM__Chunk);
+ jalv.nodes.atom_Sequence = lilv_new_uri(world, LV2_ATOM__Sequence);
+ jalv.nodes.ev_EventPort = lilv_new_uri(world, LV2_EVENT__EventPort);
+ jalv.nodes.lv2_AudioPort = lilv_new_uri(world, LV2_CORE__AudioPort);
+ jalv.nodes.lv2_ControlPort = lilv_new_uri(world, LV2_CORE__ControlPort);
+ jalv.nodes.lv2_InputPort = lilv_new_uri(world, LV2_CORE__InputPort);
+ jalv.nodes.lv2_OutputPort = lilv_new_uri(world, LV2_CORE__OutputPort);
+ jalv.nodes.lv2_connectionOptional = lilv_new_uri(world, LV2_CORE__connectionOptional);
+ jalv.nodes.lv2_control = lilv_new_uri(world, LV2_CORE__control);
+ jalv.nodes.lv2_name = lilv_new_uri(world, LV2_CORE__name);
+ jalv.nodes.midi_MidiEvent = lilv_new_uri(world, LV2_MIDI__MidiEvent);
+ jalv.nodes.pg_group = lilv_new_uri(world, LV2_PORT_GROUPS__group);
+ jalv.nodes.pset_Preset = lilv_new_uri(world, LV2_PRESETS__Preset);
+ jalv.nodes.rdfs_label = lilv_new_uri(world, LILV_NS_RDFS "label");
+ jalv.nodes.rsz_minimumSize = lilv_new_uri(world, LV2_RESIZE_PORT__minimumSize);
+ jalv.nodes.work_interface = lilv_new_uri(world, LV2_WORKER__interface);
+ jalv.nodes.work_schedule = lilv_new_uri(world, LV2_WORKER__schedule);
+ jalv.nodes.end = NULL;
+
+ /* Get plugin URI from loaded state or command line */
+ LilvState* state = NULL;
+ LilvNode* plugin_uri = NULL;
+ if (jalv.opts.load) {
+ struct stat info;
+ stat(jalv.opts.load, &info);
+ if (S_ISDIR(info.st_mode)) {
+ char* path = jalv_strjoin(jalv.opts.load, "/state.ttl");
+ state = lilv_state_new_from_file(jalv.world, &jalv.map, NULL, path);
+ free(path);
+ } else {
+ state = lilv_state_new_from_file(jalv.world, &jalv.map, NULL,
+ jalv.opts.load);
+ }
+ if (!state) {
+ fprintf(stderr, "Failed to load state from %s\n", jalv.opts.load);
+ return EXIT_FAILURE;
+ }
+ plugin_uri = lilv_node_duplicate(lilv_state_get_plugin_uri(state));
+ } else if (argc > 1) {
+ plugin_uri = lilv_new_uri(world, argv[argc - 1]);
+ }
+
+ if (!plugin_uri) {
+ fprintf(stderr, "Missing plugin URI, try lv2ls to list plugins\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Find plugin */
+ printf("Plugin: %s\n", lilv_node_as_string(plugin_uri));
+ jalv.plugin = lilv_plugins_get_by_uri(plugins, plugin_uri);
+ lilv_node_free(plugin_uri);
+ if (!jalv.plugin) {
+ fprintf(stderr, "Failed to find plugin\n");
+ lilv_world_free(world);
+ return EXIT_FAILURE;
+ }
+
+ /* Check that any required features are supported */
+ LilvNodes* req_feats = lilv_plugin_get_required_features(jalv.plugin);
+ LILV_FOREACH(nodes, f, req_feats) {
+ const char* uri = lilv_node_as_uri(lilv_nodes_get(req_feats, f));
+ if (!feature_is_supported(uri)) {
+ fprintf(stderr, "Feature %s is not supported\n", uri);
+ lilv_world_free(world);
+ return EXIT_FAILURE;
+ }
+ }
+ lilv_nodes_free(req_feats);
+
+ if (!state) {
+ printf("Creating new default state for plugin\n");
+ /* Not restoring state, load the plugin as a preset to get default */
+ state = lilv_state_new_from_world(
+ jalv.world, &jalv.map, lilv_plugin_get_uri(jalv.plugin));
+ }
+
+ /* Create port structures (jalv.ports) */
+ jalv_create_ports(&jalv);
+
+//a lilvnode is just basically any plugin configuration parameter...
+ /* Get the plugin's name */
+ LilvNode* name = lilv_plugin_get_name(jalv.plugin);
+ const char* name_str = lilv_node_as_string(name);
+
+ lilv_node_free(name);
+
+ jalv.sample_rate = SAMPLE_RATE;
+ jalv.block_length = 1024; //TODO used to be 256
+ jalv.midi_buf_size = 32768; //used to be 256
+
+ printf("Block length: %u frames\n", jalv.block_length);
+ printf("MIDI buffers: %zu bytes\n", jalv.midi_buf_size);
+
+ if (jalv.opts.buffer_size == 0) {
+ /* The UI ring is fed by plugin output ports (usually one), and the UI
+ updates roughly once per cycle. The ring size is a few times the
+ size of the MIDI output to give the UI a chance to keep up. The UI
+ should be able to keep up with 4 cycles, and tests show this works
+ for me, but this value might need increasing to avoid overflows.
+ */
+ jalv.opts.buffer_size = jalv.midi_buf_size * N_BUFFER_CYCLES;
+ }
+
+ /* The UI can only go so fast, clamp to reasonable limits */
+ jalv.ui_update_hz = MIN(60, jalv.ui_update_hz);
+ jalv.opts.buffer_size = MAX(4096, jalv.opts.buffer_size);
+ fprintf(stderr, "Comm buffers: %d bytes\n", jalv.opts.buffer_size);
+ fprintf(stderr, "Update rate: %.01f Hz\n", jalv.ui_update_hz);
+
+ /* Build options array to pass to plugin */
+ const LV2_Options_Option options[] = {
+ { LV2_OPTIONS_INSTANCE, 0, jalv.urids.param_sampleRate,
+ sizeof(float), jalv.urids.atom_Float, &jalv.sample_rate },
+ { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_minBlockLength,
+ sizeof(int32_t), jalv.urids.atom_Int, &jalv.block_length },
+ { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_maxBlockLength,
+ sizeof(int32_t), jalv.urids.atom_Int, &jalv.block_length },
+ { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_sequenceSize,
+ sizeof(int32_t), jalv.urids.atom_Int, &jalv.midi_buf_size },
+ { LV2_OPTIONS_INSTANCE, 0, jalv.urids.ui_updateRate,
+ sizeof(float), jalv.urids.atom_Float, &jalv.ui_update_hz },
+ { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL }
+ };
+
+ options_feature.data = &options;
+
+ /* Instantiate the plugin */
+ jalv.instance = lilv_plugin_instantiate(
+ jalv.plugin, jalv.sample_rate, features);
+ if (!jalv.instance) {
+ die("Failed to instantiate plugin.\n");
+ }
+
+ ext_data.data_access = lilv_instance_get_descriptor(jalv.instance)->extension_data;
+
+ fprintf(stderr, "\n");
+ if (!jalv.buf_size_set) {
+ jalv_allocate_port_buffers(&jalv);
+ }
+
+ /* Apply loaded state to plugin instance if necessary */
+ if (state) {
+ jalv_apply_state(&jalv, state);
+ printf("applying state here \n");
+ }
+
+ if (jalv.opts.controls) {
+ for (char** c = jalv.opts.controls; *c; ++c) {
+ jalv_apply_control_arg(&jalv, *c);
+ }
+ }
+
+ for (uint32_t i = 0; i < jalv.num_ports; ++i) {
+ activate_port(&jalv, i);
+ }
+
+ /* Activate plugin */
+ lilv_instance_activate(jalv.instance);
+
+
+//FIXME get sample rate from above...right? yes
+ jalv.sample_rate = SAMPLE_RATE;
+ jalv.play_state = JALV_RUNNING;
+
+ // open_wav_file here
+ char *output_file = "output.wav";
+ size_t length = SAMPLE_RATE;
+ float sample_rate = SAMPLE_RATE;
+ int nchannels = 1;
+ SNDFILE *outfile = open_wav_file(output_file, sample_rate, nchannels, length);
+ process_midi_ctx_t process_midi_ctx;
+ process_midi_ctx.jalv = &jalv;
+ process_midi_ctx.outfile = outfile;
+ process_midi_ctx.sample_rate = sample_rate;
+
+ load_midi_file("short_example.mid", (read_midi_callback)process_midi_cb, &process_midi_ctx);
+//
+//STUDY LATER
+
+
+ sf_close(outfile);
+
+ /* Run UI (or prompt at console) */
+// jalv_open_ui(&jalv);
+
+
+// zix_sem_wait(&exit_sem);
+// jalv.exit = true;
+
+ fprintf(stderr, "Exiting...\n");
+
+ /* Terminate the worker */
+
+ for (uint32_t i = 0; i < jalv.num_ports; ++i) {
+ if (jalv.ports[i].evbuf) {
+ lv2_evbuf_free(jalv.ports[i].evbuf);
+ }
+ }
+
+ /* Deactivate plugin */
+ lilv_instance_deactivate(jalv.instance);
+ lilv_instance_free(jalv.instance);
+
+ /* Clean up */
+ free(jalv.ports);
+ for (LilvNode** n = (LilvNode**)&jalv.nodes; *n; ++n) {
+ lilv_node_free(*n);
+ }
+ symap_free(jalv.symap);
+ zix_sem_destroy(&jalv.symap_lock);
+ sratom_free(jalv.sratom);
+ sratom_free(jalv.ui_sratom);
+ lilv_world_free(world);
+
+ zix_sem_destroy(&exit_sem);
+
+ remove(jalv.temp_dir);
+ free(jalv.temp_dir);
+ free(jalv.ui_event_buf);
+
+ return 0;
+}
+
diff --git a/src/LV2-render_console.c b/src/LV2-render_console.c
new file mode 100644
index 0000000..1d421d2
--- /dev/null
+++ b/src/LV2-render_console.c
@@ -0,0 +1,73 @@
+#define _XOPEN_SOURCE 500
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "jalv_config.h"
+#include "LV2-render_internal.h"
+
+#include "lv2/lv2plug.in/ns/extensions/ui/ui.h"
+
+static int
+print_usage(const char* name, bool error)
+{
+ FILE* const os = error ? stderr : stdout;
+ fprintf(os, "Usage: %s [OPTION...] PLUGIN_URI\n", name);
+ fprintf(os, "Render a midi file with an LV2 plugin instrument.\n");
+ fprintf(os, " -h Display this help and exit\n");
+ fprintf(os, " -p Print control output changes to stdout\n");
+ fprintf(os, " -c SYM=VAL Set control value (e.g. \"vol=1.4\")\n");
+ fprintf(os, " -l DIR Load state from save directory\n");
+ fprintf(os, " -d DIR Dump plugin <=> UI communication\n");
+ fprintf(os, " -b SIZE Buffer size for plugin <=> UI communication\n");
+ return error ? 1 : 0;
+}
+
+int
+jalv_init(int* argc, char*** argv, JalvOptions* opts)
+{
+ opts->controls = malloc(sizeof(char*));
+ opts->controls[0] = NULL;
+
+ int n_controls = 0;
+ int a = 1;
+ for (; a < *argc && (*argv)[a][0] == '-'; ++a) {
+ if ((*argv)[a][1] == 'h') {
+ return print_usage((*argv)[0], true);
+ } else if ((*argv)[a][1] == 's') {
+ opts->show_ui = true;
+ } else if ((*argv)[a][1] == 'p') {
+ opts->print_controls = true;
+ } else if ((*argv)[a][1] == 'l') {
+ if (++a == *argc) {
+ fprintf(stderr, "Missing argument for -l\n");
+ return 1;
+ }
+ opts->load = jalv_strdup((*argv)[a]);
+ } else if ((*argv)[a][1] == 'b') {
+ if (++a == *argc) {
+ fprintf(stderr, "Missing argument for -b\n");
+ return 1;
+ }
+ opts->buffer_size = atoi((*argv)[a]);
+ } else if ((*argv)[a][1] == 'c') {
+ if (++a == *argc) {
+ fprintf(stderr, "Missing argument for -c\n");
+ return 1;
+ }
+ opts->controls = realloc(opts->controls,
+ (++n_controls + 1) * sizeof(char*));
+ opts->controls[n_controls - 1] = (*argv)[a];
+ opts->controls[n_controls] = NULL;
+ } else if ((*argv)[a][1] == 'd') {
+ opts->dump = true;
+ } else {
+ fprintf(stderr, "Unknown option %s\n", (*argv)[a]);
+ return print_usage((*argv)[0], true);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/LV2-render_internal.h b/src/LV2-render_internal.h
new file mode 100644
index 0000000..cac20b0
--- /dev/null
+++ b/src/LV2-render_internal.h
@@ -0,0 +1,280 @@
+
+#ifndef JALV_INTERNAL_H
+#define JALV_INTERNAL_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "lilv/lilv.h"
+#include "serd/serd.h"
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
+#include "lv2/lv2plug.in/ns/ext/log/log.h"
+#include "lv2/lv2plug.in/ns/ext/midi/midi.h"
+#include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h"
+#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
+#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
+
+#include "zix/sem.h"
+#include "zix/thread.h"
+
+#include "sratom/sratom.h"
+
+#include "lv2_evbuf.h"
+#include "symap.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum PortFlow {
+ FLOW_UNKNOWN,
+ FLOW_INPUT,
+ FLOW_OUTPUT
+};
+
+enum PortType {
+ TYPE_UNKNOWN,
+ TYPE_CONTROL,
+ TYPE_AUDIO,
+ TYPE_EVENT
+};
+
+struct Port {
+ const LilvPort* lilv_port;
+ enum PortType type;
+ enum PortFlow flow;
+ jack_port_t* jack_port; ///< For audio/MIDI ports, otherwise NULL
+ LV2_Evbuf* evbuf; ///< For MIDI ports, otherwise NULL
+ void* widget; ///< Control widget, if applicable
+ size_t buf_size; ///< Custom buffer size, or 0
+ uint32_t index; ///< Port index
+ float control; ///< For control ports, otherwise 0.0f
+ bool old_api; ///< True for event, false for atom
+};
+
+/**
+ Control change event, sent through ring buffers for UI updates.
+*/
+typedef struct {
+ uint32_t index;
+ uint32_t protocol;
+ uint32_t size;
+ uint8_t body[];
+} ControlChange;
+
+typedef struct {
+ char* uuid; ///< Session UUID
+ char* load; ///< Path for state to load
+ char** controls; ///< Control values
+ uint32_t buffer_size; ///< Plugin <= >UI communication buffer size
+ double update_rate; ///< UI update rate in Hz
+ int dump; ///< Dump communication iff true
+ int generic_ui; ///< Use generic UI iff true
+ int show_hidden; ///< Show controls for notOnGUI ports
+ int no_menu; ///< Hide menu iff true
+ int show_ui; ///< Show non-embedded UI
+ int print_controls; ///< Print control changes to stdout
+} JalvOptions;
+
+typedef struct {
+ LV2_URID atom_Float;
+ LV2_URID atom_Int;
+ LV2_URID atom_eventTransfer;
+ LV2_URID bufsz_maxBlockLength;
+ LV2_URID bufsz_minBlockLength;
+ LV2_URID bufsz_sequenceSize;
+ LV2_URID log_Trace;
+ LV2_URID midi_MidiEvent;
+ LV2_URID param_sampleRate;
+ LV2_URID patch_Set;
+ LV2_URID patch_property;
+ LV2_URID patch_value;
+ LV2_URID time_Position;
+ LV2_URID time_bar;
+ LV2_URID time_barBeat;
+ LV2_URID time_beatUnit;
+ LV2_URID time_beatsPerBar;
+ LV2_URID time_beatsPerMinute;
+ LV2_URID time_frame;
+ LV2_URID time_speed;
+ LV2_URID ui_updateRate;
+} JalvURIDs;
+
+typedef struct {
+ LilvNode* atom_AtomPort;
+ LilvNode* atom_Chunk;
+ LilvNode* atom_Sequence;
+ LilvNode* ev_EventPort;
+ LilvNode* lv2_AudioPort;
+ LilvNode* lv2_ControlPort;
+ LilvNode* lv2_InputPort;
+ LilvNode* lv2_OutputPort;
+ LilvNode* lv2_connectionOptional;
+ LilvNode* lv2_control;
+ LilvNode* lv2_name;
+ LilvNode* midi_MidiEvent;
+ LilvNode* pg_group;
+ LilvNode* pset_Preset;
+ LilvNode* rdfs_label;
+ LilvNode* rsz_minimumSize;
+ LilvNode* work_interface;
+ LilvNode* work_schedule;
+ LilvNode* end; ///< NULL terminator for easy freeing of entire structure
+} JalvNodes;
+
+typedef enum {
+ JALV_RUNNING,
+ JALV_PAUSE_REQUESTED,
+ JALV_PAUSED
+} JalvPlayState;
+
+typedef struct {
+ jack_ringbuffer_t* requests; ///< Requests to the worker
+ jack_ringbuffer_t* responses; ///< Responses from the worker
+ void* response; ///< Worker response buffer
+ ZixSem sem; ///< Worker semaphore
+ ZixThread thread; ///< Worker thread
+ const LV2_Worker_Interface* iface; ///< Plugin worker interface
+} JalvWorker;
+
+typedef struct {
+ JalvOptions opts; ///< Command-line options
+ JalvURIDs urids; ///< URIDs
+ JalvNodes nodes; ///< Nodes
+ LV2_Atom_Forge forge; ///< Atom forge
+ const char* prog_name; ///< Program name (argv[0])
+ LilvWorld* world; ///< Lilv World
+ LV2_URID_Map map; ///< URI => Int map
+ LV2_URID_Unmap unmap; ///< Int => URI map
+ Sratom* sratom; ///< Atom serialiser
+ Sratom* ui_sratom; ///< Atom serialiser for UI thread
+ Symap* symap; ///< URI map
+ ZixSem symap_lock; ///< Lock for URI map
+ jack_client_t* jack_client; ///< Jack client
+ jack_ringbuffer_t* ui_events; ///< Port events from UI
+ jack_ringbuffer_t* plugin_events; ///< Port events from plugin
+ void* ui_event_buf; ///< Buffer for reading UI port events
+ JalvWorker worker; ///< Worker thread implementation
+ ZixSem* done; ///< Exit semaphore
+ ZixSem paused; ///< Paused signal from process thread
+ JalvPlayState play_state; ///< Current play state
+ char* temp_dir; ///< Temporary plugin state directory
+ char* save_dir; ///< Plugin save directory
+ const LilvPlugin* plugin; ///< Plugin class (RDF data)
+ LilvUIs* uis; ///< All plugin UIs (RDF data)
+ const LilvUI* ui; ///< Plugin UI (RDF data)
+ const LilvNode* ui_type; ///< Plugin UI type (unwrapped)
+ LilvInstance* instance; ///< Plugin instance (shared library)
+ void* window; ///< Window (if applicable)
+ struct Port* ports; ///< Port array of size num_ports
+ uint32_t block_length; ///< Jack buffer size (block length)
+ size_t midi_buf_size; ///< Size of MIDI port buffers
+ uint32_t control_in; ///< Index of control input port
+ uint32_t num_ports; ///< Size of the two following arrays:
+ uint32_t longest_sym; ///< Longest port symbol
+ float ui_update_hz; ///< Frequency of UI updates
+ jack_nframes_t sample_rate; ///< Sample rate
+ jack_nframes_t event_delta_t; ///< Frames since last update sent to UI
+ uint32_t midi_event_id; ///< MIDI event class ID in event context
+ jack_nframes_t position; ///< Transport position in frames
+ float bpm; ///< Transport tempo in beats per minute
+ bool rolling; ///< Transport speed (0=stop, 1=play)
+ bool buf_size_set; ///< True iff buffer size callback fired
+ bool exit; ///< True iff execution is finished
+} Jalv;
+
+int
+jalv_init(int* argc, char*** argv, JalvOptions* opts);
+
+void
+jalv_create_ports(Jalv* jalv);
+
+struct Port*
+jalv_port_by_symbol(Jalv* jalv, const char* sym);
+
+typedef int (*PresetSink)(Jalv* jalv,
+ const LilvNode* node,
+ const LilvNode* title,
+ void* data);
+
+int
+jalv_load_presets(Jalv* jalv, PresetSink sink, void* data);
+
+int
+jalv_unload_presets(Jalv* jalv);
+
+int
+jalv_apply_preset(Jalv* jalv, const LilvNode* preset);
+
+int
+jalv_save_preset(Jalv* jalv,
+ const char* dir,
+ const char* uri,
+ const char* label,
+ const char* filename);
+
+void
+jalv_save(Jalv* jalv, const char* dir);
+
+void
+jalv_save_port_values(Jalv* jalv,
+ SerdWriter* writer,
+ const SerdNode* subject);
+char*
+jalv_make_path(LV2_State_Make_Path_Handle handle,
+ const char* path);
+
+void
+jalv_apply_state(Jalv* jalv, LilvState* state);
+
+char*
+atom_to_turtle(LV2_URID_Unmap* unmap,
+ const SerdNode* subject,
+ const SerdNode* predicate,
+ const LV2_Atom* atom);
+
+static inline char*
+jalv_strdup(const char* str)
+{
+ const size_t len = strlen(str);
+ char* copy = (char*)malloc(len + 1);
+ memcpy(copy, str, len + 1);
+ return copy;
+}
+
+static inline char*
+jalv_strjoin(const char* a, const char* b)
+{
+ const size_t a_len = strlen(a);
+ const size_t b_len = strlen(b);
+ char* const out = (char*)malloc(a_len + b_len + 1);
+
+ memcpy(out, a, a_len);
+ memcpy(out + a_len, b, b_len);
+ out[a_len + b_len] = '\0';
+
+ return out;
+}
+
+int
+jalv_printf(LV2_Log_Handle handle,
+ LV2_URID type,
+ const char* fmt, ...);
+
+int
+jalv_vprintf(LV2_Log_Handle handle,
+ LV2_URID type,
+ const char* fmt,
+ va_list ap);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // JALV_INTERNAL_H
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..eed3298
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,43 @@
+/*
+ Copyright 2007-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "LV2-render_internal.h"
+
+int
+jalv_printf(LV2_Log_Handle handle,
+ LV2_URID type,
+ const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ const int ret = jalv_vprintf(handle, type, fmt, args);
+ va_end(args);
+ return ret;
+}
+
+int
+jalv_vprintf(LV2_Log_Handle handle,
+ LV2_URID type,
+ const char* fmt,
+ va_list ap)
+{
+ // TODO: Lock
+ Jalv* jalv = (Jalv*)handle;
+ if (type == jalv->urids.log_Trace && !jalv->opts.dump) {
+ return 0;
+ }
+ return vfprintf(stderr, fmt, ap);
+}
diff --git a/src/lv2_evbuf.c b/src/lv2_evbuf.c
new file mode 100644
index 0000000..e8c9b60
--- /dev/null
+++ b/src/lv2_evbuf.c
@@ -0,0 +1,276 @@
+/*
+ Copyright 2008-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/event/event.h"
+
+#include "lv2_evbuf.h"
+
+struct LV2_Evbuf_Impl {
+ LV2_Evbuf_Type type;
+ uint32_t capacity;
+ uint32_t atom_Chunk;
+ uint32_t atom_Sequence;
+ union {
+ LV2_Event_Buffer event;
+ LV2_Atom_Sequence atom;
+ } buf;
+};
+
+static inline uint32_t
+lv2_evbuf_pad_size(uint32_t size)
+{
+ return (size + 7) & (~7);
+}
+
+LV2_Evbuf*
+lv2_evbuf_new(uint32_t capacity,
+ LV2_Evbuf_Type type,
+ uint32_t atom_Chunk,
+ uint32_t atom_Sequence)
+{
+ // FIXME: memory must be 64-bit aligned
+ LV2_Evbuf* evbuf = (LV2_Evbuf*)malloc(
+ sizeof(LV2_Evbuf) + sizeof(LV2_Atom_Sequence) + capacity);
+ evbuf->capacity = capacity;
+ evbuf->atom_Chunk = atom_Chunk;
+ evbuf->atom_Sequence = atom_Sequence;
+ lv2_evbuf_set_type(evbuf, type);
+ lv2_evbuf_reset(evbuf, true);
+ return evbuf;
+}
+
+void
+lv2_evbuf_free(LV2_Evbuf* evbuf)
+{
+ free(evbuf);
+}
+
+void
+lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type)
+{
+ evbuf->type = type;
+ switch (type) {
+ case LV2_EVBUF_EVENT:
+ evbuf->buf.event.data = (uint8_t*)(evbuf + 1);
+ evbuf->buf.event.capacity = evbuf->capacity;
+ break;
+ case LV2_EVBUF_ATOM:
+ break;
+ }
+ lv2_evbuf_reset(evbuf, true);
+}
+
+void
+lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input)
+{
+ switch (evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ evbuf->buf.event.header_size = sizeof(LV2_Event_Buffer);
+ evbuf->buf.event.stamp_type = LV2_EVENT_AUDIO_STAMP;
+ evbuf->buf.event.event_count = 0;
+ evbuf->buf.event.size = 0;
+ break;
+ case LV2_EVBUF_ATOM:
+ if (input) {
+ evbuf->buf.atom.atom.size = sizeof(LV2_Atom_Sequence_Body);
+ evbuf->buf.atom.atom.type = evbuf->atom_Sequence;
+ } else {
+ evbuf->buf.atom.atom.size = evbuf->capacity;
+ evbuf->buf.atom.atom.type = evbuf->atom_Chunk;
+ }
+ }
+}
+
+uint32_t
+lv2_evbuf_get_size(LV2_Evbuf* evbuf)
+{
+ switch (evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ return evbuf->buf.event.size;
+ case LV2_EVBUF_ATOM:
+ assert(evbuf->buf.atom.atom.type != evbuf->atom_Sequence
+ || evbuf->buf.atom.atom.size >= sizeof(LV2_Atom_Sequence_Body));
+ return evbuf->buf.atom.atom.type == evbuf->atom_Sequence
+ ? evbuf->buf.atom.atom.size - sizeof(LV2_Atom_Sequence_Body)
+ : 0;
+ }
+ return 0;
+}
+
+void*
+lv2_evbuf_get_buffer(LV2_Evbuf* evbuf)
+{
+ switch (evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ return &evbuf->buf.event;
+ case LV2_EVBUF_ATOM:
+ return &evbuf->buf.atom;
+ }
+ return NULL;
+}
+
+LV2_Evbuf_Iterator
+lv2_evbuf_begin(LV2_Evbuf* evbuf)
+{
+ LV2_Evbuf_Iterator iter = { evbuf, 0 };
+ return iter;
+}
+
+LV2_Evbuf_Iterator
+lv2_evbuf_end(LV2_Evbuf* evbuf)
+{
+ const uint32_t size = lv2_evbuf_get_size(evbuf);
+ const LV2_Evbuf_Iterator iter = { evbuf, lv2_evbuf_pad_size(size) };
+ return iter;
+}
+
+bool
+lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter)
+{
+ return iter.offset < lv2_evbuf_get_size(iter.evbuf);
+}
+
+LV2_Evbuf_Iterator
+lv2_evbuf_next(LV2_Evbuf_Iterator iter)
+{
+ if (!lv2_evbuf_is_valid(iter)) {
+ return iter;
+ }
+
+ LV2_Evbuf* evbuf = iter.evbuf;
+ uint32_t offset = iter.offset;
+ uint32_t size;
+ switch (evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ size = ((LV2_Event*)(evbuf->buf.event.data + offset))->size;
+ offset += lv2_evbuf_pad_size(sizeof(LV2_Event) + size);
+ break;
+ case LV2_EVBUF_ATOM:
+ size = ((LV2_Atom_Event*)
+ ((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom)
+ + offset))->body.size;
+ offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
+ break;
+ }
+
+ LV2_Evbuf_Iterator next = { evbuf, offset };
+ return next;
+}
+
+bool
+lv2_evbuf_get(LV2_Evbuf_Iterator iter,
+ uint32_t* frames,
+ uint32_t* subframes,
+ uint32_t* type,
+ uint32_t* size,
+ uint8_t** data)
+{
+ *frames = *subframes = *type = *size = 0;
+ *data = NULL;
+
+ if (!lv2_evbuf_is_valid(iter)) {
+ return false;
+ }
+
+ LV2_Event_Buffer* ebuf;
+ LV2_Event* ev;
+ LV2_Atom_Sequence* aseq;
+ LV2_Atom_Event* aev;
+ switch (iter.evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ ebuf = &iter.evbuf->buf.event;
+ ev = (LV2_Event*)((char*)ebuf->data + iter.offset);
+ *frames = ev->frames;
+ *subframes = ev->subframes;
+ *type = ev->type;
+ *size = ev->size;
+ *data = (uint8_t*)ev + sizeof(LV2_Event);
+ break;
+ case LV2_EVBUF_ATOM:
+ aseq = (LV2_Atom_Sequence*)&iter.evbuf->buf.atom;
+ aev = (LV2_Atom_Event*)(
+ (char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq)
+ + iter.offset);
+ *frames = aev->time.frames;
+ *subframes = 0;
+ *type = aev->body.type;
+ *size = aev->body.size;
+ *data = LV2_ATOM_BODY(&aev->body);
+ break;
+ }
+
+ return true;
+}
+
+bool
+lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
+ uint32_t frames,
+ uint32_t subframes,
+ uint32_t type,
+ uint32_t size,
+ const uint8_t* data)
+{
+ LV2_Event_Buffer* ebuf;
+ LV2_Event* ev;
+ LV2_Atom_Sequence* aseq;
+ LV2_Atom_Event* aev;
+ switch (iter->evbuf->type) {
+ case LV2_EVBUF_EVENT:
+ ebuf = &iter->evbuf->buf.event;
+ if (ebuf->capacity - ebuf->size < sizeof(LV2_Event) + size) {
+ return false;
+ }
+
+ ev = (LV2_Event*)(ebuf->data + iter->offset);
+ ev->frames = frames;
+ ev->subframes = subframes;
+ ev->type = type;
+ ev->size = size;
+ memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size);
+
+ size = lv2_evbuf_pad_size(sizeof(LV2_Event) + size);
+ ebuf->size += size;
+ ebuf->event_count += 1;
+ iter->offset += size;
+ break;
+ case LV2_EVBUF_ATOM:
+ aseq = (LV2_Atom_Sequence*)&iter->evbuf->buf.atom;
+ if (iter->evbuf->capacity - sizeof(LV2_Atom) - aseq->atom.size
+ < sizeof(LV2_Atom_Event) + size) {
+ return false;
+ }
+
+ aev = (LV2_Atom_Event*)(
+ (char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq)
+ + iter->offset);
+ aev->time.frames = frames;
+ aev->body.type = type;
+ aev->body.size = size;
+ memcpy(LV2_ATOM_BODY(&aev->body), data, size);
+
+ size = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
+ aseq->atom.size += size;
+ iter->offset += size;
+ break;
+ }
+
+ return true;
+}
diff --git a/src/lv2_evbuf.h b/src/lv2_evbuf.h
new file mode 100644
index 0000000..6dc4e69
--- /dev/null
+++ b/src/lv2_evbuf.h
@@ -0,0 +1,149 @@
+#ifndef LV2_EVBUF_H
+#define LV2_EVBUF_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#include <stdbool.h>
+#endif
+
+/**
+ Format of actual buffer.
+*/
+typedef enum {
+ /**
+ An (old) ev:EventBuffer (LV2_Event_Buffer).
+ */
+ LV2_EVBUF_EVENT,
+
+ /**
+ A (new) atom:Sequence (LV2_Atom_Sequence).
+ */
+ LV2_EVBUF_ATOM
+} LV2_Evbuf_Type;
+
+/**
+ An abstract/opaque LV2 event buffer.
+*/
+typedef struct LV2_Evbuf_Impl LV2_Evbuf;
+
+/**
+ An iterator over an LV2_Evbuf.
+*/
+typedef struct {
+ LV2_Evbuf* evbuf;
+ uint32_t offset;
+} LV2_Evbuf_Iterator;
+
+/**
+ Allocate a new, empty event buffer.
+ URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM.
+*/
+LV2_Evbuf*
+lv2_evbuf_new(uint32_t capacity,
+ LV2_Evbuf_Type type,
+ uint32_t atom_Chunk,
+ uint32_t atom_Sequence);
+
+/**
+ Free an event buffer allocated with lv2_evbuf_new.
+*/
+void
+lv2_evbuf_free(LV2_Evbuf* evbuf);
+
+/**
+ Reset and change the type of an existing event buffer.
+ URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM.
+*/
+void
+lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type);
+
+/**
+ Clear and initialize an existing event buffer.
+ The contents of buf are ignored entirely and overwritten, except capacity
+ which is unmodified.
+ If input is false and this is an atom buffer, the buffer will be prepared
+ for writing by the plugin. This MUST be called before every run cycle.
+*/
+void
+lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input);
+
+/**
+ Return the total padded size of the events stored in the buffer.
+*/
+uint32_t
+lv2_evbuf_get_size(LV2_Evbuf* evbuf);
+
+/**
+ Return the actual buffer implementation.
+ The format of the buffer returned depends on the buffer type.
+*/
+void*
+lv2_evbuf_get_buffer(LV2_Evbuf* evbuf);
+
+/**
+ Return an iterator to the start of `evbuf`.
+*/
+LV2_Evbuf_Iterator
+lv2_evbuf_begin(LV2_Evbuf* evbuf);
+
+/**
+ Return an iterator to the end of `evbuf`.
+*/
+LV2_Evbuf_Iterator
+lv2_evbuf_end(LV2_Evbuf* evbuf);
+
+/**
+ Check if `iter` is valid.
+ @return True if `iter` is valid, otherwise false (past end of buffer)
+*/
+bool
+lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter);
+
+/**
+ Advance `iter` forward one event.
+ `iter` must be valid.
+ @return True if `iter` is valid, otherwise false (reached end of buffer)
+*/
+LV2_Evbuf_Iterator
+lv2_evbuf_next(LV2_Evbuf_Iterator iter);
+
+/**
+ Dereference an event iterator (i.e. get the event currently pointed to).
+ `iter` must be valid.
+ `type` Set to the type of the event.
+ `size` Set to the size of the event.
+ `data` Set to the contents of the event.
+ @return True on success.
+*/
+bool
+lv2_evbuf_get(LV2_Evbuf_Iterator iter,
+ uint32_t* frames,
+ uint32_t* subframes,
+ uint32_t* type,
+ uint32_t* size,
+ uint8_t** data);
+
+/**
+ Write an event at `iter`.
+ The event (if any) pointed to by `iter` will be overwritten, and `iter`
+ incremented to point to the following event (i.e. several calls to this
+ function can be done in sequence without twiddling iter in-between).
+ @return True if event was written, otherwise false (buffer is full).
+//so this function takes frames and subframes...I guess frames are samples, but you don't know offhand what subframes are, do you? don't know, i saw 0 for it in atom write ohh hmm, maybe it's just extra data or something? looks so ok cool
+*/
+bool
+lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
+ uint32_t frames, //what about this? it's asking for frames though? well it seems like this is position inside one buffer, so since we have event always at start of processing we just set it to 0 ok I'll have to think about that
+ uint32_t subframes,
+ uint32_t type,
+ uint32_t size,
+ const uint8_t* data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LV2_EVBUF_H */
diff --git a/src/midi/Makefile b/src/midi/Makefile
new file mode 100644
index 0000000..e65bef3
--- /dev/null
+++ b/src/midi/Makefile
@@ -0,0 +1,645 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# src/midi/Makefile. Generated from Makefile.in by configure.
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+
+
+
+
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/cli-dssi-host
+pkgincludedir = $(includedir)/cli-dssi-host
+pkglibdir = $(libdir)/cli-dssi-host
+pkglibexecdir = $(libexecdir)/cli-dssi-host
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = x86_64-unknown-linux-gnu
+host_triplet = x86_64-unknown-linux-gnu
+bin_PROGRAMS = test_midi_loader$(EXEEXT)
+subdir = src/midi
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libfluidmidi_la_LIBADD =
+am_libfluidmidi_la_OBJECTS = fluid_midi.lo fluid_list.lo \
+ midi_loader.lo
+libfluidmidi_la_OBJECTS = $(am_libfluidmidi_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_test_midi_loader_OBJECTS = test_midi_loader.$(OBJEXT)
+test_midi_loader_OBJECTS = $(am_test_midi_loader_OBJECTS)
+test_midi_loader_DEPENDENCIES = libfluidmidi.la
+AM_V_P = $(am__v_P_$(V))
+am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libfluidmidi_la_SOURCES) $(test_midi_loader_SOURCES)
+DIST_SOURCES = $(libfluidmidi_la_SOURCES) $(test_midi_loader_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = ${SHELL} /home/pepper/DSSI/dssi-render/missing aclocal-1.14
+ALSA_CFLAGS = -I/usr/include/alsa
+ALSA_LIBS = -lasound
+AMTAR = $${TAR-tar}
+AM_DEFAULT_VERBOSITY = 1
+AR = ar
+AUTOCONF = ${SHELL} /home/pepper/DSSI/dssi-render/missing autoconf
+AUTOHEADER = ${SHELL} /home/pepper/DSSI/dssi-render/missing autoheader
+AUTOMAKE = ${SHELL} /home/pepper/DSSI/dssi-render/missing automake-1.14
+AWK = gawk
+CC = gcc
+CCDEPMODE = depmode=gcc3
+CFLAGS = -g -O2
+CPP = gcc -E
+CPPFLAGS =
+CYGPATH_W = echo
+DEFS = -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DPACKAGE=\"cli-dssi-host\" -DVERSION=\"0.1.3\"
+DEPDIR = .deps
+DLLTOOL = false
+DSSI_CFLAGS =
+DSSI_LIBS =
+DSYMUTIL =
+DUMPBIN =
+ECHO_C =
+ECHO_N = -n
+ECHO_T =
+EGREP = /usr/bin/grep -E
+EXEEXT =
+FGREP = /usr/bin/grep -F
+GREP = /usr/bin/grep
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_SCRIPT = ${INSTALL}
+INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
+LD = /usr/bin/ld -m elf_x86_64
+LDFLAGS =
+LIBOBJS =
+LIBS =
+LIBTOOL = $(SHELL) $(top_builddir)/libtool
+LIPO =
+LN_S = ln -s
+LTLIBOBJS =
+MAKEINFO = ${SHELL} /home/pepper/DSSI/dssi-render/missing makeinfo
+MANIFEST_TOOL = :
+MKDIR_P = /usr/bin/mkdir -p
+NM = /usr/bin/nm -B
+NMEDIT =
+OBJDUMP = objdump
+OBJEXT = o
+OTOOL =
+OTOOL64 =
+PACKAGE = cli-dssi-host
+PACKAGE_BUGREPORT =
+PACKAGE_NAME =
+PACKAGE_STRING =
+PACKAGE_TARNAME =
+PACKAGE_URL =
+PACKAGE_VERSION =
+PATH_SEPARATOR = :
+PKG_CONFIG = /usr/bin/pkg-config
+PKG_CONFIG_LIBDIR =
+PKG_CONFIG_PATH =
+RANLIB = ranlib
+SED = /usr/bin/sed
+SET_MAKE =
+SHELL = /bin/sh
+SNDFILE_CFLAGS =
+SNDFILE_LIBS = -lsndfile
+STRIP = strip
+VERSION = 0.1.3
+abs_builddir = /home/pepper/DSSI/dssi-render/src/midi
+abs_srcdir = /home/pepper/DSSI/dssi-render/src/midi
+abs_top_builddir = /home/pepper/DSSI/dssi-render
+abs_top_srcdir = /home/pepper/DSSI/dssi-render
+ac_ct_AR = ar
+ac_ct_CC = gcc
+ac_ct_DUMPBIN =
+am__include = include
+am__leading_dot = .
+am__quote =
+am__tar = $${TAR-tar} chof - "$$tardir"
+am__untar = $${TAR-tar} xf -
+bindir = ${exec_prefix}/bin
+build = x86_64-unknown-linux-gnu
+build_alias =
+build_cpu = x86_64
+build_os = linux-gnu
+build_vendor = unknown
+builddir = .
+datadir = ${datarootdir}
+datarootdir = ${prefix}/share
+docdir = ${datarootdir}/doc/${PACKAGE}
+dvidir = ${docdir}
+exec_prefix = ${prefix}
+host = x86_64-unknown-linux-gnu
+host_alias =
+host_cpu = x86_64
+host_os = linux-gnu
+host_vendor = unknown
+htmldir = ${docdir}
+includedir = ${prefix}/include
+infodir = ${datarootdir}/info
+install_sh = ${SHELL} /home/pepper/DSSI/dssi-render/install-sh
+libdir = ${exec_prefix}/lib
+libexecdir = ${exec_prefix}/libexec
+localedir = ${datarootdir}/locale
+localstatedir = ${prefix}/var
+mandir = ${datarootdir}/man
+mkdir_p = $(MKDIR_P)
+oldincludedir = /usr/include
+pdfdir = ${docdir}
+prefix = /usr/local
+program_transform_name = s,x,x,
+psdir = ${docdir}
+sbindir = ${exec_prefix}/sbin
+sharedstatedir = ${prefix}/com
+srcdir = .
+sysconfdir = ${prefix}/etc
+target_alias =
+top_build_prefix = ../../
+top_builddir = ../..
+top_srcdir = ../..
+noinst_LTLIBRARIES = libfluidmidi.la
+libfluidmidi_la_SOURCES = fluid_midi.c fluid_list.c midi_loader.c
+test_midi_loader_SOURCES = test_midi_loader.c
+test_midi_loader_LDADD = -lm -ldl libfluidmidi.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/midi/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/midi/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libfluidmidi.la: $(libfluidmidi_la_OBJECTS) $(libfluidmidi_la_DEPENDENCIES) $(EXTRA_libfluidmidi_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libfluidmidi_la_OBJECTS) $(libfluidmidi_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+test_midi_loader$(EXEEXT): $(test_midi_loader_OBJECTS) $(test_midi_loader_DEPENDENCIES) $(EXTRA_test_midi_loader_DEPENDENCIES)
+ @rm -f test_midi_loader$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_midi_loader_OBJECTS) $(test_midi_loader_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+include ./$(DEPDIR)/fluid_list.Plo
+include ./$(DEPDIR)/fluid_midi.Plo
+include ./$(DEPDIR)/midi_loader.Plo
+include ./$(DEPDIR)/test_midi_loader.Po
+
+.c.o:
+ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+# $(AM_V_CC)source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(AM_V_CC_no)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+# $(AM_V_CC)source='$<' object='$@' libtool=no \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(AM_V_CC_no)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+# $(AM_V_CC)source='$<' object='$@' libtool=yes \
+# DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) \
+# $(AM_V_CC_no)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+ clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/midi/Makefile.am b/src/midi/Makefile.am
new file mode 100644
index 0000000..3282105
--- /dev/null
+++ b/src/midi/Makefile.am
@@ -0,0 +1,7 @@
+noinst_LTLIBRARIES = libfluidmidi.la
+libfluidmidi_la_SOURCES = fluid_midi.c fluid_list.c midi_loader.c
+
+bin_PROGRAMS = test_midi_loader
+
+test_midi_loader_SOURCES = test_midi_loader.c
+test_midi_loader_LDADD = -lm -ldl libfluidmidi.la
diff --git a/src/midi/Makefile.in b/src/midi/Makefile.in
new file mode 100644
index 0000000..f406eb8
--- /dev/null
+++ b/src/midi/Makefile.in
@@ -0,0 +1,645 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = test_midi_loader$(EXEEXT)
+subdir = src/midi
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/mkinstalldirs $(top_srcdir)/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libfluidmidi_la_LIBADD =
+am_libfluidmidi_la_OBJECTS = fluid_midi.lo fluid_list.lo \
+ midi_loader.lo
+libfluidmidi_la_OBJECTS = $(am_libfluidmidi_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_test_midi_loader_OBJECTS = test_midi_loader.$(OBJEXT)
+test_midi_loader_OBJECTS = $(am_test_midi_loader_OBJECTS)
+test_midi_loader_DEPENDENCIES = libfluidmidi.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libfluidmidi_la_SOURCES) $(test_midi_loader_SOURCES)
+DIST_SOURCES = $(libfluidmidi_la_SOURCES) $(test_midi_loader_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSSI_CFLAGS = @DSSI_CFLAGS@
+DSSI_LIBS = @DSSI_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SNDFILE_CFLAGS = @SNDFILE_CFLAGS@
+SNDFILE_LIBS = @SNDFILE_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LTLIBRARIES = libfluidmidi.la
+libfluidmidi_la_SOURCES = fluid_midi.c fluid_list.c midi_loader.c
+test_midi_loader_SOURCES = test_midi_loader.c
+test_midi_loader_LDADD = -lm -ldl libfluidmidi.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/midi/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/midi/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libfluidmidi.la: $(libfluidmidi_la_OBJECTS) $(libfluidmidi_la_DEPENDENCIES) $(EXTRA_libfluidmidi_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libfluidmidi_la_OBJECTS) $(libfluidmidi_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+test_midi_loader$(EXEEXT): $(test_midi_loader_OBJECTS) $(test_midi_loader_DEPENDENCIES) $(EXTRA_test_midi_loader_DEPENDENCIES)
+ @rm -f test_midi_loader$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_midi_loader_OBJECTS) $(test_midi_loader_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fluid_list.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fluid_midi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/midi_loader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_midi_loader.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \
+ clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/midi/NOTES b/src/midi/NOTES
new file mode 100644
index 0000000..4bd7b67
--- /dev/null
+++ b/src/midi/NOTES
@@ -0,0 +1,84 @@
+
+ //unsigned long nevents; weird...so the plugin must know about previous notes that are pressed? yes for each call run_synth it should know about note which are currently pressed. oh but there isn't any place to indicate that? seems like we just need
+ //to keep track of how many notes are currently running and pass it an unsigned long that has that count, but aside from that,
+ //it just takes note_on and note_off structs, which refer to only one note at a time? not really,
+ //so in C to pass an array we have to use two variables onw is a pointer to data, another is number of elements in that array. so run_synth is one of such functions, and if you have two notes pressed you send array of size 2, storing two elements nearby in memory and setting nevents to 2. got it, so current_event isn't a pointer to a struct, it's a pointer to an array of event structs, most likely? yes
+//
+//so what do we do next? we need to construct this array of currently pressed notes, so need to check events and try to construc this.
+
+
+
+
+//ok moving on...I did a bit of research about midi types...basically the reason mrswatson only supports type0 midi is that
+//the idea behind type0 midi is that it is single-channel. Here channel is a bit of a confusing word, because there is also such a thing as an audio channel, obviously
+//but the two are completely unrelated. Channel is like a track in multitrack recording, type0 midi only has one of these tracks, but potentially has program
+//changes within it (program change is just a change of voice). so what we're working on here only handles single channel midi as well, and that's good.
+//Only thing to think about is, if we abstract our loop to process the note events in the channel, rather than the note events in the entire file (unfortunately confusing
+//terminology again, because in midifiles, the entire file is called a "track"). But this is basically the situation.
+//
+//Midi events come in a few different types, but basically there are three types that are important
+//
+//meta type events
+//
+//channel events (events particular to a single channel)
+//
+//track events (events that apply to every channel)
+//
+//so meta type events are sort of special events,
+//
+//channel events and track events are more like "regular" events....
+//
+//do you follow so far? yep
+//
+//
+//
+//So the main question to ask you is, since we are iterating through all events, how do we handle multiple channels? we have the option to do what mrswatson has done
+//which is to read the midi file type from the header and end in error if it is anything but type0, or we can sort of process the channels individually in the sense that we send appropriate info to the plugin, telling it which channel it is intended for. Most plugins though don't care about multiple channels
+//
+//another option, more related to the first is to write a simple script (or find one) that separates out multi-channel midi into individual type0 files.
+//
+//so with multi channel files, if events are sorted by time in file, then we can just send them all at once as it was one channel, so plugin will produce resulting file.
+//if that's the plan, I guess we should check to make sure that dssi doesn't support multiple channels, because if it does, maybe we should tell the plugin which channel each event is intended for? i think snd_seq_event_t has that filed for channel, so we just need to copy that info there.
+//
+//
+//
+//ok do we break now? seems a bit late yeah, so basically what is left to do is to write it from pseudo code to real code of this function, add pritngin to see how it works and if there is any bugs to fix them.
+//ok then make this into a module, add it to cli-dssi-host and change cli-dssi-host source to deal with this loop? yep. got it. maybe we can look into this tomorrow morning? sure ok perfect
+
+
+
+ //ok so what were you thinking here?
+ //ok and finally that run_synth need array which is not sparse, where elements are without spaces, so either you rebuild
+ //new array every time you call run_synth, or keep elements together and use find function to locate a note. find function
+ //sounds best, either way, do we need two arrays? no need
+
+ //with this, can I assign sparsely to it, if I want to put the notes[100] = 4; , will that cause an error?
+ //so first is that you need to put size of memory you use in malloc, not e lemtns, like that, right? yep
+ //second is that we need snd_seq_event_t intead of ints. I was just thinking that if the note is pressed, it wouldn
+ //have a value , if not it wouldn't so it would be an easy way to keep track of which are pressed or not? hmm sort of short lookup arraly, might work if built properly. ok well I don't know if that's right I'll make another array
+//question is, if we're sending a list, and a NOTE_ON and NOTE_OFF must be removed from the list once it has been already processed, how do we access those elements? by note name somehow? yeah need to track each note on\off pair.
+//no clue how to do this in C, easy in any other language, just use an associative array
+//right in C it turns into function which finds an element in array. how big should our array be? do we use the heap here? ideally we need to use it, but guess we can limit number of notes which are pressed at same time, like 100 or so should be plently there are only so many midi notes, I'll get the count 128 that should be the limit good, try write with heap functions, might be useful to see how to work with it. well could I just
+
+
+
+
+ //so what does this return exactly? so then event arrives we need to try find note if it was pressed or not.
+ //so basically whole logic of get_events function is:
+ //
+ //snd_seq_event_t event = convert_event(fluid_midi_event_t);
+ //event_in_table = find_event(&event);
+ //switch(event->type ){
+ // case NOTE_ON:
+ // if(event_in_table) return; // skip if pressed while still was pressed, shouldn't happen...I think it just gets pressed again, but both are deleted on the note_off, does that make sense? hmm might be, but that change a logic a bit
+ // yeah something like this. so basic idea is clear or not really? yeah it's clear, just too advanced for me
+ // to come up with on my own right now given my current skills with C, it's coming along fast though well it's simple then written in perl or somethng else, just showing how c does the same things. so now need to implement those functions ok
+ // insert_event(event);
+ // run_synth();
+ //
+ // case NOTE_OFF:
+ // replace_events(event_in_table,event)
+ // run_synth()
+ // delete_events(event);
+ //
+ //}
diff --git a/src/midi/example.mid b/src/midi/example.mid
new file mode 100644
index 0000000..328d6ef
--- /dev/null
+++ b/src/midi/example.mid
Binary files differ
diff --git a/src/midi/example1.mid b/src/midi/example1.mid
new file mode 100644
index 0000000..2fa1b91
--- /dev/null
+++ b/src/midi/example1.mid
Binary files differ
diff --git a/src/midi/example2.mid b/src/midi/example2.mid
new file mode 100644
index 0000000..bf3bfc0
--- /dev/null
+++ b/src/midi/example2.mid
Binary files differ
diff --git a/src/midi/fluid_list.c b/src/midi/fluid_list.c
new file mode 100644
index 0000000..e06b6f9
--- /dev/null
+++ b/src/midi/fluid_list.c
@@ -0,0 +1,244 @@
+
+
+
+#include "fluid_list.h"
+#include <stdlib.h>
+
+
+fluid_list_t*
+new_fluid_list(void)
+{
+ fluid_list_t* list;
+ list = (fluid_list_t*) FLUID_MALLOC(sizeof(fluid_list_t));
+ list->data = NULL;
+ list->next = NULL;
+ return list;
+}
+
+void
+delete_fluid_list(fluid_list_t *list)
+{
+ fluid_list_t *next;
+ while (list) {
+ next = list->next;
+ FLUID_FREE(list);
+ list = next;
+ }
+}
+
+void
+delete1_fluid_list(fluid_list_t *list)
+{
+ if (list) {
+ FLUID_FREE(list);
+ }
+}
+
+fluid_list_t*
+fluid_list_append(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *last;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ if (list)
+ {
+ last = fluid_list_last(list);
+ /* g_assert (last != NULL); */
+ last->next = new_list;
+
+ return list;
+ }
+ else
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_prepend(fluid_list_t *list, void* data)
+{
+ fluid_list_t *new_list;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+ new_list->next = list;
+
+ return new_list;
+}
+
+fluid_list_t*
+fluid_list_nth(fluid_list_t *list, int n)
+{
+ while ((n-- > 0) && list) {
+ list = list->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove(fluid_list_t *list, void* data)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp->data == data) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ delete_fluid_list(tmp);
+
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+fluid_list_t*
+fluid_list_remove_link(fluid_list_t *list, fluid_list_t *link)
+{
+ fluid_list_t *tmp;
+ fluid_list_t *prev;
+
+ prev = NULL;
+ tmp = list;
+
+ while (tmp) {
+ if (tmp == link) {
+ if (prev) {
+ prev->next = tmp->next;
+ }
+ if (list == tmp) {
+ list = list->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+
+ prev = tmp;
+ tmp = tmp->next;
+ }
+
+ return list;
+}
+
+static fluid_list_t*
+fluid_list_sort_merge(fluid_list_t *l1, fluid_list_t *l2, fluid_compare_func_t compare_func)
+{
+ fluid_list_t list, *l;
+
+ l = &list;
+
+ while (l1 && l2) {
+ if (compare_func(l1->data,l2->data) < 0) {
+ l = l->next = l1;
+ l1 = l1->next;
+ } else {
+ l = l->next = l2;
+ l2 = l2->next;
+ }
+ }
+ l->next= l1 ? l1 : l2;
+
+ return list.next;
+}
+
+fluid_list_t*
+fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func)
+{
+ fluid_list_t *l1, *l2;
+
+ if (!list) {
+ return NULL;
+ }
+ if (!list->next) {
+ return list;
+ }
+
+ l1 = list;
+ l2 = list->next;
+
+ while ((l2 = l2->next) != NULL) {
+ if ((l2 = l2->next) == NULL)
+ break;
+ l1=l1->next;
+ }
+ l2 = l1->next;
+ l1->next = NULL;
+
+ return fluid_list_sort_merge(fluid_list_sort(list, compare_func),
+ fluid_list_sort(l2, compare_func),
+ compare_func);
+}
+
+
+fluid_list_t*
+fluid_list_last(fluid_list_t *list)
+{
+ if (list) {
+ while (list->next)
+ list = list->next;
+ }
+
+ return list;
+}
+
+int
+fluid_list_size(fluid_list_t *list)
+{
+ int n = 0;
+ while (list) {
+ n++;
+ list = list->next;
+ }
+ return n;
+}
+
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data)
+{
+ fluid_list_t *new_list;
+ fluid_list_t *cur;
+ fluid_list_t *prev = NULL;
+
+ new_list = new_fluid_list();
+ new_list->data = data;
+
+ cur = list;
+ while ((n-- > 0) && cur) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ new_list->next = cur;
+
+ if (prev) {
+ prev->next = new_list;
+ return list;
+ } else {
+ return new_list;
+ }
+}
+
+/* Compare function to sort strings alphabetically,
+ * for use with fluid_list_sort(). */
+int
+fluid_list_str_compare_func (void *a, void *b)
+{
+ if (a && b) return FLUID_STRCMP ((char *)a, (char *)b);
+ if (!a && !b) return 0;
+ if (a) return -1;
+ return 1;
+}
diff --git a/src/midi/fluid_list.h b/src/midi/fluid_list.h
new file mode 100644
index 0000000..bdc3291
--- /dev/null
+++ b/src/midi/fluid_list.h
@@ -0,0 +1,62 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _FLUID_LIST_H
+#define _FLUID_LIST_H
+
+#include "fluidsynth_priv.h"
+
+/*
+ *
+ * Lists
+ *
+ * A sound font loader has to pack the data from the .SF2 file into
+ * list structures of this type.
+ *
+ */
+
+typedef struct _fluid_list_t fluid_list_t;
+
+typedef int (*fluid_compare_func_t)(void* a, void* b);
+
+struct _fluid_list_t
+{
+ void* data;
+ fluid_list_t *next;
+};
+
+fluid_list_t* new_fluid_list(void);
+void delete_fluid_list(fluid_list_t *list);
+void delete1_fluid_list(fluid_list_t *list);
+fluid_list_t* fluid_list_sort(fluid_list_t *list, fluid_compare_func_t compare_func);
+fluid_list_t* fluid_list_append(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_prepend(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove(fluid_list_t *list, void* data);
+fluid_list_t* fluid_list_remove_link(fluid_list_t *list, fluid_list_t *llink);
+fluid_list_t* fluid_list_nth(fluid_list_t *list, int n);
+fluid_list_t* fluid_list_last(fluid_list_t *list);
+fluid_list_t* fluid_list_insert_at(fluid_list_t *list, int n, void* data);
+int fluid_list_size(fluid_list_t *list);
+
+#define fluid_list_next(slist) ((slist) ? (((fluid_list_t *)(slist))->next) : NULL)
+#define fluid_list_get(slist) ((slist) ? ((slist)->data) : NULL)
+
+int fluid_list_str_compare_func (void *a, void *b);
+
+#endif /* _FLUID_LIST_H */
diff --git a/src/midi/fluid_list.lo b/src/midi/fluid_list.lo
new file mode 100644
index 0000000..bbc909d
--- /dev/null
+++ b/src/midi/fluid_list.lo
@@ -0,0 +1,12 @@
+# fluid_list.lo - a libtool object file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object='.libs/fluid_list.o'
+
+# Name of the non-PIC object
+non_pic_object='fluid_list.o'
+
diff --git a/src/midi/fluid_list.o b/src/midi/fluid_list.o
new file mode 100644
index 0000000..7f329e4
--- /dev/null
+++ b/src/midi/fluid_list.o
Binary files differ
diff --git a/src/midi/fluid_midi.c b/src/midi/fluid_midi.c
new file mode 100644
index 0000000..062ba3c
--- /dev/null
+++ b/src/midi/fluid_midi.c
@@ -0,0 +1,1944 @@
+
+#include "fluid_midi.h"
+#include <math.h>
+//#include "fluid_sys.h"
+//#include "fluid_synth.h"
+//#include "fluid_settings.h"
+
+int
+fluid_log(int level, const char* fmt, ...)
+{
+
+}
+
+static int fluid_midi_event_length(unsigned char event);
+
+/* Read the entire contents of a file into memory, allocating enough memory
+ * for the file, and returning the length and the buffer.
+ * Note: This rewinds the file to the start before reading.
+ * Returns NULL if there was an error reading or allocating memory.
+ */
+static char* fluid_file_read_full(fluid_file fp, size_t* length);
+#define READ_FULL_INITIAL_BUFLEN 1024
+
+
+/***************************************************************
+ * FILE READ HERE
+ *
+ * MIDIFILE
+ */
+
+/**
+ * Return a new MIDI file handle for parsing an already-loaded MIDI file.
+ * @internal
+ * @param buffer Pointer to full contents of MIDI file (borrows the pointer).
+ * The caller must not free buffer until after the fluid_midi_file is deleted.
+ * @param length Size of the buffer in bytes.
+ * @return New MIDI file handle or NULL on error.
+ */
+fluid_midi_file *
+new_fluid_midi_file(const char* buffer, size_t length)
+{
+ fluid_midi_file *mf;
+
+ mf = FLUID_NEW(fluid_midi_file);
+ if (mf == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file));
+
+ mf->c = -1;
+ mf->running_status = -1;
+
+ mf->buffer = buffer;
+ mf->buf_len = length;
+ mf->buf_pos = 0;
+ mf->eof = FALSE;
+
+ if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { // here it calls read file
+ FLUID_FREE(mf);
+ return NULL;
+ }
+ return mf;
+}
+//ok how do you usually create a file_pointer in this case? with fopen FILE READ do you think we can just try this function directly with fopen? yes, fluid_file i think is just FILE * returned by fopen, so code will be fopen, fluid_file_read_full and then new_fluid_midi_file.
+
+static char*
+fluid_file_read_full(fluid_file fp, size_t* length)
+{
+ size_t buflen;
+ char* buffer;
+ size_t n;
+ /* Work out the length of the file in advance */
+ if (FLUID_FSEEK(fp, 0, SEEK_END) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ buflen = ftell(fp);
+ if (FLUID_FSEEK(fp, 0, SEEK_SET) != 0)
+ {
+ FLUID_LOG(FLUID_ERR, "File load: Could not seek within file");
+ return NULL;
+ }
+ FLUID_LOG(FLUID_DBG, "File load: Allocating %d bytes", buflen);
+ buffer = FLUID_MALLOC(buflen);
+ if (buffer == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return NULL;
+ }
+ n = FLUID_FREAD(buffer, 1, buflen, fp);
+ if (n != buflen) {
+ FLUID_LOG(FLUID_ERR, "Only read %d bytes; expected %d", n,
+ buflen);
+ FLUID_FREE(buffer);
+ return NULL;
+ };
+ *length = n;
+ return buffer;
+}
+
+/**
+ * Delete a MIDI file handle.
+ * @internal
+ * @param mf MIDI file handle to close and free.
+ */
+void
+delete_fluid_midi_file (fluid_midi_file *mf)
+{
+ if (mf == NULL) {
+ return;
+ }
+ FLUID_FREE(mf);
+ return;
+}
+
+/*
+ * Gets the next byte in a MIDI file, taking into account previous running status.
+ *
+ * returns FLUID_FAILED if EOF or read error
+ */
+int
+fluid_midi_file_getc (fluid_midi_file *mf)
+{
+ unsigned char c;
+ if (mf->c >= 0) {
+ c = mf->c;
+ mf->c = -1;
+ } else {
+ if (mf->buf_pos >= mf->buf_len) {
+ mf->eof = TRUE;
+ return FLUID_FAILED;
+ }
+ c = mf->buffer[mf->buf_pos++];
+ mf->trackpos++;
+ }
+ return (int) c;
+}
+
+/*
+ * Saves a byte to be returned the next time fluid_midi_file_getc() is called,
+ * when it is necessary according to running status.
+ */
+int
+fluid_midi_file_push(fluid_midi_file *mf, int c)
+{
+ mf->c = c;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read
+ */
+int
+fluid_midi_file_read(fluid_midi_file *mf, void *buf, int len)
+{
+ int num = len < mf->buf_len - mf->buf_pos
+ ? len : mf->buf_len - mf->buf_pos;
+ if (num != len) {
+ mf->eof = TRUE;
+ }
+ if (num < 0) {
+ num = 0;
+ }
+ /* Note: Read bytes, even if there aren't enough, but only increment
+ * trackpos if successful (emulates old behaviour of fluid_midi_file_read)
+ */
+ FLUID_MEMCPY(buf, mf->buffer+mf->buf_pos, num);
+ mf->buf_pos += num;
+ if (num == len)
+ mf->trackpos += num;
+#if DEBUG
+ else
+ FLUID_LOG(FLUID_DBG, "Could not read the requested number of bytes");
+#endif
+ return (num != len) ? FLUID_FAILED : FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_skip
+ */
+int
+fluid_midi_file_skip (fluid_midi_file *mf, int skip)
+{
+ int new_pos = mf->buf_pos + skip;
+ /* Mimic the behaviour of fseek: Error to seek past the start of file, but
+ * OK to seek past end (this just puts it into the EOF state). */
+ if (new_pos < 0) {
+ FLUID_LOG(FLUID_ERR, "Failed to seek position in file");
+ return FLUID_FAILED;
+ }
+ /* Clear the EOF flag, even if moved past the end of the file (this is
+ * consistent with the behaviour of fseek). */
+ mf->eof = FALSE;
+ mf->buf_pos = new_pos;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eof
+ */
+int fluid_midi_file_eof(fluid_midi_file* mf)
+{
+ /* Note: This does not simply test whether the file read pointer is past
+ * the end of the file. It mimics the behaviour of feof by actually
+ * testing the stateful EOF condition, which is set to TRUE if getc or
+ * fread have attempted to read past the end (but not if they have
+ * precisely reached the end), but reset to FALSE upon a successful seek.
+ */
+ return mf->eof;
+}
+
+/*
+ * fluid_midi_file_read_mthd
+ */
+//it's actually does all the work inside new_fluid_midi_file function to read file and get all events allocated and set, so now we just need to print those.
+int
+fluid_midi_file_read_mthd(fluid_midi_file *mf)
+{
+ char mthd[15];
+ if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6)
+ || (mthd[9] > 2)) {
+ FLUID_LOG(FLUID_ERR,
+ "Doesn't look like a MIDI file: invalid MThd header");
+ return FLUID_FAILED;
+ }
+ mf->type = mthd[9];
+ mf->ntracks = (unsigned) mthd[11];
+ mf->ntracks += (unsigned int) (mthd[10]) << 16;
+ if ((mthd[12]) < 0) {
+ mf->uses_smpte = 1;
+ mf->smpte_fps = -mthd[12];
+ mf->smpte_res = (unsigned) mthd[13];
+ FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet");
+ return FLUID_FAILED;
+ } else {
+ mf->uses_smpte = 0;
+ mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); //division is in the header for the midi file, he gets the value here. do we have access to it in custom.c? looks like player have also current time in milliseconds too, in addition to ticks, we can try to just use those. ok good. so the player is doing the parsing, we're just
+
+ FLUID_LOG(FLUID_DBG, "Division=%d", mf->division);
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_load_tracks
+ * so now we load tracks, right? yeah but looks like we already have such code in here:
+ */
+int
+fluid_midi_file_load_tracks(fluid_midi_file *mf, fluid_player_t *player)
+{
+ int i;
+ for (i = 0; i < mf->ntracks; i++) {
+ if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_isasciistring
+ */
+int
+fluid_isasciistring(char *s)
+{
+ int i;
+ int len = (int) FLUID_STRLEN(s);
+ for (i = 0; i < len; i++) {
+ if (!fluid_isascii(s[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * fluid_getlength
+ */
+long
+fluid_getlength(unsigned char *s)
+{
+ long i = 0;
+ i = s[3] | (s[2] << 8) | (s[1] << 16) | (s[0] << 24);
+ return i;
+}
+
+/*
+ * fluid_midi_file_read_tracklen
+ */
+int
+fluid_midi_file_read_tracklen(fluid_midi_file *mf)
+{
+ unsigned char length[5];
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->tracklen = fluid_getlength(length);
+ mf->trackpos = 0;
+ mf->eot = 0;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_eot
+ */
+int
+fluid_midi_file_eot(fluid_midi_file *mf)
+{
+#if DEBUG
+ if (mf->trackpos > mf->tracklen) {
+ printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen);
+ }
+#endif
+ return mf->eot || (mf->trackpos >= mf->tracklen);
+}
+
+/*
+ * fluid_midi_file_read_track
+ */
+int
+fluid_midi_file_read_track(fluid_midi_file *mf, fluid_player_t *player, int num)
+{
+ fluid_track_t *track;
+ unsigned char id[5], length[5];
+ int found_track = 0;
+ int skip;
+
+ if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ id[4] = '\0';
+ mf->dtime = 0;
+
+ while (!found_track) {
+
+ if (fluid_isasciistring((char *) id) == 0) {
+ FLUID_LOG(FLUID_ERR,
+ "An non-ascii track header found, corrupt file");
+ return FLUID_FAILED;
+
+ } else if (strcmp((char *) id, "MTrk") == 0) {
+
+ found_track = 1;
+
+ if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ track = new_fluid_track(num);
+ if (track == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ while (!fluid_midi_file_eot(mf)) {
+ if (fluid_midi_file_read_event(mf, track) != FLUID_OK) {
+ delete_fluid_track(track);
+ return FLUID_FAILED;
+ }
+ }
+
+ /* Skip remaining track data, if any */
+ if (mf->trackpos < mf->tracklen)
+ fluid_midi_file_skip(mf, mf->tracklen - mf->trackpos);
+
+ fluid_player_add_track(player, track);
+
+ } else {
+ found_track = 0;
+ if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ skip = fluid_getlength(length);
+ /* fseek(mf->fp, skip, SEEK_CUR); */
+ if (fluid_midi_file_skip(mf, skip) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ }
+ }
+ if (fluid_midi_file_eof(mf)) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_varlen
+ */
+int
+fluid_midi_file_read_varlen(fluid_midi_file *mf)
+{
+ int i;
+ int c;
+ mf->varlen = 0;
+ for (i = 0;; i++) {
+ if (i == 4) {
+ FLUID_LOG(FLUID_ERR, "Invalid variable length number");
+ return FLUID_FAILED;
+ }
+ c = fluid_midi_file_getc(mf);
+ if (c < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ if (c & 0x80) {
+ mf->varlen |= (int) (c & 0x7F);
+ mf->varlen <<= 7;
+ } else {
+ mf->varlen += c;
+ break;
+ }
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_read_event
+ */
+//could be this, right...it only takes the file and the track as args
+int
+fluid_midi_file_read_event(fluid_midi_file *mf, fluid_track_t *track)
+{
+ int status;
+ int type;
+ int tempo;
+ unsigned char *metadata = NULL;
+ unsigned char *dyn_buf = NULL;
+ unsigned char static_buf[256];
+ int nominator, denominator, clocks, notes;
+ fluid_midi_event_t *evt; //do you know what evt is? is that the event struct?
+ int channel = 0;
+ int param1 = 0;
+ int param2 = 0;
+ int size;
+
+ /* read the delta-time of the event */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+ mf->dtime += mf->varlen;
+
+ /* read the status byte */
+ status = fluid_midi_file_getc(mf);
+ if (status < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* not a valid status byte: use the running status instead */
+ if ((status & 0x80) == 0) {
+ if ((mf->running_status & 0x80) == 0) {
+ FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status");
+ return FLUID_FAILED;
+ }
+ fluid_midi_file_push(mf, status);
+ status = mf->running_status;
+ }
+
+ /* check what message we have */
+
+ mf->running_status = status;
+
+ if ((status == MIDI_SYSEX)) { /* system exclusif */
+ /* read the length of the message */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ metadata = FLUID_MALLOC(mf->varlen + 1);
+
+ if (metadata == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ /* read the data of the message */
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ FLUID_FREE (metadata);
+ return FLUID_FAILED;
+ }
+
+ evt->dtime = mf->dtime;
+ size = mf->varlen;
+
+ if (metadata[mf->varlen - 1] == MIDI_EOX)
+ size--;
+
+ /* Add SYSEX event and indicate that its dynamically allocated and should be freed with event */
+ fluid_midi_event_set_sysex(evt, metadata, size, TRUE);
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+
+ return FLUID_OK;
+
+ } else if (status == MIDI_META_EVENT) { /* meta events */
+
+ int result = FLUID_OK;
+
+ /* get the type of the meta message */
+ type = fluid_midi_file_getc(mf);
+ if (type < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ /* get the length of the data part */
+ if (fluid_midi_file_read_varlen(mf) != FLUID_OK) {
+ return FLUID_FAILED;
+ }
+
+ if (mf->varlen < 255) {
+ metadata = &static_buf[0];
+ } else {
+ FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__,
+ __LINE__, mf->varlen);
+ dyn_buf = FLUID_MALLOC(mf->varlen + 1);
+ if (dyn_buf == NULL) {
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+ metadata = dyn_buf;
+ }
+
+ /* read the data */
+ if (mf->varlen) {
+ if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) {
+ if (dyn_buf) {
+ FLUID_FREE(dyn_buf);
+ }
+ return FLUID_FAILED;
+ }
+ }
+
+ /* handle meta data */
+ switch (type) {
+
+ case MIDI_COPYRIGHT:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_TRACK_NAME:
+ metadata[mf->varlen] = 0;
+ fluid_track_set_name(track, (char *) metadata);
+ break;
+
+ case MIDI_INST_NAME:
+ metadata[mf->varlen] = 0;
+ break;
+
+ case MIDI_LYRIC:
+ break;
+
+ case MIDI_MARKER:
+ break;
+
+ case MIDI_CUE_POINT:
+ break; /* don't care much for text events */
+
+ case MIDI_EOT:
+ if (mf->varlen != 0) {
+ FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event");
+ result = FLUID_FAILED;
+ break;
+ }
+ mf->eot = 1;
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_EOT;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SET_TEMPO:
+ if (mf->varlen != 3) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SetTempo meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2];
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ result = FLUID_FAILED;
+ break;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = MIDI_SET_TEMPO;
+ evt->channel = 0;
+ evt->param1 = tempo;
+ evt->param2 = 0;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ break;
+
+ case MIDI_SMPTE_OFFSET:
+ if (mf->varlen != 5) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for SMPTE Offset meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ break; /* we don't use smtp */
+
+ case MIDI_TIME_SIGNATURE:
+ if (mf->varlen != 4) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for TimeSignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ nominator = metadata[0];
+ denominator = pow(2.0, (double) metadata[1]);
+ clocks = metadata[2];
+ notes = metadata[3];
+
+ FLUID_LOG(FLUID_DBG,
+ "signature=%d/%d, metronome=%d, 32nd-notes=%d",
+ nominator, denominator, clocks, notes);
+
+ break;
+
+ case MIDI_KEY_SIGNATURE:
+ if (mf->varlen != 2) {
+ FLUID_LOG(FLUID_ERR,
+ "Invalid length for KeySignature meta event");
+ result = FLUID_FAILED;
+ break;
+ }
+ /* We don't care about key signatures anyway */
+ /* sf = metadata[0];
+ mi = metadata[1]; */
+ break;
+
+ case MIDI_SEQUENCER_EVENT:
+ break;
+
+ default:
+ break;
+ }
+
+ if (dyn_buf) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__);
+ FLUID_FREE(dyn_buf);
+ }
+
+ return result;
+
+ } else { /* channel messages */
+
+ type = status & 0xf0;
+ channel = status & 0x0f;
+
+ /* all channel message have at least 1 byte of associated data */
+ if ((param1 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ switch (type) {
+
+ case NOTE_ON:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case NOTE_OFF:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case KEY_PRESSURE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case CONTROL_CHANGE:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+ break;
+
+ case PROGRAM_CHANGE:
+ break;
+
+ case CHANNEL_PRESSURE:
+ break;
+
+ case PITCH_BEND:
+ if ((param2 = fluid_midi_file_getc(mf)) < 0) {
+ FLUID_LOG(FLUID_ERR, "Unexpected end of file");
+ return FLUID_FAILED;
+ }
+
+ param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f);
+ param2 = 0;
+ break;
+
+ default:
+ /* Can't possibly happen !? */
+ FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event");
+ return FLUID_FAILED;
+ }
+ evt = new_fluid_midi_event();
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ evt->dtime = mf->dtime;
+ evt->type = type;
+ evt->channel = channel;
+ evt->param1 = param1;
+ evt->param2 = param2;
+ fluid_track_add_event(track, evt);
+ mf->dtime = 0;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_midi_file_get_division
+ */
+int
+fluid_midi_file_get_division(fluid_midi_file *midifile)
+{
+ return midifile->division;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/**
+ * MIDI EVENT DEFINED HERE...do we need to figure out exactly how this ties in to cli-dssi-host next? not yet, for now we need working midi loading from file and listing events according to each time sample. should we write a print event function? yeah in main() custom.c
+ * Create a MIDI event structure.
+ * @return New MIDI event structure or NULL when out of memory.
+ */
+fluid_midi_event_t *
+new_fluid_midi_event ()
+{
+ fluid_midi_event_t* evt; //oh it's just the name
+ evt = FLUID_NEW(fluid_midi_event_t);
+ if (evt == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ evt->dtime = 0;
+ evt->type = 0;
+ evt->channel = 0;
+ evt->param1 = 0;
+ evt->param2 = 0;
+ evt->next = NULL;
+ evt->paramptr = NULL;
+ return evt;
+}
+
+/**
+ * Delete MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_midi_event(fluid_midi_event_t *evt)
+{
+ fluid_midi_event_t *temp;
+
+ while (evt) {
+ temp = evt->next;
+
+ /* Dynamic SYSEX event? - free (param2 indicates if dynamic) */
+ if (evt->type == MIDI_SYSEX && evt->paramptr && evt->param2)
+ FLUID_FREE (evt->paramptr);
+
+ FLUID_FREE(evt);
+ evt = temp;
+ }
+ return FLUID_OK;
+}
+
+/**
+ * Get the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Event type field (MIDI status byte without channel)
+ */
+int
+fluid_midi_event_get_type(fluid_midi_event_t *evt)
+{
+ return evt->type;
+}
+
+/**
+ * Set the event type field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param type Event type field (MIDI status byte without channel)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_type(fluid_midi_event_t *evt, int type)
+{
+ evt->type = type;
+ return FLUID_OK;
+}
+
+/**
+ * Get the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Channel field
+ */
+int
+fluid_midi_event_get_channel(fluid_midi_event_t *evt)
+{
+ return evt->channel;
+}
+
+/**
+ * Set the channel field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param chan MIDI channel field
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_channel(fluid_midi_event_t *evt, int chan)
+{
+ evt->channel = chan;
+ return FLUID_OK;
+}
+
+/**
+ * Get the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI note number (0-127)
+ */
+int
+fluid_midi_event_get_key(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the key field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI note number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_key(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI velocity number (0-127)
+ */
+int
+fluid_midi_event_get_velocity(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the velocity field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI velocity value
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_velocity(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the control number of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI control number
+ */
+int
+fluid_midi_event_get_control(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the control field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v MIDI control number
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_control(fluid_midi_event_t *evt, int v)
+{
+ evt->param1 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the value field from a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Value field
+ */
+int
+fluid_midi_event_get_value(fluid_midi_event_t *evt)
+{
+ return evt->param2;
+}
+
+/**
+ * Set the value field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param v Value to assign
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_value(fluid_midi_event_t *evt, int v)
+{
+ evt->param2 = v;
+ return FLUID_OK;
+}
+
+/**
+ * Get the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return MIDI program number (0-127)
+ */
+int
+fluid_midi_event_get_program(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the program field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val MIDI program number (0-127)
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_midi_event_set_program(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Get the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @return Pitch value (14 bit value, 0-16383, 8192 is center)
+ */
+int
+fluid_midi_event_get_pitch(fluid_midi_event_t *evt)
+{
+ return evt->param1;
+}
+
+/**
+ * Set the pitch field of a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param val Pitch value (14 bit value, 0-16383, 8192 is center)
+ * @return Always returns FLUID_OK
+ */
+int
+fluid_midi_event_set_pitch(fluid_midi_event_t *evt, int val)
+{
+ evt->param1 = val;
+ return FLUID_OK;
+}
+
+/**
+ * Assign sysex data to a MIDI event structure.
+ * @param evt MIDI event structure
+ * @param data Pointer to SYSEX data
+ * @param size Size of SYSEX data
+ * @param dynamic TRUE if the SYSEX data has been dynamically allocated and
+ * should be freed when the event is freed (only applies if event gets destroyed
+ * with delete_fluid_midi_event())
+ * @return Always returns #FLUID_OK
+ *
+ * NOTE: Unlike the other event assignment functions, this one sets evt->type.
+ */
+int
+fluid_midi_event_set_sysex(fluid_midi_event_t *evt, void *data, int size, int dynamic)
+{
+ evt->type = MIDI_SYSEX;
+ evt->paramptr = data;
+ evt->param1 = size;
+ evt->param2 = dynamic;
+ return FLUID_OK;
+}
+
+/******************************************************
+ *
+ * fluid_track_t
+ */
+
+/*
+ * new_fluid_track
+ */
+fluid_track_t *
+new_fluid_track(int num)
+{
+ fluid_track_t *track;
+ track = FLUID_NEW(fluid_track_t);
+ if (track == NULL) {
+ return NULL;
+ }
+ track->name = NULL;
+ track->num = num;
+ track->first = NULL;
+ track->cur = NULL;
+ track->last = NULL;
+ track->ticks = 0;
+ return track;
+}
+
+/*
+ * delete_fluid_track
+ */
+int
+delete_fluid_track(fluid_track_t *track)
+{
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (track->first != NULL) {
+ delete_fluid_midi_event(track->first);
+ }
+ FLUID_FREE(track);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_set_name
+ */
+int
+fluid_track_set_name(fluid_track_t *track, char *name)
+{
+ int len;
+ if (track->name != NULL) {
+ FLUID_FREE(track->name);
+ }
+ if (name == NULL) {
+ track->name = NULL;
+ return FLUID_OK;
+ }
+ len = FLUID_STRLEN(name);
+ track->name = FLUID_MALLOC(len + 1);
+ if (track->name == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return FLUID_FAILED;
+ }
+ FLUID_STRCPY(track->name, name);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_get_name
+ */
+char *
+fluid_track_get_name(fluid_track_t *track)
+{
+ return track->name;
+}
+
+/*
+ * fluid_track_get_duration
+ */
+int
+fluid_track_get_duration(fluid_track_t *track)
+{
+ int time = 0;
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ time += evt->dtime;
+ evt = evt->next;
+ }
+ return time;
+}
+
+/*
+ * fluid_track_count_events
+ */
+int
+fluid_track_count_events(fluid_track_t *track, int *on, int *off)
+{
+ fluid_midi_event_t *evt = track->first;
+ while (evt != NULL) {
+ if (evt->type == NOTE_ON) {
+ (*on)++;
+ } else if (evt->type == NOTE_OFF) {
+ (*off)++;
+ }
+ evt = evt->next;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_add_event
+ */
+int
+fluid_track_add_event(fluid_track_t *track, fluid_midi_event_t *evt)
+{
+ evt->next = NULL;
+ if (track->first == NULL) {
+ track->first = evt;
+ track->cur = evt;
+ track->last = evt;
+ } else {
+ track->last->next = evt;
+ track->last = evt;
+ }
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_first_event
+ */
+fluid_midi_event_t *
+fluid_track_first_event(fluid_track_t *track)
+{
+ track->cur = track->first;
+ return track->cur;
+}
+
+/*
+ * fluid_track_next_event
+ */
+fluid_midi_event_t *
+fluid_track_next_event(fluid_track_t *track)
+{
+ if (track->cur != NULL) {
+ track->cur = track->cur->next;
+ }
+ return track->cur;
+}
+
+/*
+ * fluid_track_reset
+ */
+int
+fluid_track_reset(fluid_track_t *track)
+{
+ track->ticks = 0;
+ track->cur = track->first;
+ return FLUID_OK;
+}
+
+/*
+ * fluid_track_send_events
+ */
+int
+fluid_track_send_events(fluid_track_t *track,
+ fluid_synth_t *synth, //will the null pointer cause an error here? nope, will work //doesn't use it? here... I guess not
+ fluid_player_t *player,
+ unsigned int ticks)
+{
+ int status = FLUID_OK;
+ fluid_midi_event_t *event;
+
+ while (1) {
+
+ event = track->cur;
+ if (event == NULL) {
+ return status;
+ }
+
+//ok it should be printing here, right? well need to check flow again, starting from load_tracks if it reach this function or not
+//do we decide now to just listen for note on and note off, and keep track of the two? yep
+//run_synth also takes velocity, so maybe we should add that as well? i guess so
+
+ if (track->ticks + event->dtime > ticks) {
+ return status;
+ }
+
+ track->ticks += event->dtime;
+
+ if (!player || event->type == MIDI_EOT) {
+ }
+ else if (event->type == MIDI_SET_TEMPO) {
+ fluid_player_set_midi_tempo(player, event->param1);
+ }
+ else {
+ if (player->playback_callback)
+ player->playback_callback(player->playback_userdata, event);
+ }
+
+ fluid_track_next_event(track);
+
+ }
+ return status;
+}
+
+/******************************************************
+ *
+ * fluid_player
+ */
+
+/**
+ * create a new midi player.
+ * @param synth fluid synthesizer instance to create player for
+ * @return New MIDI player instance or NULL on error (out of memory)
+ */
+//ok how do we call new_fluid_player with the filename of the midifile? wmayeb call fluid_player_load() with file name
+fluid_player_t *
+new_fluid_player(void)
+{
+ int i;
+ fluid_player_t *player;
+ player = FLUID_NEW(fluid_player_t);
+ if (player == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ player->status = FLUID_PLAYER_READY;
+ player->loop = 1;
+ player->ntracks = 0;
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ player->track[i] = NULL;
+ }
+ player->synth = NULL;
+// player->system_timer = NULL;
+// player->sample_timer = NULL;
+ player->playlist = NULL;
+ player->currentfile = NULL;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ player->cur_msec = 0;
+ player->cur_ticks = 0;
+ // FIXME fluid_player_set_playback_callback(player, fluid_synth_handle_midi_event, synth);
+
+ // player->use_system_timer = fluid_settings_str_equal(synth->settings,
+ // "player.timing-source", "system");
+
+ //fluid_settings_getint(synth->settings, "player.reset-synth", &i);
+ player->reset_synth_between_songs = i;
+
+ return player;
+}
+
+/**
+ * Delete a MIDI player instance.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+delete_fluid_player(fluid_player_t *player)
+{
+ fluid_list_t *q;
+ fluid_playlist_item* pi;
+
+ if (player == NULL) {
+ return FLUID_OK;
+ }
+ fluid_player_stop(player);
+ fluid_player_reset(player);
+
+ while (player->playlist != NULL) {
+ q = player->playlist->next;
+ pi = (fluid_playlist_item*) player->playlist->data;
+ FLUID_FREE(pi->filename);
+ FLUID_FREE(pi->buffer);
+ FLUID_FREE(pi);
+ delete1_fluid_list(player->playlist);
+ player->playlist = q;
+ }
+
+ FLUID_FREE(player);
+ return FLUID_OK;
+}
+
+/**
+ * Registers settings related to the MIDI player
+ */
+//void
+//fluid_player_settings(fluid_settings_t *settings)
+//{
+ /* player.timing-source can be either "system" (use system timer)
+ or "sample" (use timer based on number of written samples) */
+ /*
+ fluid_settings_register_str(settings, "player.timing-source", "sample", 0,
+ NULL, NULL);
+ fluid_settings_add_option(settings, "player.timing-source", "sample");
+ fluid_settings_add_option(settings, "player.timing-source", "system");
+
+ Selects whether the player should reset the synth between songs, or not.
+ fluid_settings_register_int(settings, "player.reset-synth", 1, 0, 1,
+ FLUID_HINT_TOGGLED, NULL, NULL);
+*/
+//}
+
+
+int
+fluid_player_reset(fluid_player_t *player)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) {
+ if (player->track[i] != NULL) {
+ delete_fluid_track(player->track[i]);
+ player->track[i] = NULL;
+ }
+ }
+ /* player->current_file = NULL; */
+ /* player->status = FLUID_PLAYER_READY; */
+ /* player->loop = 1; */
+ player->ntracks = 0;
+ player->division = 0;
+ player->send_program_change = 1;
+ player->miditempo = 480000;
+ player->deltatime = 4.0;
+ return 0;
+}
+
+/*
+ * fluid_player_add_track
+ */
+int
+fluid_player_add_track(fluid_player_t *player, fluid_track_t *track)
+{
+ if (player->ntracks < MAX_NUMBER_OF_TRACKS) {
+ player->track[player->ntracks++] = track;
+ return FLUID_OK;
+ } else {
+ return FLUID_FAILED;
+ }
+}
+
+/*
+ * fluid_player_count_tracks
+ */
+int
+fluid_player_count_tracks(fluid_player_t *player)
+{
+ return player->ntracks;
+}
+
+/*
+ * fluid_player_get_track
+ */
+fluid_track_t *
+fluid_player_get_track(fluid_player_t *player, int i)
+{
+ if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) {
+ return player->track[i];
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Change the MIDI callback function. This is usually set to
+ * fluid_synth_handle_midi_event, but can optionally be changed
+ * to a user-defined function instead, for intercepting all MIDI
+ * messages sent to the synth. You can also use a midi router as
+ * the callback function to modify the MIDI messages before sending
+ * them to the synth.
+ * @param player MIDI player instance
+ * @param handler Pointer to callback function
+ * @param handler_data Parameter sent to the callback function
+ * @returns FLUID_OK
+ * @since 1.1.4
+ */
+int
+fluid_player_set_playback_callback(fluid_player_t* player,
+ handle_midi_event_func_t handler, void* handler_data)
+{
+ player->playback_callback = handler;
+ player->playback_userdata = handler_data;
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue.
+ * @param player MIDI player instance
+ * @param midifile File name of the MIDI file to add
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add(fluid_player_t *player, const char *midifile)
+{
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ char* f = FLUID_STRDUP(midifile);
+ if (!pi || !f) {
+ FLUID_FREE(pi);
+ FLUID_FREE(f);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ pi->filename = f;
+ pi->buffer = NULL;
+ pi->buffer_len = 0;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/**
+ * Add a MIDI file to a player queue, from a buffer in memory.
+ * @param player MIDI player instance
+ * @param buffer Pointer to memory containing the bytes of a complete MIDI
+ * file. The data is copied, so the caller may free or modify it immediately
+ * without affecting the playlist.
+ * @param len Length of the buffer, in bytes.
+ * @return #FLUID_OK or #FLUID_FAILED
+ */
+int
+fluid_player_add_mem(fluid_player_t* player, const void *buffer, size_t len)
+{
+ /* Take a copy of the buffer, so the caller can free immediately. */
+ fluid_playlist_item *pi = FLUID_MALLOC(sizeof(fluid_playlist_item));
+ void *buf_copy = FLUID_MALLOC(len);
+ if (!pi || !buf_copy) {
+ FLUID_FREE(pi);
+ FLUID_FREE(buf_copy);
+ FLUID_LOG(FLUID_PANIC, "Out of memory");
+ return FLUID_FAILED;
+ }
+
+ FLUID_MEMCPY(buf_copy, buffer, len);
+ pi->filename = NULL;
+ pi->buffer = buf_copy;
+ pi->buffer_len = len;
+ player->playlist = fluid_list_append(player->playlist, pi);
+ return FLUID_OK;
+}
+
+/*
+ * fluid_player_load
+ */
+int
+fluid_player_load(fluid_player_t *player, fluid_playlist_item *item)
+{
+ puts("inside fluid player load");
+ //ok do you think this is the function we need to check? well i found it, this long printf i uncommented wasn't used, we need to use fluid_track_send_events inside loop over loaded tracks to actually send events into our callback. ok
+ fluid_midi_file *midifile;
+ char* buffer;
+ size_t buffer_length;
+ int buffer_owned;
+ if (item->filename != NULL)
+ {
+ fluid_file fp;
+ /* This file is specified by filename; load the file from disk */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__,
+ item->filename);
+ /* Read the entire contents of the file into the buffer */
+ fp = FLUID_FOPEN(item->filename, "rb");
+ if (fp == NULL) {
+ FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file");
+ return FLUID_FAILED;
+ }
+ buffer = fluid_file_read_full(fp, &buffer_length);
+ if (buffer == NULL)
+ {
+ FLUID_FCLOSE(fp);
+ return FLUID_FAILED;
+ }
+ buffer_owned = 1;
+ FLUID_FCLOSE(fp);
+ }
+ else
+ {
+ /* This file is specified by a pre-loaded buffer; load from memory */
+ FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile from memory (%p)",
+ __FILE__, __LINE__, item->buffer);
+ buffer = (char *) item->buffer;
+ buffer_length = item->buffer_len;
+ /* Do not free the buffer (it is owned by the playlist) */
+ buffer_owned = 0;
+ }
+// here file
+ midifile = new_fluid_midi_file(buffer, buffer_length);
+ if (midifile == NULL) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_FAILED;
+ }
+ player->division = fluid_midi_file_get_division(midifile);
+ //DIVISION SET HERE
+ fluid_player_set_midi_tempo(player, player->miditempo); // Update deltatime
+ /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */
+// here it load tracks
+ if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK) {
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ delete_fluid_midi_file(midifile);
+ return FLUID_FAILED;
+ }
+ delete_fluid_midi_file(midifile);
+ if (buffer_owned) {
+ FLUID_FREE(buffer);
+ }
+ return FLUID_OK;
+}
+
+void
+fluid_player_advancefile(fluid_player_t *player)
+{
+ if (player->playlist == NULL) {
+ return; /* No files to play */
+ }
+ if (player->currentfile != NULL) {
+ player->currentfile = fluid_list_next(player->currentfile);
+ }
+ if (player->currentfile == NULL) {
+ if (player->loop == 0) {
+ return; /* We're done playing */
+ }
+ if (player->loop > 0) {
+ player->loop--;
+ }
+ player->currentfile = player->playlist;
+ }
+}
+
+void
+fluid_player_playlist_load(fluid_player_t *player, unsigned int msec)
+{
+ fluid_playlist_item* current_playitem;
+ int i;
+
+ do {
+ fluid_player_advancefile(player);
+ if (player->currentfile == NULL) {
+ /* Failed to find next song, probably since we're finished */
+ player->status = FLUID_PLAYER_DONE;
+ return;
+ }
+
+ fluid_player_reset(player);
+ current_playitem = (fluid_playlist_item *) player->currentfile->data;
+ } while (fluid_player_load(player, current_playitem) != FLUID_OK);
+
+ /* Successfully loaded midi file */
+
+ player->begin_msec = msec;
+ player->start_msec = msec;
+ player->start_ticks = 0;
+ player->cur_ticks = 0;
+
+// if (player->reset_synth_between_songs) {
+// fluid_synth_system_reset(player->synth);
+// }
+
+ for (i = 0; i < player->ntracks; i++) {
+ if (player->track[i] != NULL) {
+ fluid_track_reset(player->track[i]);
+ }
+ }
+}
+
+
+/*
+ * fluid_player_callback
+ */
+//ok so I guess it all starts here? not sure, new_fluid_player starts a new player struct, then we need fluid_player_load(player, playlist_item)
+//and playlist_item have filename of file to load got it
+int
+fluid_player_callback(void *data, unsigned int msec)
+{
+ int i;
+ int loadnextfile;
+ int status = FLUID_PLAYER_DONE;
+ fluid_player_t *player;
+ fluid_synth_t *synth;
+ player = (fluid_player_t *) data;
+ synth = player->synth;
+
+ loadnextfile = player->currentfile == NULL ? 1 : 0;
+ do {
+ if (loadnextfile) {
+ loadnextfile = 0;
+ fluid_player_playlist_load(player, msec);
+ if (player->currentfile == NULL) {
+ return 0;
+ }
+ }
+
+ player->cur_msec = msec;
+ player->cur_ticks = (player->start_ticks
+ + (int) ((double) (player->cur_msec - player->start_msec)
+ / player->deltatime));
+
+ for (i = 0; i < player->ntracks; i++) {
+ if (!fluid_track_eot(player->track[i])) {
+ status = FLUID_PLAYER_PLAYING;
+ if (fluid_track_send_events(player->track[i], synth, player,
+ player->cur_ticks) != FLUID_OK) {
+ /* */
+ }
+ }
+ }
+
+ if (status == FLUID_PLAYER_DONE) {
+ FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", __FILE__,
+ __LINE__, (msec - player->begin_msec) / 1000.0);
+ loadnextfile = 1;
+ }
+ } while (loadnextfile);
+
+ player->status = status;
+
+ return 1;
+}
+
+/**
+ * Activates play mode for a MIDI player if not already playing.
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+int
+fluid_player_play(fluid_player_t *player)
+{
+ if (player->status == FLUID_PLAYER_PLAYING) {
+ return FLUID_OK;
+ }
+
+ if (player->playlist == NULL) {
+ return FLUID_OK;
+ }
+
+ player->status = FLUID_PLAYER_PLAYING;
+
+/*
+ if (player->use_system_timer) {
+ player->system_timer = new_fluid_timer((int) player->deltatime,
+ fluid_player_callback, (void *) player, TRUE, FALSE, TRUE);
+ if (player->system_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ } else {
+ player->sample_timer = new_fluid_sample_timer(player->synth,
+ fluid_player_callback, (void *) player);
+
+ if (player->sample_timer == NULL) {
+ return FLUID_FAILED;
+ }
+ }
+ */
+ return FLUID_OK;
+}
+
+/**
+ * Stops a MIDI player.
+ * @param player MIDI player instance
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_stop(fluid_player_t *player)
+{
+/* if (player->system_timer != NULL) {
+ delete_fluid_timer(player->system_timer);
+ }
+ if (player->sample_timer != NULL) {
+ delete_fluid_sample_timer(player->synth, player->sample_timer);
+ }*/
+ player->status = FLUID_PLAYER_DONE;
+// player->sample_timer = NULL;
+ // player->system_timer = NULL;
+ return FLUID_OK;
+}
+
+/**
+ * Get MIDI player status.
+ * @param player MIDI player instance
+ * @return Player status (#fluid_player_status)
+ * @since 1.1.0
+ */
+int
+fluid_player_get_status(fluid_player_t *player)
+{
+ return player->status;
+}
+
+/**
+ * Enable looping of a MIDI player
+ * @param player MIDI player instance
+ * @param loop Times left to loop the playlist. -1 means loop infinitely.
+ * @return Always returns #FLUID_OK
+ * @since 1.1.0
+ *
+ * For example, if you want to loop the playlist twice, set loop to 2
+ * and call this function before you start the player.
+ */
+int fluid_player_set_loop(fluid_player_t *player, int loop)
+{
+ player->loop = loop;
+ return FLUID_OK;
+}
+
+/**
+ * Set the tempo of a MIDI player.
+ * @param player MIDI player instance
+ * @param tempo Tempo to set playback speed to (in microseconds per quarter note, as per MIDI file spec)
+ * @return Always returns #FLUID_OK
+ */
+int fluid_player_set_midi_tempo(fluid_player_t *player, int tempo)
+{
+ player->miditempo = tempo;
+ //DIVISION CALCULATION MADE HERE
+ //USE THIS TO FIX MRS WATSON
+ player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */
+ player->start_msec = player->cur_msec;
+ player->start_ticks = player->cur_ticks;
+
+ FLUID_LOG(FLUID_DBG,
+ "tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d",
+ tempo, player->deltatime, player->cur_msec, player->cur_ticks);
+//so this would be player->cur_msec? yes
+ return FLUID_OK;
+}
+
+/**
+ * Set the tempo of a MIDI player in beats per minute.
+ * @param player MIDI player instance
+ * @param bpm Tempo in beats per minute
+ * @return Always returns #FLUID_OK
+ */
+int
+fluid_player_set_bpm(fluid_player_t *player, int bpm)
+{
+ return fluid_player_set_midi_tempo(player, (int) ((double) 60 * 1e6 / bpm));
+}
+
+/**
+ * Wait for a MIDI player to terminate (when done playing).
+ * @param player MIDI player instance
+ * @return #FLUID_OK on success, #FLUID_FAILED otherwise
+ */
+/*
+int
+fluid_player_join(fluid_player_t *player)
+{
+ if (player->system_timer) {
+ return fluid_timer_join(player->system_timer);
+ } else if (player->sample_timer) {
+ while (player->status != FLUID_PLAYER_DONE) {
+#if defined(WIN32)
+ Sleep(10);
+#else
+ usleep(10000);
+#endif
+ }
+ }
+ return FLUID_OK;
+}
+*/
+/************************************************************************
+ * MIDI PARSER
+ *
+ */
+
+/*
+ * new_fluid_midi_parser
+ */
+fluid_midi_parser_t *
+new_fluid_midi_parser ()
+{
+ fluid_midi_parser_t *parser;
+ parser = FLUID_NEW(fluid_midi_parser_t);
+ if (parser == NULL) {
+ FLUID_LOG(FLUID_ERR, "Out of memory");
+ return NULL;
+ }
+ parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */
+ return parser;
+}
+
+/*
+ * delete_fluid_midi_parser
+ */
+int
+delete_fluid_midi_parser(fluid_midi_parser_t *parser)
+{
+ FLUID_FREE(parser);
+ return FLUID_OK;
+}
+
+/**
+ * Parse a MIDI stream one character at a time.
+ * @param parser Parser instance
+ * @param c Next character in MIDI stream
+ * @return A parsed MIDI event or NULL if none. Event is internal and should
+ * not be modified or freed and is only valid until next call to this function.
+ */
+fluid_midi_event_t *
+fluid_midi_parser_parse(fluid_midi_parser_t *parser, unsigned char c)
+{
+ fluid_midi_event_t *event;
+
+ /* Real-time messages (0xF8-0xFF) can occur anywhere, even in the middle
+ * of another message. */
+ if (c >= 0xF8) {
+ if (c == MIDI_SYSTEM_RESET) {
+ parser->event.type = c;
+ parser->status = 0; /* clear the status */
+ return &parser->event;
+ }
+
+ return NULL;
+ }
+
+ /* Status byte? - If previous message not yet complete, it is discarded (re-sync). */
+ if (c & 0x80) {
+ /* Any status byte terminates SYSEX messages (not just 0xF7) */
+ if (parser->status == MIDI_SYSEX && parser->nr_bytes > 0) {
+ event = &parser->event;
+ fluid_midi_event_set_sysex(event, parser->data, parser->nr_bytes,
+ FALSE);
+ } else
+ event = NULL;
+
+ if (c < 0xF0) /* Voice category message? */
+ {
+ parser->channel = c & 0x0F;
+ parser->status = c & 0xF0;
+
+ /* The event consumes x bytes of data... (subtract 1 for the status byte) */
+ parser->nr_bytes_total = fluid_midi_event_length(parser->status)
+ - 1;
+
+ parser->nr_bytes = 0; /* 0 bytes read so far */
+ } else if (c == MIDI_SYSEX) {
+ parser->status = MIDI_SYSEX;
+ parser->nr_bytes = 0;
+ } else
+ parser->status = 0; /* Discard other system messages (0xF1-0xF7) */
+
+ return event; /* Return SYSEX event or NULL */
+ }
+
+ /* Data/parameter byte */
+
+ /* Discard data bytes for events we don't care about */
+ if (parser->status == 0)
+ return NULL;
+
+ /* Max data size exceeded? (SYSEX messages only really) */
+ if (parser->nr_bytes == FLUID_MIDI_PARSER_MAX_DATA_SIZE) {
+ parser->status = 0; /* Discard the rest of the message */
+ return NULL;
+ }
+
+ /* Store next byte */
+ parser->data[parser->nr_bytes++] = c;
+
+ /* Do we still need more data to get this event complete? */
+ if (parser->nr_bytes < parser->nr_bytes_total)
+ return NULL;
+
+ /* Event is complete, return it.
+ * Running status byte MIDI feature is also handled here. */
+ parser->event.type = parser->status;
+ parser->event.channel = parser->channel;
+ parser->nr_bytes = 0; /* Reset data size, in case there are additional running status messages */
+
+ switch (parser->status) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ parser->event.param1 = parser->data[0]; /* For example key number */
+ parser->event.param2 = parser->data[1]; /* For example velocity */
+ break;
+ case PITCH_BEND:
+ /* Pitch-bend is transmitted with 14-bit precision. */
+ parser->event.param1 = (parser->data[1] << 7) | parser->data[0];
+ break;
+ default: /* Unlikely */
+ return NULL;
+ }
+
+ return &parser->event;
+}
+
+/* Purpose:
+ * Returns the length of a MIDI message. */
+static int
+fluid_midi_event_length(unsigned char event)
+{
+ switch (event & 0xF0) {
+ case NOTE_OFF:
+ case NOTE_ON:
+ case KEY_PRESSURE:
+ case CONTROL_CHANGE:
+ case PITCH_BEND:
+ return 3;
+ case PROGRAM_CHANGE:
+ case CHANNEL_PRESSURE:
+ return 2;
+ }
+ switch (event) {
+ case MIDI_TIME_CODE:
+ case MIDI_SONG_SELECT:
+ case 0xF4:
+ case 0xF5:
+ return 2;
+ case MIDI_TUNE_REQUEST:
+ return 1;
+ case MIDI_SONG_POSITION:
+ return 3;
+ }
+ return 1;
+}
+
+
diff --git a/src/midi/fluid_midi.h b/src/midi/fluid_midi.h
new file mode 100644
index 0000000..608ba55
--- /dev/null
+++ b/src/midi/fluid_midi.h
@@ -0,0 +1,389 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _FLUID_MIDI_H
+#define _FLUID_MIDI_H
+
+#include "fluidsynth_priv.h"
+//#include "fluid_sys.h"
+//#include "custom.h"
+#include "fluid_list.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+typedef struct _fluid_midi_parser_t fluid_midi_parser_t;
+
+fluid_midi_parser_t* new_fluid_midi_parser(void);
+int delete_fluid_midi_parser(fluid_midi_parser_t* parser);
+fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c);
+
+fluid_midi_event_t *new_fluid_midi_event (void);
+
+
+/***************************************************************
+ *
+ * CONSTANTS & ENUM
+ */
+
+
+#define MAX_NUMBER_OF_TRACKS 128
+
+enum fluid_midi_event_type {
+ /* channel messages */
+ NOTE_OFF = 0x80,
+ NOTE_ON = 0x90,
+ KEY_PRESSURE = 0xa0,
+ CONTROL_CHANGE = 0xb0,
+ PROGRAM_CHANGE = 0xc0,
+ CHANNEL_PRESSURE = 0xd0,
+ PITCH_BEND = 0xe0,
+ /* system exclusive */
+ MIDI_SYSEX = 0xf0,
+ /* system common - never in midi files */
+ MIDI_TIME_CODE = 0xf1,
+ MIDI_SONG_POSITION = 0xf2,
+ MIDI_SONG_SELECT = 0xf3,
+ MIDI_TUNE_REQUEST = 0xf6,
+ MIDI_EOX = 0xf7,
+ /* system real-time - never in midi files */
+ MIDI_SYNC = 0xf8,
+ MIDI_TICK = 0xf9,
+ MIDI_START = 0xfa,
+ MIDI_CONTINUE = 0xfb,
+ MIDI_STOP = 0xfc,
+ MIDI_ACTIVE_SENSING = 0xfe,
+ MIDI_SYSTEM_RESET = 0xff,
+ /* meta event - for midi files only */
+ MIDI_META_EVENT = 0xff
+};
+
+enum fluid_midi_control_change {
+ BANK_SELECT_MSB = 0x00,
+ MODULATION_MSB = 0x01,
+ BREATH_MSB = 0x02,
+ FOOT_MSB = 0x04,
+ PORTAMENTO_TIME_MSB = 0x05,
+ DATA_ENTRY_MSB = 0x06,
+ VOLUME_MSB = 0x07,
+ BALANCE_MSB = 0x08,
+ PAN_MSB = 0x0A,
+ EXPRESSION_MSB = 0x0B,
+ EFFECTS1_MSB = 0x0C,
+ EFFECTS2_MSB = 0x0D,
+ GPC1_MSB = 0x10, /* general purpose controller */
+ GPC2_MSB = 0x11,
+ GPC3_MSB = 0x12,
+ GPC4_MSB = 0x13,
+ BANK_SELECT_LSB = 0x20,
+ MODULATION_WHEEL_LSB = 0x21,
+ BREATH_LSB = 0x22,
+ FOOT_LSB = 0x24,
+ PORTAMENTO_TIME_LSB = 0x25,
+ DATA_ENTRY_LSB = 0x26,
+ VOLUME_LSB = 0x27,
+ BALANCE_LSB = 0x28,
+ PAN_LSB = 0x2A,
+ EXPRESSION_LSB = 0x2B,
+ EFFECTS1_LSB = 0x2C,
+ EFFECTS2_LSB = 0x2D,
+ GPC1_LSB = 0x30,
+ GPC2_LSB = 0x31,
+ GPC3_LSB = 0x32,
+ GPC4_LSB = 0x33,
+ SUSTAIN_SWITCH = 0x40,
+ PORTAMENTO_SWITCH = 0x41,
+ SOSTENUTO_SWITCH = 0x42,
+ SOFT_PEDAL_SWITCH = 0x43,
+ LEGATO_SWITCH = 0x45,
+ HOLD2_SWITCH = 0x45,
+ SOUND_CTRL1 = 0x46,
+ SOUND_CTRL2 = 0x47,
+ SOUND_CTRL3 = 0x48,
+ SOUND_CTRL4 = 0x49,
+ SOUND_CTRL5 = 0x4A,
+ SOUND_CTRL6 = 0x4B,
+ SOUND_CTRL7 = 0x4C,
+ SOUND_CTRL8 = 0x4D,
+ SOUND_CTRL9 = 0x4E,
+ SOUND_CTRL10 = 0x4F,
+ GPC5 = 0x50,
+ GPC6 = 0x51,
+ GPC7 = 0x52,
+ GPC8 = 0x53,
+ PORTAMENTO_CTRL = 0x54,
+ EFFECTS_DEPTH1 = 0x5B,
+ EFFECTS_DEPTH2 = 0x5C,
+ EFFECTS_DEPTH3 = 0x5D,
+ EFFECTS_DEPTH4 = 0x5E,
+ EFFECTS_DEPTH5 = 0x5F,
+ DATA_ENTRY_INCR = 0x60,
+ DATA_ENTRY_DECR = 0x61,
+ NRPN_LSB = 0x62,
+ NRPN_MSB = 0x63,
+ RPN_LSB = 0x64,
+ RPN_MSB = 0x65,
+ ALL_SOUND_OFF = 0x78,
+ ALL_CTRL_OFF = 0x79,
+ LOCAL_CONTROL = 0x7A,
+ ALL_NOTES_OFF = 0x7B,
+ OMNI_OFF = 0x7C,
+ OMNI_ON = 0x7D,
+ POLY_OFF = 0x7E,
+ POLY_ON = 0x7F
+};
+
+/* General MIDI RPN event numbers (LSB, MSB = 0) */
+enum midi_rpn_event {
+ RPN_PITCH_BEND_RANGE = 0x00,
+ RPN_CHANNEL_FINE_TUNE = 0x01,
+ RPN_CHANNEL_COARSE_TUNE = 0x02,
+ RPN_TUNING_PROGRAM_CHANGE = 0x03,
+ RPN_TUNING_BANK_SELECT = 0x04,
+ RPN_MODULATION_DEPTH_RANGE = 0x05
+};
+
+enum midi_meta_event {
+ MIDI_COPYRIGHT = 0x02,
+ MIDI_TRACK_NAME = 0x03,
+ MIDI_INST_NAME = 0x04,
+ MIDI_LYRIC = 0x05,
+ MIDI_MARKER = 0x06,
+ MIDI_CUE_POINT = 0x07,
+ MIDI_EOT = 0x2f,
+ MIDI_SET_TEMPO = 0x51,
+ MIDI_SMPTE_OFFSET = 0x54,
+ MIDI_TIME_SIGNATURE = 0x58,
+ MIDI_KEY_SIGNATURE = 0x59,
+ MIDI_SEQUENCER_EVENT = 0x7f
+};
+
+/* MIDI SYSEX useful manufacturer values */
+enum midi_sysex_manuf {
+ MIDI_SYSEX_MANUF_ROLAND = 0x41, /**< Roland manufacturer ID */
+ MIDI_SYSEX_UNIV_NON_REALTIME = 0x7E, /**< Universal non realtime message */
+ MIDI_SYSEX_UNIV_REALTIME = 0x7F /**< Universal realtime message */
+};
+
+#define MIDI_SYSEX_DEVICE_ID_ALL 0x7F /**< Device ID used in SYSEX messages to indicate all devices */
+
+/* SYSEX sub-ID #1 which follows device ID */
+#define MIDI_SYSEX_MIDI_TUNING_ID 0x08 /**< Sysex sub-ID #1 for MIDI tuning messages */
+#define MIDI_SYSEX_GM_ID 0x09 /**< Sysex sub-ID #1 for General MIDI messages */
+
+/**
+ * SYSEX tuning message IDs.
+ */
+enum midi_sysex_tuning_msg_id {
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ = 0x00, /**< Bulk tuning dump request (non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP = 0x01, /**< Bulk tuning dump response (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE = 0x02, /**< Tuning note change message (realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_REQ_BANK = 0x03, /**< Bulk tuning dump request (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_BULK_DUMP_BANK = 0x04, /**< Bulk tuning dump resonse (with bank, non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_1BYTE = 0x05, /**< Octave tuning dump using 1 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_DUMP_2BYTE = 0x06, /**< Octave tuning dump using 2 byte values (non-realtime) */
+ MIDI_SYSEX_TUNING_NOTE_TUNE_BANK = 0x07, /**< Tuning note change message (with bank, realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_1BYTE = 0x08, /**< Octave tuning message using 1 byte values (realtime/non-realtime) */
+ MIDI_SYSEX_TUNING_OCTAVE_TUNE_2BYTE = 0x09 /**< Octave tuning message using 2 byte values (realtime/non-realtime) */
+};
+
+/* General MIDI sub-ID #2 */
+#define MIDI_SYSEX_GM_ON 0x01 /**< Enable GM mode */
+#define MIDI_SYSEX_GM_OFF 0x02 /**< Disable GM mode */
+
+enum fluid_driver_status
+{
+ FLUID_MIDI_READY,
+ FLUID_MIDI_LISTENING,
+ FLUID_MIDI_DONE
+};
+
+/***************************************************************
+ *
+ * TYPE DEFINITIONS & FUNCTION DECLARATIONS
+ */
+
+/* From ctype.h */
+#define fluid_isascii(c) (((c) & ~0x7f) == 0)
+
+
+
+/*
+ * fluid_midi_event_t
+ */
+struct _fluid_midi_event_t {
+ fluid_midi_event_t* next; /* Link to next event */
+ void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */
+ unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+ unsigned int param1; /* First parameter */
+ unsigned int param2; /* Second parameter */
+ unsigned char type; /* MIDI event type */
+ unsigned char channel; /* MIDI channel */
+};
+
+
+/*
+ * fluid_track_t
+ */
+struct _fluid_track_t {
+ char* name;
+ int num;
+ fluid_midi_event_t *first;
+ fluid_midi_event_t *cur;
+ fluid_midi_event_t *last;
+ unsigned int ticks;
+};
+
+typedef struct _fluid_track_t fluid_track_t;
+
+fluid_track_t* new_fluid_track(int num);
+int delete_fluid_track(fluid_track_t* track);
+int fluid_track_set_name(fluid_track_t* track, char* name);
+char* fluid_track_get_name(fluid_track_t* track);
+int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt);
+fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track);
+fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track);
+int fluid_track_get_duration(fluid_track_t* track);
+int fluid_track_reset(fluid_track_t* track);
+
+int fluid_track_send_events(fluid_track_t* track,
+ fluid_synth_t* synth,
+ fluid_player_t* player,
+ unsigned int ticks);
+
+#define fluid_track_eot(track) ((track)->cur == NULL)
+
+
+/**
+ * fluid_playlist_item
+ * Used as the `data' elements of the fluid_player.playlist.
+ * Represents either a filename or a pre-loaded memory buffer.
+ * Exactly one of `filename' and `buffer' is non-NULL.
+ */
+typedef struct
+{
+ char* filename; /** Name of file (owned); NULL if data pre-loaded */
+ void* buffer; /** The MIDI file data (owned); NULL if filename */
+ size_t buffer_len; /** Number of bytes in buffer; 0 if filename */
+} fluid_playlist_item;
+
+/*
+ * fluid_player
+ */
+struct _fluid_player_t {
+ int status;
+ int ntracks;
+ fluid_track_t *track[MAX_NUMBER_OF_TRACKS];
+ fluid_synth_t* synth;
+ fluid_timer_t* system_timer;
+ fluid_sample_timer_t* sample_timer;
+
+ int loop; /* -1 = loop infinitely, otherwise times left to loop the playlist */
+ fluid_list_t* playlist; /* List of fluid_playlist_item* objects */
+ fluid_list_t* currentfile; /* points to an item in files, or NULL if not playing */
+
+ char send_program_change; /* should we ignore the program changes? */
+ char use_system_timer; /* if zero, use sample timers, otherwise use system clock timer */
+ char reset_synth_between_songs; /* 1 if system reset should be sent to the synth between songs. */
+ int start_ticks; /* the number of tempo ticks passed at the last tempo change */
+ int cur_ticks; /* the number of tempo ticks passed */
+ int begin_msec; /* the time (msec) of the beginning of the file */
+ int start_msec; /* the start time of the last tempo change */
+ int cur_msec; /* the current time */
+ int miditempo; /* as indicated by MIDI SetTempo: n 24th of a usec per midi-clock. bravo! */
+ double deltatime; /* milliseconds per midi tick. depends on set-tempo */
+ unsigned int division;
+
+ handle_midi_event_func_t playback_callback; /* function fired on each midi event as it is played */
+ void* playback_userdata; /* pointer to user-defined data passed to playback_callback function */
+};
+
+int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track);
+int fluid_player_callback(void* data, unsigned int msec);
+int fluid_player_count_tracks(fluid_player_t* player);
+fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i);
+int fluid_player_reset(fluid_player_t* player);
+int fluid_player_load(fluid_player_t* player, fluid_playlist_item *item);
+
+//void fluid_player_settings(fluid_settings_t* settings);
+
+
+/*
+ * fluid_midi_file
+ */
+//hmm no events though here, right? looks so
+typedef struct {
+ const char* buffer; /* Entire contents of MIDI file (borrowed) */
+ int buf_len; /* Length of buffer, in bytes */
+ int buf_pos; /* Current read position in contents buffer */
+ int eof; /* The "end of file" condition */
+ int running_status;
+ int c;
+ int type;
+ int ntracks;
+ int uses_smpte;
+ unsigned int smpte_fps;
+ unsigned int smpte_res;
+ unsigned int division; /* If uses_SMPTE == 0 then division is
+ ticks per beat (quarter-note) */
+ double tempo; /* Beats per second (SI rules =) */
+ int tracklen;
+ int trackpos;
+ int eot;
+ int varlen;
+ int dtime;
+} fluid_midi_file;
+
+fluid_midi_file* new_fluid_midi_file(const char* buffer, size_t length);
+void delete_fluid_midi_file(fluid_midi_file* mf);
+int fluid_midi_file_read_mthd(fluid_midi_file* midifile);
+int fluid_midi_file_load_tracks(fluid_midi_file* midifile, fluid_player_t* player);
+int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num);
+int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track);
+int fluid_midi_file_read_varlen(fluid_midi_file* mf);
+int fluid_midi_file_getc(fluid_midi_file* mf);
+int fluid_midi_file_push(fluid_midi_file* mf, int c);
+int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len);
+int fluid_midi_file_skip(fluid_midi_file* mf, int len);
+int fluid_midi_file_eof(fluid_midi_file* mf);
+int fluid_midi_file_read_tracklen(fluid_midi_file* mf);
+int fluid_midi_file_eot(fluid_midi_file* mf);
+int fluid_midi_file_get_division(fluid_midi_file* midifile);
+
+
+#define FLUID_MIDI_PARSER_MAX_DATA_SIZE 1024 /**< Maximum size of MIDI parameters/data (largest is SYSEX data) */
+
+/*
+ * fluid_midi_parser_t
+ */
+struct _fluid_midi_parser_t {
+ unsigned char status; /* Identifies the type of event, that is currently received ('Noteon', 'Pitch Bend' etc). */
+ unsigned char channel; /* The channel of the event that is received (in case of a channel event) */
+ unsigned int nr_bytes; /* How many bytes have been read for the current event? */
+ unsigned int nr_bytes_total; /* How many bytes does the current event type include? */
+ unsigned char data[FLUID_MIDI_PARSER_MAX_DATA_SIZE]; /* The parameters or SYSEX data */
+ fluid_midi_event_t event; /* The event, that is returned to the MIDI driver. */
+};
+
+int fluid_isasciistring(char* s);
+long fluid_getlength(unsigned char *s);
+
+
+#endif /* _FLUID_MIDI_H */
diff --git a/src/midi/fluid_midi.lo b/src/midi/fluid_midi.lo
new file mode 100644
index 0000000..09c84c0
--- /dev/null
+++ b/src/midi/fluid_midi.lo
@@ -0,0 +1,12 @@
+# fluid_midi.lo - a libtool object file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object='.libs/fluid_midi.o'
+
+# Name of the non-PIC object
+non_pic_object='fluid_midi.o'
+
diff --git a/src/midi/fluid_midi.o b/src/midi/fluid_midi.o
new file mode 100644
index 0000000..4460e8d
--- /dev/null
+++ b/src/midi/fluid_midi.o
Binary files differ
diff --git a/src/midi/fluid_midi_custom.h b/src/midi/fluid_midi_custom.h
new file mode 100644
index 0000000..8712b44
--- /dev/null
+++ b/src/midi/fluid_midi_custom.h
@@ -0,0 +1,23 @@
+typedef struct _fluid_list_t fluid_list_t;
+
+typedef struct _fluid_midi_event_t fluid_midi_event_t;
+typedef struct _fluid_player_t fluid_player_t;
+typedef struct _fluid_track_t fluid_track_t;
+typedef struct _fluid_synth_t fluid_synth_t;
+typedef struct _fluid_timer_t fluid_timer_t;
+typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
+typedef struct _fluid_settings_t fluid_settings_t;
+
+
+#define FLUID_DBG 0
+#define FLUID_OK 1
+#define FLUID_ERR 0
+#define FLUID_FAILED 2
+#define FLUID_PANIC 3
+#define FLUID_PLAYER_DONE 2
+#define FLUID_PLAYER_PLAYING 1
+#define FLUID_PLAYER_READY 3
+#define FLUID_HINT_TOGGLED 0x01
+#define TRUE 1
+#define FALSE 0
+typedef int (*handle_midi_event_func_t)(void* data, fluid_midi_event_t* event);
diff --git a/src/midi/fluidsynth_priv.h b/src/midi/fluidsynth_priv.h
new file mode 100644
index 0000000..93f6d32
--- /dev/null
+++ b/src/midi/fluidsynth_priv.h
@@ -0,0 +1,281 @@
+/* FluidSynth - A Software Synthesizer
+ *
+ * Copyright (C) 2003 Peter Hanappe and others.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+#ifndef _FLUIDSYNTH_PRIV_H
+#define _FLUIDSYNTH_PRIV_H
+
+#include "fluid_midi_custom.h"
+#include "midi_loader.h"
+//#include <glib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(__POWERPC__) && !(defined(__APPLE__) && defined(__MACH__))
+#include "config_maxmsp43.h"
+#endif
+
+#if defined(WIN32) && !defined(MINGW32)
+#include "config_win32.h"
+#endif
+
+#if HAVE_STRING_H
+#include <string.h>
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#if HAVE_MATH_H
+#include <math.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#if HAVE_IO_H
+#include <io.h>
+#endif
+
+#if HAVE_WINDOWS_H
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#endif
+
+/* MinGW32 special defines */
+#ifdef MINGW32
+
+#include <stdint.h>
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+
+#define DSOUND_SUPPORT 1
+#define WINMIDI_SUPPORT 1
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#endif
+
+/* Darwin special defines (taken from config_macosx.h) */
+#ifdef DARWIN
+#define MACINTOSH
+#define __Types__
+#define WITHOUT_SERVER 1
+#endif
+
+
+//#include "fluidsynth.h"
+
+
+/***************************************************************
+ *
+ * BASIC TYPES
+ */
+
+#if defined(WITH_FLOAT)
+typedef float fluid_real_t;
+#else
+typedef double fluid_real_t;
+#endif
+
+
+#if defined(WIN32)
+typedef SOCKET fluid_socket_t;
+#else
+typedef int fluid_socket_t;
+#define INVALID_SOCKET -1
+#endif
+
+#if defined(SUPPORTS_VLA)
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type _name[_len]
+#else
+# define FLUID_DECLARE_VLA(_type, _name, _len) \
+ _type* _name = g_newa(_type, (_len))
+#endif
+
+
+/** Integer types */
+//typedef int8_t sint8_t;
+typedef uint8_t uint8_t;
+//typedef int16 sint16;
+//typedef uint16 uint16;
+typedef int32_t sint32_t;
+typedef uint32_t uint32_t;
+//typedef int64 sint64;
+//typedef uint64 uint64;
+
+
+/***************************************************************
+ *
+ * FORWARD DECLARATIONS
+ */
+typedef struct _fluid_env_data_t fluid_env_data_t;
+typedef struct _fluid_adriver_definition_t fluid_adriver_definition_t;
+typedef struct _fluid_channel_t fluid_channel_t;
+typedef struct _fluid_tuning_t fluid_tuning_t;
+typedef struct _fluid_hashtable_t fluid_hashtable_t;
+typedef struct _fluid_client_t fluid_client_t;
+typedef struct _fluid_server_socket_t fluid_server_socket_t;
+typedef struct _fluid_sample_timer_t fluid_sample_timer_t;
+
+/***************************************************************
+ *
+ * CONSTANTS
+ */
+
+#define FLUID_BUFSIZE 64 /**< FluidSynth internal buffer size (in samples) */
+#define FLUID_MAX_EVENTS_PER_BUFSIZE 1024 /**< Maximum queued MIDI events per #FLUID_BUFSIZE */
+#define FLUID_MAX_RETURN_EVENTS 1024 /**< Maximum queued synthesis thread return events */
+#define FLUID_MAX_EVENT_QUEUES 16 /**< Maximum number of unique threads queuing events */
+#define FLUID_DEFAULT_AUDIO_RT_PRIO 60 /**< Default setting for audio.realtime-prio */
+#define FLUID_DEFAULT_MIDI_RT_PRIO 50 /**< Default setting for midi.realtime-prio */
+
+#ifndef PI
+#define PI 3.141592654
+#endif
+
+/***************************************************************
+ *
+ * SYSTEM INTERFACE
+ */
+typedef FILE* fluid_file;
+
+#define FLUID_MALLOC(_n) malloc(_n)
+#define FLUID_REALLOC(_p,_n) realloc(_p,_n)
+#define FLUID_NEW(_t) (_t*)malloc(sizeof(_t))
+#define FLUID_ARRAY(_t,_n) (_t*)malloc((_n)*sizeof(_t))
+#define FLUID_FREE(_p) free(_p)
+#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
+#define FLUID_FCLOSE(_f) fclose(_f)
+#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
+#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
+#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n)
+#define FLUID_MEMSET(_s,_c,_n) memset(_s,_c,_n)
+#define FLUID_STRLEN(_s) strlen(_s)
+#define FLUID_STRCMP(_s,_t) strcmp(_s,_t)
+#define FLUID_STRNCMP(_s,_t,_n) strncmp(_s,_t,_n)
+#define FLUID_STRCPY(_dst,_src) strcpy(_dst,_src)
+#define FLUID_STRNCPY(_dst,_src,_n) strncpy(_dst,_src,_n)
+#define FLUID_STRCHR(_s,_c) strchr(_s,_c)
+#define FLUID_STRRCHR(_s,_c) strrchr(_s,_c)
+#ifdef strdup
+#define FLUID_STRDUP(s) strdup(s)
+#else
+#define FLUID_STRDUP(s) FLUID_STRCPY(FLUID_MALLOC(FLUID_STRLEN(s) + 1), s)
+#endif
+#define FLUID_SPRINTF sprintf
+#define FLUID_FPRINTF fprintf
+
+#define fluid_clip(_val, _min, _max) \
+{ (_val) = ((_val) < (_min))? (_min) : (((_val) > (_max))? (_max) : (_val)); }
+
+#if WITH_FTS
+#define FLUID_PRINTF post
+#define FLUID_FLUSH()
+#else
+#define FLUID_PRINTF printf
+#define FLUID_FLUSH() fflush(stdout)
+#endif
+
+#define FLUID_LOG fluid_log
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+
+#define FLUID_ASSERT(a,b)
+#define FLUID_ASSERT_P(a,b)
+
+char* fluid_error(void);
+
+
+/* Internationalization */
+#define _(s) s
+
+
+#endif /* _FLUIDSYNTH_PRIV_H */
diff --git a/src/midi/libfluidmidi.la b/src/midi/libfluidmidi.la
new file mode 100644
index 0000000..8da7fe7
--- /dev/null
+++ b/src/midi/libfluidmidi.la
@@ -0,0 +1,41 @@
+# libfluidmidi.la - a libtool library file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname=''
+
+# Names of this library.
+library_names=''
+
+# The name of the static archive.
+old_library='libfluidmidi.a'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags=''
+
+# Libraries that this one depends upon.
+dependency_libs=''
+
+# Names of additional weak libraries provided by this library
+weak_library_names=''
+
+# Version information for libfluidmidi.
+current=
+age=
+revision=
+
+# Is this an already installed library?
+installed=no
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=no
+
+# Files to dlopen/dlpreopen
+dlopen=''
+dlpreopen=''
+
+# Directory that this library needs to be installed in:
+libdir=''
diff --git a/src/midi/midi_loader.c b/src/midi/midi_loader.c
new file mode 100644
index 0000000..2cdd539
--- /dev/null
+++ b/src/midi/midi_loader.c
@@ -0,0 +1,71 @@
+#include "midi_loader.h"
+
+//static int event_count = 0;
+size_t last_msec = 0;
+size_t nmsecs_since_last = 0;
+
+void print_event(fluid_midi_event_t *event, size_t current_msec){
+// {{{ DESCRIPTION
+// fluid_midi_event_t* next; /* Link to next event */
+// void *paramptr; /* Pointer parameter (for SYSEX data), size is stored to param1, param2 indicates if pointer should be freed (dynamic if TRUE) */
+// unsigned int dtime; /* Delay (ticks) between this and previous event. midi tracks. */
+// unsigned int param1; /* First parameter */
+// unsigned int param2; /* Second parameter */
+// unsigned char type; /* MIDI event type */
+// unsigned char channel; /* MIDI channel */
+//}}}
+// printf("EVENT_COUNT: %d\n", event_count);
+ printf("dtime:%u ", event->dtime);
+ printf("param1:%u ", event->param1);
+ printf("param2: %u ", event->param2);
+ printf("type: %x ", event->type);
+ printf("channel: %u ", event->channel);
+ printf("nframe: %u ", current_msec);
+ puts("\n");
+}
+
+int get_events(void *data, fluid_midi_event_t *event){
+ //this function is called every time a new event comes in
+ read_midi_ctx_t *ctx = (read_midi_ctx_t *)data;
+ fluid_player_t *player = ctx->player;
+ fluid_track_t *track = ctx->track;
+ read_midi_callback cb = ctx->callback;
+ size_t current_msec;
+
+// event_count++;
+ current_msec = (player->deltatime * track->ticks);
+ nmsecs_since_last = current_msec - last_msec;
+ last_msec = current_msec;
+
+// print_event(event, current_msec);
+ //process_midi_cb execution...
+ cb(event, nmsecs_since_last, ctx->callback_userdata); // seems good, check the output ok
+//ok I'm going to study this, make a git back it up clean it up etc tomorrow, but I think I understand. this is
+//definitely coming along. thanks so much for the help no problems alright awesome, I'll talk to you and send you a payment again soon thanks again
+}
+
+void load_midi_file(char *filename, read_midi_callback callback, void *callback_userdata){
+ fluid_playlist_item playlist_item;
+ playlist_item.filename = filename;
+
+ fluid_player_t *player;
+ player = (fluid_player_t *)new_fluid_player();
+ player->playback_callback = &get_events;
+
+
+ read_midi_ctx_t ctx;
+ ctx.player = player;
+ ctx.callback = callback;
+ ctx.callback_userdata = callback_userdata;
+
+ player->playback_userdata = (void *)&ctx;
+ fluid_player_load(player, &playlist_item);
+
+ int i;
+ for(i = 0; i < player->ntracks; i++){
+ ctx.track = player->track[i];//tracks...when there is more than one song in a file
+ fluid_track_send_events(player->track[i], player->synth, player, (unsigned int)-1);
+ }
+
+ delete_fluid_player(player);
+}
diff --git a/src/midi/midi_loader.h b/src/midi/midi_loader.h
new file mode 100644
index 0000000..57cee25
--- /dev/null
+++ b/src/midi/midi_loader.h
@@ -0,0 +1,46 @@
+#ifndef MIDI_LOADER_H
+#define MIDI_LOADER_H
+
+#include "fluid_list.h"
+#include "fluidsynth_priv.h" //is this the right idea? just need to load libs in the right order? yep
+#include "fluid_midi.h"
+#include <sndfile.h>
+#include <string.h>
+#include <ladspa.h>
+#include <dssi.h>
+//load all of those in the same way as below? or is the problem with how fluid_midi is accessing this file? yeah basically order issue
+
+
+typedef struct event_table_t{
+ snd_seq_event_t *events;
+ size_t length;
+ size_t last_nframe;
+ size_t nframes_since_last;
+} event_table_t;
+
+//typedef void(*read_midi_callback)(event_table_t *event_table, void *userdata);
+typedef void(*read_midi_callback)(fluid_midi_event_t *event, size_t msecs_since_last, void *userdata);
+
+
+
+typedef struct read_midi_ctx_t {
+ fluid_player_t *player;
+ fluid_track_t *track;
+ read_midi_callback callback;
+ void *callback_userdata;
+} read_midi_ctx_t;
+
+
+//void insert_event(event_table_t *event_table, snd_seq_event_t *event){
+//void delete_event(event_table_t *event_table, snd_seq_event_t *event){
+//int compare_events(snd_seq_event_t *event1, snd_seq_event_t *event2){
+//void replace_events(event_table_t *event_table, snd_seq_event_t *event){
+//like this?
+//void convert_event_format(fluid_midi_event_t *from, snd_seq_event_t *to);
+//void print_snd_seq_event(snd_seq_event_t *event);
+//void print_event_table (event_table_t *event_table);
+//int get_events(void *data, fluid_midi_event_t *event);
+
+void print_snd_seq_event(snd_seq_event_t *event);
+void load_midi_file(char *filename, read_midi_callback callback, void *callback_userdata);
+#endif
diff --git a/src/midi/midi_loader_test.c b/src/midi/midi_loader_test.c
new file mode 100644
index 0000000..d79392d
--- /dev/null
+++ b/src/midi/midi_loader_test.c
@@ -0,0 +1,240 @@
+#include "midi_loader.h"
+
+event_table_t *event_table;
+
+//{{{ TO REMOVE...event table functions
+void insert_event(event_table_t *event_table, snd_seq_event_t *event){
+ //inserts an event into the event table
+ event_table->events = realloc(event_table->events, (event_table->length + 1) * sizeof(snd_seq_event_t));
+ memcpy(&event_table->events[event_table->length], event, sizeof(snd_seq_event_t));
+ event_table->length += 1;
+}
+
+void delete_event(event_table_t *event_table, snd_seq_event_t *event){
+ //deletes an event in the event table
+ size_t i;
+ for (i=0; i< event_table->length; i++){
+ if(compare_events(&event_table->events[i], event)){
+ printf("removed_event\n");
+ memcpy(&event_table->events[i], &event_table->events[i+1], sizeof(snd_seq_event_t)*(event_table->length - i -1));
+ event_table->events = realloc(event_table->events, event_table->length * sizeof(snd_seq_event_t));
+ event_table->length--;
+ i--;
+ }
+ }
+}
+
+void delete_note_off_events(event_table_t *event_table){
+ //removes note_off events after they have happened from the event table
+ size_t i;
+ size_t i;
+ for (i=0; i< event_table->length; i++){
+ if(event_table->events[i].type == SND_SEQ_EVENT_NOTEOFF){
+ printf("removed_note_off_event\n");
+ memcpy(&event_table->events[i], &event_table->events[i+1], sizeof(snd_seq_event_t)*(event_table->length - i -1));
+ event_table->events = realloc(event_table->events, event_table->length * sizeof(snd_seq_event_t));
+ event_table->length--;
+ i--;
+ }
+ }
+}
+
+int compare_events(snd_seq_event_t *event1, snd_seq_event_t *event2){
+ //compares events in the event table
+ return (
+ (event1->data.note.note == event2->data.note.note) &&
+ (event1->data.note.channel == event2->data.note.channel)
+ ) ? 1 : 0;
+}
+
+
+void replace_events(event_table_t *event_table, snd_seq_event_t *event){
+ //replaces events in the event table
+ size_t i;
+ for (i=0; i< event_table->length; i++){
+ if(compare_events(&event_table->events[i], event)){
+ printf("replaced_event\n");
+ memcpy(&event_table->events[i], event, sizeof(snd_seq_event_t));
+ }
+ }
+}
+
+void print_event_table (event_table_t *event_table){
+ unsigned int i;
+ for(i=0; i< event_table->length; i++){
+ printf(" - %d: ", i + 1);
+ print_snd_seq_event(&event_table->events[i]);
+
+ }
+ printf("--\n");
+}
+//}}}
+
+void convert_event_format(fluid_midi_event_t *from, snd_seq_event_t *to){
+ memset(to, 0, sizeof(snd_seq_event_t));
+//{{{ from->type
+ switch(from->type){
+
+ case NOTE_ON:
+ to->type = SND_SEQ_EVENT_NOTEON;
+ to->data.note.note = from->param1;
+ to->data.note.velocity = from->param2;
+ break;
+ case NOTE_OFF:
+ to->type = SND_SEQ_EVENT_NOTEOFF;
+ to->data.note.note = from->param1;
+ to->data.note.off_velocity = from->param2;
+ break;
+ /*case FLUID_NONE: to->type = SND_SEQ_EVENT_SYSTEM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_RESULT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NOTE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_KEYPRESS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTROLLER; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PGMCHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CHANPRESS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PITCHBEND; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTROL14; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NONREGPARAM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_REGPARAM; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SONGPOS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SONGSEL; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_QFRAME; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TIMESIGN; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_KEYSIGN; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CONTINUE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_STOP; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SETPOS_TICK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SETPOS_TIME; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TEMPO; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLOCK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TICK; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_QUEUE_SKEW; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SYNC_POS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_TUNE_REQUEST; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_RESET; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SENSING; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_ECHO; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_OSS; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_EXIT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_CLIENT_CHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_START; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_EXIT; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_CHANGE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_SUBSCRIBED; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_PORT_UNSUBSCRIBED; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR0; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR1; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR2; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR3; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR4; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR5; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR6; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR7; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR8; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR9; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_SYSEX; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_BOUNCE; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR0; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR1; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR2; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR3; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_USR_VAR4; break;
+ case FLUID_NONE: to->type = SND_SEQ_EVENT_NONE; break;
+*/
+ }
+//}}}
+ //to->data.note.channel = from->channel;
+ to->data.note.channel = 0; // FIXME force channel
+ to->time.tick = 0;
+}
+
+void print_snd_seq_event(snd_seq_event_t *event){
+ char note_event[20];
+ switch(event->type){
+
+ case SND_SEQ_EVENT_NOTEON:
+ strcpy(note_event,"NOTE_ON");
+ break;
+ case SND_SEQ_EVENT_NOTEOFF:
+ strcpy(note_event,"NOTE_OFF");
+ break;
+ break;
+ }
+ printf("event_type: %s", note_event);
+ printf("channel: %d ", event->data.note.channel);
+ printf("note: %d ", event->data.note.note);
+ printf("velocity: %d ", event->data.note.velocity);
+ printf("tick: %d ", event->time.tick);
+ printf("\n");
+}
+
+
+int get_events(void *data, fluid_midi_event_t *event){
+ read_midi_ctx_t *ctx = (read_midi_ctx_t *)data;
+ fluid_player_t *player = ctx->player;
+ fluid_track_t *track = ctx->track;
+ snd_seq_event_t seq_event;
+
+ size_t last_nframe = event_table->last_nframe;
+ event_table->last_nframe = (player->deltatime * track->ticks) * 44100 / 1000; // FIXME 44100 to ctx->samplerate
+ event_table->nframes_since_last = event_table->last_nframe - last_nframe;
+
+ convert_event_format(event, &seq_event);
+
+ read_midi_callback cb = ctx->callback;
+ if(cb){
+ cb(event_table, ctx->callback_userdata);
+ }
+ delete_note_off_events(event_table);
+
+ switch(event->type){
+ case NOTE_ON:
+ insert_event(event_table, &seq_event);
+ break;
+ case NOTE_OFF:
+ replace_events(event_table, &seq_event);
+ break;
+ default:
+ break;
+ }
+#define DEBUG_MIDI 0
+
+ if(DEBUG_MIDI){
+ printf("event table last nframe: %u\n", event_table->last_nframe);
+ printf("run_synth(instancehandle, %u,\n", event_table->nframes_since_last);
+ print_event_table(event_table);
+ printf(", %u)\n", event_table->length);
+ }
+
+}
+
+
+void load_midi_file(char *filename, read_midi_callback callback, void *callback_userdata){
+ int i;
+ fluid_player_t *player;
+ fluid_playlist_item playlist_item;
+ read_midi_ctx_t ctx;
+
+ event_table = malloc(sizeof (event_table_t));
+ event_table->events = NULL;
+ event_table->length = 0;
+ event_table->last_nframe = 0;
+ event_table->nframes_since_last = 0;
+ playlist_item.filename = filename;
+ player = (fluid_player_t *)new_fluid_player();
+ player->playback_callback = &get_events;
+ player->playback_userdata = (void *)&ctx;
+ ctx.player = player;
+ ctx.callback = callback;
+ ctx.callback_userdata = callback_userdata;
+ fluid_player_load(player, &playlist_item);
+
+ for(i = 0; i < player->ntracks; i++){
+ ctx.track = player->track[i];
+ fluid_track_send_events(player->track[i], player->synth, player, (unsigned int)-1);
+ }
+
+ delete_fluid_player(player);
+}
diff --git a/src/state.c b/src/state.c
new file mode 100644
index 0000000..5afbb24
--- /dev/null
+++ b/src/state.c
@@ -0,0 +1,224 @@
+/*
+ Copyright 2007-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define _POSIX_C_SOURCE 200112L /* for fileno */
+#define _BSD_SOURCE 1 /* for lockf */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_LV2_STATE
+# include "lv2/lv2plug.in/ns/ext/state/state.h"
+#endif
+
+#include "lilv/lilv.h"
+
+#include "jalv_config.h"
+#include "LV2-render_internal.h"
+
+#define NS_JALV "http://drobilla.net/ns/jalv#"
+#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+#define NS_RDFS "http://www.w3.org/2000/01/rdf-schema#"
+#define NS_XSD "http://www.w3.org/2001/XMLSchema#"
+
+#define USTR(s) ((const uint8_t*)s)
+
+char*
+jalv_make_path(LV2_State_Make_Path_Handle handle,
+ const char* path)
+{
+ Jalv* jalv = (Jalv*)handle;
+
+ // Create in save directory if saving, otherwise use temp directory
+ const char* dir = (jalv->save_dir) ? jalv->save_dir : jalv->temp_dir;
+
+ char* fullpath = jalv_strjoin(dir, path);
+ fprintf(stderr, "MAKE PATH `%s' => `%s'\n", path, fullpath);
+
+ return fullpath;
+}
+
+static const void*
+get_port_value(const char* port_symbol,
+ void* user_data,
+ uint32_t* size,
+ uint32_t* type)
+{
+ Jalv* jalv = (Jalv*)user_data;
+ struct Port* port = jalv_port_by_symbol(jalv, port_symbol);
+ if (port && port->flow == FLOW_INPUT && port->type == TYPE_CONTROL) {
+ *size = sizeof(float);
+ *type = jalv->forge.Float;
+ return &port->control;
+ }
+ *size = *type = 0;
+ return NULL;
+}
+
+void
+jalv_save(Jalv* jalv, const char* dir)
+{
+ jalv->save_dir = jalv_strjoin(dir, "/");
+
+ LilvState* const state = lilv_state_new_from_instance(
+ jalv->plugin, jalv->instance, &jalv->map,
+ jalv->temp_dir, dir, dir, dir,
+ get_port_value, jalv,
+ LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, NULL);
+
+ lilv_state_save(jalv->world, &jalv->map, &jalv->unmap, state, NULL,
+ dir, "state.ttl");
+
+ lilv_state_free(state);
+
+ free(jalv->save_dir);
+ jalv->save_dir = NULL;
+}
+
+int
+jalv_load_presets(Jalv* jalv, PresetSink sink, void* data)
+{
+ LilvNodes* presets = lilv_plugin_get_related(jalv->plugin,
+ jalv->nodes.pset_Preset);
+ LILV_FOREACH(nodes, i, presets) {
+ const LilvNode* preset = lilv_nodes_get(presets, i);
+ printf("Preset: %s\n", lilv_node_as_uri(preset));
+ lilv_world_load_resource(jalv->world, preset);
+ LilvNodes* labels = lilv_world_find_nodes(
+ jalv->world, preset, jalv->nodes.rdfs_label, NULL);
+ if (labels) {
+ const LilvNode* label = lilv_nodes_get_first(labels);
+ sink(jalv, preset, label, data);
+ lilv_nodes_free(labels);
+ } else {
+ fprintf(stderr, "Preset <%s> has no rdfs:label\n",
+ lilv_node_as_string(lilv_nodes_get(presets, i)));
+ }
+ }
+ lilv_nodes_free(presets);
+
+ return 0;
+}
+
+int
+jalv_unload_presets(Jalv* jalv)
+{
+ LilvNodes* presets = lilv_plugin_get_related(jalv->plugin,
+ jalv->nodes.pset_Preset);
+ LILV_FOREACH(nodes, i, presets) {
+ const LilvNode* preset = lilv_nodes_get(presets, i);
+ lilv_world_unload_resource(jalv->world, preset);
+ }
+ lilv_nodes_free(presets);
+
+ return 0;
+}
+
+static void
+set_port_value(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t size,
+ uint32_t type)
+{
+ Jalv* jalv = (Jalv*)user_data;
+ struct Port* port = jalv_port_by_symbol(jalv, port_symbol);
+ if (!port) {
+ fprintf(stderr, "error: Preset port `%s' is missing\n", port_symbol);
+ return;
+ }
+
+ float fvalue;
+ if (type == jalv->forge.Float) {
+ fvalue = *(const float*)value;
+ } else if (type == jalv->forge.Double) {
+ fvalue = *(const double*)value;
+ } else if (type == jalv->forge.Int) {
+ fvalue = *(const int32_t*)value;
+ } else if (type == jalv->forge.Long) {
+ fvalue = *(const int64_t*)value;
+ } else {
+ fprintf(stderr, "error: Preset `%s' value has bad type <%s>\n",
+ port_symbol, jalv->unmap.unmap(jalv->unmap.handle, type));
+ return;
+ }
+
+ if (jalv->play_state != JALV_RUNNING) {
+ // Set value on port struct directly
+ port->control = fvalue;
+ }
+
+}
+
+void
+jalv_apply_state(Jalv* jalv, LilvState* state)
+{
+ if (state) {
+ const bool must_pause = (jalv->play_state == JALV_RUNNING);
+ if (must_pause) {
+ jalv->play_state = JALV_PAUSE_REQUESTED;
+ zix_sem_wait(&jalv->paused);
+ }
+
+ lilv_state_restore(
+ state, jalv->instance, set_port_value, jalv, 0, NULL);
+
+ if (must_pause) {
+ jalv->play_state = JALV_RUNNING;
+ }
+ }
+}
+
+int
+jalv_apply_preset(Jalv* jalv, const LilvNode* preset)
+{
+ LilvState* state = lilv_state_new_from_world(
+ jalv->world, &jalv->map, preset);
+ jalv_apply_state(jalv, state);
+ lilv_state_free(state);
+ return 0;
+}
+
+int
+jalv_save_preset(Jalv* jalv,
+ const char* dir,
+ const char* uri,
+ const char* label,
+ const char* filename)
+{
+ LilvState* const state = lilv_state_new_from_instance(
+ jalv->plugin, jalv->instance, &jalv->map,
+ jalv->temp_dir, dir, dir, dir,
+ get_port_value, jalv,
+ LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE, NULL);
+
+ if (label) {
+ lilv_state_set_label(state, label);
+ }
+
+ int ret = lilv_state_save(
+ jalv->world, &jalv->map, &jalv->unmap, state, uri, dir, filename);
+
+ lilv_state_free(state);
+
+ return ret;
+}
diff --git a/src/symap.c b/src/symap.c
new file mode 100644
index 0000000..40c8980
--- /dev/null
+++ b/src/symap.c
@@ -0,0 +1,231 @@
+/*
+ Copyright 2011-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "symap.h"
+
+/**
+ @file symap.c Implementation of Symap, a basic symbol map (string interner).
+
+ This implementation is primitive, but has some desirable qualities: good
+ (O(lg(n)) lookup performance for already-mapped symbols, minimal space
+ overhead, extremely fast (O(1)) reverse mapping (ID to string), simple code,
+ no dependencies.
+
+ The tradeoff is that mapping new symbols may be quite slow. In other words,
+ this implementation is ideal for use cases with a relatively limited set of
+ symbols, or where most symbols are mapped early. It will not fare so well
+ with very dynamic sets of symbols. For that, you're better off with a
+ tree-based implementation (and the associated space cost, especially if you
+ need reverse mapping).
+*/
+
+struct SymapImpl {
+ /**
+ Unsorted array of strings, such that the symbol for ID i is found
+ at symbols[i - 1].
+ */
+ char** symbols;
+
+ /**
+ Array of IDs, sorted by corresponding string in `symbols`.
+ */
+ uint32_t* index;
+
+ /**
+ Number of symbols (number of items in `symbols` and `index`).
+ */
+ uint32_t size;
+};
+
+Symap*
+symap_new(void)
+{
+ Symap* map = (Symap*)malloc(sizeof(Symap));
+ map->symbols = NULL;
+ map->index = NULL;
+ map->size = 0;
+ return map;
+}
+
+void
+symap_free(Symap* map)
+{
+ for (uint32_t i = 0; i < map->size; ++i) {
+ free(map->symbols[i]);
+ }
+
+ free(map->symbols);
+ free(map->index);
+ free(map);
+}
+
+static char*
+symap_strdup(const char* str)
+{
+ const size_t len = strlen(str);
+ char* copy = (char*)malloc(len + 1);
+ memcpy(copy, str, len + 1);
+ return copy;
+}
+
+/**
+ Return the index into map->index (not the ID) corresponding to `sym`,
+ or the index where a new entry for `sym` should be inserted.
+*/
+static uint32_t
+symap_search(const Symap* map, const char* sym, bool* exact)
+{
+ *exact = false;
+ if (map->size == 0) {
+ return 0; // Empty map, insert at 0
+ } else if (strcmp(map->symbols[map->index[map->size - 1] - 1], sym) < 0) {
+ return map->size; // Greater than last element, append
+ }
+
+ uint32_t lower = 0;
+ uint32_t upper = map->size - 1;
+ uint32_t i = upper;
+ int cmp;
+
+ while (upper >= lower) {
+ i = lower + ((upper - lower) / 2);
+ cmp = strcmp(map->symbols[map->index[i] - 1], sym);
+
+ if (cmp == 0) {
+ *exact = true;
+ return i;
+ } else if (cmp > 0) {
+ if (i == 0) {
+ break; // Avoid underflow
+ }
+ upper = i - 1;
+ } else {
+ lower = ++i;
+ }
+ }
+
+ assert(!*exact || strcmp(map->symbols[map->index[i] - 1], sym) > 0);
+ return i;
+}
+
+uint32_t
+symap_try_map(Symap* map, const char* sym)
+{
+ bool exact;
+ const uint32_t index = symap_search(map, sym, &exact);
+ if (exact) {
+ assert(!strcmp(map->symbols[map->index[index]], sym));
+ return map->index[index];
+ }
+
+ return 0;
+}
+
+uint32_t
+symap_map(Symap* map, const char* sym)
+{
+ bool exact;
+ const uint32_t index = symap_search(map, sym, &exact);
+ if (exact) {
+ assert(!strcmp(map->symbols[map->index[index] - 1], sym));
+ return map->index[index];
+ }
+
+ const uint32_t id = ++map->size;
+ char* const str = symap_strdup(sym);
+
+ /* Append new symbol to symbols array */
+ map->symbols = (char**)realloc(map->symbols, map->size * sizeof(str));
+ map->symbols[id - 1] = str;
+
+ /* Insert new index element into sorted index */
+ map->index = (uint32_t*)realloc(map->index, map->size * sizeof(uint32_t));
+ if (index < map->size - 1) {
+ memmove(map->index + index + 1,
+ map->index + index,
+ (map->size - index - 1) * sizeof(uint32_t));
+ }
+
+ map->index[index] = id;
+
+ return id;
+}
+
+const char*
+symap_unmap(Symap* map, uint32_t id)
+{
+ if (id == 0) {
+ return NULL;
+ } else if (id <= map->size) {
+ return map->symbols[id - 1];
+ }
+ return NULL;
+}
+
+#ifdef STANDALONE
+
+#include <stdio.h>
+
+static void
+symap_dump(Symap* map)
+{
+ fprintf(stderr, "{\n");
+ for (uint32_t i = 0; i < map->size; ++i) {
+ fprintf(stderr, "\t%u = %s\n",
+ map->index[i], map->symbols[map->index[i] - 1]);
+ }
+ fprintf(stderr, "}\n");
+}
+
+int
+main()
+{
+ #define N_SYMS 5
+ char* syms[N_SYMS] = {
+ "hello", "bonjour", "goodbye", "aloha", "salut"
+ };
+
+ Symap* map = symap_new();
+ for (int i = 0; i < N_SYMS; ++i) {
+ if (symap_try_map(map, syms[i])) {
+ fprintf(stderr, "error: Symbol already mapped\n");
+ return 1;
+ }
+
+ const uint32_t id = symap_map(map, syms[i]);
+ if (strcmp(map->symbols[id - 1], syms[i])) {
+ fprintf(stderr, "error: Corrupt symbol table\n");
+ return 1;
+ }
+
+ if (symap_map(map, syms[i]) != id) {
+ fprintf(stderr, "error: Remapped symbol to a different ID\n");
+ return 1;
+ }
+
+ symap_dump(map);
+ }
+
+ symap_free(map);
+ return 0;
+}
+
+#endif /* STANDALONE */
diff --git a/src/symap.h b/src/symap.h
new file mode 100644
index 0000000..79de8ff
--- /dev/null
+++ b/src/symap.h
@@ -0,0 +1,69 @@
+/*
+ Copyright 2011-2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file symap.h API for Symap, a basic symbol map (string interner).
+
+ Particularly useful for implementing LV2 URI mapping.
+
+ @see <a href="http://lv2plug.in/ns/ext/urid">LV2 URID</a>
+ @see <a href="http://lv2plug.in/ns/ext/uri-map">LV2 URI Map</a>
+*/
+
+#ifndef SYMAP_H
+#define SYMAP_H
+
+#include <stdint.h>
+
+struct SymapImpl;
+
+typedef struct SymapImpl Symap;
+
+/**
+ Create a new symbol map.
+*/
+Symap*
+symap_new(void);
+
+/**
+ Free a symbol map.
+*/
+void
+symap_free(Symap* map);
+
+/**
+ Map a string to a symbol ID if it is already mapped, otherwise return 0.
+*/
+uint32_t
+symap_try_map(Symap* map, const char* sym);
+
+/**
+ Map a string to a symbol ID.
+
+ Note that 0 is never a valid symbol ID.
+*/
+uint32_t
+symap_map(Symap* map, const char* sym);
+
+/**
+ Unmap a symbol ID back to a symbol, or NULL if no such ID exists.
+
+ Note that 0 is never a valid symbol ID.
+*/
+const char*
+symap_unmap(Symap* map, uint32_t id);
+
+#endif /* SYMAP_H */
diff --git a/src/worker.c b/src/worker.c
new file mode 100644
index 0000000..074ecf2
--- /dev/null
+++ b/src/worker.c
@@ -0,0 +1,118 @@
+/*
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "worker.h"
+
+static LV2_Worker_Status
+jalv_worker_respond(LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ Jalv* jalv = (Jalv*)handle;
+ jack_ringbuffer_write(jalv->worker.responses,
+ (const char*)&size, sizeof(size));
+ jack_ringbuffer_write(jalv->worker.responses, (const char*)data, size);
+ return LV2_WORKER_SUCCESS;
+}
+
+static void*
+worker_func(void* data)
+{
+ Jalv* jalv = (Jalv*)data;
+ void* buf = NULL;
+ while (true) {
+ zix_sem_wait(&jalv->worker.sem);
+ if (jalv->exit) {
+ break;
+ }
+
+ uint32_t size = 0;
+ jack_ringbuffer_read(jalv->worker.requests, (char*)&size, sizeof(size));
+
+ if (!(buf = realloc(buf, size))) {
+ fprintf(stderr, "error: realloc() failed\n");
+ free(buf);
+ return NULL;
+ }
+
+ jack_ringbuffer_read(jalv->worker.requests, (char*)buf, size);
+
+ jalv->worker.iface->work(
+ jalv->instance->lv2_handle, jalv_worker_respond, jalv, size, buf);
+ }
+
+ free(buf);
+ return NULL;
+}
+
+void
+jalv_worker_init(Jalv* jalv,
+ JalvWorker* worker,
+ const LV2_Worker_Interface* iface)
+{
+ worker->iface = iface;
+ zix_thread_create(&worker->thread, 4096, worker_func, jalv);
+ worker->requests = jack_ringbuffer_create(4096);
+ worker->responses = jack_ringbuffer_create(4096);
+ worker->response = malloc(4096);
+ jack_ringbuffer_mlock(worker->requests);
+ jack_ringbuffer_mlock(worker->responses);
+}
+
+void
+jalv_worker_finish(JalvWorker* worker)
+{
+ if (worker->requests) {
+ zix_sem_post(&worker->sem);
+ zix_thread_join(worker->thread, NULL);
+ jack_ringbuffer_free(worker->requests);
+ jack_ringbuffer_free(worker->responses);
+ free(worker->response);
+ }
+}
+
+LV2_Worker_Status
+jalv_worker_schedule(LV2_Worker_Schedule_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ Jalv* jalv = (Jalv*)handle;
+ jack_ringbuffer_write(jalv->worker.requests,
+ (const char*)&size, sizeof(size));
+ jack_ringbuffer_write(jalv->worker.requests, (const char*)data, size);
+ zix_sem_post(&jalv->worker.sem);
+ return LV2_WORKER_SUCCESS;
+}
+
+void
+jalv_worker_emit_responses(Jalv* jalv, JalvWorker* worker)
+{
+ if (worker->responses) {
+ uint32_t read_space = jack_ringbuffer_read_space(worker->responses);
+ while (read_space) {
+ uint32_t size = 0;
+ jack_ringbuffer_read(worker->responses, (char*)&size, sizeof(size));
+
+ jack_ringbuffer_read(
+ worker->responses, (char*)worker->response, size);
+
+ worker->iface->work_response(
+ jalv->instance->lv2_handle, size, worker->response);
+
+ read_space -= sizeof(size) + size;
+ }
+ }
+}
diff --git a/src/worker.h b/src/worker.h
new file mode 100644
index 0000000..eecd7ec
--- /dev/null
+++ b/src/worker.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 2007-2013 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
+
+#include "LV2-render_internal.h"
+
+void
+jalv_worker_init(Jalv* jalv,
+ JalvWorker* worker,
+ const LV2_Worker_Interface* iface);
+
+void
+jalv_worker_finish(JalvWorker* worker);
+
+LV2_Worker_Status
+jalv_worker_schedule(LV2_Worker_Schedule_Handle handle,
+ uint32_t size,
+ const void* data);
+
+void
+jalv_worker_emit_responses(Jalv* jalv, JalvWorker* worker);
diff --git a/src/zix/common.h b/src/zix/common.h
new file mode 100644
index 0000000..59e1f55
--- /dev/null
+++ b/src/zix/common.h
@@ -0,0 +1,83 @@
+/*
+ Copyright 2011 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef ZIX_COMMON_H
+#define ZIX_COMMON_H
+
+/**
+ @addtogroup zix
+ @{
+*/
+
+/** @cond */
+#ifdef ZIX_SHARED
+# ifdef _WIN32
+# define ZIX_LIB_IMPORT __declspec(dllimport)
+# define ZIX_LIB_EXPORT __declspec(dllexport)
+# else
+# define ZIX_LIB_IMPORT __attribute__((visibility("default")))
+# define ZIX_LIB_EXPORT __attribute__((visibility("default")))
+# endif
+# ifdef ZIX_INTERNAL
+# define ZIX_API ZIX_LIB_EXPORT
+# else
+# define ZIX_API ZIX_LIB_IMPORT
+# endif
+#else
+# define ZIX_API
+#endif
+/** @endcond */
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+typedef enum {
+ ZIX_STATUS_SUCCESS,
+ ZIX_STATUS_ERROR,
+ ZIX_STATUS_NO_MEM,
+ ZIX_STATUS_NOT_FOUND,
+ ZIX_STATUS_EXISTS,
+ ZIX_STATUS_BAD_ARG,
+ ZIX_STATUS_BAD_PERMS,
+} ZixStatus;
+
+/**
+ Function for comparing two elements.
+*/
+typedef int (*ZixComparator)(const void* a, const void* b, void* user_data);
+
+/**
+ Function for testing equality of two elements.
+*/
+typedef bool (*ZixEqualFunc)(const void* a, const void* b);
+
+/**
+ Function to destroy an element.
+*/
+typedef void (*ZixDestroyFunc)(void* ptr);
+
+/**
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_COMMON_H */
diff --git a/src/zix/sem.h b/src/zix/sem.h
new file mode 100644
index 0000000..6a6dc8e
--- /dev/null
+++ b/src/zix/sem.h
@@ -0,0 +1,227 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef ZIX_SEM_H
+#define ZIX_SEM_H
+
+#ifdef __APPLE__
+# include <mach/mach.h>
+#elif defined(_WIN32)
+# include <limits.h>
+# include <windows.h>
+#else
+# include <semaphore.h>
+#endif
+
+#include "zix/common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ @addtogroup zix
+ @{
+ @name Semaphore
+ @{
+*/
+
+/**
+ A counting semaphore.
+
+ This is an integer that is always positive, and has two main operations:
+ increment (post) and decrement (wait). If a decrement can not be performed
+ (i.e. the value is 0) the caller will be blocked until another thread posts
+ and the operation can succeed.
+
+ Semaphores can be created with any starting value, but typically this will
+ be 0 so the semaphore can be used as a simple signal where each post
+ corresponds to one wait.
+
+ Semaphores are very efficient (much moreso than a mutex/cond pair). In
+ particular, at least on Linux, post is async-signal-safe, which means it
+ does not block and will not be interrupted. If you need to signal from
+ a realtime thread, this is the most appropriate primitive to use.
+*/
+typedef struct ZixSemImpl ZixSem;
+
+/**
+ Create and initialize `sem` to `initial`.
+*/
+static inline ZixStatus
+zix_sem_init(ZixSem* sem, unsigned initial);
+
+/**
+ Destroy `sem`.
+*/
+static inline void
+zix_sem_destroy(ZixSem* sem);
+
+/**
+ Increment (and signal any waiters).
+ Realtime safe.
+*/
+static inline void
+zix_sem_post(ZixSem* sem);
+
+/**
+ Wait until count is > 0, then decrement.
+ Obviously not realtime safe.
+*/
+static inline void
+zix_sem_wait(ZixSem* sem);
+
+/**
+ Non-blocking version of wait().
+
+ @return true if decrement was successful (lock was acquired).
+*/
+static inline bool
+zix_sem_try_wait(ZixSem* sem);
+
+/**
+ @cond
+*/
+
+#ifdef __APPLE__
+
+struct ZixSemImpl {
+ semaphore_t sem;
+};
+
+static inline ZixStatus
+zix_sem_init(ZixSem* sem, unsigned initial)
+{
+ return semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0)
+ ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS;
+}
+
+static inline void
+zix_sem_destroy(ZixSem* sem)
+{
+ semaphore_destroy(mach_task_self(), sem->sem);
+}
+
+static inline void
+zix_sem_post(ZixSem* sem)
+{
+ semaphore_signal(sem->sem);
+}
+
+static inline void
+zix_sem_wait(ZixSem* sem)
+{
+ semaphore_wait(sem->sem);
+}
+
+static inline bool
+zix_sem_try_wait(ZixSem* sem)
+{
+ const mach_timespec_t zero = { 0, 0 };
+ return semaphore_timedwait(sem->sem, zero) == KERN_SUCCESS;
+}
+
+#elif defined(_WIN32)
+
+struct ZixSemImpl {
+ HANDLE sem;
+};
+
+static inline ZixStatus
+zix_sem_init(ZixSem* sem, unsigned initial)
+{
+ sem->sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL);
+ return (sem->sem) ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS;
+}
+
+static inline void
+zix_sem_destroy(ZixSem* sem)
+{
+ CloseHandle(sem->sem);
+}
+
+static inline void
+zix_sem_post(ZixSem* sem)
+{
+ ReleaseSemaphore(sem->sem, 1, NULL);
+}
+
+static inline void
+zix_sem_wait(ZixSem* sem)
+{
+ WaitForSingleObject(sem->sem, INFINITE);
+}
+
+static inline bool
+zix_sem_try_wait(ZixSem* sem)
+{
+ WaitForSingleObject(sem->sem, 0);
+}
+
+#else /* !defined(__APPLE__) && !defined(_WIN32) */
+
+struct ZixSemImpl {
+ sem_t sem;
+};
+
+static inline ZixStatus
+zix_sem_init(ZixSem* sem, unsigned initial)
+{
+ return sem_init(&sem->sem, 0, initial)
+ ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS;
+}
+
+static inline void
+zix_sem_destroy(ZixSem* sem)
+{
+ sem_destroy(&sem->sem);
+}
+
+static inline void
+zix_sem_post(ZixSem* sem)
+{
+ sem_post(&sem->sem);
+}
+
+static inline void
+zix_sem_wait(ZixSem* sem)
+{
+ /* Note that sem_wait always returns 0 in practice, except in
+ gdb (at least), where it returns nonzero, so the while is
+ necessary (and is the correct/safe solution in any case).
+ */
+ while (sem_wait(&sem->sem) != 0) {}
+}
+
+static inline bool
+zix_sem_try_wait(ZixSem* sem)
+{
+ return (sem_trywait(&sem->sem) == 0);
+}
+
+#endif
+
+/**
+ @endcond
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_SEM_H */
diff --git a/src/zix/thread.h b/src/zix/thread.h
new file mode 100644
index 0000000..b007efa
--- /dev/null
+++ b/src/zix/thread.h
@@ -0,0 +1,133 @@
+/*
+ Copyright 2012-2014 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef ZIX_THREAD_H
+#define ZIX_THREAD_H
+
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <errno.h>
+# include <pthread.h>
+#endif
+
+#include "zix/common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#else
+# include <stdbool.h>
+#endif
+
+/**
+ @addtogroup zix
+ @{
+ @name Thread
+ @{
+*/
+
+#ifdef _WIN32
+typedef HANDLE ZixThread;
+#else
+typedef pthread_t ZixThread;
+#endif
+
+/**
+ Initialize `thread` to a new thread.
+
+ The thread will immediately be launched, calling `function` with `arg`
+ as the only parameter.
+*/
+static inline ZixStatus
+zix_thread_create(ZixThread* thread,
+ size_t stack_size,
+ void* (*function)(void*),
+ void* arg);
+
+/**
+ Join `thread` (block until `thread` exits).
+*/
+static inline ZixStatus
+zix_thread_join(ZixThread thread, void** retval);
+
+#ifdef _WIN32
+
+static inline ZixStatus
+zix_thread_create(ZixThread* thread,
+ size_t stack_size,
+ void* (*function)(void*),
+ void* arg)
+{
+ *thread = CreateThread(NULL, stack_size,
+ (LPTHREAD_START_ROUTINE)function, arg,
+ 0, NULL);
+ return *thread ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
+}
+
+static inline ZixStatus
+zix_thread_join(ZixThread thread, void** retval)
+{
+ return WaitForSingleObject(thread, INFINITE)
+ ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR;
+}
+
+#else /* !defined(_WIN32) */
+
+static inline ZixStatus
+zix_thread_create(ZixThread* thread,
+ size_t stack_size,
+ void* (*function)(void*),
+ void* arg)
+{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, stack_size);
+
+ const int ret = pthread_create(thread, NULL, function, arg);
+ pthread_attr_destroy(&attr);
+
+ if (ret == EAGAIN) {
+ return ZIX_STATUS_NO_MEM;
+ } else if (ret == EINVAL) {
+ return ZIX_STATUS_BAD_ARG;
+ } else if (ret == EPERM) {
+ return ZIX_STATUS_BAD_PERMS;
+ } else if (ret) {
+ return ZIX_STATUS_ERROR;
+ }
+
+ return ZIX_STATUS_SUCCESS;
+}
+
+static inline ZixStatus
+zix_thread_join(ZixThread thread, void** retval)
+{
+ return pthread_join(thread, retval)
+ ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS;
+}
+
+#endif
+
+/**
+ @}
+ @}
+*/
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ZIX_THREAD_H */