/* pluginhost~ - A plugin 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 * * liblo (CPL license): Copyright (C) 2004 Steve Harris * * 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 #include #include /* for exit() */ #include /* for fork() */ #include /* for kill() */ #include /* for readdir() */ #include /* for dlsym() */ #include #include "dssi.h" #include "jutils.h" #include "ph_common.h" #define DEBUG_STRING_SIZE 8192 /*From dx7_voice_data.c */ uint8_t dx7_init_performance[DX7_PERFORMANCE_SIZE] = { 0, 0, 0, 2, 0, 0, 0, 0, 0, 15, 1, 0, 4, 15, 2, 15, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static LADSPA_Data ph_get_port_default(ph *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 void ph_set_port_info(ph *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]; ph_debug_post("Port %d: %s", i, x->descriptor->LADSPA_Plugin->PortNames[i]); 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_control_ins; } else if (LADSPA_IS_PORT_OUTPUT(pod)){ ++x->plugin_control_outs; 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) ph_get_port_default(x, i); x->port_info[i].name.a_w.w_symbol = gensym ((char *) x->descriptor->LADSPA_Plugin->PortNames[i]); } ph_debug_post("%d inputs, %d outputs, %d control inputs, %d control outs", x->plugin_ins, x->plugin_outs, x->plugin_control_ins, x->plugin_control_outs); } static void ph_assign_ports(ph *x) { unsigned int i; ph_debug_post("%d instances", x->n_instances); x->plugin_ins *= x->n_instances; x->plugin_outs *= x->n_instances; x->plugin_control_ins *= x->n_instances; x->plugin_control_outs *= x->n_instances; ph_debug_post("%d plugin outs", x->plugin_outs); x->plugin_input_buffers = (float **)malloc(x->plugin_ins * sizeof(float *)); x->plugin_output_buffers = (float **)malloc(x->plugin_outs * sizeof(float *)); x->plugin_control_input = (float *)calloc(x->plugin_control_ins, sizeof(float)); x->plugin_control_output = (float *)calloc(x->plugin_control_outs, sizeof(float)); for(i = 0; i < x->plugin_ins; i++) x->plugin_input_buffers[i] = (float *)calloc(x->blksize, sizeof(float)); for(i = 0; i < x->plugin_outs; i++) x->plugin_output_buffers[i] = (float *)calloc(x->blksize, sizeof(float)); x->instance_event_buffers = (snd_seq_event_t **)malloc(x->n_instances * sizeof(snd_seq_event_t *)); x->instance_handles = (LADSPA_Handle *)malloc(x->n_instances * sizeof(LADSPA_Handle)); x->instance_event_counts = (unsigned long *)malloc(x->n_instances * sizeof(unsigned long)); for(i = 0; i < x->n_instances; i++){ x->instance_event_buffers[i] = (snd_seq_event_t *)malloc(EVENT_BUFSIZE * sizeof(snd_seq_event_t)); x->instances[i].plugin_port_ctlin_numbers = (int *)malloc(x->descriptor->LADSPA_Plugin->PortCount * sizeof(int)); } x->plugin_ctlin_port_numbers = (unsigned long *)malloc(sizeof(unsigned long) * x->plugin_control_ins); ph_debug_post("Buffers assigned!"); } static void ph_init_instance(ph *x, unsigned int i) { x->instances[i].plugin_pgm_count = 0; x->instances[i].ui_needs_pgm_update = 0; x->instances[i].ui_osc_control_path = NULL; x->instances[i].ui_osc_configure_path = NULL; x->instances[i].ui_osc_program_path = NULL; x->instances[i].ui_osc_show_path = NULL; x->instances[i].ui_osc_hide_path = NULL; x->instances[i].ui_osc_quit_path = NULL; x->instances[i].osc_url_path = NULL; x->instances[i].current_bank = 0; x->instances[i].current_pgm = 0; x->instances[i].pending_pgm_change = -1; x->instances[i].pending_bank_lsb = -1; x->instances[i].pending_bank_msb = -1; x->instances[i].ui_hidden = 1; x->instances[i].ui_show = 0; memcpy(x->instances[i].perf_buffer, &dx7_init_performance, DX7_PERFORMANCE_SIZE); //x->instances[i].plugin_port_ctlin_numbers = NULL; x->instances[i].plugin_pgms = NULL; ph_debug_post("Instance %d initialized!", i); } static void ph_connect_ports(ph *x, unsigned int i) { unsigned int n; for(n = 0; n < x->descriptor->LADSPA_Plugin->PortCount; n++){ ph_debug_post("PortCount: %d of %d", n, x->descriptor->LADSPA_Plugin->PortCount); LADSPA_PortDescriptor pod = x->descriptor->LADSPA_Plugin->PortDescriptors[n]; x->instances[i].plugin_port_ctlin_numbers[n] = -1; if (LADSPA_IS_PORT_AUDIO(pod)) { if (LADSPA_IS_PORT_INPUT(pod)) { x->descriptor->LADSPA_Plugin->connect_port (x->instance_handles[i], n, x->plugin_input_buffers[x->ports_in++]); } else if (LADSPA_IS_PORT_OUTPUT(pod)) { x->descriptor->LADSPA_Plugin->connect_port (x->instance_handles[i], n, x->plugin_output_buffers[x->ports_out++]); ph_debug_post("Audio Input port %d connected", x->ports_in); ph_debug_post("Audio Output port %d connected", x->ports_out); } } else if (LADSPA_IS_PORT_CONTROL(pod)) { if (LADSPA_IS_PORT_INPUT(pod)) { x->plugin_ctlin_port_numbers[x->ports_control_in] = (unsigned long) i; x->instances[i].plugin_port_ctlin_numbers[n] = x->ports_control_in; x->plugin_control_input[x->ports_control_in] = (t_float) ph_get_port_default(x, n); ph_debug_post("default for port %d, control_in, %d is %.2f", n, x->ports_control_in, x->plugin_control_input[x->ports_control_in]); x->descriptor->LADSPA_Plugin->connect_port (x->instance_handles[i], n, &x->plugin_control_input[x->ports_control_in++]); } else if (LADSPA_IS_PORT_OUTPUT(pod)) { x->descriptor->LADSPA_Plugin->connect_port (x->instance_handles[i], n, &x->plugin_control_output[x->ports_control_out++]); } ph_debug_post("control port %d connected", x->ports_control_in); ph_debug_post("control port %d connected", x->ports_control_out); } } } static void ph_activate_plugin(ph *x, unsigned int i) { if(x->descriptor->LADSPA_Plugin->activate){ ph_debug_post("trying to activate instance: %d", i); x->descriptor->LADSPA_Plugin->activate(x->instance_handles[i]); } ph_debug_post("plugin activated!"); } static void ph_deactivate_plugin(ph *x, unsigned int instance) { if(x->descriptor->LADSPA_Plugin->deactivate) { x->descriptor->LADSPA_Plugin->deactivate(x->instance_handles[instance]); } ph_debug_post("plugin deactivated!"); } static void ph_cleanup_plugin(ph *x, unsigned int instance) { if (x->descriptor->LADSPA_Plugin && x->descriptor->LADSPA_Plugin->cleanup) { x->descriptor->LADSPA_Plugin->cleanup (x->instance_handles[instance]); } } static void ph_get_current_pgm(ph *x, unsigned int i) { t_int argc = 3; t_atom argv[argc]; ph_instance *instance; unsigned int pgm; instance = &x->instances[i]; pgm = instance->current_pgm; SETFLOAT(argv, i); SETFLOAT(argv+1, instance->plugin_pgms[pgm].Program); SETSYMBOL(argv+2, gensym(instance->plugin_pgms[pgm].Name)); outlet_anything(x->message_out, gensym ("program"), argc, argv); } static void ph_init_programs(ph *x, unsigned int i) { ph_instance *instance = &x->instances[i]; ph_debug_post("Setting up program data"); ph_query_programs(x, i); if (x->descriptor->select_program && instance->plugin_pgm_count > 0) { /* select program at index 0 */ unsigned long bank = instance->plugin_pgms[0].Bank; instance->pending_bank_msb = bank / 128; instance->pending_bank_lsb = bank % 128; instance->pending_pgm_change = instance->plugin_pgms[0].Program; instance->ui_needs_pgm_update = 1; } } /* TODO:OSC */ #if 0 static void ph_load_gui(ph *x, int instance) { t_int err = 0; 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);*/ ph_debug_post("gui_base: %s", gui_base); gui_str = (char *)malloc(sizeof(char) * (strlen("channel 00") + 1)); sprintf (gui_str,"channel %02d", instance); ph_debug_post("GUI name string, %s", gui_str); if(!(dp = opendir(gui_base))){ post("pluginhost~: 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; } } ph_debug_post("GUI filename: %s", dir_entry->d_name); } 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); ph_debug_post("gui_path: %s", gui_path); /* osc_url_base was of the form: * osc.udp://127.0.0.1:9997/dssi */ osc_url = (char *)malloc (sizeof(char) * (strlen(x->osc_url_base) + strlen(instance->osc_url_path) + 2)); sprintf(osc_url, "%s/%s", x->osc_url_base, instance->osc_url_path); post("pluginhost~: instance %d URL: %s",instance, osc_url); ph_debug_post("Trying to open GUI!"); instance->gui_pid = fork(); if (instance->gui_pid == 0){ 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 */ } ph_debug_post("errorcode = %d", err); free(gui_path); free(osc_url); free(gui_str); if(dp){ ph_debug_post("directory handle closed = %d", closedir(dp)); } } #endif static t_int ph_configure_buffer_free(ph *x) { ph_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 ph_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) { ph_debug_post("pluginhost~: searching plugin \"%s\"...", full_filename); 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++){ ph_debug_post("pluginhost~: label \"%s\"", descriptor->LADSPA_Plugin->Label); if (strcasecmp (name, descriptor->LADSPA_Plugin->Label) == 0) { *out_lib_name = strdup (full_filename); ph_debug_post("pluginhost~: found plugin \"%s\" in library \"%s\"", name, full_filename); /* 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 (ph *x, const char *name) { char* lib_name = NULL; void* user_data[2]; user_data[0] = (void*)(&lib_name); user_data[1] = (void*)name; ph_debug_post("search plugin by label: '%s'\n", name); lib_name = NULL; LADSPAPluginSearch (ph_search_plugin_callback, (void*)user_data); /* The callback (allocates and) writes lib_name, if it finds the plugin */ return lib_name; } static void osc_setup(ph *x, unsigned int i) { ph_instance *instance = &x->instances[i]; if(i == 0){ x->osc_port = OSC_PORT; } instance->osc_url_path = malloc(sizeof(char) * (strlen(x->plugin_basename) + strlen(x->descriptor->LADSPA_Plugin->Label) + //strlen("chan00") + 3)); 6 + 3)); sprintf(instance->osc_url_path, "%s/%s/chan%02d", x->plugin_basename, x->descriptor->LADSPA_Plugin->Label, i); ph_debug_post("OSC Path is: %s", instance->osc_url_path); } /* ==================================== */ void ph_debug_post(const char *fmt, ...) { #if DEBUG == 1 unsigned int currpos; char newfmt[DEBUG_STRING_SIZE]; char result[DEBUG_STRING_SIZE]; size_t fmt_length; va_list args; fmt_length = strlen(fmt); sprintf(newfmt, "%s: ", PH_NAME); strncat(newfmt, fmt, fmt_length); currpos = strlen(PH_NAME) + 2 + fmt_length; newfmt[currpos] = '\0'; va_start(args, fmt); vsprintf(result, newfmt, args); va_end(args); post(result); #endif } void ph_quit_plugin(ph *x) { unsigned int i; t_atom argv[2]; t_int argc; ph_instance *instance; argc = 2; for(i = 0; i < x->n_instances; i++) { instance = &x->instances[i]; if(x->is_dssi){ argc = 2; if(instance->ui_osc_quit_path != NULL) { SETSYMBOL(argv, gensym(instance->ui_osc_quit_path)); SETSYMBOL(argv+1, gensym("")); ph_instance_send_osc(x->message_out, instance, argc, argv); } } ph_deactivate_plugin(x, i); ph_cleanup_plugin(x, i); } } void ph_query_programs(ph *x, unsigned int i) { unsigned int n; ph_instance *instance = &x->instances[i]; ph_debug_post("querying programs"); /* free old lot */ if (instance->plugin_pgms != NULL) { for (n = 0; n < instance->plugin_pgm_count; n++) { free((void *)instance->plugin_pgms[n].Name); } free(instance->plugin_pgms); instance->plugin_pgms = NULL; instance->plugin_pgm_count = 0; } instance->pending_bank_lsb = -1; instance->pending_bank_msb = -1; instance->pending_pgm_change = -1; if (x->descriptor->get_program && x->descriptor->select_program) { /* Count the plugins first */ for (n = 0; x->descriptor-> get_program(x->instance_handles[i], n); ++n); if (n > 0) { instance->plugin_pgm_count = n; instance->plugin_pgms = malloc(n * sizeof(DSSI_Program_Descriptor)); while (n > 0) { const DSSI_Program_Descriptor *descriptor; --n; descriptor = x->descriptor->get_program( x->instance_handles[i], n); instance->plugin_pgms[n].Bank = descriptor->Bank; instance->plugin_pgms[n].Program = descriptor->Program; instance->plugin_pgms[n].Name = strdup(descriptor->Name); ph_debug_post("program %d is MIDI bank %lu program %lu," " named '%s'",i, instance->plugin_pgms[n].Bank, instance->plugin_pgms[n].Program, instance->plugin_pgms[n].Name); } } else { assert(instance->plugin_pgm_count == 0); } } } void ph_program_change(ph *x, unsigned int i) { /* 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'? */ ph_instance *instance; t_int argc = 3; t_atom argv[argc]; instance = &x->instances[i]; ph_debug_post("executing program change"); if (instance->pending_pgm_change >= 0){ if (instance->pending_bank_lsb >= 0) { if (instance->pending_bank_msb >= 0) { instance->current_bank = instance->pending_bank_lsb + 128 * instance->pending_bank_msb; } else { instance->current_bank = instance->pending_bank_lsb + 128 * (instance->current_bank / 128); } } else if (instance->pending_bank_msb >= 0) { instance->current_bank = (instance->current_bank % 128) + 128 * instance->pending_bank_msb; } instance->current_pgm = instance->pending_pgm_change; if (x->descriptor->select_program) { x->descriptor->select_program(x->instance_handles[i], instance->current_bank, instance->current_pgm); } if (instance->ui_needs_pgm_update){ ph_debug_post("Updating GUI program"); /* TODO - this is a hack to make text ui work*/ if(x->is_dssi){ // FIX: need to check this because if we don't have a UI, // update didn't get called if (false) { SETSYMBOL(argv, gensym(instance->ui_osc_program_path)); SETFLOAT(argv+1, instance->current_bank); SETFLOAT(argv+2, instance->current_pgm); ph_instance_send_osc(x->message_out, instance, argc, argv); } } } instance->ui_needs_pgm_update = 0; instance->pending_pgm_change = -1; instance->pending_bank_msb = -1; instance->pending_bank_lsb = -1; } ph_get_current_pgm(x, i); } char *ph_send_configure(ph *x, const char *key, const char *value, unsigned int i) { char *debug; debug = x->descriptor->configure(x->instance_handles[i], key, value); /* TODO:OSC */ /* if(instance->ui_target != NULL && x->is_dssi) { lo_send(instance->ui_target, instance->ui_osc_configure_path, "ss", key, value); } */ ph_query_programs(x, i); return debug; } void ph_instance_send_osc(t_outlet *outlet, ph_instance *instance, t_int argc, t_atom *argv) { outlet_anything(outlet, gensym("connect"), UI_TARGET_ELEMS, instance->ui_target); outlet_anything(outlet, gensym("send"), argc, argv); outlet_anything(outlet, gensym("disconnect"), 0, NULL); } void *ph_load_plugin(ph *x, t_int argc, t_atom *argv) { char *plugin_basename = NULL, *plugin_full_path = NULL, *tmpstr, *plugin_label, plugin_dir[MAXPDSTRING]; ph_debug_post("argc = %d", argc); unsigned int i; int stop; int fd; size_t pathlen; stop = 0; if (!argc){ pd_error(x, "no arguments given, please supply a path"); return x; } 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); ph_debug_post("plugin path = %s", plugin_full_path); ph_debug_post("plugin name = %s", x->plugin_label); if(plugin_full_path == NULL){ pd_error(x, "can't get path to plugin"); return x; } x->plugin_full_path = (char *)plugin_full_path; /* 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) { ph_debug_post("plugin directory is %s, filename is %s", plugin_dir, plugin_basename); 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 { /* 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_handle = loadLADSPAPluginLibrary(plugin_full_path); } if (x->plugin_handle == NULL) { error("pluginhost~: can't find plugin in Pd paths, " "try using [declare] to specify the path."); return x; } 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")){ pd_error(x, "invalid plugin path, must end in .so"); return x; } plugin_basename = strtok((char *)tmpstr, "/"); while(strstr(plugin_basename, ".so") == NULL) { plugin_basename = strtok(NULL, "/"); } x->plugin_basename = strdup(plugin_basename); ph_debug_post("plugin basename = %s", x->plugin_basename); } free(tmpstr); if((x->desc_func = (DSSI_Descriptor_Function)dlsym(x->plugin_handle, "dssi_descriptor"))){ x->is_dssi = true; 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 = false; 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; } ph_debug_post("n_instances = %d", x->n_instances); x->instances = (ph_instance *)malloc(sizeof(ph_instance) * x->n_instances); if(!x->descriptor){ pd_error(x, "error: couldn't get plugin descriptor"); return x; } ph_debug_post("%s loaded successfully!", x->descriptor->LADSPA_Plugin->Label); x->port_info = (ph_port_info *)malloc (x->descriptor->LADSPA_Plugin->PortCount * sizeof(ph_port_info)); ph_set_port_info(x); ph_assign_ports(x); for(i = 0; i < x->n_instances; i++){ x->instance_handles[i] = x->descriptor->LADSPA_Plugin-> instantiate(x->descriptor->LADSPA_Plugin, x->sr); if (!x->instance_handles[i]){ pd_error(x, "instantiation of instance %d failed", i); stop = 1; break; } } if(!stop){ for(i = 0;i < x->n_instances; i++) ph_init_instance(x, i); for(i = 0;i < x->n_instances; i++) ph_connect_ports(x, i); for(i = 0;i < x->n_instances; i++) ph_activate_plugin(x, i); if(x->is_dssi){ for(i = 0;i < x->n_instances; i++) osc_setup(x, i); #if LOADGUI for(i = 0;i < x->n_instances; i++) ph_load_gui(x, i); #endif for(i = 0;i < x->n_instances; i++) ph_init_programs(x, i); for(i = 0; i < x->n_instances && i < 128; i++){ x->channel_map[i] = i; } } } x->message_out = outlet_new (&x->x_obj, gensym("control")); 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); } else { pd_error(x, "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); } } else { pd_error(x, "plugin has no inputs"); } x->dsp = true; post("%s: %d instances of %s, ready.", PH_NAME, x->n_instances, x->plugin_label); return (void *)x; } void ph_init_plugin(ph *x) { x->port_info = NULL; x->descriptor = NULL; x->instance_event_counts = NULL; x->instances = NULL; x->instance_handles = NULL; x->osc_url_base = NULL; x->configure_buffer_head = NULL; x->project_dir = NULL; x->outlets = NULL; x->inlets = NULL; x->message_out = NULL; x->plugin_handle = NULL; x->plugin_full_path = NULL; x->plugin_label = NULL; x->plugin_basename = NULL; x->plugin_control_input = NULL; x->plugin_control_output = NULL; x->plugin_input_buffers = NULL; x->plugin_output_buffers = NULL; x->plugin_ctlin_port_numbers = NULL; x->plugin_ins = 0; x->plugin_outs = 0; x->plugin_control_ins = 0; x->plugin_control_outs = 0; x->is_dssi = 0; x->n_instances = 0; x->dsp = 0; x->dsp_loop = 0; x->ports_in = 0; x->ports_out = 0; x->ports_control_in = 0; x->ports_control_out = 0; x->buf_write_index = 0; x->buf_read_index = 0; } void ph_free_plugin(ph *x) { unsigned int i; unsigned int n; if(x->plugin_label != NULL) { free((char *)x->plugin_label); } if(x->plugin_handle == NULL) { return; } free((LADSPA_Handle)x->instance_handles); free(x->plugin_ctlin_port_numbers); free((t_float *)x->plugin_input_buffers); free(x->instance_event_counts); free(x->plugin_control_input); free(x->plugin_control_output); i = x->n_instances; while(i--){ ph_instance *instance = &x->instances[i]; /* TODO:OSC */ /* if(instance->gui_pid){ ph_debug_post("Killing GUI process PID = %d", instance->gui_pid); kill(instance->gui_pid, SIGINT); } */ if (instance->plugin_pgms) { for (n = 0; n < instance->plugin_pgm_count; n++) { free((void *)instance->plugin_pgms[n].Name); } free(instance->plugin_pgms); instance->plugin_pgms = NULL; instance->plugin_pgm_count = 0; } free(x->instance_event_buffers[i]); if(x->is_dssi){ free(instance->ui_osc_control_path); free(instance->ui_osc_configure_path); free(instance->ui_osc_program_path); free(instance->ui_osc_show_path); free(instance->ui_osc_hide_path); free(instance->ui_osc_quit_path); free(instance->osc_url_path); } free(instance->plugin_port_ctlin_numbers); if(x->plugin_outs) { free(x->plugin_output_buffers[i]); } } if(x->is_dssi) { if(x->project_dir != NULL) { free(x->project_dir); } free(x->osc_url_base); ph_configure_buffer_free(x); } free((snd_seq_event_t *)x->instance_event_buffers); free(x->instances); free((t_float *)x->plugin_output_buffers); if(x->plugin_ins){ for(n = 0; n < x->plugin_ins; n++) { inlet_free((t_inlet *)x->inlets[n]); } freebytes(x->inlets, x->plugin_ins * sizeof(t_inlet *)); } if(x->plugin_outs){ for(n = 0; n < x->plugin_outs; n++) { outlet_free((t_outlet *)x->outlets[n]); } freebytes(x->outlets, x->plugin_outs * sizeof(t_outlet *)); } if(x->message_out) { outlet_free(x->message_out); } if(x->plugin_basename) { free(x->plugin_basename); } if(x->port_info) { free(x->port_info); } } 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; }