[Demudi-commits] r659 - / dssi dssi/branches dssi/branches/upstream dssi/branches/upstream/current dssi/branches/upstream/current/examples dssi/branches/upstream/current/jack-dssi-host

Free Ekanayaka free-guest at costa.debian.org
Mon Feb 6 23:54:29 UTC 2006


Author: free-guest
Date: 2006-02-06 23:54:20 +0000 (Mon, 06 Feb 2006)
New Revision: 659

Added:
   dssi/
   dssi/branches/
   dssi/branches/upstream/
   dssi/branches/upstream/current/
   dssi/branches/upstream/current/Makefile
   dssi/branches/upstream/current/dssi.pc
   dssi/branches/upstream/current/examples/
   dssi/branches/upstream/current/examples/Makefile
   dssi/branches/upstream/current/jack-dssi-host/
   dssi/branches/upstream/current/jack-dssi-host/Makefile
   dssi/branches/upstream/current/jack-dssi-host/jack-dssi-host.c
   dssi/tags/
Log:
[svn-inject] Installing original source of dssi

Added: dssi/branches/upstream/current/Makefile
===================================================================
--- dssi/branches/upstream/current/Makefile	2006-02-06 21:46:24 UTC (rev 658)
+++ dssi/branches/upstream/current/Makefile	2006-02-06 23:54:20 UTC (rev 659)
@@ -0,0 +1,38 @@
+
+PREFIX ?= /usr/local
+PKGCONFIG_INSTALL_DIR = $(PREFIX)/lib/pkgconfig
+
+FLUID ?= $(PWD)/../fluidsynth-1.0.3
+
+export PREFIX FLUID
+
+all:	doc/RFC.html doc/why-use.html
+	@$(MAKE) -C jack-dssi-host
+	@$(MAKE) -C examples
+	@$(MAKE) -C tests
+	@test -d $(FLUID)/src && $(MAKE) -C fluidsynth-dssi || echo "WARNING: Fluidsynth sources not found in $(FLUID), not building fluidsynth-dssi"
+
+clean:
+	@$(MAKE) -C jack-dssi-host clean
+	@$(MAKE) -C examples clean
+	@$(MAKE) -C tests clean
+	@$(MAKE) -C fluidsynth-dssi clean
+
+distclean:
+	@$(MAKE) -C jack-dssi-host distclean
+	@$(MAKE) -C examples distclean
+	@$(MAKE) -C tests distclean
+	@$(MAKE) -C fluidsynth-dssi distclean
+	rm -f *~ doc/*~ dssi/*~
+
+install: all
+	mkdir -p $(PREFIX)/include
+	mkdir -p $(PKGCONFIG_INSTALL_DIR)
+	cp dssi/dssi.h $(PREFIX)/include/
+	sed s:.PREFIX.:$(PREFIX): dssi.pc >$(PKGCONFIG_INSTALL_DIR)/dssi.pc
+	@$(MAKE) -C jack-dssi-host install
+	@$(MAKE) -C examples install
+	@test -d $(FLUID)/src && $(MAKE) -C fluidsynth-dssi install || echo "Not installing fluidsynth-dssi"
+
+doc/%.html:	doc/%.txt
+	perl ./scripts/txt2html.pl $< | perl ./scripts/tableofcontents.pl > $@

Added: dssi/branches/upstream/current/dssi.pc
===================================================================
--- dssi/branches/upstream/current/dssi.pc	2006-02-06 21:46:24 UTC (rev 658)
+++ dssi/branches/upstream/current/dssi.pc	2006-02-06 23:54:20 UTC (rev 659)
@@ -0,0 +1,11 @@
+prefix=.PREFIX.
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: dssi
+Version: 0.9
+Description: A plugin API for software instruments
+Libs: 
+Cflags: -I${includedir}
+

Added: dssi/branches/upstream/current/examples/Makefile
===================================================================
--- dssi/branches/upstream/current/examples/Makefile	2006-02-06 21:46:24 UTC (rev 658)
+++ dssi/branches/upstream/current/examples/Makefile	2006-02-06 23:54:20 UTC (rev 659)
@@ -0,0 +1,90 @@
+
+PREFIX ?= /usr/local
+
+LIBLO_CFLAGS ?= $(shell pkg-config liblo --cflags)
+LIBLO_LIBS ?= $(shell pkg-config liblo --libs || echo -llo -lpthread)
+
+QTDIR ?= /usr/lib/qt3
+QTINCDIR ?= $(QTDIR)/include
+QTLIBDIR ?= $(QTDIR)/lib
+MOC ?= $(QTDIR)/bin/moc
+
+TARGETS = \
+	trivial_synth.so \
+	less_trivial_synth.so \
+	trivial_sampler.so \
+	dssi_osc_send \
+	dssi_osc_update \
+	less_trivial_synth_qt_gui \
+	less_trivial_synth/LTS_qt \
+	trivial_sampler_qt_gui \
+	trivial_sampler/trivial_sampler_qt
+
+all: $(TARGETS)
+
+install: all
+	mkdir -p $(PREFIX)/bin
+	cp dssi_osc_update dssi_osc_send $(PREFIX)/bin/
+	mkdir -p $(PREFIX)/lib/dssi
+	cp trivial_synth.so $(PREFIX)/lib/dssi/
+	cp trivial_sampler.so $(PREFIX)/lib/dssi/
+	mkdir -p $(PREFIX)/lib/dssi/less_trivial_synth
+	cp less_trivial_synth.so $(PREFIX)/lib/dssi/
+	cp less_trivial_synth_qt_gui $(PREFIX)/lib/dssi/less_trivial_synth/LTS_qt
+	mkdir -p $(PREFIX)/lib/dssi/trivial_sampler
+	cp trivial_sampler.so $(PREFIX)/lib/dssi/
+	cp trivial_sampler_qt_gui $(PREFIX)/lib/dssi/trivial_sampler/trivial_sampler_qt
+	test -x $(PREFIX)/bin/jack-dssi-host && ln -sf jack-dssi-host $(PREFIX)/bin/trivial_sampler
+
+
+CFLAGS = -Wall -O2 -I../dssi $(LIBLO_CFLAGS)
+CXXFLAGS = $(CFLAGS) -I$(QTINCDIR)
+LDLIBS = $(LIBLO_LIBS)
+
+QTGUI_LDLIBS = -L$(QTDIR)/lib -lqt-mt $(LIBLO_LIBS)
+
+MB_O = ../message_buffer/message_buffer.o
+
+dssi_osc_send:	dssi_osc_send.o
+
+dssi_osc_update:	dssi_osc_update.o
+
+less_trivial_synth_qt_gui.o:	less_trivial_synth_qt_gui.h
+
+less_trivial_synth_qt_gui.moc.cpp:	less_trivial_synth_qt_gui.h
+	$(MOC) $< > $@
+
+less_trivial_synth_qt_gui: less_trivial_synth_qt_gui.o less_trivial_synth_qt_gui.moc.o
+	$(CXX) -o $@ $^ $(QTGUI_LDLIBS)
+
+less_trivial_synth/LTS_qt: less_trivial_synth_qt_gui
+	mkdir -p less_trivial_synth
+	cp less_trivial_synth_qt_gui less_trivial_synth/LTS_qt
+
+trivial_sampler_qt_gui.o:	trivial_sampler_qt_gui.h trivial_sampler.h
+
+trivial_sampler_qt_gui.moc.cpp:	trivial_sampler_qt_gui.h trivial_sampler.h
+	$(MOC) $< > $@
+
+trivial_sampler_qt_gui: trivial_sampler_qt_gui.o trivial_sampler_qt_gui.moc.o
+	$(CXX) -o $@ $^ -lsndfile $(QTGUI_LDLIBS)
+
+trivial_sampler/trivial_sampler_qt: trivial_sampler_qt_gui
+	mkdir -p trivial_sampler
+	cp trivial_sampler_qt_gui trivial_sampler/trivial_sampler_qt
+
+%.so: %.o ../dssi/dssi.h $(MB_O)
+	gcc -nostartfiles -shared -lc -lm -o $*.so $*.o $(MB_O)
+
+trivial_sampler.o:	../dssi/dssi.h trivial_sampler.h
+
+trivial_sampler.so: trivial_sampler.o ../dssi/dssi.h
+	gcc -nostartfiles -shared -lsndfile -lsamplerate -lc -lm -o trivial_sampler.so trivial_sampler.o
+
+clean:
+	rm -f *.o *.moc.*
+
+distclean:	clean
+	rm -f *~ $(TARGETS)
+	rm -rf less_trivial_synth
+	rm -rf trivial_sampler

Added: dssi/branches/upstream/current/jack-dssi-host/Makefile
===================================================================
--- dssi/branches/upstream/current/jack-dssi-host/Makefile	2006-02-06 21:46:24 UTC (rev 658)
+++ dssi/branches/upstream/current/jack-dssi-host/Makefile	2006-02-06 23:54:20 UTC (rev 659)
@@ -0,0 +1,33 @@
+
+PREFIX ?= /usr/local
+
+TARGETS = \
+	jack-dssi-host
+
+all: $(TARGETS)
+
+install: all
+	mkdir -p $(PREFIX)/bin
+	cp jack-dssi-host $(PREFIX)/bin/
+
+CFLAGS = -Wall -g3 -I../dssi $(shell pkg-config liblo --cflags)
+CXXFLAGS = $(CFLAGS)
+
+LDLIBS = -ljack -lasound -lpthread $(shell pkg-config liblo --libs)
+
+MB_O = ../message_buffer/message_buffer.o
+
+jack-dssi-host:	jack-dssi-host.o $(MB_O)
+
+jack-dssi-host.o:	../dssi/dssi.h jack-dssi-host.h
+
+%.so: %.o ../dssi/dssi.h $(MB_O)
+	gcc -nostartfiles -shared -lc -lm -o $*.so $*.o ../message_buffer/message_buffer.o
+
+clean:
+	rm -f *.o *.moc.*
+
+distclean:	clean
+	rm -f *~ $(TARGETS)
+
+

Added: dssi/branches/upstream/current/jack-dssi-host/jack-dssi-host.c
===================================================================
--- dssi/branches/upstream/current/jack-dssi-host/jack-dssi-host.c	2006-02-06 21:46:24 UTC (rev 658)
+++ dssi/branches/upstream/current/jack-dssi-host/jack-dssi-host.c	2006-02-06 23:54:20 UTC (rev 659)
@@ -0,0 +1,1786 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/* jack-dssi-host.c
+ *
+ * Disposable Soft Synth Interface
+ *
+ * This is a host for DSSI plugins.  It listens for MIDI events on an
+ * ALSA sequencer port, delivers them to DSSI synths and outputs the
+ * result via JACK.  It does not currently support audio input (e.g.
+ * for DSSI effects plugins).
+ *
+ * This program expects the names of up to 16 DSSI synth plugins, in
+ * the form '<dll-name>:<label>',* to be provided on the command line.
+ * If just '<dll-name>' is provided, the first plugin in the DLL is
+ * is used.  MIDI channels are assigned to each plugin instance, in
+ * order, beginning with channel 0 (zero-based).  A plugin may be
+ * easily instantiated multiple times by preceding its name and label
+ * with a dash followed immediately by the desired number of instances,
+ * e.g. '-3 my_plugins.so:zoomy' would create three instances of the
+ * 'zoomy' plugin.
+ */
+
+/*
+ * Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton.
+ * 
+ * Permission to use, copy, modify, distribute, and sell this software
+ * for any purpose is hereby granted without fee, provided that the
+ * above copyright notice and this permission notice are included in
+ * all copies or substantial portions of the software.
+ */
+
+#include <ladspa.h>
+#include "dssi.h"
+#include <alsa/asoundlib.h>
+#include <alsa/seq.h>
+#include <jack/jack.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <dirent.h>
+#include <time.h>
+#include <libgen.h>
+
+#include <lo/lo.h>
+
+#include "jack-dssi-host.h"
+
+#include "../message_buffer/message_buffer.h"
+
+static snd_seq_t *alsaClient;
+
+static jack_client_t *jackClient;
+static jack_port_t **inputPorts, **outputPorts;
+
+static d3h_dll_t     *dlls;
+
+static d3h_plugin_t  *plugins;
+static int            plugin_count = 0;
+
+static float sample_rate;
+
+static d3h_instance_t instances[D3H_MAX_INSTANCES];
+static int            instance_count = 0;
+
+static LADSPA_Handle    *instanceHandles;
+static snd_seq_event_t **instanceEventBuffers;
+static unsigned long    *instanceEventCounts;
+
+static int insTotal, outsTotal;
+static float **pluginInputBuffers, **pluginOutputBuffers;
+
+static int controlInsTotal, controlOutsTotal;
+static float *pluginControlIns, *pluginControlOuts;
+static d3h_instance_t *channel2instance[D3H_MAX_CHANNELS]; /* maps MIDI channel to instance */
+static d3h_instance_t **pluginControlInInstances;          /* maps global control in # to instance */
+static unsigned long *pluginControlInPortNumbers;          /* maps global control in # to instance LADSPA port # */
+static int *pluginPortUpdated;                             /* indexed by global control in # */
+
+static char osc_path_tmp[1024];
+
+static char *projectDirectory;
+
+lo_server_thread serverThread;
+
+static sigset_t _signals;
+
+int exiting = 0;
+static int verbose = 0;
+const char *myName = NULL;
+
+#define EVENT_BUFFER_SIZE 1024
+static snd_seq_event_t midiEventBuffer[EVENT_BUFFER_SIZE]; /* ring buffer */
+static int midiEventReadIndex = 0, midiEventWriteIndex = 0;
+
+static pthread_mutex_t midiEventBufferMutex = PTHREAD_MUTEX_INITIALIZER;
+
+LADSPA_Data get_port_default(const LADSPA_Descriptor *plugin, int port);
+
+void osc_error(int num, const char *m, const char *path);
+
+int osc_message_handler(const char *path, const char *types, lo_arg **argv, int
+		      argc, void *data, void *user_data) ;
+int osc_debug_handler(const char *path, const char *types, lo_arg **argv, int
+		      argc, void *data, void *user_data) ;
+
+void
+signalHandler(int sig)
+{
+    fprintf(stderr, "%s: signal caught, trying to clean up and exit\n", myName);
+    exiting = 1;
+}
+
+void
+midi_callback()
+{
+    snd_seq_event_t *ev = 0;
+    struct timeval tv;
+
+    pthread_mutex_lock(&midiEventBufferMutex);
+
+    do {
+	if (snd_seq_event_input(alsaClient, &ev) > 0) {
+
+	    if (midiEventReadIndex == midiEventWriteIndex + 1) {
+		fprintf(stderr, "%s: Warning: MIDI event buffer overflow! ignoring incoming event\n", myName);
+		continue;
+	    }
+
+	    midiEventBuffer[midiEventWriteIndex] = *ev;
+
+	    ev = &midiEventBuffer[midiEventWriteIndex];
+
+	    /* At this point we change the event timestamp so that its
+	       real-time field contains the actual time at which it was
+	       received and processed (i.e. now).  Then in the audio
+	       callback we use that to calculate frame offset. */
+
+	    gettimeofday(&tv, NULL);
+	    ev->time.time.tv_sec = tv.tv_sec;
+	    ev->time.time.tv_nsec = tv.tv_usec * 1000L;
+
+	    if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) {
+		ev->type =  SND_SEQ_EVENT_NOTEOFF;
+	    }
+
+	    /* We don't need to handle EVENT_NOTE here, because ALSA
+	       won't ever deliver them on the sequencer queue -- it
+	       unbundles them into NOTE_ON and NOTE_OFF when they're
+	       dispatched.  We would only need worry about them when
+	       retrieving MIDI events from some other source. */
+
+	    midiEventWriteIndex = (midiEventWriteIndex + 1) % EVENT_BUFFER_SIZE;
+	}
+	
+    } while (snd_seq_event_input_pending(alsaClient, 0) > 0);
+
+    pthread_mutex_unlock(&midiEventBufferMutex);
+}
+
+void
+setControl(d3h_instance_t *instance, long controlIn, snd_seq_event_t *event)
+{
+    long port = pluginControlInPortNumbers[controlIn];
+
+    const LADSPA_Descriptor *p = instance->plugin->descriptor->LADSPA_Plugin;
+
+    LADSPA_PortRangeHintDescriptor d = p->PortRangeHints[port].HintDescriptor;
+
+    LADSPA_Data lb = p->PortRangeHints[port].LowerBound *
+	(LADSPA_IS_HINT_SAMPLE_RATE(p->PortRangeHints[port].HintDescriptor) ?
+	 sample_rate : 1.0f);
+
+    LADSPA_Data ub = p->PortRangeHints[port].UpperBound *
+	(LADSPA_IS_HINT_SAMPLE_RATE(p->PortRangeHints[port].HintDescriptor) ?
+	 sample_rate : 1.0f);
+
+    float value = (float)event->data.control.value;
+
+    if (!LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* unbounded: might as well leave the value alone. */
+	} else {
+	    /* bounded above only. just shift the range. */
+	    value = ub - 127.0f + value;
+	}
+    } else {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* bounded below only. just shift the range. */
+	    value = lb + value;
+	} else {
+	    /* bounded both ends.  more interesting. */
+	    if (LADSPA_IS_HINT_LOGARITHMIC(d)) {
+		const float llb = logf(lb);
+		const float lub = logf(ub);
+
+		value = expf(llb + ((lub - llb) * value / 127.0f));
+	    } else {
+		value = lb + ((ub - lb) * value / 127.0f);
+	    }
+	}
+    }
+
+    if (verbose) {
+	printf("%s: %s MIDI controller %d=%d -> control in %ld=%f\n", myName,
+	       instance->friendly_name, event->data.control.param,
+	       event->data.control.value, controlIn, value);
+    }
+
+    pluginControlIns[controlIn] = value;
+    pluginPortUpdated[controlIn] = 1;
+}
+
+int
+audio_callback(jack_nframes_t nframes, void *arg)
+{
+    int i;
+    int outCount;
+    d3h_instance_t *instance;
+    struct timeval tv, evtv, diff;
+    long framediff;
+
+    gettimeofday(&tv, NULL);
+
+    /* Not especially pretty or efficient */
+
+    for (i = 0; i < instance_count; i++) {
+        instanceEventCounts[i] = 0;
+    }
+
+    for ( ; midiEventReadIndex != midiEventWriteIndex;
+         midiEventReadIndex = (midiEventReadIndex + 1) % EVENT_BUFFER_SIZE) {
+
+	snd_seq_event_t *ev = &midiEventBuffer[midiEventReadIndex];
+
+        if (!snd_seq_ev_is_channel_type(ev)) {
+            /* discard non-channel oriented messages */
+            continue;
+        }
+
+        instance = channel2instance[ev->data.note.channel];
+        if (!instance
+	    /* || instance->inactive */) /* no -- see comment in osc_exiting_handler */
+	{
+            /* discard messages intended for channels we aren't using or
+	       absent or exited plugins */
+            continue;
+        }
+        i = instance->number;
+
+        /* Stop processing incoming MIDI if an instance's event buffer is
+         * full. */
+	if (instanceEventCounts[i] == EVENT_BUFFER_SIZE)
+            break;
+
+	/* Each event has a real-time timestamp indicating when it was
+	 * received (set by midi_callback).  We need to calculate the
+	 * difference between then and the start of the audio callback
+	 * (held in tv), and use that to assign a frame offset, to
+	 * avoid jitter.  We should stop processing when we reach any
+	 * event received after the start of the audio callback. */
+
+	evtv.tv_sec = ev->time.time.tv_sec;
+	evtv.tv_usec = ev->time.time.tv_nsec / 1000;
+
+	if (evtv.tv_sec > tv.tv_sec ||
+	    (evtv.tv_sec == tv.tv_sec &&
+	     evtv.tv_usec > tv.tv_usec)) {
+	    break;
+	}
+
+	diff.tv_sec = tv.tv_sec - evtv.tv_sec;
+	if (tv.tv_usec < evtv.tv_usec) {
+	    --diff.tv_sec;
+	    diff.tv_usec = tv.tv_usec + 1000000 - evtv.tv_usec;
+	} else {
+	    diff.tv_usec = tv.tv_usec - evtv.tv_usec;
+	}
+
+	framediff =
+	    diff.tv_sec * sample_rate +
+	    ((diff.tv_usec / 1000) * sample_rate) / 1000 +
+	    ((diff.tv_usec - 1000 * (diff.tv_usec / 1000)) * sample_rate) / 1000000;
+
+	if (framediff >= nframes) framediff = nframes - 1;
+	else if (framediff < 0) framediff = 0;
+
+	ev->time.tick = nframes - framediff - 1;
+
+	if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
+	    
+	    int controller = ev->data.control.param;
+#ifdef DEBUG
+	    MB_MESSAGE("%s CC %d(0x%02x) = %d\n", instance->friendly_name,
+                       controller, controller, ev->data.control.value);
+#endif
+
+	    if (controller == 0) { // bank select MSB
+
+		instance->pendingBankMSB = ev->data.control.value;
+
+	    } else if (controller == 32) { // bank select LSB
+
+		instance->pendingBankLSB = ev->data.control.value;
+
+	    } else if (controller > 0 && controller < MIDI_CONTROLLER_COUNT) {
+
+		long controlIn = instance->controllerMap[controller];
+		if (controlIn >= 0) {
+
+                    /* controller is mapped to LADSPA port, update the port */
+		    setControl(instance, controlIn, ev);
+
+		} else {
+
+                    /* controller is not mapped, so pass the event through to plugin */
+                    instanceEventBuffers[i][instanceEventCounts[i]] = *ev;
+                    instanceEventCounts[i]++;
+                }
+	    }
+
+	} else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+	    
+	    instance->pendingProgramChange = ev->data.control.value;
+	    instance->uiNeedsProgramUpdate = 1;
+
+	} else {
+
+            instanceEventBuffers[i][instanceEventCounts[i]] = *ev;
+            instanceEventCounts[i]++;
+	}
+    }
+
+    /* process pending program changes */
+    for (i = 0; i < instance_count; i++) {
+        instance = &instances[i];
+
+	/* no -- see comment in osc_exiting_handler */
+	/* if (instance->inactive) continue; */
+
+        if (instance->pendingProgramChange >= 0) {
+
+            int pc = instance->pendingProgramChange;
+            int msb = instance->pendingBankMSB;
+            int lsb = instance->pendingBankLSB;
+
+            //!!! gosh, I don't know this -- need to check with the specs:
+            // if you only send one of MSB/LSB controllers, should the
+            // other go to zero or remain as it was?  Assume it remains as
+            // it was, for now.
+
+            if (lsb >= 0) {
+                if (msb >= 0) {
+                    instance->currentBank = lsb + 128 * msb;
+                } else {
+                    instance->currentBank = lsb + 128 * (instance->currentBank / 128);
+                }
+            } else if (msb >= 0) {
+                instance->currentBank = (instance->currentBank % 128) + 128 * msb;
+            }
+
+            instance->currentProgram = pc;
+
+            instance->pendingProgramChange = -1;
+            instance->pendingBankMSB = -1;
+            instance->pendingBankLSB = -1;
+
+            if (instance->plugin->descriptor->select_program) {
+                instance->plugin->descriptor->
+                    select_program(instanceHandles[instance->number],
+                                   instance->currentBank,
+                                   instance->currentProgram);
+            }
+        }
+    }
+
+    /* call run_synth() or run_multiple_synths() for all instances */
+
+    i = 0;
+    outCount = 0;
+
+    while (i < instance_count) {
+
+	/* no -- see comment in osc_exiting_handler */
+/*
+	if (instances[i].inactive) {
+	    int j;
+	    for (j = 0; j < instances[i].plugin->outs; ++j) {
+		memset(pluginOutputBuffers[outCount + j], 0, nframes * sizeof(LADSPA_Data));
+	    }
+	    outCount += j;
+	    ++i;
+	    continue;
+	}
+*/
+	outCount += instances[i].plugin->outs;
+
+        if (instances[i].plugin->descriptor->run_multiple_synths) {
+            instances[i].plugin->descriptor->run_multiple_synths
+                (instances[i].plugin->instances,
+                 instanceHandles + i,
+                 nframes,
+                 instanceEventBuffers + i,
+                 instanceEventCounts + i);
+            i += instances[i].plugin->instances;
+        } else {
+            instances[i].plugin->descriptor->run_synth(instanceHandles[i],
+                                                       nframes,
+                                                       instanceEventBuffers[i],
+                                                       instanceEventCounts[i]);
+            i++;
+        }
+    }
+
+    assert(sizeof(LADSPA_Data) == sizeof(jack_default_audio_sample_t));
+
+    for (outCount = 0; outCount < outsTotal; ++outCount) {
+
+	jack_default_audio_sample_t *buffer =
+	    jack_port_get_buffer(outputPorts[outCount], nframes);
+	
+	memcpy(buffer, pluginOutputBuffers[outCount], nframes * sizeof(LADSPA_Data));
+    }
+
+    return 0;
+}
+
+char *
+load(const char *dllName, void **dll, int quiet) /* returns directory where dll found */
+{
+    static char *defaultDssiPath = 0;
+    const char *dssiPath = getenv("DSSI_PATH");
+    char *path, *element, *message;
+    void *handle = 0;
+
+    /* If the dllName is an absolute path */
+    if (*dllName == '/') {
+	if ((handle = dlopen(dllName, RTLD_NOW))) {  /* real-time programs should not use RTLD_LAZY */
+	    *dll = handle;
+            path = strdup(dllName);
+	    return dirname(path);
+	} else {
+	    if (!quiet) {
+		fprintf(stderr, "Cannot find DSSI plugin at '%s'\n", dllName);
+	    }
+	    return NULL;
+	}
+    }
+
+    if (!dssiPath) {
+	if (!defaultDssiPath) {
+	    const char *home = getenv("HOME");
+	    if (home) {
+		defaultDssiPath = malloc(strlen(home) + 60);
+		sprintf(defaultDssiPath, "/usr/local/lib/dssi:/usr/lib/dssi:%s/.dssi", home);
+	    } else {
+		defaultDssiPath = strdup("/usr/local/lib/dssi:/usr/lib/dssi");
+	    }
+	}
+	dssiPath = defaultDssiPath;
+	if (!quiet) {
+	    fprintf(stderr, "\n%s: Warning: DSSI path not set\n%s: Defaulting to \"%s\"\n\n", myName, myName, dssiPath);
+	}
+    }
+
+    path = strdup(dssiPath);
+    *dll = 0;
+
+    while ((element = strtok(path, ":")) != 0) {
+
+	path = 0;
+
+	if (element[0] != '/') {
+	    if (!quiet) {
+		fprintf(stderr, "%s: Ignoring relative element \"%s\" in path\n", myName, element);
+	    }
+	    continue;
+	}
+
+	if (!quiet && verbose) {
+	    fprintf(stderr, "%s: Looking for library \"%s\" in %s... ", myName, dllName, element);
+	}
+
+	char *filePath = (char *)malloc(strlen(element) + strlen(dllName) + 2);
+	sprintf(filePath, "%s/%s", element, dllName);
+
+	if ((handle = dlopen(filePath, RTLD_NOW))) {  /* real-time programs should not use RTLD_LAZY */
+	    if (!quiet && verbose) {
+		fprintf(stderr, "found\n");
+	    }
+	    *dll = handle;
+            free(filePath);
+            path = strdup(element);
+	    return path;
+	}
+
+	if (!quiet && verbose) {
+	    message = dlerror();
+	    if (message) {
+		fprintf(stderr, "not found: %s\n", message);
+	    } else {
+		fprintf(stderr, "not found\n");
+	    }
+	}
+
+        free(filePath);
+    }
+
+    return 0;
+}
+
+static int
+instance_sort_cmp(const void *a, const void *b)
+{
+    d3h_instance_t *ia = (d3h_instance_t *)a;
+    d3h_instance_t *ib = (d3h_instance_t *)b;
+
+    if (ia->plugin->number != ib->plugin->number) {
+        return ia->plugin->number - ib->plugin->number;
+    } else {
+        return ia->channel - ib->channel;
+    }
+}
+
+void
+startGUI(const char *directory, const char *dllName, const char *label,
+	 const char *oscUrl, const char *instanceTag)
+{
+    struct dirent *entry;
+    char *dllBase = strdup(dllName);
+    char *subpath;
+    DIR *subdir;
+    char *filename;
+    struct stat buf;
+    int fuzzy;
+
+    if (strlen(dllBase) > 3 &&
+        !strcasecmp(dllBase + strlen(dllBase) - 3, ".so")) {
+	dllBase[strlen(dllBase) - 3] = '\0';
+    }
+
+    if (*dllBase == '/') {
+	subpath = strdup(dllBase);
+    } else {
+	subpath = (char *)malloc(strlen(directory) + strlen(dllBase) + 2);
+	sprintf(subpath, "%s/%s", directory, dllBase);
+    }
+
+    for (fuzzy = 0; fuzzy <= 1; ++fuzzy) {
+
+	if (!(subdir = opendir(subpath))) {
+	    if (verbose) {
+		fprintf(stderr, "%s: can't open plugin GUI directory \"%s\"\n", myName, subpath);
+	    }
+	    free(subpath);
+	    free(dllBase);
+	    return;
+	}
+
+	while ((entry = readdir(subdir))) {
+	    
+	    if (entry->d_name[0] == '.') continue;
+	    if (!strchr(entry->d_name, '_')) continue;
+
+	    if (fuzzy) {
+		if (verbose) {
+		    fprintf(stderr, "checking %s against %s\n", entry->d_name, dllBase);
+		}
+		if (strncmp(entry->d_name, dllBase, strlen(dllBase))) continue;
+	    } else {
+		if (verbose) {
+		    fprintf(stderr, "checking %s against %s\n", entry->d_name, label);
+		}
+		if (strncmp(entry->d_name, label, strlen(label))) continue;
+	    }
+	    
+	    filename = (char *)malloc(strlen(subpath) + strlen(entry->d_name) + 2);
+	    sprintf(filename, "%s/%s", subpath, entry->d_name);
+	    
+	    if (stat(filename, &buf)) {
+		perror("stat failed");
+		free(filename);
+		continue;
+	    }
+	    
+	    if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) &&
+		(buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+
+		if (verbose) {
+		    fprintf(stderr, "%s: trying to execute GUI at \"%s\"\n",
+			    myName, filename);
+		}
+		
+		if (fork() == 0) {
+		    execlp(filename, filename, oscUrl, dllName, label, instanceTag, 0);
+		    perror("exec failed");
+		    exit(1);
+		}
+		
+		free(filename);
+		free(subpath);
+		free(dllBase);
+		return;
+	    }
+	    
+	    free(filename);
+	}
+    }	
+
+    if (verbose) {
+	fprintf(stderr, "%s: no GUI found for plugin \"%s\" in \"%s/\"\n",
+		myName, label, subpath);
+    }
+    free(subpath);
+    free(dllBase);
+}
+
+void
+query_programs(d3h_instance_t *instance)
+{
+    int i;
+
+    /* free old lot */
+    if (instance->pluginPrograms) {
+        for (i = 0; i < instance->pluginProgramCount; i++)
+            free((void *)instance->pluginPrograms[i].Name);
+	free((char *)instance->pluginPrograms);
+	instance->pluginPrograms = NULL;
+	instance->pluginProgramCount = 0;
+    }
+
+    instance->pendingBankLSB = -1;
+    instance->pendingBankMSB = -1;
+    instance->pendingProgramChange = -1;
+
+    if (instance->plugin->descriptor->get_program &&
+        instance->plugin->descriptor->select_program) {
+
+	/* Count the plugins first */
+	for (i = 0; instance->plugin->descriptor->
+                        get_program(instanceHandles[instance->number], i); ++i);
+
+	if (i > 0) {
+	    instance->pluginProgramCount = i;
+	    instance->pluginPrograms = (DSSI_Program_Descriptor *)
+		malloc(i * sizeof(DSSI_Program_Descriptor));
+	    while (i > 0) {
+		const DSSI_Program_Descriptor *descriptor;
+		--i;
+		descriptor = instance->plugin->descriptor->
+		    get_program(instanceHandles[instance->number], i);
+		instance->pluginPrograms[i].Bank = descriptor->Bank;
+		instance->pluginPrograms[i].Program = descriptor->Program;
+		instance->pluginPrograms[i].Name = strdup(descriptor->Name);
+		if (verbose) {
+		    printf("%s: %s program %d is MIDI bank %lu program %lu, named '%s'\n",
+			   myName, instance->friendly_name, i,
+			   instance->pluginPrograms[i].Bank,
+			   instance->pluginPrograms[i].Program,
+			   instance->pluginPrograms[i].Name);
+		}
+	    }
+	}
+    }
+}
+
+int
+main(int argc, char **argv)
+{
+    int portid;
+    int npfd;
+    struct pollfd *pfd;
+
+    d3h_dll_t *dll;
+    d3h_plugin_t *plugin;
+    d3h_instance_t *instance;
+    void *pluginObject;
+    char *dllName;
+    char *label;
+    const char **ports;
+    char *tmp;
+    char *url;
+    int i, reps, j;
+    int in, out, controlIn, controlOut;
+
+    setsid();
+    sigemptyset (&_signals);
+    sigaddset(&_signals, SIGHUP);
+    sigaddset(&_signals, SIGINT);
+    sigaddset(&_signals, SIGQUIT);
+    sigaddset(&_signals, SIGPIPE);
+    sigaddset(&_signals, SIGTERM);
+    sigaddset(&_signals, SIGUSR1);
+    sigaddset(&_signals, SIGUSR2);
+    pthread_sigmask(SIG_BLOCK, &_signals, 0);
+
+    insTotal = outsTotal = controlInsTotal = controlOutsTotal = 0;
+
+    /* Handle run-plugin-from-executable-name special case */
+
+    if (argc == 1) {
+
+	const char *basename = strrchr(argv[0], '/');
+	if (!basename) basename = argv[0];
+	else ++basename;
+
+	if (basename[0] && strcmp(basename, "jack-dssi-host")) {
+	    /* look for basename + .so as plugin */
+	    dllName = malloc(strlen(basename) + 4);
+	    sprintf(dllName, "%s.so", basename);
+	    if (load(dllName, &pluginObject, 1)) {
+		dlclose(pluginObject);
+		argc = 2;
+		myName = strdup(argv[0]);
+		argv = (char **)malloc(2 * sizeof(char *));
+		argv[0] = "jack-dssi-host";
+		argv[1] = dllName;
+	    }
+	}
+    }
+
+    if (!myName) myName = strdup(argv[0]);
+
+    /* Parse args and report usage */
+
+    if (argc < 2) {
+	fprintf(stderr, "\nUsage: %s [-v] [-p <projdir>] [-<i>] <libname>[%c<label>] [...]\n", argv[0], LABEL_SEP);
+	fprintf(stderr, "\n  -v        Verbose mode\n");
+	fprintf(stderr, "  <projdir> Project directory to pass to plugin and UI\n");
+	fprintf(stderr, "  <i>       Number of instances of each plugin to run (max %d total, default 1)\n", D3H_MAX_INSTANCES);
+	fprintf(stderr, "  <libname> DSSI plugin library .so to load (searched for in $DSSI_PATH)\n");
+	fprintf(stderr, "  <label>   Label of plugin to load from library\n");
+	fprintf(stderr, "  [...]     Optionally more instance counts, plugins and labels\n");
+	fprintf(stderr, "\nExample: %s -2 lib1.so -1 lib2.so%cfuzzy\n", argv[0], LABEL_SEP);
+	fprintf(stderr, "  run two instances of the first plugin found in lib1.so, assigned to MIDI\n  channels 0 and 1 and connected to the first available JACK outputs, and one\n  instance of the \"fuzzy\" plugin in lib2.so with MIDI channels 2 and 3 and\n  connected to the next available JACK outputs.\n");
+	fprintf(stderr,"\nAs a special case, if this program is started with a name other than\njack-dssi-host, and if that name (plus .so suffix) can be found in the DSSI path\nas a valid plugin library, and if no further command line arguments are given,\nthen the first plugin in that library will be loaded automatically.\n\n");
+	return 2;
+    }
+   
+    if (verbose) {
+	fprintf(stderr, "%s: Starting...\n", myName);
+    }
+
+    projectDirectory = NULL;
+
+    reps = 1;
+    for (i = 1; i < argc; i++) {
+
+	if (!strcmp(argv[i], "-v")) {
+	    verbose = 1;
+	    continue;
+	}
+
+	if (!strcmp(argv[i], "-p")) {
+	    if (i < argc - 1) {
+		projectDirectory = argv[++i];
+	    } else {
+		fprintf(stderr, "%s: project directory expected after -p\n", myName);
+		return 2;
+	    }
+	    continue;
+	}
+
+        if (instance_count >= D3H_MAX_INSTANCES) {
+            fprintf(stderr, "%s: too many plugin instances specified (max is %d)\n", myName, D3H_MAX_INSTANCES);
+            return 2;
+        }
+
+        /* parse repetition count */
+        if (argv[i][0] == '-') {
+            reps = atoi(&argv[i][1]);
+            if (reps > 0) {
+                continue;
+            } else {
+                reps = 1;
+            }
+        }
+
+        /* parse dll name, plus a label if supplied */
+        tmp = strchr(argv[i], LABEL_SEP);
+        if (tmp) {
+            dllName = calloc(1, tmp - argv[i] + 1);
+            strncpy(dllName, argv[i], tmp - argv[i]);
+            label = strdup(tmp + 1);
+        } else {
+            dllName = strdup(argv[i]);
+            label = NULL;
+        }
+
+        /* check if we've seen this plugin before */
+        for (plugin = plugins; plugin; plugin = plugin->next) {
+            if (label) {
+                if (!strcmp(dllName, plugin->dll->name) &&
+                    !strcmp(label,   plugin->label))
+                    break;
+            } else {
+               if (!strcmp(dllName, plugin->dll->name) &&
+                   plugin->is_first_in_dll)
+                   break;
+            }
+        }
+
+        if (plugin) {
+            /* have already seen this plugin */
+
+            free(dllName);
+            free(label);
+
+        } else {
+            /* this is a new plugin */
+
+            plugin = (d3h_plugin_t *)calloc(1, sizeof(d3h_plugin_t));
+            plugin->number = plugin_count;
+            plugin->label = label;
+
+            /* check if we've seen this dll before */
+            for (dll = dlls; dll; dll = dll->next) {
+                if (!strcmp(dllName, dll->name))
+                    break;
+            }
+            if (!dll) {
+                /* this is a new dll */
+                dll = (d3h_dll_t *)calloc(1, sizeof(d3h_dll_t));
+                dll->name = dllName;
+                
+                dll->directory = load(dllName, &pluginObject, 0);
+                if (!dll->directory || !pluginObject) {
+                    fprintf(stderr, "\n%s: Error: Failed to load plugin library \"%s\"\n", myName, dllName);
+                    return 1;
+                }
+                
+                dll->descfn = (DSSI_Descriptor_Function)dlsym(pluginObject,
+                                                              "dssi_descriptor");
+                if (!dll->descfn) {
+                    fprintf(stderr, "\n%s: Error: \"%s\" is not a DSSI plugin library\n", myName, dllName);
+                    return 1;
+                } 
+
+                dll->next = dlls;
+                dlls = dll;
+            }
+            plugin->dll = dll;
+
+            /* get the plugin descriptor */
+            j = 0;
+            while ((plugin->descriptor = dll->descfn(j++))) {
+                if (!plugin->label ||
+                    !strcmp(plugin->descriptor->LADSPA_Plugin->Label,
+                            plugin->label))
+                    break;
+            }
+            if (!plugin->descriptor) {
+                fprintf(stderr, "\n%s: Error: Plugin label \"%s\" not found in library \"%s\"\n",
+                        myName, plugin->label ? plugin->label : "(none)", dllName);
+                return 1;
+            }
+            plugin->is_first_in_dll = (j = 1);
+            if (!plugin->label) {
+                plugin->label = strdup(plugin->descriptor->LADSPA_Plugin->Label);
+            }
+
+            /* Count number of i/o buffers and ports required */
+            plugin->ins = 0;
+            plugin->outs = 0;
+            plugin->controlIns = 0;
+            plugin->controlOuts = 0;
+ 
+            for (j = 0; j < plugin->descriptor->LADSPA_Plugin->PortCount; j++) {
+
+                LADSPA_PortDescriptor pod =
+                    plugin->descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+                if (LADSPA_IS_PORT_AUDIO(pod)) {
+
+                    if (LADSPA_IS_PORT_INPUT(pod)) ++plugin->ins;
+                    else if (LADSPA_IS_PORT_OUTPUT(pod)) ++plugin->outs;
+
+                } else if (LADSPA_IS_PORT_CONTROL(pod)) {
+
+                    if (LADSPA_IS_PORT_INPUT(pod)) ++plugin->controlIns;
+                    else if (LADSPA_IS_PORT_OUTPUT(pod)) ++plugin->controlOuts;
+                }
+            }
+
+            /* finish up new plugin */
+            plugin->instances = 0;
+            plugin->next = plugins;
+            plugins = plugin;
+            plugin_count++;
+        }
+
+        /* set up instances */
+        for (j = 0; j < reps; j++) {
+            if (instance_count < D3H_MAX_INSTANCES) {
+                instance = &instances[instance_count];
+
+                instance->plugin = plugin;
+                instance->channel = instance_count;
+		instance->inactive = 1;
+                tmp = (char *)malloc(strlen(plugin->dll->name) +
+                                     strlen(plugin->label) + 9);
+                instance->friendly_name = tmp;
+                strcpy(tmp, plugin->dll->name);
+                if (strlen(tmp) > 3 &&
+                    !strcasecmp(tmp + strlen(tmp) - 3, ".so")) {
+                    tmp = tmp + strlen(tmp) - 3;
+                } else {
+                    tmp = tmp + strlen(tmp);
+                }
+                sprintf(tmp, "/%s/chan%02d", plugin->label, instance->channel);
+                instance->firstControlIn = controlInsTotal;
+                instance->pluginProgramCount = 0;
+                instance->pluginPrograms = NULL;
+                instance->currentBank = 0;
+                instance->currentProgram = 0;
+                instance->pendingBankLSB = -1;
+                instance->pendingBankMSB = -1;
+                instance->pendingProgramChange = -1;
+                instance->uiTarget = NULL;
+                instance->ui_initial_show_sent = 0;
+                instance->uiNeedsProgramUpdate = 0;
+                instance->ui_osc_control_path = NULL;
+                instance->ui_osc_program_path = NULL;
+                instance->ui_osc_show_path = NULL;
+
+                insTotal += plugin->ins;
+                outsTotal += plugin->outs;
+                controlInsTotal += plugin->controlIns;
+                controlOutsTotal += plugin->controlOuts;
+
+                plugin->instances++;
+                instance_count++;
+            } else {
+                fprintf(stderr, "%s: too many plugin instances specified\n", myName);
+                return 2;
+            }
+        }
+        reps = 1;
+    }
+
+    if (instance_count == 0) {
+	fprintf(stderr, "%s: No plugin specified\n", myName);
+	return 2;
+    }
+
+    /* sort array of instances to group them by plugin */
+    if (instance_count > 1) {
+        qsort(instances, instance_count, sizeof(d3h_instance_t), instance_sort_cmp);
+    }
+
+    /* build channel2instance[] while showing what our instances are */
+    for (i = 0; i < D3H_MAX_CHANNELS; i++)
+        channel2instance[i] = NULL;
+    for (i = 0; i < instance_count; i++) {
+        instance = &instances[i];
+        instance->number = i;
+        channel2instance[instance->channel] = instance;
+	if (verbose) {
+	    fprintf(stderr, "%s: instance %2d on channel %2d, plugin %2d is \"%s\"\n",
+		    myName, i, instance->channel, instance->plugin->number,
+		    instance->friendly_name);
+	}
+    }
+
+    /* Create buffers and JACK client and ports */
+
+    char clientName[33];
+    if (instance_count > 1) strcpy(clientName, "jack-dssi-host");
+    else {
+	strncpy(clientName, plugin->descriptor->LADSPA_Plugin->Name, 32);
+	clientName[32] = '\0';
+    }
+
+    if ((jackClient = jack_client_new(clientName)) == 0) {
+
+	clientName[22] = '\0';
+	sprintf(clientName + strlen(clientName), " (%d)", (int)getpid());
+
+	if ((jackClient = jack_client_new(clientName)) == 0) {
+	    fprintf(stderr, "\n%s: Error: Failed to connect to JACK server\n",
+		myName);
+	    return 1;
+	}
+    }
+
+    sample_rate = jack_get_sample_rate(jackClient);
+
+    inputPorts = (jack_port_t **)malloc(insTotal * sizeof(jack_port_t *));
+    pluginInputBuffers = (float **)malloc(insTotal * sizeof(float *));
+    pluginControlIns = (float *)calloc(controlInsTotal, sizeof(float));
+    pluginControlInInstances =
+        (d3h_instance_t **)malloc(controlInsTotal * sizeof(d3h_instance_t *));
+    pluginControlInPortNumbers =
+        (unsigned long *)malloc(controlInsTotal * sizeof(unsigned long));
+    pluginPortUpdated = (int *)malloc(controlInsTotal * sizeof(int));
+
+    outputPorts = (jack_port_t **)malloc(outsTotal * sizeof(jack_port_t *));
+    pluginOutputBuffers = (float **)malloc(outsTotal * sizeof(float *));
+    pluginControlOuts = (float *)calloc(controlOutsTotal, sizeof(float));
+
+    instanceHandles = (LADSPA_Handle *)malloc(instance_count *
+                                              sizeof(LADSPA_Handle));
+    instanceEventBuffers = (snd_seq_event_t **)malloc(instance_count *
+                                                      sizeof(snd_seq_event_t *));
+    instanceEventCounts = (unsigned long *)malloc(instance_count *
+                                                  sizeof(unsigned long));
+
+    for (i = 0; i < instance_count; i++) {
+        instanceEventBuffers[i] = (snd_seq_event_t *)malloc(EVENT_BUFFER_SIZE *
+                                                            sizeof(snd_seq_event_t));
+        instances[i].pluginPortControlInNumbers =
+            (int *)malloc(instances[i].plugin->descriptor->LADSPA_Plugin->PortCount *
+                          sizeof(int));
+    }
+
+    in = 0;
+    out = 0;
+    reps = 0;
+    for (i = 0; i < instance_count; i++) {
+	if (i > 0 &&
+	    !strcmp(instances[i  ].plugin->descriptor->LADSPA_Plugin->Name,
+		    instances[i-1].plugin->descriptor->LADSPA_Plugin->Name)) {
+	    ++reps;
+	} else if (i < instance_count - 1 &&
+		   !strcmp(instances[i  ].plugin->descriptor->LADSPA_Plugin->Name,
+			   instances[i+1].plugin->descriptor->LADSPA_Plugin->Name)) {
+	    reps = 1;
+	} else {
+	    reps = 0;
+	}
+	for (j = 0; j < instances[i].plugin->ins; ++j) {
+	    char portName[40];
+	    strncpy(portName, instances[i].plugin->descriptor->LADSPA_Plugin->Name, 30);
+	    if (reps > 0) {
+		portName[25] = '\0';
+		sprintf(portName + strlen(portName), " %d in_%d", reps, j + 1);
+	    } else {
+		portName[30] = '\0';
+		sprintf(portName + strlen(portName), " in_%d", j + 1);
+	    }
+	    inputPorts[in] = jack_port_register(jackClient, portName,
+						JACK_DEFAULT_AUDIO_TYPE,
+						JackPortIsInput, 0);
+	    pluginInputBuffers[in] =
+		(float *)calloc(jack_get_buffer_size(jackClient), sizeof(float));
+	    ++in;
+	}
+	for (j = 0; j < instances[i].plugin->outs; ++j) {
+	    char portName[40];
+	    strncpy(portName, instances[i].plugin->descriptor->LADSPA_Plugin->Name, 30);
+	    if (reps > 0) {
+		portName[25] = '\0';
+		sprintf(portName + strlen(portName), " %d out_%d", reps, j + 1);
+	    } else {
+		portName[30] = '\0';
+		sprintf(portName + strlen(portName), " out_%d", j + 1);
+	    }
+	    outputPorts[out] = jack_port_register(jackClient, portName,
+						  JACK_DEFAULT_AUDIO_TYPE,
+						  JackPortIsOutput, 0);
+	    pluginOutputBuffers[out] =
+		(float *)calloc(jack_get_buffer_size(jackClient), sizeof(float));
+	    ++out;
+	}
+    }
+    
+    jack_set_process_callback(jackClient, audio_callback, 0);
+
+    /* Instantiate plugins */
+
+    for (i = 0; i < instance_count; i++) {
+        plugin = instances[i].plugin;
+        instanceHandles[i] = plugin->descriptor->LADSPA_Plugin->instantiate
+            (plugin->descriptor->LADSPA_Plugin, sample_rate);
+        if (!instanceHandles[i]) {
+            fprintf(stderr, "\n%s: Error: Failed to instantiate instance %d!, plugin \"%s\"\n",
+                    myName, i, plugin->label);
+            return 1;
+        }
+	if (projectDirectory && plugin->descriptor->configure) {
+	    char *rv =plugin->descriptor->configure(instanceHandles[i],
+						    DSSI_PROJECT_DIRECTORY_KEY,
+						    projectDirectory);
+	    if (rv) {
+		fprintf(stderr, "%s: Warning: plugin doesn't like project directory: \"%s\"\n", myName, rv);
+	    }
+	}
+    }
+
+    /* Create OSC thread */
+
+    serverThread = lo_server_thread_new(NULL, osc_error);
+    snprintf((char *)osc_path_tmp, 31, "/dssi");
+    tmp = lo_server_thread_get_url(serverThread);
+    url = (char *)malloc(strlen(tmp) + strlen(osc_path_tmp));
+    sprintf(url, "%s%s", tmp, osc_path_tmp + 1);
+    if (verbose) {
+	printf("%s: registering %s\n", myName, url);
+    }
+    free(tmp);
+
+    lo_server_thread_add_method(serverThread, NULL, NULL, osc_message_handler,
+				NULL);
+    lo_server_thread_start(serverThread);
+
+    /* Connect and activate plugins */
+
+    for (in = 0; in < insTotal; in++) {
+        pluginPortUpdated[in] = 0;
+    }
+
+    in = out = controlIn = controlOut = 0;
+
+    for (i = 0; i < instance_count; i++) {   /* i is instance number */
+        instance = &instances[i];
+
+        for (j = 0; j < MIDI_CONTROLLER_COUNT; j++) {
+            instance->controllerMap[j] = -1;
+        }
+
+        plugin = instance->plugin;
+        for (j = 0; j < plugin->descriptor->LADSPA_Plugin->PortCount; j++) {  /* j is LADSPA port number */
+
+            LADSPA_PortDescriptor pod =
+                plugin->descriptor->LADSPA_Plugin->PortDescriptors[j];
+
+            instance->pluginPortControlInNumbers[j] = -1;
+
+            if (LADSPA_IS_PORT_AUDIO(pod)) {
+
+                if (LADSPA_IS_PORT_INPUT(pod)) {
+                    plugin->descriptor->LADSPA_Plugin->connect_port
+                        (instanceHandles[i], j, pluginInputBuffers[in++]);
+
+                } else if (LADSPA_IS_PORT_OUTPUT(pod)) {
+                    plugin->descriptor->LADSPA_Plugin->connect_port
+                        (instanceHandles[i], j, pluginOutputBuffers[out++]);
+                }
+
+            } else if (LADSPA_IS_PORT_CONTROL(pod)) {
+
+                if (LADSPA_IS_PORT_INPUT(pod)) {
+
+                    if (plugin->descriptor->get_midi_controller_for_port) {
+
+                        int controller = plugin->descriptor->
+                            get_midi_controller_for_port(instanceHandles[i], j);
+
+                        if (controller == 0) {
+                            MB_MESSAGE
+                                ("Buggy plugin: wants mapping for bank MSB\n");
+                        } else if (controller == 32) {
+                            MB_MESSAGE
+                                ("Buggy plugin: wants mapping for bank LSB\n");
+                        } else if (DSSI_IS_CC(controller)) {
+                            instance->controllerMap[DSSI_CC_NUMBER(controller)]
+                                = controlIn;
+                        }
+                    }
+
+                    pluginControlInInstances[controlIn] = instance;
+                    pluginControlInPortNumbers[controlIn] = j;
+                    instance->pluginPortControlInNumbers[j] = controlIn;
+
+                    pluginControlIns[controlIn] = get_port_default
+                        (plugin->descriptor->LADSPA_Plugin, j);
+
+                    plugin->descriptor->LADSPA_Plugin->connect_port
+                        (instanceHandles[i], j, &pluginControlIns[controlIn++]);
+
+                } else if (LADSPA_IS_PORT_OUTPUT(pod)) {
+                    plugin->descriptor->LADSPA_Plugin->connect_port
+                        (instanceHandles[i], j, &pluginControlOuts[controlOut++]);
+                }
+            }
+        }  /* 'for (j...'  LADSPA port number */
+
+        if (plugin->descriptor->LADSPA_Plugin->activate) {
+            plugin->descriptor->LADSPA_Plugin->activate(instanceHandles[i]);
+        }
+	instance->inactive = 0;
+    } /* 'for (i...' instance number */
+
+    assert(in == insTotal);
+    assert(out == outsTotal);
+    assert(controlIn == controlInsTotal);
+    assert(controlOut == controlOutsTotal);
+
+    /* Look up synth programs */
+
+    for (i = 0; i < instance_count; i++) {
+        instance = &instances[i];
+
+        query_programs(instance);
+        
+        if (instance->plugin->descriptor->select_program &&
+            instance->pluginProgramCount > 0) {
+
+	    /* select program at index 0 */
+            unsigned long bank = instance->pluginPrograms[0].Bank;
+            instance->pendingBankMSB = bank / 128;
+            instance->pendingBankLSB = bank % 128;
+            instance->pendingProgramChange = instance->pluginPrograms[0].Program;
+	    instance->uiNeedsProgramUpdate = 1;
+        }
+    }
+
+    /* Create ALSA MIDI port */
+
+    if (snd_seq_open(&alsaClient, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
+	fprintf(stderr, "\n%s: Error: Failed to open ALSA sequencer interface\n",
+		myName);
+	return 1;
+    }
+
+    snd_seq_set_client_name(alsaClient, clientName);
+
+    if ((portid = snd_seq_create_simple_port
+	 (alsaClient, clientName,
+	  SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, 0)) < 0) {
+	fprintf(stderr, "\n%s: Error: Failed to create ALSA sequencer port\n",
+		myName);
+	return 1;
+    }
+
+    npfd = snd_seq_poll_descriptors_count(alsaClient, POLLIN);
+    pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd));
+    snd_seq_poll_descriptors(alsaClient, pfd, npfd, POLLIN);
+
+    mb_init("host: ");
+
+    if (jack_activate(jackClient)) {
+        fprintf (stderr, "cannot activate jack client");
+        exit(1);
+    }
+
+    /* activate JACK and connect ports */
+    /* !FIX! this to more intelligently connect ports: */
+    ports = jack_get_ports(jackClient, NULL, NULL,
+                           JackPortIsPhysical|JackPortIsInput);
+    if (ports && ports[0]) {
+        for (i = 0, j = 0; i < outsTotal; ++i) {
+            if (jack_connect(jackClient, jack_port_name(outputPorts[i]),
+                             ports[j])) {
+                fprintf (stderr, "cannot connect output port %d\n", i);
+            }
+            if (!ports[++j]) j = 0;
+        }
+        free(ports);
+    }
+
+    signal(SIGINT, signalHandler);
+    signal(SIGTERM, signalHandler);
+    signal(SIGHUP, signalHandler);
+    signal(SIGQUIT, signalHandler);
+    pthread_sigmask(SIG_UNBLOCK, &_signals, 0);
+
+    /* Attempt to locate and start up a GUI for the plugin -- but
+     * continue even if we can't */
+    /* -FIX- Ack! So many windows all at once! */
+    for (i = 0; i < instance_count; i++) {
+        char tag[12];
+        plugin = instances[i].plugin;
+        snprintf(osc_path_tmp, 1024, "%s/%s", url, instances[i].friendly_name);
+        snprintf(tag, 12, "channel %d", instances[i].channel);
+	printf("\n%s: OSC URL is:\n%s\n\n", myName, osc_path_tmp);
+        startGUI(plugin->dll->directory, plugin->dll->name,
+                 plugin->descriptor->LADSPA_Plugin->Label, osc_path_tmp, tag);
+    }
+
+    MB_MESSAGE("Ready\n");
+
+    exiting = 0;
+
+    while (!exiting) {
+
+	if (poll(pfd, npfd, 100) > 0) {
+	    midi_callback();
+	}
+
+	/* Race conditions here, because the programs and ports are
+	   updated from the audio thread.  We at least try to minimise
+	   trouble by copying out before the expensive OSC call */
+
+        for (i = 0; i < instance_count; i++) {
+            instance = &instances[i];
+            if (instance->uiNeedsProgramUpdate && instance->pendingProgramChange < 0) {
+                int bank = instance->currentBank;
+                int program = instance->currentProgram;
+                instance->uiNeedsProgramUpdate = 0;
+                if (instance->uiTarget) {
+                    lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program);
+                }
+            }
+        }
+
+	for (i = 0; i < controlInsTotal; ++i) {
+	    if (pluginPortUpdated[i]) {
+                instance = pluginControlInInstances[i];
+		int port = pluginControlInPortNumbers[i];
+		float value = pluginControlIns[i];
+		pluginPortUpdated[i] = 0;
+		if (instance->uiTarget) {
+		    lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port, value);
+		}
+	    }
+	}
+    }
+
+    jack_client_close(jackClient);
+
+    while (i < instance_count) {
+	/* no -- see comment in osc_exiting_handler */
+	/* if (!instances[i].inactive) { */
+	    if (instances[i].plugin->descriptor->LADSPA_Plugin->deactivate) {
+		instances[i].plugin->descriptor->LADSPA_Plugin->deactivate
+		    (instanceHandles[i]);
+	    }
+	/* } */
+        if (instances[i].plugin->descriptor->LADSPA_Plugin &&
+	    instances[i].plugin->descriptor->LADSPA_Plugin->cleanup) {
+            instances[i].plugin->descriptor->LADSPA_Plugin->cleanup
+		(instanceHandles[i]);
+	}
+	++i;
+    }
+
+    kill(0, SIGHUP);
+
+    return 0;
+}
+
+LADSPA_Data get_port_default(const LADSPA_Descriptor *plugin, int port)
+{
+    LADSPA_PortRangeHint hint = plugin->PortRangeHints[port];
+    float lower = hint.LowerBound *
+	(LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+    float upper = hint.UpperBound *
+	(LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? sample_rate : 1.0f);
+
+    if (!LADSPA_IS_HINT_HAS_DEFAULT(hint.HintDescriptor)) {
+	if (!LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) ||
+	    !LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+	    /* No hint, its not bounded, wild guess */
+	    return 0.0f;
+	}
+
+	if (lower <= 0.0f && upper >= 0.0f) {
+	    /* It spans 0.0, 0.0 is often a good guess */
+	    return 0.0f;
+	}
+
+	/* No clues, return minimum */
+	return lower;
+    }
+
+    /* Try all the easy ones */
+    
+    if (LADSPA_IS_HINT_DEFAULT_0(hint.HintDescriptor)) {
+	return 0.0f;
+    } else if (LADSPA_IS_HINT_DEFAULT_1(hint.HintDescriptor)) {
+	return 1.0f;
+    } else if (LADSPA_IS_HINT_DEFAULT_100(hint.HintDescriptor)) {
+	return 100.0f;
+    } else if (LADSPA_IS_HINT_DEFAULT_440(hint.HintDescriptor)) {
+	return 440.0f;
+    }
+
+    /* All the others require some bounds */
+
+    if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+	if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint.HintDescriptor)) {
+	    return lower;
+	}
+    }
+    if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) {
+	if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint.HintDescriptor)) {
+	    return upper;
+	}
+	if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) {
+	    if (LADSPA_IS_HINT_DEFAULT_LOW(hint.HintDescriptor)) {
+		return lower * 0.75f + upper * 0.25f;
+	    } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint.HintDescriptor)) {
+		return lower * 0.5f + upper * 0.5f;
+	    } else if (LADSPA_IS_HINT_DEFAULT_HIGH(hint.HintDescriptor)) {
+		return lower * 0.25f + upper * 0.75f;
+	    }
+	}
+    }
+
+    /* fallback */
+    return 0.0f;
+}
+
+void osc_error(int num, const char *msg, const char *path)
+{
+    fprintf(stderr, "%s: liblo server error %d in path %s: %s\n",
+	    myName, num, path, msg);
+}
+
+int
+osc_midi_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    static snd_midi_event_t *alsaCoder = NULL;
+    static snd_seq_event_t alsaEncodeBuffer[10];
+    long count;
+    snd_seq_event_t *ev = &alsaEncodeBuffer[0];
+
+    if (verbose) {
+	printf("%s: OSC: got midi request for %s "
+	       "(%02x %02x %02x %02x)\n", myName, instance->friendly_name,
+	       argv[0]->m[0], argv[0]->m[1], argv[0]->m[2], argv[0]->m[3]);
+    }
+
+    if (!alsaCoder) {
+        if (snd_midi_event_new(10, &alsaCoder)) {
+            fprintf(stderr, "%s: Failed to initialise ALSA MIDI coder!\n",
+		    myName);
+            return 0;
+        }
+    }
+
+    snd_midi_event_reset_encode(alsaCoder);
+
+    count = snd_midi_event_encode
+	(alsaCoder, (argv[0]->m) + 1, 3, alsaEncodeBuffer); /* ignore OSC "port id" in argv[0]->m[0] */
+
+    if (!count || !snd_seq_ev_is_channel_type(ev)) {
+        return 0;
+    }
+
+    /* substitute correct MIDI channel */
+    ev->data.note.channel = instance->channel;
+    
+    if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) {
+        ev->type =  SND_SEQ_EVENT_NOTEOFF;
+    }
+        
+    pthread_mutex_lock(&midiEventBufferMutex);
+
+    if (midiEventReadIndex == midiEventWriteIndex + 1) {
+
+        fprintf(stderr, "%s: Warning: MIDI event buffer overflow!\n", myName);
+
+    } else if (ev->type == SND_SEQ_EVENT_CONTROLLER &&
+               (ev->data.control.param == 0 || ev->data.control.param == 32)) {
+
+        fprintf(stderr, "%s: Warning: %s UI sent bank select controller (should use /program OSC call), ignoring\n", myName, instance->friendly_name);
+
+    } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+
+        fprintf(stderr, "%s: Warning: %s UI sent program change (should use /program OSC call), ignoring\n", myName, instance->friendly_name);
+
+    } else {
+
+        midiEventBuffer[midiEventWriteIndex] = *ev;
+        midiEventWriteIndex = (midiEventWriteIndex + 1) % EVENT_BUFFER_SIZE;
+
+    }
+
+    pthread_mutex_unlock(&midiEventBufferMutex);
+
+    return 0;
+}
+
+int
+osc_control_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    int port = argv[0]->i;
+    LADSPA_Data value = argv[1]->f;
+
+    if (port < 0 || port > instance->plugin->descriptor->LADSPA_Plugin->PortCount) {
+	fprintf(stderr, "%s: OSC: %s port number (%d) is out of range\n",
+                myName, instance->friendly_name, port);
+	return 0;
+    }
+    if (instance->pluginPortControlInNumbers[port] == -1) {
+	fprintf(stderr, "%s: OSC: %s port %d is not a control in\n",
+                myName, instance->friendly_name, port);
+	return 0;
+    }
+    pluginControlIns[instance->pluginPortControlInNumbers[port]] = value;
+    if (verbose) {
+	printf("%s: OSC: %s port %d = %f\n",
+	       myName, instance->friendly_name, port, value);
+    }
+    
+    return 0;
+}
+
+int
+osc_program_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    int bank = argv[0]->i;
+    int program = argv[1]->i;
+    int i;
+    int found = 0;
+
+    for (i = 0; i < instance->pluginProgramCount; ++i) {
+	if (instance->pluginPrograms[i].Bank == bank &&
+	    instance->pluginPrograms[i].Program == program) {
+	    if (verbose) {
+		printf("%s: OSC: %s setting bank %d, program %d, name %s\n",
+		       myName,
+		       instance->friendly_name, bank, program,
+		       instance->pluginPrograms[i].Name);
+	    }
+	    found = 1;
+	    break;
+	}
+    }
+
+    if (!found) {
+	printf("%s: OSC: %s UI requested unknown program: bank %d, program %d: sending to plugin anyway (plugin should ignore it)\n",
+	       myName, instance->friendly_name, bank, program);
+    }
+
+    instance->pendingBankMSB = bank / 128;
+    instance->pendingBankLSB = bank % 128;
+    instance->pendingProgramChange = program;
+
+    return 0;
+}
+
+int
+osc_configure_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    const char *key = (const char *)&argv[0]->s;
+    const char *value = (const char *)&argv[1]->s;
+    char *message;
+
+    /* This is pretty much the simplest legal implementation of
+     * configure in a DSSI host. */
+
+    /* The host has the option to remember the set of (key,value)
+     * pairs associated with a particular instance, so that if it
+     * wants to restore the "same" instance on another occasion it can
+     * just call configure() on it for each of those pairs and so
+     * restore state without any input from a GUI.  Any real-world GUI
+     * host will probably want to do that.  This host doesn't have any
+     * concept of restoring an instance from one run to the next, so
+     * we don't bother remembering these at all. */
+
+    if (instance->plugin->descriptor->configure) {
+
+	int n = instance->number;
+	int m = n;
+
+	if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX,
+		     strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) {
+	    fprintf(stderr, "%s: OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", myName, instance->friendly_name, key);
+	    return 0;
+	}
+
+	if (instance->plugin->instances > 1 &&
+	    !strncmp(key, DSSI_GLOBAL_CONFIGURE_PREFIX,
+		     strlen(DSSI_GLOBAL_CONFIGURE_PREFIX))) {
+	    while (n > 0 && instances[n-1].plugin == instances[m].plugin) --n;
+	    m = n + instances[n].plugin->instances - 1;
+	}
+	
+	while (n <= m) {
+
+	    message = instances[n].plugin->descriptor->configure
+		(instanceHandles[n], key, value);
+	    if (message) {
+		printf("%s: on configure '%s' '%s', plugin '%s' returned error '%s'\n",
+		       myName, key, value, instance->friendly_name, message);
+		free(message);
+	    }
+
+	    // also call back on UIs for plugins other than the one
+	    // that requested this:
+	    if (n != instance->number && instances[n].uiTarget) {
+		lo_send(instances[n].uiTarget,
+			instances[n].ui_osc_configure_path, "ss", key, value);
+	    }
+		
+	    /* configure invalidates bank and program information, so
+	       we should do this again now: */
+	    query_programs(&instances[n]);
+
+	    ++n;
+	}	    
+    }
+
+    return 0;
+}
+
+int
+osc_update_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    const char *url = (char *)&argv[0]->s;
+    const char *path;
+    unsigned int i;
+    char *host, *port;
+
+    if (verbose) {
+	printf("%s: OSC: got update request from <%s>\n", myName, url);
+    }
+
+    if (instance->uiTarget) lo_address_free(instance->uiTarget);
+    host = lo_url_get_hostname(url);
+    port = lo_url_get_port(url);
+    instance->uiTarget = lo_address_new(host, port);
+    free(host);
+    free(port);
+
+    path = lo_url_get_path(url);
+
+    if (instance->ui_osc_control_path) free(instance->ui_osc_control_path);
+    instance->ui_osc_control_path = (char *)malloc(strlen(path) + 10);
+    sprintf(instance->ui_osc_control_path, "%s/control", path);
+
+    if (instance->ui_osc_configure_path) free(instance->ui_osc_configure_path);
+    instance->ui_osc_configure_path = (char *)malloc(strlen(path) + 12);
+    sprintf(instance->ui_osc_configure_path, "%s/configure", path);
+
+    if (instance->ui_osc_program_path) free(instance->ui_osc_program_path);
+    instance->ui_osc_program_path = (char *)malloc(strlen(path) + 10);
+    sprintf(instance->ui_osc_program_path, "%s/program", path);
+
+    if (instance->ui_osc_show_path) free(instance->ui_osc_show_path);
+    instance->ui_osc_show_path = (char *)malloc(strlen(path) + 10);
+    sprintf(instance->ui_osc_show_path, "%s/show", path);
+
+    free((char *)path);
+
+    /* At this point a more substantial host might also call
+     * configure() on the UI to set any state that it had remembered
+     * for the plugin instance.  But we don't remember state for
+     * plugin instances (see our own configure() implementation in
+     * osc_configure_handler), and so we have nothing to send except
+     * the optional project directory. */
+
+    if (projectDirectory) {
+	lo_send(instance->uiTarget, instance->ui_osc_configure_path, "ss",
+		DSSI_PROJECT_DIRECTORY_KEY, projectDirectory);
+    }
+
+    /* Send current bank/program  (-FIX- another race...) */
+    if (instance->pendingProgramChange < 0) {
+        unsigned long bank = instance->currentBank;
+        unsigned long program = instance->currentProgram;
+        instance->uiNeedsProgramUpdate = 0;
+        if (instance->uiTarget) {
+            lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program);
+        }
+    }
+
+    /* Send control ports */
+    for (i = 0; i < instance->plugin->controlIns; i++) {
+        int in = i + instance->firstControlIn;
+	int port = pluginControlInPortNumbers[in];
+	lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port,
+                pluginControlIns[in]);
+	/* Avoid overloading the GUI if there are lots and lots of ports */
+	if ((i+1) % 50 == 0) usleep(300000);
+    }
+
+    /* Send 'show' */
+    if (!instance->ui_initial_show_sent) {
+	lo_send(instance->uiTarget, instance->ui_osc_show_path, "");
+	instance->ui_initial_show_sent = 1;
+    }
+
+    return 0;
+}
+
+int
+osc_exiting_handler(d3h_instance_t *instance, lo_arg **argv)
+{
+    int i;
+
+    if (verbose) {
+	printf("%s: OSC: got exiting notification for instance %d\n", myName,
+	       instance->number);
+    }
+
+    if (instance->plugin) {
+
+	/*!!! No, this isn't safe -- plugins deactivated in this way
+	  would still be included in a run_multiple_synths call unless
+	  we re-jigged the instance array at the same time -- leave it
+	  for now
+	if (instance->plugin->descriptor->LADSPA_Plugin->deactivate) {
+            instance->plugin->descriptor->LADSPA_Plugin->deactivate
+		(instanceHandles[instance->number]);
+	}
+	*/
+	/* Leave this flag though, as we need it to determine when to exit */
+	instance->inactive = 1;
+    }
+
+    /* Do we have any plugins left running? */
+
+    for (i = 0; i < instance_count; ++i) {
+	if (!instances[i].inactive) return 0;
+    }
+
+    if (verbose) {
+	printf("%s: That was the last remaining plugin, exiting...\n", myName);
+    }
+    exiting = 1;
+    return 0;
+}
+
+int osc_debug_handler(const char *path, const char *types, lo_arg **argv,
+                      int argc, void *data, void *user_data)
+{
+    int i;
+
+    printf("%s: got unhandled OSC message:\npath: <%s>\n", myName, path);
+    for (i=0; i<argc; i++) {
+        printf("%s: arg %d '%c' ", myName, i, types[i]);
+        lo_arg_pp(types[i], argv[i]);
+        printf("\n");
+    }
+    printf("%s:\n", myName);
+
+    return 1;
+}
+
+int osc_message_handler(const char *path, const char *types, lo_arg **argv,
+                        int argc, void *data, void *user_data)
+{
+    int i;
+    d3h_instance_t *instance = NULL;
+    const char *method;
+
+    if (strncmp(path, "/dssi/", 6))
+        return osc_debug_handler(path, types, argv, argc, data, user_data);
+
+    for (i = 0; i < instance_count; i++) {
+        if (!strncmp(path + 6, instances[i].friendly_name,
+                     strlen(instances[i].friendly_name))) {
+            instance = &instances[i];
+            break;
+        }
+    }
+    if (!instance)
+        return osc_debug_handler(path, types, argv, argc, data, user_data);
+
+    /* no -- see comment in osc_exiting_handler */
+    /*
+    if (instance->inactive) 
+	return 0;
+    */
+    method = path + 6 + strlen(instance->friendly_name);
+    if (*method != '/' || *(method + 1) == 0)
+        return osc_debug_handler(path, types, argv, argc, data, user_data);
+    method++;
+
+    if (!strcmp(method, "configure") && argc == 2 && !strcmp(types, "ss")) {
+
+        return osc_configure_handler(instance, argv);
+
+    } else if (!strcmp(method, "control") && argc == 2 && !strcmp(types, "if")) {
+
+        return osc_control_handler(instance, argv);
+
+    } else if (!strcmp(method, "midi") && argc == 1 && !strcmp(types, "m")) {
+
+        return osc_midi_handler(instance, argv);
+
+    } else if (!strcmp(method, "program") && argc == 2 && !strcmp(types, "ii")) {
+
+        return osc_program_handler(instance, argv);
+
+    } else if (!strcmp(method, "update") && argc == 1 && !strcmp(types, "s")) {
+
+        return osc_update_handler(instance, argv);
+
+    } else if (!strcmp(method, "exiting") && argc == 0) {
+
+        return osc_exiting_handler(instance, argv);
+    }
+
+    return osc_debug_handler(path, types, argv, argc, data, user_data);
+}
+




More information about the Demudi-commits mailing list