From 5b43da5a8491e6bb577b9e508c55c9619a1a850f Mon Sep 17 00:00:00 2001 From: Jamie Bullock Date: Wed, 8 Dec 2010 09:45:09 +0000 Subject: - moved dssi to pluginhost~ in preparation for renaming svn path=/trunk/externals/postlude/; revision=14575 --- pluginhost~/src/dssi~.c | 2537 +++++++++++++++++++++++++++++++++++++++++++++ pluginhost~/src/dssi~.h | 183 ++++ pluginhost~/src/jload.c | 214 ++++ pluginhost~/src/jsearch.c | 154 +++ pluginhost~/src/jutils.h | 69 ++ 5 files changed, 3157 insertions(+) create mode 100644 pluginhost~/src/dssi~.c create mode 100644 pluginhost~/src/dssi~.h create mode 100644 pluginhost~/src/jload.c create mode 100644 pluginhost~/src/jsearch.c create mode 100644 pluginhost~/src/jutils.h (limited to 'pluginhost~/src') diff --git a/pluginhost~/src/dssi~.c b/pluginhost~/src/dssi~.c new file mode 100644 index 0000000..f195637 --- /dev/null +++ b/pluginhost~/src/dssi~.c @@ -0,0 +1,2537 @@ +/* dssi~ - A DSSI host for PD + * + * Copyright (C) 2006 Jamie Bullock and others + * + * This file incorporates code from the following sources: + * + * jack-dssi-host (BSD-style license): Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton. + * + * Hexter (GPL license): Copyright (C) 2004 Sean Bolton and others. + * + * plugin~ (GPL license): Copyright (C) 2000 Jarno Seppänen, remIXed 2005 + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include "dssi~.h" +#include "jutils.h" + +static t_class *dssi_tilde_class; + +/*From dx7_voice_data.c by Sean Bolton */ + +static char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/*static void init_MidiEventBuf(snd_seq_event_t *MidiEventBuf){ + int i; + for(i = 0; i < EVENT_BUFSIZE; i++){ + MidiEventBuf[i].*/ + +/* + * encode_7in6 + ** Taken from gui_data.c by Sean Bolton ** + * + * encode a block of 7-bit data, in base64-ish style + */ + static char * +encode_7in6(uint8_t *data, int length) +{ + char *buffer; + int in, reg, above, below, shift, out; + int outchars = (length * 7 + 5) / 6; + unsigned int sum = 0; + + if (!(buffer = (char *)malloc(25 + outchars))) + return NULL; + + out = snprintf(buffer, 12, "%d ", length); + + in = reg = above = below = 0; + while (outchars) { + if (above == 6) { + buffer[out] = base64[reg >> 7]; + reg &= 0x7f; + above = 0; + out++; + outchars--; + } + if (below == 0) { + if (in < length) { + reg |= data[in] & 0x7f; + sum += data[in]; + } + below = 7; + in++; + } + shift = 6 - above; + if (below < shift) shift = below; + reg <<= shift; + above += shift; + below -= shift; + } + + snprintf(buffer + out, 12, " %d", sum); + + return buffer; +} + +/* + * dx7_bulk_dump_checksum + ** Taken from dx7_voice_data.c by Sean Bolton ** + */ + static int +dx7_bulk_dump_checksum(uint8_t *data, int length) +{ + int sum = 0; + int i; + + for (i = 0; i < length; sum -= data[i++]); + return sum & 0x7F; +} + +static DSSI_Descriptor * ladspa_to_dssi( + LADSPA_Descriptor *ladspaDesc){ + DSSI_Descriptor *dssiDesc; + dssiDesc = (DSSI_Descriptor *)calloc(1, sizeof(DSSI_Descriptor)); + ((DSSI_Descriptor *)dssiDesc)->DSSI_API_Version = 1; + ((DSSI_Descriptor *)dssiDesc)->LADSPA_Plugin = + (LADSPA_Descriptor *)ladspaDesc; + return (DSSI_Descriptor *)dssiDesc; +} +/* + static void dssi_tilde_load_plugin(const char *dll_path, void **dll_handle){ + + *dll_handle = dlopen(dll_path, RTLD_NOW | RTLD_LOCAL); + if (*dll_handle){ + post("%s loaded successfully", dll_path); + } + else + post("Failed: %s", dlerror()); + + } + */ +static void dssi_tilde_port_info(t_dssi_tilde *x){ + t_int i; + + for (i = 0; i < (t_int)x->descriptor->LADSPA_Plugin->PortCount; i++) { + + x->port_info[i].type.a_type = A_SYMBOL; + x->port_info[i].data_type.a_type = A_SYMBOL; + x->port_info[i].name.a_type = A_SYMBOL; + x->port_info[i].upper_bound.a_type = A_FLOAT; + x->port_info[i].lower_bound.a_type = A_FLOAT; + x->port_info[i].p_default.a_type = A_FLOAT; + + LADSPA_PortDescriptor pod = + x->descriptor->LADSPA_Plugin->PortDescriptors[i]; +#if DEBUG + post("Port %d: %s", i, x->descriptor->LADSPA_Plugin->PortNames[i]); +#endif + if (LADSPA_IS_PORT_AUDIO(pod)) { + x->port_info[i].data_type.a_w.w_symbol = + gensym("audio"); + if (LADSPA_IS_PORT_INPUT(pod)){ + x->port_info[i].type.a_w.w_symbol = + gensym("in"); + ++x->plugin_ins; + } + else if (LADSPA_IS_PORT_OUTPUT(pod)){ + x->port_info[i].type.a_w.w_symbol = + gensym("out"); + ++x->plugin_outs; + } + } + else if (LADSPA_IS_PORT_CONTROL(pod)) { + x->port_info[i].data_type.a_w.w_symbol = + gensym("control"); + if (LADSPA_IS_PORT_INPUT(pod)){ + x->port_info[i].type.a_w.w_symbol = + gensym("in"); + ++x->plugin_controlIns; + } + else if (LADSPA_IS_PORT_OUTPUT(pod)){ + ++x->plugin_controlOuts; + x->port_info[i].type.a_w.w_symbol = + gensym("out"); + } + } + if (LADSPA_IS_HINT_BOUNDED_BELOW( + x->descriptor->LADSPA_Plugin->PortRangeHints[i].HintDescriptor)) + x->port_info[i].lower_bound.a_w.w_float = + x->descriptor->LADSPA_Plugin-> + PortRangeHints[i].LowerBound; + else + x->port_info[i].lower_bound.a_w.w_float = 0; + + if (LADSPA_IS_HINT_BOUNDED_ABOVE( + x->descriptor->LADSPA_Plugin->PortRangeHints[i].HintDescriptor)) + x->port_info[i].upper_bound.a_w.w_float = + x->descriptor->LADSPA_Plugin-> + PortRangeHints[i].UpperBound; + else + x->port_info[i].lower_bound.a_w.w_float = 1; + + x->port_info[i].p_default.a_w.w_float = (float) + get_port_default(x, i); + + x->port_info[i].name.a_w.w_symbol = + gensym ((char *) + x->descriptor->LADSPA_Plugin->PortNames[i]); + } +#if DEBUG + post("%d inputs, %d outputs, %d control inputs, %d control outs", x->plugin_ins, x->plugin_outs, x->plugin_controlIns, x->plugin_controlOuts); +#endif +} + +static void dssi_tilde_assign_ports(t_dssi_tilde *x){ + int i; + +#if DEBUG + post("%d instances", x->n_instances); +#endif + + x->plugin_ins *= x->n_instances; + x->plugin_outs *= x->n_instances; + x->plugin_controlIns *= x->n_instances; + x->plugin_controlOuts *= x->n_instances; + +#if DEBUG + post("%d plugin outs", x->plugin_outs); +#endif + + x->plugin_InputBuffers = + (float **)malloc(x->plugin_ins * sizeof(float *)); + x->plugin_OutputBuffers = + (float **)malloc(x->plugin_outs * sizeof(float *)); + x->plugin_ControlDataInput = + (float *)calloc(x->plugin_controlIns, sizeof(float)); + x->plugin_ControlDataOutput = + (float *)calloc(x->plugin_controlOuts, sizeof(float)); + for(i = 0; i < x->plugin_ins; i++) + x->plugin_InputBuffers[i] = + (float *)calloc(x->blksize, sizeof(float)); + for(i = 0; i < x->plugin_outs; i++) + x->plugin_OutputBuffers[i] = + (float *)calloc(x->blksize, sizeof(float)); + x->instanceEventBuffers = + (snd_seq_event_t **)malloc(x->n_instances * sizeof(snd_seq_event_t *)); + + x->instanceHandles = (LADSPA_Handle *)malloc(x->n_instances * + sizeof(LADSPA_Handle)); + x->instanceEventCounts = (unsigned long *)malloc(x->n_instances * + sizeof(unsigned long)); + + for(i = 0; i < x->n_instances; i++){ + x->instanceEventBuffers[i] = (snd_seq_event_t *)malloc(EVENT_BUFSIZE * + sizeof(snd_seq_event_t)); + + x->instances[i].plugin_PortControlInNumbers = + (int *)malloc(x->descriptor->LADSPA_Plugin->PortCount * + sizeof(int));/* hmmm... as we don't support instances of differing plugin types, we probably don't need to do this dynamically*/ + } + + x->plugin_ControlInPortNumbers = + (unsigned long *)malloc(sizeof(unsigned long) * x->plugin_controlIns); + +#if DEBUG + post("Buffers assigned!"); +#endif + +} + +static void dssi_tilde_init_instance(t_dssi_tilde *x, t_int instance){ + + x->instances[instance].pluginPrograms = NULL; + x->instances[instance].currentBank = 0; + x->instances[instance].currentProgram = 0; + x->instances[instance].uiTarget = NULL; + x->instances[instance].ui_osc_control_path = NULL; + x->instances[instance].ui_osc_program_path = NULL; + x->instances[instance].ui_osc_show_path = NULL; + x->instances[instance].ui_osc_hide_path = NULL; + x->instances[instance].ui_osc_quit_path = NULL; + x->instances[instance].ui_osc_configure_path = NULL; + x->instances[instance].uiNeedsProgramUpdate = 0; + x->instances[instance].pendingProgramChange = -1; + x->instances[instance].plugin_ProgramCount = 0; + x->instances[instance].pendingBankMSB = -1; + x->instances[instance].pendingBankLSB = -1; + x->instances[instance].ui_hidden = 1; + x->instances[instance].ui_show = 0; + x->instances[instance].gui_pid = 0; +#if DEBUG + post("Instance %d initialized!", instance); +#endif + +} + +static void dssi_tilde_connect_ports(t_dssi_tilde *x, t_int instance){ + + t_int i; + + for(i = 0; i < (t_int)x->descriptor->LADSPA_Plugin->PortCount; i++){ +#if DEBUG + post("PortCount: %d of %d", i, + x->descriptor->LADSPA_Plugin->PortCount); +#endif + LADSPA_PortDescriptor pod = + x->descriptor->LADSPA_Plugin->PortDescriptors[i]; + + x->instances[instance].plugin_PortControlInNumbers[i] = -1; + + if (LADSPA_IS_PORT_AUDIO(pod)) { + if (LADSPA_IS_PORT_INPUT(pod)) { + x->descriptor->LADSPA_Plugin->connect_port + (x->instanceHandles[instance], i, + x->plugin_InputBuffers[x->ports_in++]); + } + else if (LADSPA_IS_PORT_OUTPUT(pod)) { + x->descriptor->LADSPA_Plugin->connect_port + (x->instanceHandles[instance], i, + x->plugin_OutputBuffers[x->ports_out++]); +#if DEBUG + post("Audio Input port %d connected", x->ports_in); + post("Audio Output port %d connected", x->ports_out); +#endif + } + } + else if (LADSPA_IS_PORT_CONTROL(pod)) { + if (LADSPA_IS_PORT_INPUT(pod)) { + x->plugin_ControlInPortNumbers[x->ports_controlIn] = (unsigned long) i; + x->instances[instance].plugin_PortControlInNumbers[i] = x->ports_controlIn; + x->plugin_ControlDataInput[x->ports_controlIn] = + (t_float) get_port_default(x, i); +#if DEBUG + post("default for port %d, controlIn, %d is %.2f",i, + x->ports_controlIn, x->plugin_ControlDataInput[x->ports_controlIn]); +#endif + + x->descriptor->LADSPA_Plugin->connect_port + (x->instanceHandles[instance], i, + &x->plugin_ControlDataInput[x->ports_controlIn++]); + + } else if (LADSPA_IS_PORT_OUTPUT(pod)) { + x->descriptor->LADSPA_Plugin->connect_port + (x->instanceHandles[instance], i, + &x->plugin_ControlDataOutput[x->ports_controlOut++]); + } +#if DEBUG + post("Control Input port %d connected", x->ports_controlIn); + post("Control Output port %d connected", x->ports_controlOut); +#endif + } + } + +#if DEBUG + post("ports connected!"); +#endif + +} + +static void dssi_tilde_activate_plugin(t_dssi_tilde *x, t_int instance){ + + if(x->descriptor->LADSPA_Plugin->activate){ +#if DEBUG + post("trying to activate instance: %d", instance); +#endif + x->descriptor->LADSPA_Plugin->activate(x->instanceHandles[instance]); + } +#if DEBUG + post("plugin activated!"); +#endif +} + +static void dssi_tilde_deactivate_plugin(t_dssi_tilde *x, t_float instance_f){ + + t_int instance = (t_int)instance_f; + if(x->descriptor->LADSPA_Plugin->deactivate) + x->descriptor->LADSPA_Plugin->deactivate(x->instanceHandles[instance]); +#if DEBUG + post("plugin deactivated!"); +#endif +} + +static void osc_error(int num, const char *msg, const char *where) +{ + post("dssi~: osc error %d in path %s: %s\n",num, where, msg); +} + +static void query_programs(t_dssi_tilde *x, t_int instance) { + int i; +#if DEBUG + post("querying programs"); +#endif + /* free old lot */ + if (x->instances[instance].pluginPrograms) { + for (i = 0; i < x->instances[instance].plugin_ProgramCount; i++) + free((void *)x->instances[instance].pluginPrograms[i].Name); + free((char *)x->instances[instance].pluginPrograms); + x->instances[instance].pluginPrograms = NULL; + x->instances[instance].plugin_ProgramCount = 0; + } + + x->instances[instance].pendingBankLSB = -1; + x->instances[instance].pendingBankMSB = -1; + x->instances[instance].pendingProgramChange = -1; + + if (x->descriptor->get_program && + x->descriptor->select_program) { + + /* Count the plugins first */ + /*FIX ?? */ + for (i = 0; x->descriptor-> + get_program(x->instanceHandles[instance], i); ++i); + + if (i > 0) { + x->instances[instance].plugin_ProgramCount = i; + x->instances[instance].pluginPrograms = + (DSSI_Program_Descriptor *)malloc(i * sizeof(DSSI_Program_Descriptor)); + while (i > 0) { + const DSSI_Program_Descriptor *descriptor; + --i; + descriptor = x->descriptor-> + get_program(x->instanceHandles[instance], i); + x->instances[instance].pluginPrograms[i].Bank = + descriptor->Bank; + x->instances[instance].pluginPrograms[i].Program = + descriptor->Program; + x->instances[instance].pluginPrograms[i].Name = + strdup(descriptor->Name); +#if DEBUG + post("program %d is MIDI bank %lu program %lu, named '%s'",i, + x->instances[instance].pluginPrograms[i].Bank, + x->instances[instance].pluginPrograms[i].Program, + x->instances[instance].pluginPrograms[i].Name); +#endif + } + } + /* No - it should be 0 anyway - dssi_init */ + /* else + x->instances[instance].plugin_ProgramCount = 0; + */ } +} + +static LADSPA_Data get_port_default(t_dssi_tilde *x, int port) +{ + LADSPA_Descriptor *plugin = (LADSPA_Descriptor *)x->descriptor->LADSPA_Plugin; + LADSPA_PortRangeHint hint = plugin->PortRangeHints[port]; + float lower = hint.LowerBound * + (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? x->sr : 1.0f); + float upper = hint.UpperBound * + (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor) ? x->sr : 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; +} + +static unsigned dssi_tilde_get_parm_number (t_dssi_tilde *x, + const char *str) +/* find out if str points to a parameter number or not and return the + number or zero. The number string has to begin with a '#' character */ +{ + long num = 0; + char* strend = NULL; + + if (str == NULL) { + return 0; + } + if (str[0] != '#') { + return 0; + } + num = strtol (&str[1], &strend, 10); + if (str[1] == 0 || *strend != 0) { + /* invalid string */ + return 0; + } + else if (num >= 1 && num <= (long)x->plugin_controlIns) { + /* string ok and within range */ + return (unsigned)num; + } + else { + /* number out of range */ + return 0; + } +} + +static void dssi_tilde_set_control_input_by_index (t_dssi_tilde *x, + signed ctrl_input_index, + float value, + t_int instance) +{ + long port, portno; + + if (ctrl_input_index >= x->plugin_controlIns) { + post("dssi~: control port number %d is out of range [1, %d]", + ctrl_input_index + 1, x->plugin_controlIns); + return; + } + +#if DEBUG + post("ctrl input number = %d", ctrl_input_index); +#endif + + + port = x->plugin_ControlInPortNumbers[ctrl_input_index]; + + + /* FIX - temporary hack */ + if(x->is_DSSI) + portno = + x->instances[instance].plugin_PortControlInNumbers[ctrl_input_index + 1]; + else + portno = + x->instances[instance].plugin_PortControlInNumbers[ctrl_input_index]; +#if DEBUG + post("Global ctrl input number = %d", ctrl_input_index); + post("Global ctrl input value = %.2f", value); +#endif + + + + + /* set the appropriate control port value */ + x->plugin_ControlDataInput[portno] = value; + + /* Update the UI if there is one */ + if(x->is_DSSI){ + if(x->instances[instance].uiTarget == NULL){ +#if DEBUG + post("dssi~: unable to send to NULL target"); +#endif + return; + } + if(x->instances[instance].ui_osc_control_path == NULL){ +#if DEBUG + post("dssi~: unable to send to NULL control path"); +#endif + return; + } + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_control_path, "if", port, value); + } + + +} + +static void dssi_tilde_set_control_input_by_name (t_dssi_tilde *x, + const char* name, + float value, + t_int instance) +{ + unsigned port_index = 0; + unsigned ctrl_input_index = 0; + int found_port = 0; /* boolean */ + + if (name == NULL || strlen (name) == 0) { + post("dssi~: no control port name specified"); + return; + } + + /* compare control name to LADSPA control input ports' names + case-insensitively */ + found_port = 0; + ctrl_input_index = 0; + for (port_index = 0; port_index < x->descriptor->LADSPA_Plugin->PortCount; port_index++) + { + LADSPA_PortDescriptor port_type; + port_type = x->descriptor->LADSPA_Plugin->PortDescriptors[port_index]; + if (LADSPA_IS_PORT_CONTROL (port_type) + && LADSPA_IS_PORT_INPUT (port_type)) + { + const char* port_name = NULL; + unsigned cmp_length = 0; + port_name = x->descriptor->LADSPA_Plugin->PortNames[port_index]; + cmp_length = MIN (strlen (name), strlen (port_name)); + if (cmp_length != 0 + && strncasecmp (name, port_name, cmp_length) == 0) + { + /* found the first port to match */ + found_port = 1; + break; + } + ctrl_input_index++; + } + } + + if (!found_port) + { + post("dssi~: plugin doesn't have a control input port named \"%s\"", + name); + return; + } + + dssi_tilde_set_control_input_by_index (x, ctrl_input_index, value, instance); +} + +static void dssi_tilde_control (t_dssi_tilde *x, + t_symbol* ctrl_name, + t_float ctrl_value, + t_float instance_f) +/* Change the value of a named control port of the plug-in */ +{ + unsigned parm_num = 0; + t_int instance = (t_int)instance_f - 1; + int n_instances = x->n_instances; + + if (instance > x->n_instances || instance < -1){ + post("dssi~: control: invalid instance number %d", instance); + return; + } + +#if DEBUG + post("Received LADSPA control data for instance %d", instance); +#endif + + if (ctrl_name->s_name == NULL || strlen (ctrl_name->s_name) == 0) { + post("dssi~: control messages must have a name and a value"); + return; + } + parm_num = dssi_tilde_get_parm_number (x, ctrl_name->s_name); + if (parm_num) { + if(instance >= 0) + dssi_tilde_set_control_input_by_index (x, parm_num - 1, + ctrl_value, instance); + else if (instance == -1){ + while(n_instances--) + dssi_tilde_set_control_input_by_index (x, parm_num - 1, + ctrl_value, n_instances); + } + } + else { + if(instance >= 0) + dssi_tilde_set_control_input_by_name (x, ctrl_name->s_name, + ctrl_value, instance); + else if (instance == -1){ + while(n_instances--) + dssi_tilde_set_control_input_by_name (x, + ctrl_name->s_name, ctrl_value, n_instances); + } + } +} + +static void dssi_tilde_info (t_dssi_tilde *x){ + unsigned int i, + ctrl_portno, + audio_portno; + t_atom argv[7]; + + ctrl_portno = audio_portno = 0; + + if (x->descriptor == NULL) + return; + + for(i = 0; i < x->descriptor->LADSPA_Plugin->PortCount; i++){ + memcpy(&argv[0], &x->port_info[i].type, + sizeof(t_atom)); + memcpy(&argv[1], &x->port_info[i].data_type, + sizeof(t_atom)); + memcpy(&argv[3], &x->port_info[i].name, + sizeof(t_atom)); + memcpy(&argv[4], &x->port_info[i].lower_bound, + sizeof(t_atom)); + memcpy(&argv[5], &x->port_info[i].upper_bound, + sizeof(t_atom)); + memcpy(&argv[6], &x->port_info[i].p_default, + sizeof(t_atom)); + argv[2].a_type = A_FLOAT; + if(!strcmp(argv[1].a_w.w_symbol->s_name, "control")) + argv[2].a_w.w_float = (t_float)++ctrl_portno; + + else if(!strcmp(argv[1].a_w.w_symbol->s_name, "audio")) + argv[2].a_w.w_float = (t_float)++audio_portno; + + outlet_anything (x->control_outlet, gensym ("port"), 7, argv); + } +} + +static void dssi_tilde_ladspa_description(t_dssi_tilde *x, t_atom *at, + DSSI_Descriptor *psDescriptor){ + at[0].a_w.w_symbol = + gensym ((char*)psDescriptor->LADSPA_Plugin->Name); + outlet_anything (x->control_outlet, gensym ("name"), 1, at); + at[0].a_w.w_symbol = + gensym ((char*)psDescriptor->LADSPA_Plugin->Label); + outlet_anything (x->control_outlet, gensym ("label"), 1, at); + at[0].a_type = A_FLOAT; + at[0].a_w.w_float = psDescriptor->LADSPA_Plugin->UniqueID; + outlet_anything (x->control_outlet, gensym ("id"), 1, at); + at[0].a_type = A_SYMBOL; + at[0].a_w.w_symbol = + gensym ((char*)psDescriptor->LADSPA_Plugin->Maker); + outlet_anything (x->control_outlet, gensym ("maker"), 1, at); +} + +static void dssi_tilde_ladspa_describe(const char * pcFullFilename, + void * pvPluginHandle, + DSSI_Descriptor_Function fDescriptorFunction, + void* user_data, + int is_DSSI) { + + t_dssi_tilde *x = (((void**)user_data)[0]); + t_atom at[1]; + DSSI_Descriptor *psDescriptor; + long lIndex; + + at[0].a_type = A_SYMBOL; + at[0].a_w.w_symbol = gensym ((char*)pcFullFilename); + outlet_anything (x->control_outlet, gensym ("library"), 1, at); + + if(is_DSSI){ +#if DEBUG + post("DSSI plugin found by listinfo"); +#endif + for (lIndex = 0; + (psDescriptor = (DSSI_Descriptor *) + fDescriptorFunction(lIndex)) != NULL; lIndex++) + dssi_tilde_ladspa_description(x, &at[0], psDescriptor); + } + + else if(!is_DSSI) + lIndex = 0; + do{ + psDescriptor = ladspa_to_dssi((LADSPA_Descriptor *)fDescriptorFunction(lIndex++)); + /* psDescriptor = (DSSI_Descriptor *)calloc(1, + sizeof(DSSI_Descriptor)); + ((DSSI_Descriptor *)psDescriptor)->DSSI_API_Version = 1; + ((DSSI_Descriptor *)psDescriptor)->LADSPA_Plugin = + (LADSPA_Descriptor *) + fDescriptorFunction(lIndex++); + */ + if(psDescriptor->LADSPA_Plugin != NULL){ + dssi_tilde_ladspa_description(x, &at[0], psDescriptor); + free((DSSI_Descriptor *)psDescriptor); + } + else + break; + } while(1); + /* Not needed + dlclose(pvPluginHandle); + */ +} + +static void dssi_tilde_list_plugins (t_dssi_tilde *x) { + void* user_data[1]; + user_data[0] = x; + LADSPAPluginSearch(dssi_tilde_ladspa_describe,(void*)user_data); +} + +static int osc_debug_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, t_dssi_tilde *x) +{ + int i; + printf("got unhandled OSC message:\npath: <%s>\n", path); + for (i=0; iinstances[instance].currentProgram; + + argv[0].a_w.w_float = (t_float)instance; + argv[1].a_w.w_float = x->instances[instance].pluginPrograms[i].Program; + argv[2].a_w.w_symbol = + gensym ((char*)x->instances[instance].pluginPrograms[i].Name); + outlet_anything (x->control_outlet, gensym ("program"), 3, argv); + +} + +static void dssi_tilde_program_change(t_dssi_tilde *x, int instance){ + /* jack-dssi-host queues program changes by using pending program change variables. In the audio callback, if a program change is received via MIDI it over writes the pending value (if any) set by the GUI. If unset, or processed the value will default back to -1. The following call to select_program is then made. I don't think it eventually needs to be done this way - i.e. do we need 'pending'? */ +#if DEBUG + post("executing program change"); +#endif + if (x->instances[instance].pendingProgramChange >= 0){ + if (x->instances[instance].pendingBankLSB >= 0) { + if (x->instances[instance].pendingBankMSB >= 0) { + x->instances[instance].currentBank = x->instances[instance].pendingBankLSB + 128 * x->instances[instance].pendingBankMSB; + } + else { + x->instances[instance].currentBank = x->instances[instance].pendingBankLSB + + 128 * (x->instances[instance].currentBank / 128); + } + } + else if (x->instances[instance].pendingBankMSB >= 0) { + x->instances[instance].currentBank = (x->instances[instance].currentBank % 128) + 128 * x->instances[instance].pendingBankMSB; + } + + x->instances[instance].currentProgram = x->instances[instance].pendingProgramChange; + + if (x->descriptor->select_program) { + x->descriptor->select_program(x->instanceHandles[instance], + x->instances[instance].currentBank, x->instances[instance].currentProgram); + } + if (x->instances[instance].uiNeedsProgramUpdate){ +#if DEBUG + post("Updating GUI program"); +#endif + /* FIX - this is a hack to make text ui work*/ + if(x->instances[instance].uiTarget) + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_program_path, "ii", + x->instances[instance].currentBank, + x->instances[instance].currentProgram); + + } + x->instances[instance].uiNeedsProgramUpdate = 0; + x->instances[instance].pendingProgramChange = -1; + x->instances[instance].pendingBankMSB = -1; + x->instances[instance].pendingBankLSB = -1; + } + dssi_tilde_get_current_program(x, instance); +} + +static int osc_program_handler(t_dssi_tilde *x, lo_arg **argv, int instance) +{ + unsigned long bank = argv[0]->i; + unsigned long program = argv[1]->i; + int i; + int found = 0; + +#if DEBUG + post("osc_program_hander active!"); + + post("%d programs", x->instances[instance].plugin_ProgramCount); + +#endif + for (i = 0; i < x->instances[instance].plugin_ProgramCount; ++i) { + if (x->instances[instance].pluginPrograms[i].Bank == bank && + x->instances[instance].pluginPrograms[i].Program == program) { + post("dssi~: OSC: setting bank %u, program %u, name %s\n", + bank, program, x->instances[instance].pluginPrograms[i].Name); + + found = 1; + break; + } + } + + if (!found) { + printf(": OSC: UI requested unknown program: bank %d, program %u: sending to plugin anyway (plugin should ignore it)\n", (int)bank,(int)program); + } + + x->instances[instance].pendingBankMSB = bank / 128; + x->instances[instance].pendingBankLSB = bank % 128; + x->instances[instance].pendingProgramChange = program; +#if DEBUG + post("bank = %d, program = %d, BankMSB = %d BankLSB = %d", bank, program, x->instances[instance].pendingBankMSB, x->instances[instance].pendingBankLSB); +#endif + dssi_tilde_program_change(x, instance); + + return 0; +} + +static int osc_control_handler(t_dssi_tilde *x, lo_arg **argv, int instance) +{ + int port = argv[0]->i; + LADSPA_Data value = argv[1]->f; + + x->plugin_ControlDataInput[x->instances[instance].plugin_PortControlInNumbers[port]] = value; +#if DEBUG + post("OSC: port %d = %f", port, value); +#endif + + return 0; +} + +static int osc_midi_handler(t_dssi_tilde *x, lo_arg **argv, t_int instance) +{ + + int ev_type = 0, chan = 0; +#if DEBUG + post("OSC: got midi request for" + "(%02x %02x %02x %02x)", + argv[0]->m[0], argv[0]->m[1], argv[0]->m[2], argv[0]->m[3]); +#endif + chan = instance; +#if DEBUG + post("channel: %d", chan); +#endif + + if(argv[0]->m[1] <= 239){ + if(argv[0]->m[1] >= 224) + ev_type = SND_SEQ_EVENT_PITCHBEND; + else if(argv[0]->m[1] >= 208) + ev_type = SND_SEQ_EVENT_CHANPRESS; + else if(argv[0]->m[1] >= 192) + ev_type = SND_SEQ_EVENT_PGMCHANGE; + else if(argv[0]->m[1] >= 176) + ev_type = SND_SEQ_EVENT_CONTROLLER; + else if(argv[0]->m[1] >= 160) + ev_type = SND_SEQ_EVENT_KEYPRESS; + else if(argv[0]->m[1] >= 144) + ev_type = SND_SEQ_EVENT_NOTEON; + else if(argv[0]->m[1] >= 128) + ev_type = SND_SEQ_EVENT_NOTEOFF; + } + if(ev_type != 0) + MIDIbuf(ev_type, chan, argv[0]->m[2], argv[0]->m[3], x); + + return 0; +} + +static int osc_configure_handler(t_dssi_tilde *x, lo_arg **argv, int instance) +{ + const char *key = (const char *)&argv[0]->s; + const char *value = (const char *)&argv[1]->s; + char *message; + +#if DEBUG + post("osc_configure_handler active!"); +#endif + + if (x->descriptor->configure) { + + if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, + strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { + fprintf(stderr, ": OSC: UI for plugin '' attempted to use reserved configure key \"%s\", ignoring\n", key); + return 0; + } + + message = x->descriptor->configure(x->instanceHandles[instance], key, value); + if (message) { + printf(": on configure '%s', plugin '' returned error '%s'\n", + key, message); + free(message); + } + + query_programs(x, instance); + + } + + return 0; +} + +static int osc_exiting_handler(t_dssi_tilde *x, lo_arg **argv, int instance){ + +#if DEBUG + post("exiting handler called: Freeing ui_osc"); +#endif + if(x->instances[instance].uiTarget){ + lo_address_free(x->instances[instance].uiTarget); + x->instances[instance].uiTarget = NULL; + } + free(x->instances[instance].ui_osc_control_path); + free(x->instances[instance].ui_osc_configure_path); + free(x->instances[instance].ui_osc_hide_path); + free(x->instances[instance].ui_osc_program_path); + free(x->instances[instance].ui_osc_show_path); + free(x->instances[instance].ui_osc_quit_path); + x->instances[instance].uiTarget = NULL; + x->instances[instance].ui_osc_control_path = NULL; + x->instances[instance].ui_osc_configure_path = NULL; + x->instances[instance].ui_osc_hide_path = NULL; + x->instances[instance].ui_osc_program_path = NULL; + x->instances[instance].ui_osc_show_path = NULL; + x->instances[instance].ui_osc_quit_path = NULL; + + x->instances[instance].ui_hidden = 1; + + return 0; +} + +static int osc_update_handler(t_dssi_tilde *x, lo_arg **argv, int instance) +{ + const char *url = (char *)&argv[0]->s; + const char *path; + t_int i; + char *host, *port; + t_dssi_configure_pair *p; + + p = x->configure_buffer_head; + +#if DEBUG + post("OSC: got update request from <%s>, instance %d", url, instance); +#endif + + if (x->instances[instance].uiTarget) + lo_address_free(x->instances[instance].uiTarget); + host = lo_url_get_hostname(url); + port = lo_url_get_port(url); + x->instances[instance].uiTarget = lo_address_new(host, port); + free(host); + free(port); + + path = lo_url_get_path(url); + + if (x->instances[instance].ui_osc_control_path) + free(x->instances[instance].ui_osc_control_path); + x->instances[instance].ui_osc_control_path = + (char *)malloc(strlen(path) + 10); + sprintf(x->instances[instance].ui_osc_control_path, "%s/control", path); + + if (x->instances[instance].ui_osc_configure_path) + free(x->instances[instance].ui_osc_configure_path); + x->instances[instance].ui_osc_configure_path = + (char *)malloc(strlen(path) + 12); + sprintf(x->instances[instance].ui_osc_configure_path, "%s/configure", path); + + if (x->instances[instance].ui_osc_program_path) + free(x->instances[instance].ui_osc_program_path); + x->instances[instance].ui_osc_program_path = + (char *)malloc(strlen(path) + 10); + sprintf(x->instances[instance].ui_osc_program_path, "%s/program", path); + + if (x->instances[instance].ui_osc_quit_path) + free(x->instances[instance].ui_osc_quit_path); + x->instances[instance].ui_osc_quit_path = (char *)malloc(strlen(path) + 10); + sprintf(x->instances[instance].ui_osc_quit_path, "%s/quit", path); + + if (x->instances[instance].ui_osc_show_path) + free(x->instances[instance].ui_osc_show_path); + x->instances[instance].ui_osc_show_path = (char *)malloc(strlen(path) + 10); + sprintf(x->instances[instance].ui_osc_show_path, "%s/show", path); + + if (x->instances[instance].ui_osc_hide_path) + free(x->instances[instance].ui_osc_hide_path); + x->instances[instance].ui_osc_hide_path = (char *)malloc(strlen(path) + 10); + sprintf(x->instances[instance].ui_osc_hide_path, "%s/hide", path); + + free((char *)path); + + while(p){ + if(p->instance == instance) + dssi_tilde_send_configure(x, p->key, + p->value, instance); + p = p->next; + } + + /* Send current bank/program (-FIX- another race...) */ + if (x->instances[instance].pendingProgramChange >= 0) + dssi_tilde_program_change(x, instance); +#if DEBUG + post("pendingProgramChange = %d", x->instances[instance].pendingProgramChange); +#endif + if (x->instances[instance].pendingProgramChange < 0) { + unsigned long bank = x->instances[instance].currentBank; + unsigned long program = x->instances[instance].currentProgram; + x->instances[instance].uiNeedsProgramUpdate = 0; + if (x->instances[instance].uiTarget) { + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_program_path, + "ii", bank, program); + } + } + + /* Send control ports */ + for (i = 0; i < x->plugin_controlIns; i++) { + lo_send(x->instances[instance].uiTarget, x->instances[instance].ui_osc_control_path, "if", + x->plugin_ControlInPortNumbers[i], x->plugin_ControlDataInput[i]); +#if DEBUG + post("Port: %d, Default value: %.2f", x->plugin_ControlInPortNumbers[i], x->plugin_ControlDataInput[i]); +#endif + } + + /* Send 'show' */ + if (x->instances[instance].ui_show) { + lo_send(x->instances[instance].uiTarget, x->instances[instance].ui_osc_show_path, ""); + x->instances[instance].ui_hidden = 0; + x->instances[instance].ui_show = 0; + } + + return 0; +} + +static void dssi_tilde_osc_setup(t_dssi_tilde *x, int instance){ + + if(instance == 0){ + x->osc_thread = lo_server_thread_new(NULL, osc_error); + char *osc_url_tmp; + osc_url_tmp = lo_server_thread_get_url(x->osc_thread); +#if DEBUG + post("string length of osc_url_tmp:%d", strlen(osc_url_tmp)); +#endif + x->osc_url_base = (char *)malloc(sizeof(char) + * (strlen(osc_url_tmp) + strlen("dssi") + 1)); + sprintf(x->osc_url_base, "%s%s", osc_url_tmp, "dssi"); + free(osc_url_tmp); + lo_server_thread_add_method(x->osc_thread, NULL, NULL, + osc_message_handler, x); + lo_server_thread_start(x->osc_thread); + } + x->instances[instance].osc_url_path = (char *)malloc(sizeof(char) * + (strlen(x->plugin_basename) + strlen(x->descriptor->LADSPA_Plugin->Label) + strlen("chan00") + 3)); + sprintf(x->instances[instance].osc_url_path, "%s/%s/chan%02d", x->plugin_basename, + x->descriptor->LADSPA_Plugin->Label, instance); +#if DEBUG + post("OSC Path is: %s", x->instances[instance].osc_url_path); + post("OSC thread started: %s", x->osc_url_base); +#endif +} + +static void dssi_tilde_init_programs(t_dssi_tilde *x, int instance){ + +#if DEBUG + post("Setting up program data"); +#endif + query_programs(x, instance); + if (x->descriptor->select_program && + x->instances[instance].plugin_ProgramCount > 0) { + + /* select program at index 0 */ + unsigned long bank = + x->instances[instance].pluginPrograms[0].Bank; + x->instances[instance].pendingBankMSB = bank / 128; + x->instances[instance].pendingBankLSB = bank % 128; + x->instances[instance].pendingProgramChange = + x->instances[instance].pluginPrograms[0].Program; + x->instances[instance].uiNeedsProgramUpdate = 1; + } +} + +static void dssi_tilde_load_gui(t_dssi_tilde *x, int instance){ + t_int err = 0; + char *osc_url; + char *gui_path; + struct dirent *dir_entry = NULL; + char *gui_base; + size_t baselen; + DIR *dp; + char *gui_str; + + gui_base = (char *)malloc((baselen = sizeof(char) * (strlen(x->plugin_full_path) - strlen(".so"))) + 1); + + strncpy(gui_base, x->plugin_full_path, baselen); + gui_base[baselen] = '\0'; + + /* don't use strndup - GNU only */ + /* gui_base = strndup(x->plugin_full_path, baselen);*/ +#if DEBUG + post("gui_base: %s", gui_base); +#endif + + gui_str = (char *)malloc(sizeof(char) * (strlen("channel 00") + 1)); + sprintf (gui_str,"channel %02d", instance); + +#if DEBUG + post("GUI name string, %s", gui_str); +#endif + + if(!(dp = opendir(gui_base))){ + post("dssi~: unable to find GUI in %s, continuing without...", gui_base); + return; + } + else { + while((dir_entry = readdir(dp))){ + if (dir_entry->d_name[0] == '.') continue; + if (strchr(dir_entry->d_name, '_')){ + if (strstr(dir_entry->d_name, "gtk") || + strstr(dir_entry->d_name, "qt") || + strstr(dir_entry->d_name, "text")) + break; + } + } +#if DEBUG + post("GUI filename: %s", dir_entry->d_name); +#endif + } + + gui_path = (char *)malloc(sizeof(char) * (strlen(gui_base) + strlen("/") + + strlen(dir_entry->d_name) + 1)); + + sprintf(gui_path, "%s/%s", gui_base, dir_entry->d_name); + + free(gui_base); +#if DEBUG + post("gui_path: %s", gui_path); +#endif + + osc_url = (char *)malloc + (sizeof(char) * (strlen(x->osc_url_base) + + strlen(x->instances[instance].osc_url_path) + 2)); + + /* char osc_url[1024];*/ + sprintf(osc_url, "%s/%s", x->osc_url_base, + x->instances[instance].osc_url_path); + post("dssi~: instance %d URL: %s",instance, osc_url); +#if DEBUG + post("Trying to open GUI!"); +#endif + + x->instances[instance].gui_pid = fork(); + if (x->instances[instance].gui_pid == 0){ + //pthread_mutex_init(&x->midiEventBufferMutex, NULL); + err = execlp(gui_path, gui_path, osc_url, dir_entry->d_name, + x->descriptor->LADSPA_Plugin->Label, gui_str, NULL); + perror("exec failed"); + exit(1); /* terminates the process */ + } + +#if DEBUG + post("errorcode = %d", err); +#endif + + free(gui_path); + free(osc_url); + free(gui_str); + if(dp){ + +#if DEBUG + post("directory handle closed = %d", closedir(dp)); +#endif + } +} + +static void MIDIbuf(int type, int chan, int param, int val, t_dssi_tilde *x){ + + if(chan > x->n_instances - 1 || chan < 0){ + post("dssi~: note discarded: MIDI data is destined for a channel that doesn't exist"); + return; + } + + t_int time_ref = x->time_ref; + t_int mapped; + + pthread_mutex_lock(&x->midiEventBufferMutex); + + mapped = x->channelMap[chan + 1] - 1; + + x->midiEventBuf[x->bufWriteIndex].time.time.tv_sec = + (t_int)(clock_gettimesince(time_ref) * .001); + x->midiEventBuf[x->bufWriteIndex].time.time.tv_nsec = + (t_int)(clock_gettimesince(time_ref) * 1000); /*actually usec - we can't store this in nsec! */ + + if ((type == SND_SEQ_EVENT_NOTEON && val != 0) || + type != SND_SEQ_EVENT_NOTEON) { + x->midiEventBuf[x->bufWriteIndex].type = type; + switch (type) { + case SND_SEQ_EVENT_NOTEON: + x->midiEventBuf[x->bufWriteIndex].data.note.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.note.note = param; + x->midiEventBuf[x->bufWriteIndex].data.note.velocity = val; + break; + case SND_SEQ_EVENT_NOTEOFF: + x->midiEventBuf[x->bufWriteIndex].data.note.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.note.note = param; + x->midiEventBuf[x->bufWriteIndex].data.note.velocity = val; + break; + case SND_SEQ_EVENT_CONTROLLER: + x->midiEventBuf[x->bufWriteIndex].data.control.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.control.param = param; + x->midiEventBuf[x->bufWriteIndex].data.control.value = val; + break; + case SND_SEQ_EVENT_PITCHBEND: + x->midiEventBuf[x->bufWriteIndex].data.control.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.control.param = 0; + x->midiEventBuf[x->bufWriteIndex].data.control.value = val; + break; + case SND_SEQ_EVENT_CHANPRESS: + x->midiEventBuf[x->bufWriteIndex].data.control.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.control.param = 0; + x->midiEventBuf[x->bufWriteIndex].data.control.value = val; + break; + case SND_SEQ_EVENT_KEYPRESS: + x->midiEventBuf[x->bufWriteIndex].data.note.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.note.note = param; + x->midiEventBuf[x->bufWriteIndex].data.note.velocity = val; + break; + case SND_SEQ_EVENT_PGMCHANGE: + x->instances[mapped].pendingBankMSB = (param - 1) / 128; + x->instances[mapped].pendingBankLSB = (param - 1) % 128; + x->instances[mapped].pendingProgramChange = val; + x->instances[mapped].uiNeedsProgramUpdate = 1; +#if DEBUG + post("pgm chabge received in buffer: MSB: %d, LSB %d, prog: %d", + x->instances[mapped].pendingBankMSB, x->instances[mapped].pendingBankLSB, val); +#endif + dssi_tilde_program_change(x, mapped); + break; + } + } + else if (type == SND_SEQ_EVENT_NOTEON && val == 0) { + x->midiEventBuf[x->bufWriteIndex].type = SND_SEQ_EVENT_NOTEOFF; + x->midiEventBuf[x->bufWriteIndex].data.note.channel = mapped; + x->midiEventBuf[x->bufWriteIndex].data.note.note = param; + x->midiEventBuf[x->bufWriteIndex].data.note.velocity = val; + } + +#if DEBUG + post("MIDI received in buffer: chan %d, param %d, val %d, mapped to %d", + chan, param, val, mapped); +#endif + x->bufWriteIndex = (x->bufWriteIndex + 1) % EVENT_BUFSIZE; + pthread_mutex_unlock(&x->midiEventBufferMutex); /**release mutex*/ +} + +static void dssi_tilde_list(t_dssi_tilde *x, t_symbol *s, int argc, t_atom *argv) { + char *msg_type; + int ev_type = 0; + msg_type = (char *)malloc(TYPE_STRING_SIZE); + atom_string(argv, msg_type, TYPE_STRING_SIZE); + int chan = (int)atom_getfloatarg(1, argc, argv) - 1; + int param = (int)atom_getfloatarg(2, argc, argv); + int val = (int)atom_getfloatarg(3, argc, argv); + int n_instances = x->n_instances; + + switch (msg_type[0]){ + case ASCII_n: ev_type = SND_SEQ_EVENT_NOTEON; + break; + case ASCII_c: ev_type = SND_SEQ_EVENT_CONTROLLER; + break; + case ASCII_p: ev_type = SND_SEQ_EVENT_PGMCHANGE; + break; + case ASCII_b: ev_type = SND_SEQ_EVENT_PITCHBEND; + break; + case ASCII_t: ev_type = SND_SEQ_EVENT_CHANPRESS; + break; + case ASCII_a: ev_type = SND_SEQ_EVENT_KEYPRESS; + break; + } +#if DEBUG + post("initial midi NOTE:, arg1 = %d, arg2 = %d, arg3 = %d, arg4 = %d",ev_type,chan,param,val); +#endif + if(ev_type != 0){ + if(chan >= 0) + MIDIbuf(ev_type, chan, param, val, x); + else { + while(n_instances--) + MIDIbuf(ev_type, n_instances, param, val, x); + } + } + free(msg_type); +} + +static char *dssi_tilde_send_configure(t_dssi_tilde *x, char *key, + char *value, t_int instance){ + + char *debug; + + debug = x->descriptor->configure( + x->instanceHandles[instance], + key, value); + if(x->instances[instance].uiTarget != NULL && x->is_DSSI) + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_configure_path, + "ss", key, value); + query_programs(x, instance); + + return debug; +} + +static void dssi_show(t_dssi_tilde *x, t_int instance, t_int toggle){ + + if(x->instances[instance].uiTarget){ + if (x->instances[instance].ui_hidden && toggle) { + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_show_path, ""); + x->instances[instance].ui_hidden = 0; + } + else if (!x->instances[instance].ui_hidden && !toggle) { + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_hide_path, ""); + x->instances[instance].ui_hidden = 1; + } + } + else if(toggle){ + x->instances[instance].ui_show = 1; + dssi_tilde_load_gui(x, instance); + + } +} + +static t_int dssi_tilde_configure_buffer(t_dssi_tilde *x, char *key, + char *value, t_int instance){ + + /*#ifdef BLAH*/ + t_dssi_configure_pair *current, *p; + t_int add_node; + add_node = 0; + current = x->configure_buffer_head; + + while(current){ + if(!strcmp(current->key, key) && + current->instance == instance) + break; + current = current->next; + } + if(current) + free(current->value); + else { + current = (t_dssi_configure_pair *)malloc(sizeof + (t_dssi_configure_pair)); + current->next = x->configure_buffer_head; + x->configure_buffer_head = current; + current->key = strdup(key); + current->instance = instance; + } + current->value = strdup(value); + + p = x->configure_buffer_head; + + /*FIX: eventually give ability to query this buffer (to outlet?) */ +#if DEBUG + while(p){ + post("key: %s", p->key); + post("val: %s", p->value); + post("instance: %d", p->instance); + p = p->next; + } +#endif + return 0; +} + +static t_int dssi_tilde_configure_buffer_free(t_dssi_tilde *x){ + t_dssi_configure_pair *curr, *prev; + prev = curr = NULL; + + for(curr = x->configure_buffer_head; curr != NULL; curr = curr->next){ + if(prev != NULL) + free(prev); + free(curr->key); + free(curr->value); + prev = curr; + } + free(curr); + + return 0; +} + +/* + static void dssi_tilde_plug (t_dssi_tilde *x, t_symbol* plug_name) { + plugin_tilde_ladspa_close_plugin(x); + x->plugin_library_filename = NULL; + x->plugin_library_filename = plugin_tilde_search_plugin_by_label (x, plug_name->s_name); + if (x->plugin_library_filename == NULL) + post("plugin~: plugin not found in any library"); + if (plugin_tilde_open_plugin (x, plug_name->s_name, x->plugin_library_filename,(unsigned long)sys_getsr ())) + post("plugin~: Unable to open plugin"); + } + */ + +static t_int dssi_tilde_reset(t_dssi_tilde *x, t_float instance_f){ + + t_int instance = (t_int)instance_f - 1; + if (instance == -1){ + for(instance = 0; instance < x->n_instances; instance++) { + if (x->descriptor->LADSPA_Plugin->deactivate && + x->descriptor->LADSPA_Plugin->activate){ + x->descriptor->LADSPA_Plugin->deactivate + (x->instanceHandles[instance]); + x->descriptor->LADSPA_Plugin->activate + (x->instanceHandles[instance]); + } + } + } + else if (x->descriptor->LADSPA_Plugin->deactivate && + x->descriptor->LADSPA_Plugin->activate) { + x->descriptor->LADSPA_Plugin->deactivate + (x->instanceHandles[instance]); + x->descriptor->LADSPA_Plugin->activate + (x->instanceHandles[instance]); + } + return 0; +} + +static void dssi_tilde_search_plugin_callback ( + const char* full_filename, + void* plugin_handle, + DSSI_Descriptor_Function descriptor_function, + void* user_data, + int is_DSSI) +{ + DSSI_Descriptor* descriptor = NULL; + unsigned plug_index = 0; + + char** out_lib_name = (char**)(((void**)user_data)[0]); + char* name = (char*)(((void**)user_data)[1]); + + /* Stop searching when a first matching plugin is found */ + if (*out_lib_name == NULL) + { +#if DEBUG + post("plugin~: searching plugin \"%s\"...", full_filename); +#endif + for(plug_index = 0;(is_DSSI ? + (descriptor = + (DSSI_Descriptor *)descriptor_function(plug_index)) : + ((DSSI_Descriptor *)(descriptor = + ladspa_to_dssi((LADSPA_Descriptor *) + descriptor_function(plug_index)))->LADSPA_Plugin)) + != NULL; plug_index++){ +#if DEBUG + post("plugin~: label \"%s\"", descriptor->LADSPA_Plugin->Label); +#endif + if (strcasecmp (name, descriptor->LADSPA_Plugin->Label) + == 0) + { + *out_lib_name = strdup (full_filename); +#if DEBUG + post("plugin~: found plugin \"%s\" in library \"%s\"", + name, full_filename); +#endif + /* if(!is_DSSI){ + free((DSSI_Descriptor *)descriptor); + descriptor = NULL; + }*/ + break; + } + /* if (descriptor != NULL){ + free((DSSI_Descriptor *)descriptor); + descriptor = NULL; + }*/ + } + } +} + +static const char* plugin_tilde_search_plugin_by_label (t_dssi_tilde *x, + const char *name) +{ + char* lib_name = NULL; + void* user_data[2]; + + user_data[0] = (void*)(&lib_name); + user_data[1] = (void*)name; +#if DEBUG + post("search plugin by label: '%s'\n", name); +#endif + + lib_name = NULL; + LADSPAPluginSearch (dssi_tilde_search_plugin_callback, + (void*)user_data); + + /* The callback (allocates and) writes lib_name, if it finds the plugin */ + return lib_name; + +} + +static t_int dssi_tilde_dssi_methods(t_dssi_tilde *x, t_symbol *s, int argc, t_atom *argv) +{ + if (!x->is_DSSI) { + post("dssi~: plugin is not a DSSI plugin, operation not supported"); + return 0; + } + char *msg_type, + *debug, + *filename, + *filepath, + *key, + *value, + *temp, + mydir[MAXPDSTRING]; + int instance = -1, + pathlen, + toggle, + fd, + n_instances = x->n_instances, + count, + i, + chan, + maxpatches; + t_float val; + long filelength = 0; + unsigned char *raw_patch_data = NULL; + FILE *fp = NULL; + size_t filename_length, key_size, value_size; + dx7_patch_t *patchbuf, *firstpatch; + msg_type = (char *)malloc(TYPE_STRING_SIZE); + atom_string(argv, msg_type, TYPE_STRING_SIZE); + debug = NULL; + key = NULL; + value = NULL; + maxpatches = 128; + patchbuf = malloc(32 * sizeof(dx7_patch_t)); + firstpatch = &patchbuf[0]; + val = 0; + + /*FIX: Temporary - at the moment we always load the first 32 patches to 0 */ + if(strcmp(msg_type, "configure")){ + instance = (int)atom_getfloatarg(2, argc, argv) - 1; + + if(!strcmp(msg_type, "load") && x->descriptor->configure){ + filename = argv[1].a_w.w_symbol->s_name; + post("dssi~: loading patch: %s for instance %d", filename, instance); + + if(!strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter") || + !strcmp(x->descriptor->LADSPA_Plugin->Label, "hexter6")) { + + key = malloc(10 * sizeof(char)); /* holds "patchesN" */ + strcpy(key, "patches0"); + + /* FIX: duplicates code from load_plugin() */ + fd = canvas_open(x->x_canvas, filename, "", + mydir, &filename, MAXPDSTRING, 0); + + if(fd >= 0){ + filepath = mydir; + pathlen = strlen(mydir); + temp = &mydir[pathlen]; + sprintf(temp, "/%s", filename); + fp = fopen(filepath, "rb"); + } + else{ + post("dssi~: unable to get file descriptor"); + } + + /*From dx7_voice_data by Sean Bolton */ + if(fp == NULL){ + post("dssi~: unable to open patch file: %s", filename); + return 0; + } + if (fseek(fp, 0, SEEK_END) || + (filelength = ftell(fp)) == -1 || + fseek(fp, 0, SEEK_SET)) { + post("dssi~: couldn't get length of patch file: %s", + filename); + fclose(fp); + return 0; + } + if (filelength == 0) { + post("dssi~: patch file has zero length"); + fclose(fp); + return 0; + } else if (filelength > 16384) { + post("dssi~: patch file is too large"); + fclose(fp); + return 0; + } + if (!(raw_patch_data = (unsigned char *) + malloc(filelength))) { + post( + "dssi~: couldn't allocate memory for raw patch file"); + fclose(fp); + return 0; + } + if (fread(raw_patch_data, 1, filelength, fp) + != (size_t)filelength) { + post("dssi~: short read on patch file: %s", filename); + free(raw_patch_data); + fclose(fp); + return 0; + } + fclose(fp); +#if DEBUG + post("Patch file length is %ul", filelength); +#endif + /* figure out what kind of file it is */ + filename_length = strlen(filename); + if (filename_length > 4 && + !strcmp(filename + filename_length - 4, ".dx7") && + filelength % DX7_VOICE_SIZE_PACKED == 0) { + /* It's a raw DX7 patch bank */ + +#if DEBUG + post("Raw DX7 format patch bank passed"); +#endif + count = filelength / DX7_VOICE_SIZE_PACKED; + if (count > maxpatches) + count = maxpatches; + memcpy(firstpatch, raw_patch_data, count * + DX7_VOICE_SIZE_PACKED); + + } else if (filelength > 6 && + raw_patch_data[0] == 0xf0 && + raw_patch_data[1] == 0x43 && + /*This was used to fix some problem with Galaxy exports - possibly dump in worng format. It is not needed, but it did work, so in future, we may be able to support more formats not just DX7 */ + /* ((raw_patch_data[2] & 0xf0) == 0x00 || + raw_patch_data[2] == 0x7e) &&*/ + (raw_patch_data[2] & 0xf0) == 0x00 && + raw_patch_data[3] == 0x09 && + (raw_patch_data[4] == 0x10 || + raw_patch_data[4] == 0x20) && + /* 0x10 is actual, 0x20 matches typo in manual */ + raw_patch_data[5] == 0x00) { + /* It's a DX7 sys-ex 32 voice dump */ + +#if DEBUG + post("SYSEX header check passed"); +#endif + + if (filelength != DX7_DUMP_SIZE_BULK || + raw_patch_data[DX7_DUMP_SIZE_BULK - 1] != 0xf7) { + post("dssi~: badly formatted DX7 32 voice dump!"); + count = 0; + +#ifdef CHECKSUM_PATCH_FILES_ON_LOAD + } else if (dx7_bulk_dump_checksum(&raw_patch_data[6], + DX7_VOICE_SIZE_PACKED * 32) != + raw_patch_data[DX7_DUMP_SIZE_BULK - 2]) { + + post("dssi~: DX7 32 voice dump with bad checksum!"); + count = 0; + +#endif + } else { + + count = 32; + if (count > maxpatches) + count = maxpatches; + memcpy(firstpatch, raw_patch_data + 6, count * DX7_VOICE_SIZE_PACKED); + + } + } else { + + /* unsuccessful load */ + post("dssi~: unknown patch bank file format!"); + count = 0; + + } + + free(raw_patch_data); + + if(count == 32) + value = encode_7in6((uint8_t *)&patchbuf[0].data[0], + count * DX7_VOICE_SIZE_PACKED); + + } + else if(!strcmp(x->descriptor->LADSPA_Plugin->Label, + "FluidSynth-DSSI")){ + key = malloc(6 * sizeof(char)); + strcpy(key, "load"); + value = filename; + } + else{ + post("dssi~: %s patches are not supported", + x->descriptor->LADSPA_Plugin->Label); + } + + } + + if(!strcmp(msg_type, "dir") && x->descriptor->configure){ + pathlen = strlen(argv[1].a_w.w_symbol->s_name) + 2; + x->project_dir = malloc((pathlen) * sizeof(char)); + atom_string(&argv[1], x->project_dir, pathlen); + post("dssi~: project directory for instance %d has been set to: %s", instance, x->project_dir); + key = DSSI_PROJECT_DIRECTORY_KEY; + value = x->project_dir; + } + + else if(!strcmp(msg_type, "dir")) + post("dssi~: %s %s: operation not supported", msg_type, + argv[1].a_w.w_symbol->s_name); + + if(!strcmp(msg_type, "show") || !strcmp(msg_type, "hide")){ + instance = (int)atom_getfloatarg(1, argc, argv) - 1; + if(!strcmp(msg_type, "show")) + toggle = 1; + else + toggle = 0; + + if(instance == -1){ + while(n_instances--) + dssi_show(x, n_instances, toggle); + } + else + dssi_show(x, instance, toggle); + } + + if(!strcmp(msg_type, "remap")) { + /* remap channel to instance */ + for(i = 0; i < x->n_instances && i < 128; i++){ + chan = (int)atom_getfloatarg(1 + i, argc, argv); + post("dssi~: remapped MIDI channel %d to %d", 1+i, chan); + x->channelMap[i+1] = chan; + } + } + + } + + /*Use this to send arbitrary configure message to plugin */ + else if(!strcmp(msg_type, "configure")){ + key = + (char *)malloc(key_size = (strlen(argv[1].a_w.w_symbol->s_name) + 2) * sizeof(char)); + atom_string(&argv[1], key, key_size); + if(argc >= 3){ + if (argv[2].a_type == A_FLOAT){ + val = atom_getfloatarg(2, argc, argv); + value = (char *)malloc(TYPE_STRING_SIZE * + sizeof(char)); + sprintf(value, "%.2f", val); + } + else if(argv[2].a_type == A_SYMBOL){ + value = + (char *)malloc(value_size = + (strlen(argv[2].a_w.w_symbol->s_name) + 2) * + sizeof(char)); + atom_string(&argv[2], value, value_size); + } + + } + + if(argc == 4 && argv[3].a_type == A_FLOAT) + instance = atom_getfloatarg(3, argc, argv) - 1; + else if (n_instances) + instance = -1; + } + + if(key != NULL && value != NULL){ + if(instance == -1){ + while(n_instances--){ + debug = dssi_tilde_send_configure( + x, key, value, n_instances); + dssi_tilde_configure_buffer(x, key, value, n_instances); + } + } + /*FIX: Put some error checking in here to make sure instance is valid*/ + else{ + + debug = dssi_tilde_send_configure(x, key, value, instance); + dssi_tilde_configure_buffer(x, key, value, instance); + } + } +#if DEBUG + post("The plugin returned %s", debug); +#endif + free(msg_type); + free(patchbuf); + + return 0; +} + +static void dssi_tilde_bang(t_dssi_tilde *x) +{ + t_atom at[3]; + + at[0].a_type = A_FLOAT; + at[1].a_type = A_SYMBOL; + at[2].a_type = A_SYMBOL; + + if(x->plugin_label != NULL){ + at[0].a_w.w_float = x->n_instances; + at[1].a_w.w_symbol = gensym ((char *)x->plugin_label); + } + else{ + at[0].a_w.w_float = 0; + at[1].a_w.w_symbol = gensym ("plugin"); + } + at[2].a_w.w_symbol = gensym ("instances"); + outlet_anything (x->control_outlet, gensym ("running"), 3, at); +} + +static t_int *dssi_tilde_perform(t_int *w) +{ + int N = (t_int)(w[2]); + t_dssi_tilde *x = (t_dssi_tilde *)(w[1]); + t_float **inputs = (t_float **)(&w[3]); + t_float **outputs = (t_float **)(&w[3] + x->plugin_ins); + int i, n, timediff, framediff, instance = 0; + /*See comment for dssi_tilde_plug_plugin */ + if(x->dsp){ + x->dsp_loop = 1; + + for(i = 0; i < x->plugin_ins; i++) + memcpy(x->plugin_InputBuffers[i], inputs[i], N * + sizeof(LADSPA_Data)); + + for (i = 0; i < x->n_instances; i++) + x->instanceEventCounts[i] = 0; + + for (;x->bufReadIndex != x->bufWriteIndex; x->bufReadIndex = + (x->bufReadIndex + 1) % EVENT_BUFSIZE) { + + instance = x->midiEventBuf[x->bufReadIndex].data.note.channel; + + /*This should never happen, but check anyway*/ + if(instance > x->n_instances || instance < 0){ + post( + "dssi~: %s: discarding spurious MIDI data, for instance %d", + x->descriptor->LADSPA_Plugin->Label, + instance); +#if DEBUG + post("n_instances = %d", x->n_instances); +#endif + continue; + } + + if (x->instanceEventCounts[instance] == EVENT_BUFSIZE){ + post("dssi~: MIDI overflow on channel %d", instance); + continue; + } + + timediff = (t_int)(clock_gettimesince(x->time_ref) * 1000) - + x->midiEventBuf[x->bufReadIndex].time.time.tv_nsec; + framediff = (t_int)((t_float)timediff * .000001 / x->sr_inv); + + if (framediff >= N || framediff < 0) + x->midiEventBuf[x->bufReadIndex].time.tick = 0; + else + x->midiEventBuf[x->bufReadIndex].time.tick = + N - framediff - 1; + + x->instanceEventBuffers[instance] + [x->instanceEventCounts[instance]] = + x->midiEventBuf[x->bufReadIndex]; +#if DEBUG + post("%s, note received on channel %d", + x->descriptor->LADSPA_Plugin->Label, + x->instanceEventBuffers[instance] + [x->instanceEventCounts[instance]].data.note.channel); +#endif + x->instanceEventCounts[instance]++; + +#if DEBUG + post("Instance event count for instance %d of %d: %d\n", + instance + 1, x->n_instances, x->instanceEventCounts[instance]); +#endif + + } + + i = 0; + while(i < x->n_instances){ + if(x->instanceHandles[i] && + x->descriptor->run_multiple_synths){ + x->descriptor->run_multiple_synths + (x->n_instances, x->instanceHandles, + (unsigned long)N, x->instanceEventBuffers, + &x->instanceEventCounts[0]); + break; + } + else if (x->instanceHandles[i] && + x->descriptor->run_synth){ + x->descriptor->run_synth(x->instanceHandles[i], + (unsigned long)N, x->instanceEventBuffers[i], + x->instanceEventCounts[i]); + i++; + } + else if (x->instanceHandles[i] && + x->descriptor->LADSPA_Plugin->run){ + x->descriptor->LADSPA_Plugin->run + (x->instanceHandles[i], N); + i++; + } + } + + + for(i = 0; i < x->plugin_outs; i++) + memcpy(outputs[i], (t_float *)x->plugin_OutputBuffers[i], N * + sizeof(LADSPA_Data)); + + /* + for(i = 0; i < x->plugin_outs; i++) + memcpy(x->outlets[i], (t_outlet *)x->plugin_OutputBuffers[i], N * + sizeof(LADSPA_Data));*/ + x->dsp_loop = 0; + } + return w + (x->plugin_ins + x->plugin_outs + 3); +} + +static void dssi_tilde_dsp(t_dssi_tilde *x, t_signal **sp) +{ + if(x->n_instances){ + + + t_int *dsp_vector, i, N, M; + + M = x->plugin_ins + x->plugin_outs + 2; + + dsp_vector = (t_int *) getbytes(M * sizeof(t_int)); + + dsp_vector[0] = (t_int)x; + dsp_vector[1] = (t_int)sp[0]->s_n; + + for(i = 2; i < M; i++) + dsp_vector[i] = (t_int)sp[i - 1]->s_vec; + + dsp_addv(dssi_tilde_perform, M, dsp_vector); + + + /* int n, m; + + t_float **outlets; + t_float **inlets; + + for(n = 0, m = 1; n < x->plugin_ins; n++, m++) + inlets[n] = sp[m]->s_vec; + for(n = 0; n < x->plugin_outs; n++, ++m) + outlets[n] = sp[m]->s_vec; + +*/ /* + + t_float **outlets = (t_float **)x->outlets; + t_float **inlets = (t_float **)x->inlets; + + m = 1; + + for(n = 0; n < x->plugin_ins; n++) + *inlets++ = sp[m++]->s_vec; + for(n = 0; n < x->plugin_outs; n++) + *outlets++ = sp[m++]->s_vec; + */ } + /* dsp_add(dssi_tilde_perform, 2, sp[0]->s_n, x); */ + +} + +static void dssi_tilde_quit_plugin(t_dssi_tilde *x){ + + t_int i, instance; + for(instance = 0; instance < x->n_instances; instance++) { + if(x->instances[instance].uiTarget && x->is_DSSI){ + lo_send(x->instances[instance].uiTarget, + x->instances[instance].ui_osc_quit_path, ""); + lo_address_free(x->instances[instance].uiTarget); + x->instances[instance].uiTarget = NULL; + } + /* no -- see comment in osc_exiting_handler */ + /* if (!instances[i].inactive) { */ + /* if (x->descriptor->LADSPA_Plugin->deactivate) { + x->descriptor->LADSPA_Plugin->deactivate + (x->instanceHandles[instance]); + }*/ + dssi_tilde_deactivate_plugin(x, (t_float)instance); + /* } */ + if (x->descriptor->LADSPA_Plugin && + x->descriptor->LADSPA_Plugin->cleanup) { + x->descriptor->LADSPA_Plugin->cleanup + (x->instanceHandles[instance]); + } + } +} + +static void dssi_tilde_free_plugin(t_dssi_tilde *x){ + + t_int i, instance; + if(x->plugin_label != NULL) + free((char *)x->plugin_label); + if(x->plugin_handle != NULL){ + instance = x->n_instances; + free((LADSPA_Handle)x->instanceHandles); + free(x->plugin_ControlInPortNumbers); + free((t_float *)x->plugin_InputBuffers); + free(x->instanceEventCounts); + free(x->plugin_ControlDataInput); + free(x->plugin_ControlDataOutput); + + while(instance--){ + + if(x->instances[instance].gui_pid){ +#if DEBUG + post("Killing GUI process PID = %d", x->instances[instance].gui_pid); +#endif + kill(x->instances[instance].gui_pid, SIGINT); + } + if (x->instances[instance].pluginPrograms) { + for (i = 0; i < + x->instances[instance].plugin_ProgramCount; i++) + free((void *) + x->instances[instance].pluginPrograms[i].Name); + free((char *)x->instances[instance].pluginPrograms); + x->instances[instance].pluginPrograms = NULL; + x->instances[instance].plugin_ProgramCount = 0; + } + free(x->instanceEventBuffers[instance]); + if(x->is_DSSI){ + free(x->instances[instance].ui_osc_control_path); + free(x->instances[instance].ui_osc_configure_path); + free(x->instances[instance].ui_osc_program_path); + free(x->instances[instance].ui_osc_show_path); + free(x->instances[instance].ui_osc_hide_path); + free(x->instances[instance].ui_osc_quit_path); + free(x->instances[instance].osc_url_path); + } + free(x->instances[instance].plugin_PortControlInNumbers); + if(x->plugin_outs) + free(x->plugin_OutputBuffers[instance]); + } + if(x->is_DSSI){ + if(x->project_dir != NULL) + free(x->project_dir); + free(x->osc_url_base); + dssi_tilde_configure_buffer_free(x); + } + free((snd_seq_event_t *)x->instanceEventBuffers); + free(x->instances); + free((t_float *)x->plugin_OutputBuffers); + + /*sleep(1);*/ + if(x->plugin_ins){ + for(i = 0; i < x->plugin_ins; i++) + inlet_free((t_inlet *)x->inlets[i]); + freebytes(x->inlets, x->plugin_ins * sizeof(t_inlet *)); + } + + if(x->plugin_outs){ + for(i = 0; i < x->plugin_outs; i++) + outlet_free((t_outlet *)x->outlets[i]); + freebytes(x->outlets, x->plugin_outs * sizeof(t_outlet *)); + } + if(x->control_outlet) + outlet_free(x->control_outlet); + if(x->plugin_basename) + free(x->plugin_basename); + if(x->port_info) + free(x->port_info); + } +} + +static void dssi_tilde_init_plugin(t_dssi_tilde *x){ + + x->project_dir = NULL; + x->configure_buffer_head = NULL; + x->outlets = NULL; + x->inlets = NULL; + x->control_outlet = NULL; + x->plugin_handle = NULL; + x->plugin_full_path = NULL; + x->plugin_label = NULL; + x->plugin_basename = NULL; + x->osc_url_base = NULL; + x->plugin_ControlDataInput = x->plugin_ControlDataOutput = NULL; + x->plugin_InputBuffers = x->plugin_OutputBuffers = NULL; + x->plugin_ControlInPortNumbers = NULL; + x->port_info = NULL; + x->descriptor = NULL; + x->instanceEventCounts = NULL; + x->instances = NULL; + x->instanceHandles = NULL; + x->is_DSSI = 0; + x->n_instances = 0; + x->dsp = 0; + x->dsp_loop = 0; + x->plugin_ins = x->plugin_outs = + x->plugin_controlIns = x->plugin_controlOuts = 0; + x->ports_in = x->ports_out = x->ports_controlIn = x->ports_controlOut = 0; + x->bufWriteIndex = x->bufReadIndex = 0; + +} + +static void *dssi_tilde_load_plugin(t_dssi_tilde *x, t_int argc, t_atom *argv){ + char *plugin_basename = NULL, + *plugin_full_path = NULL, + *tmpstr, + *plugin_label, + plugin_dir[MAXPDSTRING]; + +#if DEBUG + post("argc = %d", argc); +#endif + int i, + stop, + fd; + size_t pathlen; + + stop = 0; + + if (argc){ + char *argstr = strdup(argv[0].a_w.w_symbol->s_name); + + if(strstr(argstr, ":") != NULL){ + tmpstr = strtok(argstr, ":"); + plugin_full_path = strdup(tmpstr); + plugin_label = strtok(NULL, ":"); + // first part of the string is empty, i.e. ':mystring' + if (plugin_label == NULL) { + x->plugin_label = plugin_full_path; + plugin_full_path = NULL; + } else { + x->plugin_label = strdup(plugin_label); + } + } + else{ + x->plugin_label = strdup(argstr); + tmpstr = (char *)plugin_tilde_search_plugin_by_label(x, x->plugin_label); + if(tmpstr) + plugin_full_path = strdup(tmpstr); + } + free(argstr); +#if DEBUG + post("plugin path = %s", plugin_full_path); + post("plugin name = %s", x->plugin_label); +#endif + + if(plugin_full_path != NULL){ + /* First try to load as is: this will work if plugin_full_path is an + * absolute path, or the name of a library that is in DSSI_PATH + * or LADSPA_PATH environment variables */ + x->plugin_full_path = (char *)plugin_full_path; + /* If that didn't work, search for it in the 'canvas' path, which + * includes the Pd search dirs and any 'extra' paths set with + * [declare] */ + fd = canvas_open(x->x_canvas, plugin_full_path, "", + plugin_dir, &plugin_basename, MAXPDSTRING, 0); + + if (fd >= 0) { +#if DEBUG + post("plugin directory is %s, filename is %s", + plugin_dir, plugin_basename); +#endif + x->plugin_basename = strdup(plugin_basename); + pathlen = strlen(plugin_dir); + tmpstr = &plugin_dir[pathlen]; + sprintf(tmpstr, "/%s", plugin_basename); + tmpstr = plugin_dir; + x->plugin_handle = loadLADSPAPluginLibrary(tmpstr); + } else { + x->plugin_handle = loadLADSPAPluginLibrary(plugin_full_path); + } + + if (x->plugin_handle == NULL) + error("dssi~: can't find plugin in Pd paths, " + "try using [declare] to specify the path."); + + } + + if (x->plugin_handle != NULL){ + tmpstr = strdup(plugin_full_path); + /* Don't bother working out the plugin name if we used canvas_open() + * to get the path */ + if(plugin_basename == NULL){ + if(strstr(tmpstr, ".so")){ + plugin_basename = strtok((char *)tmpstr, "/"); + while(strstr(plugin_basename, ".so") == NULL) + plugin_basename = strtok(NULL, "/"); + x->plugin_basename = strdup(plugin_basename); +#if DEBUG + post("plugin basename = %s", x->plugin_basename); +#endif + } + else{ + post("dssi~: invalid plugin path, must end in .so"); + return (void *) x; + } + } + free(tmpstr); + if(x->desc_func = (DSSI_Descriptor_Function)dlsym(x->plugin_handle, "dssi_descriptor")){ + x->is_DSSI = 1; + x->descriptor = (DSSI_Descriptor *)x->desc_func(0); + } + else if(x->desc_func = + (DSSI_Descriptor_Function)dlsym(x->plugin_handle, "ladspa_descriptor")){ + x->is_DSSI = 0; + x->descriptor = + ladspa_to_dssi((LADSPA_Descriptor *) + x->desc_func(0)); + } + + + if(argc >= 2) + x->n_instances = (t_int)argv[1].a_w.w_float; + else + x->n_instances = 1; + + +#if DEBUG + post("n_instances = %d", x->n_instances); +#endif + x->instances = (t_dssi_instance *)malloc(sizeof(t_dssi_instance) * + x->n_instances); + + if(x->descriptor){ +#if DEBUG + post("%s loaded successfully!", + x->descriptor->LADSPA_Plugin->Label); +#endif + + /*allocate memory for port_info*/ + + x->port_info = (t_port_info *)malloc + (x->descriptor->LADSPA_Plugin->PortCount * + sizeof(t_port_info)); + + dssi_tilde_port_info(x); + dssi_tilde_assign_ports(x); + for(i = 0; i < x->n_instances; i++){ + x->instanceHandles[i] = + x->descriptor->LADSPA_Plugin-> + instantiate(x->descriptor->LADSPA_Plugin, x->sr); + if (!x->instanceHandles[i]){ + post("dssi~: instantiation of instance %d failed", i); + stop = 1; + break; + } + } + if(!stop){ + for(i = 0;i < x->n_instances; i++) + dssi_tilde_init_instance(x, i); + for(i = 0;i < x->n_instances; i++) + dssi_tilde_connect_ports(x, i); + for(i = 0;i < x->n_instances; i++) + dssi_tilde_activate_plugin(x, i); + + if(x->is_DSSI){ + for(i = 0;i < x->n_instances; i++) + dssi_tilde_osc_setup(x, i); +#if LOADGUI + for(i = 0;i < x->n_instances; i++) + dssi_tilde_load_gui(x, i); +#endif + for(i = 0;i < x->n_instances; i++) + dssi_tilde_init_programs(x, i); + + for(i = 0; i < x->n_instances && i < 128; i++){ + x->channelMap[i] = i; + } + } + } + } + } + else + post("dssi~: error: plugin not loaded"); + } + else + post("dssi~: no arguments given, please supply a path"); + + x->control_outlet = + outlet_new (&x->x_obj, gensym("control")); + + if (x->plugin_handle != NULL){ + if(x->plugin_outs){ + x->outlets = (t_outlet **)getbytes(x->plugin_outs * sizeof(t_outlet *)); + for(i = 0;i < x->plugin_outs; i++) + x->outlets[i] = outlet_new(&x->x_obj, &s_signal); + /* x->outlets = + (t_float **)calloc(x->plugin_outs, + sizeof(t_float *)); + */ + } + else + post("dssi~: error: plugin has no outputs"); + if(x->plugin_ins){ + x->inlets = (t_inlet **)getbytes(x->plugin_ins * sizeof(t_inlet *)); + for(i = 0;i < x->plugin_ins; i++) + x->inlets[i] = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + /* x->inlets = + (t_float **)calloc(x->plugin_ins, + sizeof(t_float *)); + */ + } + else + post("dssi~: error: plugin has no inputs"); + + x->dsp = 1; + post("dssi~: %d instances of %s, ready.", x->n_instances, + x->plugin_label); + } + else + post("dssi~: error: no plugin handle"); + + return (void *)x; +} + + +/* This method is currently buggy. PD's inlet/outlet handling seems buggy if you try to create ins/outs on the fly. Needs further investigation ...*/ +static void dssi_tilde_plug_plugin(t_dssi_tilde *x, t_symbol *s, int argc, t_atom *argv){ + + x->dsp = 0; + dssi_tilde_quit_plugin(x); + while(1){ + if(!x->dsp_loop){ + dssi_tilde_free_plugin(x); + break; + } + } + dssi_tilde_init_plugin(x); + dssi_tilde_load_plugin(x, argc, argv); +} + +static void *dssi_tilde_new(t_symbol *s, t_int argc, t_atom *argv){ + + t_dssi_tilde *x = (t_dssi_tilde *)pd_new(dssi_tilde_class); + post("\n========================================\ndssi~: DSSI/LADSPA host - version %.2f\n========================================\n", VERSION); + + dssi_tilde_init_plugin(x); + + x->sr = (t_int)sys_getsr(); + x->sr_inv = 1 / (t_float)x->sr; + x->time_ref = (t_int)clock_getlogicaltime; + x->blksize = sys_getblksize(); + x->dsp = 0; + x->x_canvas = canvas_getcurrent(); + + pthread_mutex_init(&x->midiEventBufferMutex, NULL); + return dssi_tilde_load_plugin(x, argc, argv); + +} + +static void dssi_tilde_free(t_dssi_tilde *x){ + +#if DEBUG + post("Calling dssi_tilde_free"); +#endif + + dssi_tilde_quit_plugin(x); + dssi_tilde_free_plugin(x); + +} + +static void dssi_tilde_sigchld_handler(int sig) { + wait(NULL); +} + +void dssi_tilde_setup(void) { + + dssi_tilde_class = class_new(gensym("dssi~"), (t_newmethod)dssi_tilde_new, + (t_method)dssi_tilde_free, sizeof(t_dssi_tilde), 0, A_GIMME, 0); + class_addlist(dssi_tilde_class, dssi_tilde_list); + class_addbang(dssi_tilde_class, dssi_tilde_bang); + class_addmethod(dssi_tilde_class, + (t_method)dssi_tilde_dsp, gensym("dsp"), 0); + class_addmethod(dssi_tilde_class, (t_method)dssi_tilde_dssi_methods, + gensym("dssi"), A_GIMME, 0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_control, + gensym ("control"),A_DEFSYM, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_info, + gensym ("info"),0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_list_plugins, + gensym ("listplugins"),0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_reset, + gensym ("reset"), A_DEFFLOAT, 0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_plug_plugin, + gensym ("plug"),A_GIMME,0); + /* class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_activate_plugin, + gensym ("activate"),A_DEFFLOAT - 1,0); + class_addmethod (dssi_tilde_class,(t_method)dssi_tilde_deactivate_plugin, + gensym ("deactivate"),A_DEFFLOAT - 1,0);*/ + class_sethelpsymbol(dssi_tilde_class, gensym("dssi-help")); + CLASS_MAINSIGNALIN(dssi_tilde_class, t_dssi_tilde, f); + signal(SIGCHLD, dssi_tilde_sigchld_handler); +} + +static int osc_message_handler(const char *path, const char *types, + lo_arg **argv,int argc, void *data, void *user_data) +{ +#if DEBUG + post("osc_message_handler active"); +#endif + int i, instance = 0; + const char *method; + char chantemp[2]; + t_dssi_tilde *x = (t_dssi_tilde *)(user_data); + + if (strncmp(path, "/dssi/", 6)){ +#if DEBUG + post("calling osc_debug_handler"); +#endif + return osc_debug_handler(path, types, argv, argc, data, x); + } + for (i = 0; i < x->n_instances; i++) { + if (!strncmp(path + 6, x->instances[i].osc_url_path, + strlen(x->instances[i].osc_url_path))) { + instance = i; + break; + } + } +#if DEBUG + for(i = 0; i < argc; i++){ + post("got osc request %c from instance %d, path: %s", + types[i],instance,path); + } +#endif + + if (!x->instances[instance].osc_url_path){ +#if DEBUG + post("calling osc_debug_handler"); +#endif + return osc_debug_handler(path, types, argv, argc, data, x); + } + method = path + 6 + strlen(x->instances[instance].osc_url_path); + if (*method != '/' || *(method + 1) == 0){ +#if DEBUG + post("calling osc_debug_handler"); +#endif + return osc_debug_handler(path, types, argv, argc, data, x); + } + method++; + + if (!strcmp(method, "configure") && argc == 2 && !strcmp(types, "ss")) { + +#if DEBUG + post("calling osc_configure_handler"); +#endif + return osc_configure_handler(x, argv, instance); + + } else if (!strcmp(method, "control") && argc == 2 && !strcmp(types, "if")) { +#if DEBUG + post("calling osc_control_handler"); +#endif + return osc_control_handler(x, argv, instance); + } + + else if (!strcmp(method, "midi") && argc == 1 && !strcmp(types, "m")) { + +#if DEBUG + post("calling osc_midi_handler"); +#endif + return osc_midi_handler(x, argv, instance); + + } else if (!strcmp(method, "program") && argc == 2 && !strcmp(types, "ii")){ +#if DEBUG + post("calling osc_program_handler"); +#endif + return osc_program_handler(x, argv, instance); + + } else if (!strcmp(method, "update") && argc == 1 && !strcmp(types, "s")){ +#if DEBUG + post("calling osc_update_handler"); +#endif + return osc_update_handler(x, argv, instance); + + } else if (!strcmp(method, "exiting") && argc == 0) { + + return osc_exiting_handler(x, argv, instance); + } + + return osc_debug_handler(path, types, argv, argc, data, x); +} + diff --git a/pluginhost~/src/dssi~.h b/pluginhost~/src/dssi~.h new file mode 100644 index 0000000..6aadb85 --- /dev/null +++ b/pluginhost~/src/dssi~.h @@ -0,0 +1,183 @@ +/* dssi~ - A DSSI host for PD + * + * Copyright 2006 Jamie Bullock and others + * + * This file incorporates code from the following sources: + * + * jack-dssi-host (BSD-style license): Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton. + * + * Hexter (GPL license): Copyright (C) 2004 Sean Bolton and others. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "m_pd.h" +#include "dssi.h" +#include +#include +#include +#include +#include +#include /*for exit()*/ +#include /* for fork() */ +#include /* for kill() */ +#include /* for wait() */ +#include /* for readdir() */ + +#define DX7_VOICE_SIZE_PACKED 128 /*From hexter_types.h by Sean Bolton */ +#define DX7_DUMP_SIZE_BULK 4096+8 + + +#define VERSION 0.99 +#define EVENT_BUFSIZE 1024 +#define OSC_BASE_MAX 1024 +#define TYPE_STRING_SIZE 20 /* Max size of event type string (must be two more bytes than needed) */ +#define DIR_STRING_SIZE 1024 /* Max size of directory string */ +#define ASCII_n 110 +#define ASCII_p 112 +#define ASCII_c 99 +#define ASCII_b 98 +#define ASCII_t 116 +#define ASCII_a 97 + +#define LOADGUI 0 /* FIX: depracate this */ +#ifdef DEBUG +#define CHECKSUM_PATCH_FILES_ON_LOAD 1 +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + + +/*From dx7_voice.h by Sean Bolton */ + +typedef struct _dx7_patch_t { + uint8_t data[128]; +} dx7_patch_t; + +typedef struct _dssi_instance { + + long currentBank; + long currentProgram; + int pendingBankLSB; + int pendingBankMSB; + int pendingProgramChange; + + int plugin_ProgramCount; + DSSI_Program_Descriptor *pluginPrograms; + + lo_address uiTarget; /*osc stuff */ + int ui_hidden; + int ui_show; + int uiNeedsProgramUpdate; + char *ui_osc_control_path; + char *ui_osc_configure_path; + char *ui_osc_program_path; + char *ui_osc_show_path; + char *ui_osc_hide_path; + char *ui_osc_quit_path; + + int *plugin_PortControlInNumbers; /*not sure if this should go here?*/ + + char *osc_url_path; + pid_t gui_pid; + +} t_dssi_instance; + +struct dssi_configure_pair { + t_int instance; + char *key, + *value; + struct dssi_configure_pair *next; +}; + +typedef struct dssi_configure_pair t_dssi_configure_pair; + +typedef struct _port_info { + t_atom type, + data_type, + name, + lower_bound, + upper_bound, + p_default; +} t_port_info; + +typedef struct _dssi_tilde { + t_object x_obj; + t_int is_DSSI; + char *plugin_label; + char *plugin_full_path; /*absolute path to plugin */ + t_canvas *x_canvas; /* pointer to the canvas the object is instantiated on */ + void *plugin_handle; + char *project_dir; /* project dircetory */ + LADSPA_Handle *instanceHandles; /*was handle*/ + t_dssi_instance *instances; + int n_instances; + unsigned long *instanceEventCounts; + unsigned char channelMap[128]; + snd_seq_event_t **instanceEventBuffers; + + snd_seq_event_t midiEventBuf[EVENT_BUFSIZE]; + /*static snd_seq_event_t **instanceEventBuffers;*/ + int bufWriteIndex, bufReadIndex; + pthread_mutex_t midiEventBufferMutex; + /*static pthread_mutex_t listHandlerMutex = PTHREAD_MUTEX_INITIALIZER;*/ + + DSSI_Descriptor_Function desc_func; + DSSI_Descriptor *descriptor; + + t_port_info *port_info; + + t_int ports_in, ports_out, ports_controlIn, ports_controlOut; + t_int plugin_ins;/* total audio input ports for plugin*/ + t_int plugin_outs;/* total audio output ports plugin*/ + t_int plugin_controlIns;/* total control input ports*/ + t_int plugin_controlOuts;/* total control output ports */ + + unsigned long *plugin_ControlInPortNumbers; /*Array of input port numbers for the plugin */ + + t_float **plugin_InputBuffers, **plugin_OutputBuffers; /* arrays of arrays for buffering audio for each audio port */ + t_float *plugin_ControlDataInput, *plugin_ControlDataOutput; /*arrays for control data for each port (1 item per 'run')*/ + lo_server_thread osc_thread; + char *osc_url_base; + char *plugin_basename; + + t_int time_ref; /*logical time reference */ + t_int sr; + t_float sr_inv; + t_int blksize; + t_float f; + + t_outlet **outlets; + t_inlet **inlets; + t_outlet *control_outlet; + + t_dssi_configure_pair *configure_buffer_head; + + t_int dsp; /* boolean dsp setting */ + t_int dsp_loop; + +} t_dssi_tilde; + +static char *dssi_tilde_send_configure(t_dssi_tilde *x, char *key, + char *value, t_int instance); +static int osc_message_handler(const char *path, const char *types, + lo_arg **argv, int argc, void *data, void *user_data); +static LADSPA_Data get_port_default(t_dssi_tilde *x, int port); +static void MIDIbuf(int type, int chan, int param, int val, t_dssi_tilde *x); + + diff --git a/pluginhost~/src/jload.c b/pluginhost~/src/jload.c new file mode 100644 index 0000000..1672b91 --- /dev/null +++ b/pluginhost~/src/jload.c @@ -0,0 +1,214 @@ +/* load.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/* patched by Jarno Seppänen, jams@cs.tut.fi, for plugin~ */ + +/* patched by Jamie Bullock, jamie@postlude.co.uk, for dssi~ */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "jutils.h" + +/*****************************************************************************/ + +/* This function provides a wrapping of dlopen(). When the filename is + not an absolute path (i.e. does not begin with / character), this + routine will search the LADSPA_PATH for the file. */ +static void * +dlopenLADSPA(const char * pcFilename, int iFlag) { + + char * pcBuffer; + const char * pcEnd; + const char * pcLADSPAPath; + const char * pcStart; + int iEndsInSO; + int iNeedSlash; + size_t iFilenameLength; + void * pvResult; + char *pluginPath; + + + iFilenameLength = strlen(pcFilename); + pvResult = NULL; + + /* First we just try calling dlopen(). This works if the user knows + about dlopen() and has placed the file on the LD_LIBRARY path or + has used an absolute directory. */ + pvResult = dlopen(pcFilename, iFlag); + if (pvResult != NULL) + return pvResult; + + /* If the filename is not absolute then we wish to check along the + LADSPA_PATH path to see if we can find the file there. */ + if (pcFilename[0] != '/') { + + pcLADSPAPath = NULL; + + if(getenv("LADSPA_PATH") && getenv("DSSI_PATH")){ + pluginPath = malloc(sizeof(char) * + (strlen(getenv("LADSPA_PATH")) + 1) + + sizeof(char) * strlen(getenv("DSSI_PATH"))); + sprintf(pluginPath, "%s:%s", + getenv("LADSPA_PATH"), getenv("DSSI_PATH")); + pcLADSPAPath = pluginPath; + free(pluginPath); + } + if (pcLADSPAPath == NULL) { + fprintf(stderr, "Warning: no LADSPA_PATH and DSSI_PATH, assuming /usr/lib/ladspa:/usr/local/lib/ladspa:/usr/lib/dssi:/usr/local/lib/dssi\n"); + pcLADSPAPath = + "/usr/lib/ladspa:/usr/local/lib/ladspa:/usr/lib/dssi:/usr/local/lib/dssi"; + } + + if (pcLADSPAPath) { + + pcStart = pcLADSPAPath; + while (*pcStart != '\0') { + pcEnd = pcStart; + while (*pcEnd != ':' && *pcEnd != '\0') + pcEnd++; + + pcBuffer = malloc(iFilenameLength + 2 + (pcEnd - pcStart)); + if (pcEnd > pcStart) + strncpy(pcBuffer, pcStart, pcEnd - pcStart); + iNeedSlash = 0; + if (pcEnd > pcStart) + if (*(pcEnd - 1) != '/') { + iNeedSlash = 1; + pcBuffer[pcEnd - pcStart] = '/'; + } + strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename); + + pvResult = dlopen(pcBuffer, iFlag); + + free (pcBuffer); + if (pvResult != NULL){ + return pvResult; + } + + pcStart = pcEnd; + if (*pcStart == ':') + pcStart++; + } + } else { + fputs ("warning: You haven't specified the LADSPA_PATH environment variable and didn't specify an absolute path to the plug-in.\n" + "Please set the LADSPA_PATH variable to point to your LADSPA plug-in directories (eg. \"export LADSPA_PATH=/usr/local/lib/ladspa\").\n", stderr); + } + } + + /* As a last ditch effort, check if filename does not end with + ".so". In this case, add this suffix and recurse. */ + iEndsInSO = 0; + if (iFilenameLength > 3) + iEndsInSO = (strcmp(pcFilename + iFilenameLength - 3, ".so") == 0); + if (!iEndsInSO) { + pcBuffer = malloc(iFilenameLength + 4); + strcpy(pcBuffer, pcFilename); + strcat(pcBuffer, ".so"); + pvResult = dlopenLADSPA(pcBuffer, iFlag); + } + + if (pvResult != NULL) + return pvResult; + + /* If nothing has worked, then at least we can make sure we set the + correct error message - and this should correspond to a call to + dlopen() with the actual filename requested. The dlopen() manual + page does not specify whether the first or last error message + will be kept when multiple calls are made to dlopen(). We've + covered the former case - now we can handle the latter by calling + dlopen() again here. */ + return dlopen(pcFilename, iFlag); +} + +/*****************************************************************************/ + +void * +loadLADSPAPluginLibrary(const char * pcPluginFilename) { + + void * pvPluginHandle; + + pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW); + if (!pvPluginHandle) { + fprintf(stderr, + "Failed to load plugin \"%s\": %s\n", + pcPluginFilename, + dlerror()); +#if 0 + exit(1); +#else + return NULL; +#endif + } + + return pvPluginHandle; +} + +/*****************************************************************************/ + +void +unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary) { + dlclose(pvLADSPAPluginLibrary); +} + +/*****************************************************************************/ + +const LADSPA_Descriptor * +findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary, + const char * pcPluginLibraryFilename, + const char * pcPluginLabel) { + + const LADSPA_Descriptor * psDescriptor; + LADSPA_Descriptor_Function pfDescriptorFunction; + unsigned long lPluginIndex; + + dlerror(); + pfDescriptorFunction + = (LADSPA_Descriptor_Function)dlsym(pvLADSPAPluginLibrary, + "ladspa_descriptor"); + if (!pfDescriptorFunction) { + const char * pcError = dlerror(); + if (pcError) + fprintf(stderr, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a LADSPA plugin file?\n", + pcPluginLibraryFilename, + pcError); +#if 0 + exit(1); +#else + return NULL; +#endif + } + + for (lPluginIndex = 0;; lPluginIndex++) { + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (psDescriptor == NULL) { + fprintf(stderr, + "Unable to find label \"%s\" in plugin library file \"%s\".\n", + pcPluginLabel, + pcPluginLibraryFilename); +#if 0 + exit(1); +#else + return NULL; +#endif + } + if (strcmp(psDescriptor->Label, pcPluginLabel) == 0) + return psDescriptor; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/pluginhost~/src/jsearch.c b/pluginhost~/src/jsearch.c new file mode 100644 index 0000000..593c6f5 --- /dev/null +++ b/pluginhost~/src/jsearch.c @@ -0,0 +1,154 @@ +/* search.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/* patched by Jarno Seppänen, jams@cs.tut.fi, for plugin~ */ + +/* patched by Jamie Bullock, jamie@postlude.co.uk, for dssi~ */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "dssi.h" +#include "jutils.h" + +/*****************************************************************************/ + +/* Search just the one directory. */ + static void +LADSPADirectoryPluginSearch (const char * pcDirectory, + LADSPAPluginSearchCallbackFunction fCallbackFunction, + void* user_data) +{ + char * pcFilename; + DIR * psDirectory; + DSSI_Descriptor_Function fDescriptorFunction; + long lDirLength; + long iNeedSlash; + struct dirent * psDirectoryEntry; + void * pvPluginHandle; + int is_DSSI = 0; + + lDirLength = strlen(pcDirectory); + if (!lDirLength) + return; + if (pcDirectory[lDirLength - 1] == '/') + iNeedSlash = 0; + else + iNeedSlash = 1; + + psDirectory = opendir(pcDirectory); + if (!psDirectory) + return; + + while (1) { + + psDirectoryEntry = readdir(psDirectory); + if (!psDirectoryEntry) { + closedir(psDirectory); + return; + } + + pcFilename = malloc(lDirLength + + strlen(psDirectoryEntry->d_name) + + 1 + iNeedSlash); + strcpy(pcFilename, pcDirectory); + if (iNeedSlash) + strcat(pcFilename, "/"); + strcat(pcFilename, psDirectoryEntry->d_name); + + pvPluginHandle = dlopen(pcFilename, RTLD_LAZY); + if (pvPluginHandle) { + /* This is a file and the file is a shared library! */ + + dlerror(); + if(fDescriptorFunction = (DSSI_Descriptor_Function)dlsym(pvPluginHandle, + "ladspa_descriptor")) + is_DSSI = 0; + + else if(fDescriptorFunction = (DSSI_Descriptor_Function)dlsym(pvPluginHandle, + "dssi_descriptor")) + is_DSSI = 1; + + if (dlerror() == NULL && fDescriptorFunction) { + /* We've successfully found a ladspa_descriptor function. Pass + it to the callback function. */ + fCallbackFunction(pcFilename, + pvPluginHandle, + fDescriptorFunction, + user_data, + is_DSSI); + dlclose (pvPluginHandle); + } + else { + /* It was a library, but not a LADSPA one. Unload it. */ + dlclose(pcFilename); + } + } + } +} + +/*****************************************************************************/ + + void +LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction, + void* user_data) +{ + + char * pcBuffer; + const char * pcEnd; + const char * pcLADSPAPath; + char *pluginPath; + const char * pcStart; + + + pcLADSPAPath = NULL; + + if(getenv("LADSPA_PATH") && getenv("DSSI_PATH")){ + pluginPath = malloc(sizeof(char) * + (strlen(getenv("LADSPA_PATH")) + 1) + + sizeof(char) * strlen(getenv("DSSI_PATH"))); + sprintf(pluginPath, "%s:%s", + getenv("LADSPA_PATH"), getenv("DSSI_PATH")); + pcLADSPAPath = pluginPath; + free(pluginPath); + } + if (pcLADSPAPath == NULL) { + fprintf(stderr, "Warning: no LADSPA_PATH and DSSI_PATH, assuming /usr/lib/ladspa:/usr/local/lib/ladspa:/usr/lib/dssi:/usr/local/lib/dssi\n"); + pcLADSPAPath = + "/usr/lib/ladspa:/usr/local/lib/ladspa:/usr/lib/dssi:/usr/local/lib/dssi"; + } + + pcStart = pcLADSPAPath; + while (*pcStart != '\0') { + pcEnd = pcStart; + while (*pcEnd != ':' && *pcEnd != '\0') + pcEnd++; + + pcBuffer = malloc(1 + pcEnd - pcStart); + if (pcEnd > pcStart) + strncpy(pcBuffer, pcStart, pcEnd - pcStart); + pcBuffer[pcEnd - pcStart] = '\0'; + + LADSPADirectoryPluginSearch(pcBuffer, fCallbackFunction, user_data); + + pcStart = pcEnd; + if (*pcStart == ':') + pcStart++; + } +} + + +/*****************************************************************************/ + +/* EOF */ diff --git a/pluginhost~/src/jutils.h b/pluginhost~/src/jutils.h new file mode 100644 index 0000000..fdf718f --- /dev/null +++ b/pluginhost~/src/jutils.h @@ -0,0 +1,69 @@ +/* utils.h + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/* patched by Jarno Seppänen, jams@cs.tut.fi, for plugin~ */ + +/* patched by Jamie Bullock, jamie@postlude.co.uk, for dssi~ */ + +#ifndef LADSPA_SDK_LOAD_PLUGIN_LIB +#define LADSPA_SDK_LOAD_PLUGIN_LIB + +/*****************************************************************************/ + +#include "dssi.h" + +/*****************************************************************************/ + +/* Functions in load.c: */ + +/* This function call takes a plugin library filename, searches for + the library along the LADSPA_PATH, loads it with dlopen() and + returns a plugin handle for use with findPluginDescriptor() or + unloadLADSPAPluginLibrary(). Errors are handled by writing a + message to stderr and calling exit(1). It is alright (although + inefficient) to call this more than once for the same file. */ +void * loadLADSPAPluginLibrary(const char * pcPluginFilename); + +/* This function unloads a LADSPA plugin library. */ +void unloadLADSPAPluginLibrary(void * pvLADSPAPluginLibrary); + +/* This function locates a LADSPA plugin within a plugin library + loaded with loadLADSPAPluginLibrary(). Errors are handled by + writing a message to stderr and calling exit(1). Note that the + plugin library filename is only included to help provide + informative error messages. */ +const LADSPA_Descriptor * +findLADSPAPluginDescriptor(void * pvLADSPAPluginLibrary, + const char * pcPluginLibraryFilename, + const char * pcPluginLabel); + +/*****************************************************************************/ + +/* Functions in search.c: */ + +/* Callback function for use with LADSPAPluginSearch(). The callback + function passes the filename (full path), a plugin handle (dlopen() + style) and a LADSPA_DescriptorFunction (from which + LADSPA_Descriptors can be acquired). */ +typedef void LADSPAPluginSearchCallbackFunction +(const char * pcFullFilename, + void * pvPluginHandle, + DSSI_Descriptor_Function fDescriptorFunction, + void* user_data, + int is_DSSI); + +/* Search through the $(LADSPA_PATH) (or a default path) for any + LADSPA plugin libraries. Each plugin library is tested using + dlopen() and dlsym(,"ladspa_descriptor"). After loading each + library, the callback function is called to process it. This + function leaves items passed to the callback function open. */ +void LADSPAPluginSearch(LADSPAPluginSearchCallbackFunction fCallbackFunction, + void* user_data); + +/*****************************************************************************/ + +#endif + +/* EOF */ -- cgit v1.2.1