diff --git a/src/bindings/fluid_cmd.c b/src/bindings/fluid_cmd.c index 4128f76f..9ca42346 100644 --- a/src/bindings/fluid_cmd.c +++ b/src/bindings/fluid_cmd.c @@ -48,6 +48,11 @@ struct _fluid_cmd_handler_t { fluid_midi_router_rule_t *cmd_rule; /* Rule currently being processed by shell command handler */ int cmd_rule_type; /* Type of the rule (#fluid_midi_router_rule_type) */ + +#ifdef LADSPA + /* Instance id of the LADSPA plugin currently being processed by shell command handler */ + int ladspa_plugin_id; +#endif }; @@ -165,16 +170,22 @@ static fluid_cmd_t fluid_commands[] = { "echo arg Print arg" }, /* LADSPA-related commands */ #ifdef LADSPA - { "ladspa_clear", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_clear, NULL, - "ladspa_clear Resets LADSPA effect unit to bypass state"}, - { "ladspa_add", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_add, NULL, - "ladspa_add lib plugin n1 <- p1 n2 -> p2 ... Loads and connects LADSPA plugin"}, - { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_start, NULL, - "ladspa_start Starts LADSPA effect unit"}, - { "ladspa_declnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_declnode, NULL, - "ladspa_declnode node value Declares control node `node' with value `value'"}, - { "ladspa_setnode", "ladspa", (fluid_cmd_func_t) fluid_LADSPA_handle_setnode, NULL, - "ladspa_setnode node value Assigns `value' to `node'"}, + { "ladspa_plugin", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_plugin, NULL, + "ladspa_plugin Instantiate a new LADSPA plugin"}, + { "ladspa_port", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_port, NULL, + "ladspa_port Connect a LADSPA plugin port"}, + { "ladspa_node", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_node, NULL, + "ladspa_node Create a LADSPA audio or control node"}, + { "ladspa_control", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_control, NULL, + "ladspa_control Set the value of a LADSPA control node"}, + { "ladspa_check", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_check, NULL, + "ladspa_check Check LADSPA configuration"}, + { "ladspa_start", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_start, NULL, + "ladspa_start Start LADSPA effects"}, + { "ladspa_stop", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_stop, NULL, + "ladspa_stop Stop LADSPA effect unit"}, + { "ladspa_reset", "ladspa", (fluid_cmd_func_t) fluid_handle_ladspa_reset, NULL, + "ladspa_reset Stop and reset LADSPA effects"}, #endif { "router_clear", "router", (fluid_cmd_func_t) fluid_handle_router_clear, NULL, "router_clear Clears all routing rules from the midi router"}, @@ -1817,6 +1828,271 @@ int fluid_handle_router_par2(fluid_cmd_handler_t* handler, int ac, char** av, fl return FLUID_OK; } +#ifdef LADSPA + +#define CHECK_LADSPA_ENABLED(_fx, _out) \ + if (_fx == NULL) \ + { \ + fluid_ostream_printf(_out, "LADSPA is not enabled.\n"); \ + return FLUID_FAILED; \ + } + +#define LADSPA_ERR_LEN (1024) + +int fluid_handle_ladspa_start(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + char error[LADSPA_ERR_LEN]; + + CHECK_LADSPA_ENABLED(fx, out); + + if (fluid_ladspa_is_active(fx)) + { + fluid_ostream_printf(out, "LADSPA already started.\n"); + return FLUID_FAILED; + } + + if (fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK) + { + fluid_ostream_printf(out, "LADSPA check failed: %s", error); + fluid_ostream_printf(out, "LADSPA not started.\n"); + return FLUID_FAILED; + } + + if (fluid_ladspa_activate(fx, handler->synth) != FLUID_OK) + { + fluid_ostream_printf(out, "Unable to start LADSPA.\n"); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +int fluid_handle_ladspa_stop(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + + CHECK_LADSPA_ENABLED(fx, out); + + if (!fluid_ladspa_is_active(fx)) + { + fluid_ostream_printf(out, "LADSPA has not been started.\n"); + } + + if (fluid_ladspa_deactivate(fx, handler->synth) != FLUID_OK) + { + fluid_ostream_printf(out, "Unable to stop LADSPA.\n"); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +int fluid_handle_ladspa_reset(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + + CHECK_LADSPA_ENABLED(fx, out); + + if (fluid_ladspa_is_active(fx)) + { + fluid_ladspa_deactivate(fx, handler->synth); + } + + fluid_ladspa_reset(fx); + + return FLUID_OK; +} + +int fluid_handle_ladspa_check(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + char error[LADSPA_ERR_LEN]; + + CHECK_LADSPA_ENABLED(fx, out); + + if (fluid_ladspa_check(fx, error, LADSPA_ERR_LEN) != FLUID_OK) + { + fluid_ostream_printf(out, "LADSPA check failed: %s", error); + return FLUID_FAILED; + } + + fluid_ostream_printf(out, "LADSPA check ok\n"); + + return FLUID_OK; +} + +int fluid_handle_ladspa_control(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + + CHECK_LADSPA_ENABLED(fx, out); + + if (ac != 2) + { + fluid_ostream_printf(out, "ladspa_control needs two arguments: node name and value.\n"); + return FLUID_FAILED; + }; + + /* Redundant check, just here to give a more detailed error message */ + if (!fluid_ladspa_node_exists(fx, av[0])) + { + fluid_ostream_printf(out, "Node '%s' not found.\n", av[0]); + return FLUID_FAILED; + } + + if (fluid_ladspa_set_control_node(fx, av[0], atof(av[1])) != FLUID_OK) + { + fluid_ostream_printf(out, "Failed to set node '%s', maybe it's not a control node?\n", + av[0]); + return FLUID_FAILED; + } + + return FLUID_OK; +}; + +int fluid_handle_ladspa_node(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + char *name; + char *type; + + CHECK_LADSPA_ENABLED(fx, out); + + if (ac < 2) + { + fluid_ostream_printf(out, "ladspa_node needs at least two arguments: node name and type.\n"); + return FLUID_FAILED; + }; + + name = av[0]; + type = av[1]; + + /* audio node - additional no arguments */ + if (FLUID_STRCMP(type, "audio") == 0) + { + if (fluid_ladspa_add_audio_node(fx, name) != FLUID_OK) + { + fluid_ostream_printf(out, "Failed to add audio node.\n"); + return FLUID_FAILED; + } + } + /* control node - arguments: */ + else if (FLUID_STRCMP(type, "control") == 0) + { + if (ac != 3) + { + fluid_ostream_printf(out, "Control nodes need 3 arguments.\n"); + return FLUID_FAILED; + } + + if (fluid_ladspa_add_control_node(fx, name, atof(av[2])) != FLUID_OK) + { + fluid_ostream_printf(out, "Failed to add contrl node.\n"); + return FLUID_FAILED; + } + } + else { + fluid_ostream_printf(out, "Invalid node type.\n"); + return FLUID_FAILED; + } + + return FLUID_OK; +}; + +int fluid_handle_ladspa_plugin(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + int plugin_id; + + CHECK_LADSPA_ENABLED(fx, out); + + if (ac != 2) + { + fluid_ostream_printf(out, "ladspa_plugin needs 2 arguments: library and plugin id.\n"); + return FLUID_FAILED; + } + + plugin_id = fluid_ladspa_add_plugin(fx, av[0], av[1]); + if (plugin_id < 0) + { + fluid_ostream_printf(out, "Failed to add plugin.\n"); + return FLUID_FAILED; + } + + /* store current plugin in the handler, so that subsequent ladspa_port + * commands know which plugin to configure */ + handler->ladspa_plugin_id = plugin_id; + + return FLUID_OK; +} + +int fluid_handle_ladspa_port(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out) +{ + fluid_ladspa_fx_t *fx = handler->synth->ladspa_fx; + int dir; + + CHECK_LADSPA_ENABLED(fx, out); + + if (ac != 3) + { + fluid_ostream_printf(out, "ladspa_port needs 3 arguments: " + "port name, direction and node name.\n"); + return FLUID_FAILED; + } + + if (handler->ladspa_plugin_id == -1) + { + fluid_ostream_printf(out, "Please choose a plugin with ladspa_plugin first.\n"); + return FLUID_FAILED; + } + + if (FLUID_STRCMP(av[1], "<") == 0) + { + dir = FLUID_LADSPA_INPUT; + } + else if (FLUID_STRCMP(av[1], ">") == 0) + { + dir = FLUID_LADSPA_OUTPUT; + } + else if (FLUID_STRCMP(av[1], "=") == 0) + { + dir = FLUID_LADSPA_FIXED; + } + else + { + fluid_ostream_printf(out, "Invalid direction, please use <, > or =\n"); + return FLUID_FAILED; + } + + /* Check port and node name before trying to connect them by name. This is + * redundant, as fluid_ladspa_connect checks them as well, but we do it + * here anyway to give the user better feedback in case a port or node + * could not be found. + */ + if (!fluid_ladspa_port_exists(fx, handler->ladspa_plugin_id, av[0])) + { + fluid_ostream_printf(out, "Port '%s' not found.\n", av[0]); + return FLUID_FAILED; + } + + if (dir != FLUID_LADSPA_FIXED && !fluid_ladspa_node_exists(fx, av[2])) + { + fluid_ostream_printf(out, "Node '%s' not found.\n", av[2]); + return FLUID_FAILED; + } + + if (fluid_ladspa_connect(fx, handler->ladspa_plugin_id, dir, av[0], av[2]) != FLUID_OK) + { + fluid_ostream_printf(out, "Failed to connect plugin port.\n"); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +#endif /* LADSPA */ + int fluid_is_number(char* a) { @@ -1943,6 +2219,10 @@ fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth, fluid_midi_rout } } +#ifdef LADSPA + handler->ladspa_plugin_id = -1; +#endif + return handler; } diff --git a/src/bindings/fluid_cmd.h b/src/bindings/fluid_cmd.h index 98ade2c5..27f528ef 100644 --- a/src/bindings/fluid_cmd.h +++ b/src/bindings/fluid_cmd.h @@ -86,6 +86,16 @@ int fluid_handle_router_chan(fluid_cmd_handler_t* handler, int ac, char** av, fl int fluid_handle_router_par1(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); int fluid_handle_router_par2(fluid_cmd_handler_t* handler, int ac, char** av, fluid_ostream_t out); +#ifdef LADSPA +int fluid_handle_ladspa_plugin(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_port(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_node(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_control(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_check(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_start(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_stop(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +int fluid_handle_ladspa_reset(fluid_cmd_handler_t *handler, int ac, char **av, fluid_ostream_t out); +#endif fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd); void delete_fluid_cmd(fluid_cmd_t* cmd); diff --git a/src/bindings/fluid_ladspa.c b/src/bindings/fluid_ladspa.c index f99966a1..5210a97c 100644 --- a/src/bindings/fluid_ladspa.c +++ b/src/bindings/fluid_ladspa.c @@ -22,1107 +22,1288 @@ * Author: Markus Nentwig, nentwig@users.sourceforge.net */ -#define PrintErrorMessage -1 - #include "fluidsynth_priv.h" #ifdef LADSPA -#include - -/* Dynamic library functions */ -#include #include "fluid_ladspa.h" #include "fluid_synth.h" +#include "fluid_sys.h" +#include +#include -/* Logging to stdout. */ -//#define L(x) x;printf("\n"); -#define L(x); +#define LADSPA_API_ENTER(_fx) (fluid_rec_mutex_lock((_fx)->api_mutex)) -fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth){ - if(synth == NULL) - return NULL; +#define LADSPA_API_RETURN(_fx, _ret) \ + fluid_rec_mutex_unlock((_fx)->api_mutex); \ + return (_ret); - fluid_LADSPA_FxUnit_t* FxUnit=FLUID_NEW(fluid_LADSPA_FxUnit_t); - if(FxUnit == NULL) - return NULL; +static void clear_ladspa(fluid_ladspa_fx_t *fx); - /* The default state is 'bypassed'. The Fx unit has to be turned on explicitly by the user. */ - /* Those settings have to be done in order to allow fluid_LADSPA_clean. */ - FxUnit->Bypass=fluid_LADSPA_Bypassed; - FxUnit->NumberNodes=0; - FxUnit->NumberPlugins=0; - FxUnit->NumberLibs=0; - FxUnit->NumberCommands=0; - FxUnit->NumberUserControlNodes=0; - FxUnit->synth=synth; - pthread_cond_init(&FxUnit->cond,NULL); - return FxUnit; +static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, + fluid_ladspa_node_type_t type, int buf_size); +static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node); +static int create_input_output_nodes(fluid_ladspa_fx_t *fx); +static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name); + +static int get_plugin_port_idx(const fluid_ladspa_plugin_t *plugin, const char *name); +static const LADSPA_Descriptor *get_plugin_descriptor(const fluid_ladspa_lib_t *lib, const char *name); +static int fuzzy_prefix_match(const char *haystack, const char *needle); + +static fluid_ladspa_plugin_t * +new_fluid_ladspa_plugin(fluid_ladspa_fx_t *fx, const fluid_ladspa_lib_t *lib, const char *name); +static void delete_fluid_ladspa_plugin(fluid_ladspa_plugin_t *plugin); +static void activate_plugin(fluid_ladspa_plugin_t *plugin); +static void deactivate_plugin(fluid_ladspa_plugin_t *plugin); +static fluid_ladspa_plugin_t *get_plugin_by_id(fluid_ladspa_fx_t *fx, int id); + +static fluid_ladspa_lib_t *new_fluid_ladspa_lib(fluid_ladspa_fx_t *fx, const char *filename); +static void delete_fluid_ladspa_lib(fluid_ladspa_lib_t *lib); +static fluid_ladspa_lib_t *get_ladspa_library(fluid_ladspa_fx_t *fx, const char *filename); +static int load_plugin_library(fluid_ladspa_lib_t *lib); +static void unload_plugin_library(fluid_ladspa_lib_t *lib); + +static FLUID_INLINE void node_to_buffer(fluid_ladspa_node_t *node, fluid_real_t *buffer); +static FLUID_INLINE void buffer_to_node(fluid_real_t *buffer, fluid_ladspa_node_t *node); + +/** + * Creates a new LADSPA effects unit. + * + * @param synth FluidSynth instance + * @return pointer to the new LADSPA effects unit + */ +fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_synth_t *synth) +{ + fluid_ladspa_fx_t *fx; + + fx = FLUID_NEW(fluid_ladspa_fx_t); + if (fx == NULL) + { + return NULL; + } + + fx->state = FLUID_LADSPA_INACTIVE; + + fx->sample_rate = synth->sample_rate; + + fx->audio_groups = synth->audio_groups; + fx->effects_channels = synth->effects_channels; + fx->audio_channels = synth->audio_channels; + + fx->num_libs = 0; + fx->num_nodes = 0; + fx->num_plugins = 0; + + fx->next_plugin_id = 0; + + /* Setup mutex and cond used to signal deactivation from rvoice mixer thread */ + fx->state_mutex = new_fluid_cond_mutex(); + if (fx->state_mutex == NULL) + { + FLUID_FREE(fx); + return NULL; + } + fx->state_cond = new_fluid_cond(); + if (fx->state_cond == NULL) + { + FLUID_FREE(fx); + return NULL; + } + + /* Setup recursive mutex to protect access to public API */ + fluid_rec_mutex_init(fx->api_mutex); + + /* Finally, create the nodes that carry audio into LADSPA and back out + * again to FluidSynth. They will always be the first in the fx->nodes + * array and not removed on fluid_ladspa_reset but only when this LADSPA fx + * instance is deleted. */ + if (create_input_output_nodes(fx) != FLUID_OK) + { + fluid_rec_mutex_destroy(fx->api_mutex); + FLUID_FREE(fx); + return NULL; + } + + return fx; }; -/* Purpose: - * Creates the system nodes to get data into and out of the Fx unit. +/** + * Destroys and frees a LADSPA effects unit previously created + * with new_fluid_ladspa_fx. + * + * @note This function does not check the engine state for + * possible users, so make sure that you only call this + * if you are sure nobody is using the engine anymore (especially + * that the FluidSynth rvoice mixer has been shut down) + * + * @param fx LADSPA effects instance */ -void fluid_LADSPA_CreateSystemNodes(fluid_LADSPA_FxUnit_t* FxUnit){ - char str[99]; - int nr_input_nodes; - int nr_fx_input_nodes; - int nr_output_nodes; - int i; +void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx) +{ + int i; - /* Retrieve the number of synth / audio out / Fx send nodes */ - nr_input_nodes = FxUnit->synth->audio_groups; - nr_output_nodes = FxUnit->synth->audio_channels; - nr_fx_input_nodes = FxUnit->synth->effects_channels; + clear_ladspa(fx); - /* Create regular input nodes (associated with audio groups) */ - for (i=0; i < nr_input_nodes; i++){ - sprintf(str, "in%i_L",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); - sprintf(str, "in%i_R",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); - }; + /* clear the remaining input or output nodes used to interface with FluidSynth */ + for (i = 0; i < fx->num_nodes; i++) + { + delete_fluid_ladspa_node(fx->nodes[i]); + }; - /* Create effects send nodes (for example reverb, chorus send) */ - for (i=0; i < nr_fx_input_nodes; i++){ - sprintf(str, "send%i_L",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); - sprintf(str, "send%i_R",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_source); - }; - - /* Create output nodes (usually towards the sound card) */ - for (i=0; i < nr_output_nodes; i++){ - sprintf(str, "out%i_L",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); - sprintf(str, "out%i_R",(i+1)); - fluid_LADSPA_CreateNode(FxUnit, str, fluid_LADSPA_node_is_audio | fluid_LADSPA_node_is_sink); - }; + delete_fluid_cond(fx->state_cond); + delete_fluid_cond_mutex(fx->state_mutex); + fluid_rec_mutex_destroy(fx->api_mutex); + FLUID_FREE(fx); }; -/* Purpose: - * Creates predeclared nodes for control of the Fx unit during operation. +/** + * Check if the LADSPA engine is in use by FluidSynth. + * + * If an engine is active, the only allowed user actions are deactivation or + * changing user control nodes. Anything else, especially adding or removing + * plugins, nodes or ports, is only allowed in deactivated state. + * + * @param fx LADSPA fx instance + * @param synth FluidSynth instance + * @return 1 if LADSPA effects engine is active, otherwise 0 */ -void fluid_LADSPA_CreateUserControlNodes(fluid_LADSPA_FxUnit_t* FxUnit){ - int i; - fluid_LADSPA_Node_t* CurrentNode; +int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx) +{ + int is_active; - for (i=0; iNumberUserControlNodes; i++){ - CurrentNode=fluid_LADSPA_CreateNode(FxUnit,FxUnit->UserControlNodeNames[i],fluid_LADSPA_node_is_control); - assert(CurrentNode); - CurrentNode->buf[0]=FxUnit->UserControlNodeValues[i]; - CurrentNode->InCount++; /* The constant counts as input */ - CurrentNode->flags=fluid_LADSPA_node_is_source | fluid_LADSPA_node_is_user_ctrl; /* It is a user control node */ - }; + LADSPA_API_ENTER(fx); + + is_active = (fx->state != FLUID_LADSPA_INACTIVE); + + LADSPA_API_RETURN(fx, is_active); +} + +/** + * Activate the LADSPA fx instance. Also activates each configured plugin. + * + * @param fx LADSPA fx instance + * @param synth FluidSynth instance + * @return FLUID_OK if activation succeeded, otherwise FLUID_FAILED + */ +int fluid_ladspa_activate(fluid_ladspa_fx_t *fx, fluid_synth_t *synth) +{ + int i; + + LADSPA_API_ENTER(fx); + + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + for (i = 0; i < fx->num_plugins; i++) + { + activate_plugin(fx->plugins[i]); + } + + fx->state = FLUID_LADSPA_ACTIVE; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Deactivate a LADSPA fx instance. Also deactivates each active plugin. + * + * @param fx LADSPA fx instance + * @param synth FluidSynth instance + * @return FLUID_OK if deactivation succeeded, otherwise FLUID_FAILED + */ +int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx, fluid_synth_t *synth) +{ + int i; + + LADSPA_API_ENTER(fx); + + if (!fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if (fluid_synth_deactivate_ladspa(synth) != FLUID_OK) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + /* Wait for the rvoice mixer thread to deactivate LADSPA */ + fluid_cond_mutex_lock(fx->state_mutex); + while (fx->state != FLUID_LADSPA_INACTIVE) + { + fluid_cond_wait(fx->state_cond, fx->state_mutex); + } + fluid_cond_mutex_unlock(fx->state_mutex); + + for (i = 0; i < fx->num_plugins; i++) + { + deactivate_plugin(fx->plugins[i]); + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Clear the LADSPA effects engine. Removes all plugin instances, unloads all libraries + * and resets the LADSPA engine to default state. + * + * @param fx LADSPA fx instance + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_ladspa_reset(fluid_ladspa_fx_t *fx) +{ + LADSPA_API_ENTER(fx); + + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + clear_ladspa(fx); + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Processes a block of audio data via the LADSPA effects unit. + * + * This function is called during the main FluidSynth output mixing, just after + * the internal reverb and chorus effects have been processed. + * + * It reads data from the supplied buffers, runs all LADSPA plugins and writes the + * resulting audio back into the same buffers. + * + * @param fx LADSPA effects instance + * @param buf array of pointers into the interleaved left and right audio group buffers + * @param fx_buf array of pointers into the interleaved left and right effects channel buffers + */ +void fluid_ladspa_run(fluid_ladspa_fx_t *fx, fluid_real_t *left_buf[], fluid_real_t *right_buf[], + fluid_real_t *fx_left_buf[], fluid_real_t *fx_right_buf[]) +{ + int i, n; + + /* The nodes used to interface with FluidSynth are always the first nodes in the fx->nodes + * array. They are created in the order: inputs, effect sends, outputs. Left and right channels + * interleaved, so in1_L, in1_R, ... */ + + n = 0; + + /* Incoming main audio data */ + for (i = 0; i < fx->audio_groups; i++) + { + buffer_to_node(left_buf[i], fx->nodes[n++]); + buffer_to_node(right_buf[i], fx->nodes[n++]); + }; + + /* Effect send paths */ + for (i = 0; i < fx->effects_channels; i++) + { + buffer_to_node(fx_left_buf[i], fx->nodes[n++]); + buffer_to_node(fx_right_buf[i], fx->nodes[n++]); + }; + + /* Run each plugin on a block of data */ + for (i = 0; i < fx->num_plugins; i++) + { + fx->plugins[i]->desc->run(fx->plugins[i]->handle, FLUID_BUFSIZE); + }; + + /* Copy the data from the output nodes back to the synth. */ + for (i = 0; i < fx->audio_channels; i++) + { + node_to_buffer(fx->nodes[n++], left_buf[i]); + node_to_buffer(fx->nodes[n++], right_buf[i]); + }; }; -/* Purpose: - * Returns the pointer to the shared library loaded using 'LibraryFilename' (if it has been loaded). - * Return NULL otherwise. - * If not: Clear Fx and abort. +/** + * Creates the nodes to get data from FluidSynth into the LADSPA effects + * and back out again to FluidSynth + * + * The nodes are named from LADSPAs perspective. So the "in*" and "send*" nodes carry audio data + * from FluidSynth into LADSPA, "out*" nodes carry audio data from LADSPA back into FluidSynth. + * + * System nodes are referenced from the fx->nodes array, just like user created nodes. The system + * nodes are always the first in the array. The order of nodes is: + * in1_L, in1_R, ..., send1_L, send1_R, ..., out1_L, out1_R, ..., [user created nodes ...] + * + * @param fx LADSPA fx instance + * @return FLUID_OK on success, otherwise FLUID_FAILED */ -void * fluid_LADSPA_RetrieveSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ - void * CurrentLib=NULL; - int LibCount; - for (LibCount=0; LibCountNumberLibs; LibCount++){ - assert(FxUnit->ppvPluginLibNames[LibCount]); - if (FLUID_STRCMP(FxUnit->ppvPluginLibNames[LibCount],LibraryFilename)==0){ - CurrentLib=FxUnit->ppvPluginLibs[LibCount]; - }; - }; - return CurrentLib; -}; +static int create_input_output_nodes(fluid_ladspa_fx_t *fx) +{ + char name[99]; + int i; -/* Purpose: - * Loads a shared LADSPA library. - * Return NULL, if failed. - * TODO: use LADSPA_PATH - */ -void * fluid_LADSPA_LoadSharedLibrary(fluid_LADSPA_FxUnit_t* FxUnit, char * LibraryFilename){ - void * LoadedLib; - assert(LibraryFilename); - LoadedLib=dlopen(LibraryFilename,RTLD_NOW); - if (!LoadedLib){ - return NULL; - }; - FxUnit->ppvPluginLibs[FxUnit->NumberLibs]=LoadedLib; - FxUnit->ppvPluginLibNames[FxUnit->NumberLibs]=FLUID_STRDUP(LibraryFilename); - FxUnit->NumberLibs++; - return LoadedLib; -}; + /* These nodes transport the main audio generated by FluidSynth into the + * LADSPA effects unit. Create left and right input nodes for each audio group. */ + for (i = 0; i < fx->audio_groups; i++) + { + FLUID_SPRINTF(name, "in%i_L", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } -/* Purpose: - * Retrieves a descriptor to the plugin labeled 'PluginLabel' from the shared library. - */ -const LADSPA_Descriptor * fluid_LADSPA_Retrieve_Plugin_Descriptor(void * CurrentLib, char * PluginLabel){ - LADSPA_Descriptor_Function pfDescriptorFunction; - unsigned long lPluginIndex=0; - const LADSPA_Descriptor * psDescriptor; - pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); - while (pfDescriptorFunction(lPluginIndex)){ - psDescriptor = pfDescriptorFunction(lPluginIndex); - if (FLUID_STRCMP(psDescriptor->Label, PluginLabel) == 0){ - return psDescriptor; - }; - lPluginIndex++; - }; - return NULL; -}; - -/* Purpose: - * Finds out, if 'PortName' starts with 'PluginPort'. - * Spaces and underscore mean the same. - * The comparison is not case sensitive. - * The result distinguishes between a full match (strings are equal), and a partial match (PortName starts with Plugin_Port, but is longer). - */ - -fluid_LADSPA_Stringmatch_t fluid_LADSPA_Check_SubString_Match(const char * Plugin_Port, const char * PortName){ - unsigned int CharCount; - char a; - char b; - for(CharCount=0; CharCount='a' && a <='z'){a-=32;} - if (b>='a' && b <='z'){b-=32;} - if (a == ' '){a='_';}; - if (b == ' '){b='_';}; - if ( a != b){ - return fluid_LADSPA_NoMatch; - }; - }; - if (FLUID_STRLEN(Plugin_Port) == FLUID_STRLEN(PortName)){ - return fluid_LADSPA_FullMatch; - }; - return fluid_LADSPA_PartialMatch; - -}; - -/* Purpose: - * Load the plugins added with 'ladspa_add' and then start the Fx unit. - */ - -int -fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ - fluid_LADSPA_FxUnit_t* FxUnit; - int CommandLineCount; - char * LibraryFilename; - char * PluginLabel; - char ** TokenSequence; - void * CurrentLib; - int NodeCount; //x - int IngoingSignalCount; // Count signals going into LADSPA Fx section - int OutgoingSignalCount; // Count signals going out of LADSPA Fx section - int ReturnVal=FLUID_OK; /* If warnings occur, this is set to -1. */ - char * LADSPA_Path = getenv("LADSPA_PATH"); - - L(fluid_ostream_printf(out,"ladspa_start: starting...")); - assert(synth); - FxUnit=synth->LADSPA_FxUnit; - if (!FxUnit) { - fluid_ostream_printf(out, "ladspa not active!\n"); - return FLUID_FAILED; - } - - /* When calling fluid_ladspastart, the Fx unit must be 'cleared' (no plugins, no libs, no nodes). Verify this here. */ - if (FxUnit->NumberPlugins || FxUnit->NumberLibs){ - fluid_ostream_printf(out, "***Error006***\n" - "Fx unit is currently in use!\n" - "Please run the ladspa_clear command before attempting to use ladspa_add!\n"); - /* In this case do _not_ clear the Fx unit. */ - return(PrintErrorMessage); - }; - if (!FxUnit->NumberCommands){ - fluid_ostream_printf(out, "***Error007***\n" - "Refusing to start the Fx unit without any plugin.\n" - "Use ladspa_add first!\n"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - /* Create predefined nodes */ - L(fluid_ostream_printf(out,"ladspa_start: creating predefined nodes...")); - fluid_LADSPA_CreateSystemNodes(FxUnit); - - /* Create predeclared nodes, that will allow to control the Fx unit during operation */ - L(fluid_ostream_printf(out,"ladspa_start: creating user control nodes...")); - fluid_LADSPA_CreateUserControlNodes(FxUnit); - - L(fluid_ostream_printf(out,"ladspa_start: Processing command lines...")); - for (CommandLineCount=0; CommandLineCountNumberCommands; CommandLineCount++){ - int CurrentPlugin_PortConnected[FLUID_LADSPA_MaxTokens/3]; /* For example: 100 tokens corresponds to roughly 30 ports. */ - char LibFullPath[FLUID_LADSPA_MaxPathLength]; - const LADSPA_Descriptor * CurrentPluginDescriptor; - LADSPA_Handle CurrentPlugin; - unsigned long PortCount; - int TokenCount=0; - char * Search; - int HasASlash=0; - - if (FxUnit->NumberPlugins>=FLUID_LADSPA_MaxPlugins){ - fluid_ostream_printf(out, "***Error003***\n" - "Too many plugins at the same time (%i).\n" - "Change FLUID_LADSPA_MaxPlugins!\n", - FxUnit->NumberPlugins); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); + FLUID_SPRINTF(name, "in%i_R", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } }; - L(fluid_ostream_printf(out,"Processing plugin nr. %i",FxUnit->NumberPlugins)); - - TokenSequence=FxUnit->LADSPA_Command_Sequence[CommandLineCount]; - assert(TokenSequence); - - /* Check, if the library is already loaded. If not, load. */ - LibraryFilename=TokenSequence[TokenCount++]; assert(LibraryFilename); - - L(fluid_ostream_printf(out,"Library name from ladspa_add: %s",LibraryFilename)); - /* A slash-free filename refers to the LADSPA_PATH directory. Add that path, if needed. */ - - - - /* Determine, if the library filename contains a slash. - * If no, try to retrieve the environment variable LADSPA_PATH. - * If that fails, signal error. - * Otherwise leave the filename as it is. - */; - - /* Determine, if the library name is just the filename, or a path (including a slash) */ - Search=LibraryFilename; - while (*Search != '\0') { - if ((*Search)== '/'){ - HasASlash=1; - }; - Search++; - }; - - if (!HasASlash){ - if (!LADSPA_Path){ - fluid_ostream_printf(out, "***Error018***\n" - "The library file name %s does not include a path.\n" - "The environment variable LADSPA_PATH is not set.\n" - "- Use an absolute path (i.e. /home/myself/mylib.so)\n" - "- For the current directory use ./mylib.so\n" - "- set the environment variable LADSPA_PATH (export LADSPA_PATH=/usr/lib/ladspa)\n" - "- depending on your shell, try 'setenv' instead of 'export'\n", - LibraryFilename); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; /* if no LADSPA_PATH */ - FLUID_SNPRINTF (LibFullPath,FLUID_LADSPA_MaxPathLength,"%s/%s",LADSPA_Path,LibraryFilename); - /* If no slash in filename */ - } else { - FLUID_SNPRINTF (LibFullPath,FLUID_LADSPA_MaxPathLength,"%s",LibraryFilename); - }; - - L(fluid_ostream_printf(out,"Full Library path name: %s",LibFullPath)); - - CurrentLib=fluid_LADSPA_RetrieveSharedLibrary(FxUnit, LibFullPath); - if (!CurrentLib){ - LADSPA_Descriptor_Function pfDescriptorFunction; - - L(fluid_ostream_printf(out,"Library %s not yet loaded. Loading.",LibFullPath)); - - if (FxUnit->NumberLibs>=FLUID_LADSPA_MaxLibs){ - fluid_ostream_printf(out, "***Error004***\n" - "Too many libraries open (%i)\n" - "Change FLUID_LADSPA_MaxLibs",FxUnit->NumberPlugins); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - /* Load a LADSPA plugin library and store it for future use.*/ - CurrentLib=fluid_LADSPA_LoadSharedLibrary(FxUnit, LibFullPath); - - if (!CurrentLib){ - fluid_ostream_printf(out, "***Error008***\n" - "Failed to load plugin library %s.", - LibraryFilename); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - - dlerror(); - pfDescriptorFunction = (LADSPA_Descriptor_Function)dlsym(CurrentLib,"ladspa_descriptor"); - if (!pfDescriptorFunction) { - const char * pcError = dlerror(); - if (!pcError) {pcError="Huh?! No error from lib!";}; - fluid_ostream_printf(out, "***Error015***\n" - "Unable to find ladspa_descriptor() function in plugin library file \"%s\": %s.\n" - "Are you sure this is a LADSPA plugin file?\n", - LibraryFilename, - pcError); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - L(fluid_ostream_printf(out,"Library loaded.")); - }; - - PluginLabel=TokenSequence[TokenCount++]; assert(PluginLabel); - L(fluid_ostream_printf(out,"Plugin Label from ladspa_add: %s",PluginLabel)); - /* Retrieve a 'plugin descriptor' from the library. */ - L(fluid_ostream_printf(out,"Looking for the plugin labeled %s",PluginLabel)); - - CurrentPluginDescriptor=fluid_LADSPA_Retrieve_Plugin_Descriptor(CurrentLib, PluginLabel); - - - /* Check, that the plugin was actually found.*/ - if (CurrentPluginDescriptor==NULL){ - fluid_ostream_printf(out, "***Error016***\n" - "Unable to find the plugin labeled \"%s\" in plugin library file \"%s\".\n" - "Hint: run analyzeplugin %s from the command line to get a list of valid plugin labels.\n", - PluginLabel, - LibraryFilename, - LibraryFilename); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - /* Create an instance of the plugin type from the descriptor */ - L(fluid_ostream_printf(out,"instantiating plugin %s",PluginLabel)); - CurrentPlugin=CurrentPluginDescriptor - ->instantiate(CurrentPluginDescriptor,44100); /* Sample rate hardcoded */ - assert(CurrentPlugin); - - /* The descriptor ("type of plugin") and the instance are stored for each plugin instantiation. - If one plugin type is instantiated several times, they will have the same descriptor, only - different instances.*/ - FxUnit->PluginDescriptorTable[FxUnit->NumberPlugins]=CurrentPluginDescriptor; - FxUnit->PluginInstanceTable[FxUnit->NumberPlugins]=CurrentPlugin; - - /* + /* These nodes transport the reverb and chorus audio generated by FluidSynth into the + * LADSPA effects unit. Create left and right input nodes for each effect channel. * - * Wire up the inputs and outputs + * Note: even though FluidSynth has an "effects-channels" setting, that number is currently not + * used. If set to anything larger than 2, those additional buffers are created but never filled + * with any data. Only effect channels 0 (reverb) and 1 (chorus) are used. * + * Unless the fluid_synth_nwrite_float multi-channel output is used in combination with + * fluid_rvoice_mixer_set_mix_fx(0), then the effects are mixed into the first main audio + * channels and not into these dedicated effect sends. + * + * Also: the reverb and chorus send buffers are only filled if the respective effect is active + * in the synth. If it is switched off, the buffers are not filled at all. This is due to an + * optimization in fluid_mixer_buffers_prepare. So there is no way to get the dry effect send + * for reverb and chorus, i.e. the signal that is meant to be fed into such an effect as + * specified for the instrument in the soundfont without having an active reverb or chrous unit. + * Only the already processed signal is available. */ + for (i = 0; i < fx->effects_channels; i++) + { + FLUID_SPRINTF(name, "send%i_L", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } - /* List for checking, that each plugin port is exactly connected once */ - for (PortCount=0; PortCountPortCount; PortCount++){ - CurrentPlugin_PortConnected[PortCount]=0; + FLUID_SPRINTF(name, "send%i_R", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } }; - /* Note: There are three NULL tokens at the end. The last condition may be evaluated even if the first one already detects the end. */ - while (TokenSequence[TokenCount] && TokenSequence[TokenCount+1] && TokenSequence[TokenCount+2]){ - int CurrentPort_StringMatchCount; - LADSPA_PortDescriptor CurrentPort_Descriptor; - char * Plugin_Port=TokenSequence[TokenCount++]; - char * Direction=TokenSequence[TokenCount++]; - char * FLUID_Node=TokenSequence[TokenCount++]; - fluid_LADSPA_Node_t* Current_Node; - const char * PortName=NULL; - int CurrentPort_Index=-1; - fluid_LADSPA_Stringmatch_t StringMatchType=fluid_LADSPA_NoMatch; - fluid_LADSPA_Stringmatch_t CurrentPort_StringMatchType=fluid_LADSPA_NoMatch; - CurrentPort_StringMatchCount=0; + /* These nodes transport the audio as processed by the LADSPA effects back into + * the FluidSynth output buffers (and then usually to a soundcard). Create + * left and right output nodes for each audio channel. */ + for (i = 0; i < fx->audio_channels; i++) + { + FLUID_SPRINTF(name, "out%i_L", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } - L(fluid_ostream_printf(out,"Wiring %s %s %s",Plugin_Port, Direction, FLUID_Node)); - - /* Find the port number on the plugin, that belongs to "Plugin_Port". - * Match the identifier specified by the user against the first characters - * of the port name. - * - * If the given identifier matches several port names only partly, then the input is ambiguous, an error - * message results. - * - * Example: cmt.so, limit_peak: This plugin uses the labels - * - Output Envelope Attack (s) - * - Output Envelope Decay (s) - * - Output - * - * The user input 'Output' matches the first two labels partly, the third fully. This will be accepted. - * The user input 'Out' matches all three only partly, this results in an error message. - */ - - for (PortCount=0; PortCountPortCount; PortCount++){ - PortName=CurrentPluginDescriptor->PortNames[PortCount]; - - StringMatchType=fluid_LADSPA_Check_SubString_Match(Plugin_Port, PortName); - /* If a full-string match has been found earlier, reject all partial matches. */ - if (StringMatchType==fluid_LADSPA_FullMatch || - (StringMatchType==fluid_LADSPA_PartialMatch && CurrentPort_StringMatchType != fluid_LADSPA_FullMatch)){ - - if(StringMatchType==fluid_LADSPA_FullMatch && CurrentPort_StringMatchType==fluid_LADSPA_FullMatch){ - fluid_ostream_printf(out, "***Error027***\n" - "While processing plugin %s: The port label %s appears more than once!\n" - "This is an error in the plugin itself. Please correct it or use another plugin.",PluginLabel, Plugin_Port); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - CurrentPort_Index=PortCount; - CurrentPort_StringMatchType=StringMatchType; - CurrentPort_StringMatchCount++; - - }; /* if suitable match */ - }; /* For port count */ - - /* Several partial matches? Then the identifier is not unique. */ - if (CurrentPort_StringMatchCount > 1 && CurrentPort_StringMatchType == fluid_LADSPA_PartialMatch){ - fluid_ostream_printf(out, "***Error019***\n" - "While processing plugin %s: The identifier %s matches more than one plugin port.\n" - "Please use more letters for the port name. If needed, replace spaces with underscores (_).\n" - "This error will not occur, if you use the full name of a port.\n",PluginLabel, Plugin_Port); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - if (CurrentPort_Index<0){ - fluid_ostream_printf(out, "***Error017***\n" - "Unable to find port '%s' on plugin %s\n" - "Port names are:\n",Plugin_Port,PluginLabel); - for (PortCount=0; PortCountPortCount; PortCount++){ - printf("- `%s'\n",CurrentPluginDescriptor->PortNames[PortCount]); - }; - - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - CurrentPort_Descriptor=CurrentPluginDescriptor->PortDescriptors[CurrentPort_Index]; - assert(CurrentPort_Descriptor); - - /* Retrieve the node with the right name. */ - Current_Node=fluid_LADSPA_RetrieveNode(FxUnit,FLUID_Node); -#define PortIsAudio LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor)) -#define PortIsControl LADSPA_IS_PORT_CONTROL(CurrentPort_Descriptor) && !(LADSPA_IS_PORT_AUDIO(CurrentPort_Descriptor)) - if (!Current_Node){ - /* Doesn't exist? Then create it. */ - if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ - fluid_ostream_printf(out, "***Error005***\n" - "Too many nodes (%i)\n" - "Change FLUID_LADSPA_MaxNodes",FxUnit->NumberNodes); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - if (PortIsAudio){ - Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_audio); - } else if (PortIsControl){ - Current_Node=fluid_LADSPA_CreateNode(FxUnit,FLUID_Node,fluid_LADSPA_node_is_control); - } else { - fluid_ostream_printf(out, "***Error025***\n" - "Plugin port number %i is neither input nor output!\n" - "This is an error in the plugin.\n" - "Please check plugin sourcecode.\n", - CurrentPort_Index); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - }; - assert(Current_Node); - - /* - * - * Check flowgraph for some possible errors. - * - */ - - if (FLUID_STRCMP(Direction,"->")==0){ - /* Data from plugin to FLUID, into node - * - * *** Rule: **** - * A node may not have more than one data source.*/ - if (Current_Node->InCount !=0){ - fluid_ostream_printf(out, "***Error009***\n" - "Plugin %s tries to feed data from output %s into node %s, which is already connected to a data source.\n",PluginLabel,Plugin_Port,FLUID_Node); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - Current_Node->InCount++; - } else if (FLUID_STRCMP(Direction,"<-")==0){ - /* Data from FLUID to plugin, out of node - * - * This check verifies the integrity of the flow graph: - * *** Rule *** - * The execution order of the plugins is the order, in which they are programmed. - * The plugins must be ordered so, that the input of a plugin is already computed at the time of its execution. - * If the user tries to read data out of a node that has not yet an input, then something is wrong.*/ - assert(Current_Node->InCount<=1); - if (Current_Node->InCount !=1){ - fluid_ostream_printf(out, "***Error010***\n" - "Plugin %s tries to read data through input %s from node %s.\n" - "But at this point there is no valid data at that node.\n" - "Please check the flowgraph and especially the execution order!\n",PluginLabel,Plugin_Port,FLUID_Node); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - Current_Node->OutCount++; - } else { - fluid_ostream_printf(out, "***Error024***\n" - "Syntax error: Illegal `arrow' `%s', expecting -> or <-\n", - Direction); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - /* In any case, there must be a valid data source for the port at this time. */ - assert(Current_Node->InCount==1); - - /* Keep track on the number of connections to each port. - * This error occurs only, if an attempt is made to connect one port twice (i.e. ladspa_add libname pluginname port <- nodex port <- nodey) */ - if (CurrentPlugin_PortConnected[CurrentPort_Index]){ - fluid_ostream_printf(out, "***Error011***\n" - "Refusing to connect twice to port %s on plugin %s.\n",CurrentPluginDescriptor->PortNames[CurrentPort_Index], PluginLabel); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - - /* - * - * Connect the port - * - */ - - L(fluid_ostream_printf(out,"Connecting %i",CurrentPort_Index)); - CurrentPluginDescriptor->connect_port - (CurrentPlugin, - CurrentPort_Index, - Current_Node->buf - ); - CurrentPlugin_PortConnected[CurrentPort_Index]++; - - }; /* While Tokensequence (more connections) */ - - /* - * - * Check for left-over tokens - * - */ - - if (TokenSequence[TokenCount]){ - char * T1="";char * T2="";char * T3=""; - if (TokenSequence[TokenCount]){T1=TokenSequence[TokenCount];}; - if (TokenSequence[TokenCount+1]){T2=TokenSequence[TokenCount+1];}; - if (TokenSequence[TokenCount+2]){T3=TokenSequence[TokenCount+2];}; - fluid_ostream_printf(out, "***Error012***\n" - "Leftover tokens: %s %s %s...\n",T1,T2,T3); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); + FLUID_SPRINTF(name, "out%i_R", (i + 1)); + if (new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE) == NULL) + { + return FLUID_FAILED; + } }; - /* - * - * Check, that all plugin ports are connected - * - */ - L(fluid_ostream_printf(out,"Checking left-over ports")); - assert(CurrentPluginDescriptor); - for (PortCount=0; PortCountPortCount; PortCount++){ - assert(CurrentPlugin_PortConnected[PortCount] <=1); - if (CurrentPlugin_PortConnected[PortCount] !=1){ - fluid_ostream_printf(out, "***Error013***\nPlugin: %s. Port %s is unconnected!\n",PluginLabel, CurrentPluginDescriptor->PortNames[PortCount]); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - }; - - /* - * - *Run activate function on plugin, where possible - * - */ - - if (CurrentPluginDescriptor->activate !=NULL){ - CurrentPluginDescriptor->activate(CurrentPlugin); - }; - - FxUnit->NumberPlugins++; - }; /* For CommandLineCount: once for each new command (i.e. plugin instantiation request from user */ - - /* - * - * Further flow graph checks - * - */ - - L(fluid_ostream_printf(out,"Checking flow graph")); - - IngoingSignalCount=0; - OutgoingSignalCount=0; - - for (NodeCount=0; NodeCountNumberNodes; NodeCount++){ - fluid_LADSPA_Node_t* Current_Node; - Current_Node=FxUnit->Nodelist[NodeCount]; - assert(Current_Node); - if (Current_Node->flags & fluid_LADSPA_node_is_source){ - IngoingSignalCount+=Current_Node->OutCount; - } else if (Current_Node->flags & fluid_LADSPA_node_is_sink){ - OutgoingSignalCount+=Current_Node->InCount; - } else { - - /* A node without any input doesn't make sense. - * The flow graph check aborts with an error. This case cannot happen. - */ - if (Current_Node->InCount==0 && !Current_Node->flags && fluid_LADSPA_node_is_dummy){ /* There can only be one warning at a time. */ - fluid_ostream_printf(out, "***Warning020***" - "No input into node %s.\n" - "Use '_' as first char in nodename to suppress this warning.\n" - "Hint: Check for typos in the node name.\n",Current_Node->Name); - /* A warning can also be printed as an error message (check fluid_cmd.c). The only difference between - the return values -1 and 0 is, that -1 prints the result. */ - }; - ReturnVal=PrintErrorMessage; - }; - - /* A node without any output doesn't make sense. */ - if (Current_Node->OutCount==0 && !Current_Node->flags && fluid_LADSPA_node_is_dummy){ - fluid_ostream_printf(out, "***Warning021***\n" - "No output from node %s.\n" - "Use '_' as first char in nodename to suppress this warning.\n" - "Hint: Check for typos in the node name.\n",Current_Node->Name); - ReturnVal=PrintErrorMessage; - }; - /* A free-flying node simply cannot happen. */ - assert(Current_Node->OutCount+Current_Node->InCount); - }; /* Foreach node */ - - /* Issue a warning, if no signal goes into the Fx section. */ - if (IngoingSignalCount==0){ - fluid_ostream_printf(out, "***Warning022***\n" - "You have not connected anything to the synthesizer section (in1_L, in1_R).\n"); - ReturnVal=PrintErrorMessage; - }; - - /* Issue a warning, if no signal leaves the Fx section. */ - if (OutgoingSignalCount==0){ - fluid_ostream_printf(out, "***Warning023***\n" - "You have not connected anything to the output (out1_L, out1_R).\n"); - }; - - /* Finally turn on the Fx unit. */ - FxUnit->Bypass=fluid_LADSPA_Active; - L(fluid_ostream_printf(out,"LADSPA Init OK")); - return(ReturnVal); + return FLUID_OK; }; -void -fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* FxUnit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]){ - int i; - int ii; +static void clear_ladspa(fluid_ladspa_fx_t *fx) +{ + int i; + int num_system_nodes; + /* Deactivate and free all plugins */ + for (i = 0; i < fx->num_plugins; i++) + { + deactivate_plugin(fx->plugins[i]); + delete_fluid_ladspa_plugin(fx->plugins[i]); + } + fx->num_plugins = 0; - int nr_audio_channels; - int nr_fx_sends; - int nr_groups; - int byte_size = FLUID_BUFSIZE * sizeof(LADSPA_Data); - char str[99]; - fluid_LADSPA_Node_t* n; - - /* Retrieve the number of synth / audio out / Fx send nodes */ - nr_groups = FxUnit->synth->audio_groups; - nr_audio_channels = FxUnit->synth->audio_channels; - nr_fx_sends = FxUnit->synth->effects_channels; - - /* Fixme: Retrieving nodes via names is inefficient - * (but not that bad, because the interesting nodes are always at the start of the list). - */ - - /* Input and output are processed via the same buffers. Therefore the effect is bypassed by just skipping everything else. */ - if (FxUnit->Bypass==fluid_LADSPA_Bypassed){ - return; - }; - - if (FxUnit->Bypass==fluid_LADSPA_BypassRequest){ - FxUnit->Bypass=fluid_LADSPA_Bypassed; - pthread_mutex_lock(&FxUnit->mutex); - pthread_cond_broadcast(&FxUnit->cond); - pthread_mutex_unlock(&FxUnit->mutex); - L(printf("LADSPA_Run: Command line asked for bypass of Fx unit. Acknowledged.")); - return; - }; - - assert(FxUnit); - - /* Clear the output buffers, if they are not connected anywhere */ - for (ii=0; ii < nr_audio_channels; ii++){ - - sprintf(str, "out%i_L",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - if (n->InCount == 0){ -/* printf("Output node %s is not connected -> clear buffer\n", str); */ - FLUID_MEMSET(n->buf, 0, byte_size); - }; - - sprintf(str, "out%i_R",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - if (n->InCount == 0){ -/* printf("Output node %s is not connected -> clear buffer\n", str); */ - FLUID_MEMSET(n->buf, 0, byte_size); - }; - }; - - /* Prepare the incoming data: - * Convert fluid_real_t data type to LADSPA_Data type */ - for (ii=0; ii < nr_groups; ii++){ - fluid_real_t* src_buf=left_buf[ii]; - sprintf(str, "in%i_L",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - - assert(FLUID_BUFSIZE % 2 == 0); - - /* Add a very small high frequency signal. This avoids denormal number problems. */ - for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); - i++; - n->buf[i]=(LADSPA_Data)(src_buf[i]); - i++; - }; - - src_buf=right_buf[ii]; - sprintf(str, "in%i_R",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - - /* Add a very small high frequency signal. This avoids denormal number problems. */ - for (i=0; ibuf[i]=(LADSPA_Data)(src_buf[i]+1.e-15); - i++; - n->buf[i]=(LADSPA_Data)(src_buf[i]); - i++; - }; - }; - - /* Effect send paths */ - for (ii=0; ii < nr_fx_sends; ii++){ - sprintf(str, "send%i_L",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - for (i=0; ibuf[i]=(LADSPA_Data)(fx_left_buf[ii][i]); - }; - - sprintf(str, "send%i_R",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - for (i=0; ibuf[i]=(LADSPA_Data)(fx_right_buf[ii][i]); - }; - }; - - /* Run each plugin on a block of data. - * The execution order has been checked during setup.*/ - for (i=0; iNumberPlugins; i++){ - FxUnit->PluginDescriptorTable[i]->run(FxUnit->PluginInstanceTable[i],FLUID_BUFSIZE); - }; - - /* Copy the data from the output nodes back to the synth. */ - for (ii=0; ii < nr_audio_channels; ii++){ - fluid_real_t* dest_buf=left_buf[ii]; - sprintf(str, "out%i_L",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - for (i=0; ibuf[i]; - }; - - dest_buf=right_buf[ii]; - sprintf(str, "out%i_R",(ii+1)); - n=fluid_LADSPA_RetrieveNode(FxUnit, str); assert(n); - for (i=0; ibuf[i]; - }; - }; -}; - -fluid_LADSPA_Node_t* -fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name){ - int i=0; - assert(FxUnit);assert(Name); - for (i=0; iNumberNodes; i++){ - assert(FxUnit->Nodelist[i]); - if (FLUID_STRCMP(FxUnit->Nodelist[i]->Name,Name)==0){ - return FxUnit->Nodelist[i]; + /* Unload and free all libraries */ + for (i = 0; i < fx->num_libs; i++) + { + unload_plugin_library(fx->libs[i]); + delete_fluid_ladspa_lib(fx->libs[i]); }; - }; - return NULL; + fx->num_libs = 0; + + /* Free all user defined nodes, leaving system nodes untouched */ + num_system_nodes = 2 * (fx->audio_groups + fx->effects_channels + fx->audio_channels); + for (i = num_system_nodes; i < fx->num_nodes; i++) + { + delete_fluid_ladspa_node(fx->nodes[i]); + }; + fx->num_nodes = num_system_nodes; }; - -/* Purpose: - * Creates a new node from the node name given by the user. +/** + * Check if a named node exists. Nodes are searched by exact string comparison. + * + * @param fx LADSPA fx instance + * @param name the node name string + * @return TRUE if the node exists, otherwise FALSE */ -fluid_LADSPA_Node_t* -fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags){ - int Dummy=0; - fluid_LADSPA_Node_t* NewNode; - assert(FxUnit); - assert(Name); -// printf("Flags is %i\n",flags); - L(printf("Create node: %s",Name)); - if (FxUnit->NumberNodes>=FLUID_LADSPA_MaxNodes){ - printf( "***Error014***\n" - "Too many nodes (%i)\n" - "Change FLUID_LADSPA_MaxNodes.\n",FxUnit->NumberNodes); - fluid_LADSPA_clear(FxUnit); - return NULL; - }; +int fluid_ladspa_node_exists(fluid_ladspa_fx_t *fx, const char *name) +{ + int node_exists; - /* Don't allow node names, which start with -, 0..9 */ - if (Name[0] == '-' || (Name[0]>='0' && Name[0]<='9')){ - printf( "***Error026***\n" - "The node name %s starts with a digit / minus sign!\n" - "Please use a letter to start a node name.\n" - "A constant node is created by using `$' as first character,\n" - "for example $-2.5.\n", - Name); - fluid_LADSPA_clear(FxUnit); - return NULL; - }; + LADSPA_API_ENTER(fx); - /* A nodename starting with "_" is a possible dummy node, which may (but need not) act as a data sink or source (dummy node). */ - if (Name[0] == ' '){ /* ??? Should be '_' ??? */ - Dummy=1; - }; - NewNode=FLUID_NEW(fluid_LADSPA_Node_t);assert(NewNode); - if (flags && fluid_LADSPA_node_is_audio){ - /* Audio node contains buffer. */ - NewNode->buf=FLUID_ARRAY(LADSPA_Data, (FLUID_BUFSIZE));assert(NewNode->buf); - /* It is permitted to use a dummy node without input. Therefore clear all node buffers at startup. */ - FLUID_MEMSET(NewNode->buf, 0, (FLUID_BUFSIZE*sizeof(LADSPA_Data))); - } else if (flags & fluid_LADSPA_node_is_control){ - /* Control node contains single value. */ - NewNode->buf=FLUID_ARRAY(LADSPA_Data, 1);assert(NewNode->buf); - } else { - assert(0); - }; - NewNode->Name=FLUID_STRDUP(Name);assert(NewNode->Name); - if (Dummy){ - flags |= fluid_LADSPA_node_is_dummy; - }; - NewNode->InCount=0; - NewNode->OutCount=0; - NewNode->flags=flags; + node_exists = (get_node(fx, name) != NULL); - /* A nodename starting with "$" means that the node holds a constant value. */ - if (NewNode->Name[0] == '$'){ - assert(flags & fluid_LADSPA_node_is_control); - /* Skip the first character => +1 */ - NewNode->buf[0]=(LADSPA_Data)atof(NewNode->Name+1); - NewNode->InCount++; - }; - if (flags & fluid_LADSPA_node_is_source){ - NewNode->InCount++; -// printf("****************************** Source!\n"); - } else if (flags & fluid_LADSPA_node_is_sink){ - NewNode->OutCount++; - }; - FxUnit->Nodelist[FxUnit->NumberNodes++]=NewNode; + LADSPA_API_RETURN(fx, node_exists); +} - L(printf("Node %s created.",Name)); - return NewNode; -}; +/** + * Check if the named port exists on a plugin instance. + * + * The string match is case-insensitive and treats spaces and underscores as equal. + * + * @param fx LADSPA fx instance + * @param plugin_id integer plugin id as returned by fluid_ladspa_add_*_node + * @param name the port name + * @return TRUE if port was found, otherwise FALSE + */ +int fluid_ladspa_port_exists(fluid_ladspa_fx_t *fx, int plugin_id, const char *name) +{ + fluid_ladspa_plugin_t *plugin; + int port_exists; -void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit){ - int i; - int ii; - L(printf("ladspa_clear")); - assert(FxUnit); + LADSPA_API_ENTER(fx); - if (FxUnit->Bypass==fluid_LADSPA_Active){ - L(printf("clear: Requesting bypass from synthesis thread")); - /* Bypass the Fx unit before anything else. - * Reason: Not a good idea to release plugins, while another thread runs them. - */ - FxUnit->Bypass=fluid_LADSPA_BypassRequest; - pthread_mutex_lock(&FxUnit->mutex); - pthread_cond_wait(&FxUnit->cond,&FxUnit->mutex); - pthread_mutex_unlock(&FxUnit->mutex); - L(printf("clear: Synthesis thread has switched to bypass.")); - } else { - L(printf("clear: Fx unit was already bypassed. No action needed.")); - }; + plugin = get_plugin_by_id(fx, plugin_id); + if (plugin == NULL) + { + LADSPA_API_RETURN(fx, 0); + } - L(printf("Clear all user control node declarations")); - for (i=0; iNumberUserControlNodes; i++){ - FLUID_FREE(FxUnit->UserControlNodeNames[i]); - }; - FxUnit->NumberUserControlNodes=0; + port_exists = (get_plugin_port_idx(plugin, name) != -1); - L(printf("Clear all plugin instances")); - for (i=0; iNumberPlugins; i++){ - assert(FxUnit->PluginDescriptorTable[i]); - assert(FxUnit->PluginInstanceTable[i]); + LADSPA_API_RETURN(fx, port_exists); +} - /* Run deactivate function on plugin, if possible */ - if (FxUnit->PluginDescriptorTable[i]->deactivate){ - FxUnit->PluginDescriptorTable[i]->deactivate(FxUnit->PluginInstanceTable[i]); +/** + * Create and add a new LADSPA audio node. + * + * Audio nodes are used to connect the output of one plugin to the input of + * another. + * + * @param fx LADSPA effects instance + * @param name name of the new node + * @return FLUID_OK on success, FLUID_FAILED on error + */ +int fluid_ladspa_add_audio_node(fluid_ladspa_fx_t *fx, const char *name) +{ + fluid_ladspa_node_t *node; + + LADSPA_API_ENTER(fx); + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node = new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_AUDIO, FLUID_BUFSIZE); + if (node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Create and add a new LADSPA control node that can be set manually. + * + * @param fx LADSPA effects instance + * @param name name of the new node + * @param val the initial float value of the node + * @return FLUID_OK on success, FLUID_FAILED on error + */ +int fluid_ladspa_add_control_node(fluid_ladspa_fx_t *fx, const char *name, fluid_real_t val) +{ + fluid_ladspa_node_t *node; + + LADSPA_API_ENTER(fx); + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node = new_fluid_ladspa_node(fx, name, FLUID_LADSPA_NODE_CONTROL, 1); + if (node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node->buf[0] = val; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Set the value of a user constrol node + * + * Nodes are searched by exact string comparison. + * + * @param fx LADSPA fx instance + * @param name node name string + * @param val floating point value + * @return FLUID_OK on success, FLUID_FAILED on error + */ +int fluid_ladspa_set_control_node(fluid_ladspa_fx_t *fx, const char *name, fluid_real_t val) +{ + fluid_ladspa_node_t *node; + + LADSPA_API_ENTER(fx); + + node = get_node(fx, name); + if (node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + if (node->type != FLUID_LADSPA_NODE_CONTROL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + node->buf[0] = val; + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Instantiate a plugin from a LADSPA plugin library and return it's unique id + * + * The returned id can be used to reference this plugin instance at a later time, + * for example when connecting the plugin instance ports with nodes. Plugin instance + * ids are never reused in a synth. + * + * @param fx LADSPA effects instance + * @param lib_name filename of ladspa plugin library + * @param plugin_name plugin name (the unique label of the plugin in the LADSPA library) + * @return integer plugin id or -1 on error + */ +int fluid_ladspa_add_plugin(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name) +{ + fluid_ladspa_lib_t *lib; + fluid_ladspa_plugin_t *plugin; + + LADSPA_API_ENTER(fx); + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, -1); + } + + if (fx->num_plugins >= FLUID_LADSPA_MAX_PLUGINS) + { + FLUID_LOG(FLUID_ERR, "Maximum number of LADSPA plugins reached"); + LADSPA_API_RETURN(fx, -1); + } + + lib = get_ladspa_library(fx, lib_name); + if (lib == NULL) + { + LADSPA_API_RETURN(fx, -1); + } + + plugin = new_fluid_ladspa_plugin(fx, lib, plugin_name); + if (plugin == NULL) + { + LADSPA_API_RETURN(fx, -1); + } + + plugin->id = fx->next_plugin_id++; + fx->plugins[fx->num_plugins++] = plugin; + + LADSPA_API_RETURN(fx, plugin->id); +} + +/** + * Connect an input or output plugin port to a node + * + * @note There is no corresponding disconnect function. If the connections need to be changed, + * clear everything with fluid_ladspa_reset and start again from scratch. + * + * @param fx LADSPA effects instance + * @param plugin_id the integer plugin id as returned by fluid_ladspa_add_plugin + * @param dir connect to port as FLUID_LADSPA_INPUT or FLUID_LADSPA_OUTPUT + * @param port_name the port name to connect to (fuzzy match, see get_plugin_port_idx) + * @param node_name the node name to connect to (exact match) + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_ladspa_connect(fluid_ladspa_fx_t *fx, int plugin_id, fluid_ladspa_dir_t dir, + const char *port_name, const char *node_name) +{ + fluid_ladspa_plugin_t *plugin; + fluid_ladspa_node_t *node; + int port_idx; + int port_flags; + const char *full_port_name; + const char *plugin_name; + + LADSPA_API_ENTER(fx); + + if (fluid_ladspa_is_active(fx)) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + plugin = get_plugin_by_id(fx, plugin_id); + if (plugin == NULL) + { + FLUID_LOG(FLUID_ERR, "LADSPA plugin with ID %d not found", plugin_id); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + port_idx = get_plugin_port_idx(plugin, port_name); + if (port_idx < 0) + { + FLUID_LOG(FLUID_ERR, "LADSPA plugin port '%s' not found on plugin '%s'", port_name, + plugin->desc->Label); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + /* fixed node, create a new 'anonymous' node and interpret the node name as it's fixed float + * value */ + if (dir == FLUID_LADSPA_FIXED) + { + node = new_fluid_ladspa_node(fx, "", FLUID_LADSPA_NODE_CONTROL, 1); + if (node == NULL) + { + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + node->buf[0] = atof(node_name); + } + else + { + node = get_node(fx, node_name); + if (node == NULL) + { + FLUID_LOG(FLUID_ERR, "LADSPA node '%s' not found", node_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + } + + /* For easier access during sanity checks */ + port_flags = plugin->desc->PortDescriptors[port_idx]; + full_port_name = plugin->desc->PortNames[port_idx]; + plugin_name = plugin->desc->Label; + + /* Check that requested direction matches with port direction */ + if (dir == FLUID_LADSPA_INPUT && !LADSPA_IS_PORT_INPUT(port_flags)) + { + FLUID_LOG(FLUID_ERR, "Port '%s' on plugin '%s' is not an input port", full_port_name, plugin_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + else if (dir == FLUID_LADSPA_OUTPUT && !LADSPA_IS_PORT_OUTPUT(port_flags)) + { + FLUID_LOG(FLUID_ERR, "Port '%s' on plugin '%s' is not an output port", full_port_name, plugin_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + /* Check that requested port type matches the node type */ + if (LADSPA_IS_PORT_CONTROL(port_flags) && node->type == FLUID_LADSPA_NODE_AUDIO) + { + FLUID_LOG(FLUID_ERR, "Cannot connect control port '%s' on plugin '%s' to an audio node", + full_port_name, plugin_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + else if (LADSPA_IS_PORT_AUDIO(port_flags) && node->type != FLUID_LADSPA_NODE_AUDIO) + { + FLUID_LOG(FLUID_ERR, "Cannot connect audio port '%s' on plugin '%s' to a control node", + full_port_name, plugin_name); + LADSPA_API_RETURN(fx, FLUID_FAILED); + } + + FLUID_LOG(FLUID_DBG, "Connecting LADSPA plugin '%s': port '%s' %s node '%s'", plugin->desc->Label, + full_port_name, (dir == FLUID_LADSPA_INPUT) ? "<" : ">", node_name); + + plugin->desc->connect_port(plugin->handle, port_idx, node->buf); + + /* Mark port and node as connected in the respective direction */ + if (dir == FLUID_LADSPA_INPUT || dir == FLUID_LADSPA_FIXED) + { + plugin->ports[port_idx].num_inputs++; + node->num_outputs++; + } + else + { + plugin->ports[port_idx].num_outputs++; + node->num_inputs++; + } + + LADSPA_API_RETURN(fx, FLUID_OK); +} + +/** + * Do a sanity check for problems in the LADSPA setup + * + * @param fx LADSPA fx instance + * @param err pointer to string that should be filled with an error message, if applicable + * @param err_size size of the error buffer + * @return FLUID_OK on success, otherwise FLUID_FAILED + */ +int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size) +{ + int i; + unsigned int k; + int has_connections; + int num_system_nodes; + fluid_ladspa_plugin_t *plugin; + LADSPA_PortDescriptor port_flags; + + LADSPA_API_ENTER(fx); + + /* Check that there is at least one plugin */ + if (fx->num_plugins == 0) + { + FLUID_SNPRINTF(err, err_size, "No plugins loaded\n"); + return FLUID_FAILED; + } + + /* Check that all plugin ports are connected */ + for (i = 0; i < fx->num_plugins; i++) + { + plugin = fx->plugins[i]; + + for (k = 0; k < plugin->desc->PortCount; k++) + { + port_flags = plugin->desc->PortDescriptors[k]; + + if (LADSPA_IS_PORT_INPUT(port_flags) && plugin->ports[k].num_inputs == 0) + { + FLUID_SNPRINTF(err, err_size, "Input port '%s' on plugin '%s' is not connected\n", + plugin->desc->PortNames[k], plugin->desc->Label); + return FLUID_FAILED; + } + else if (LADSPA_IS_PORT_OUTPUT(port_flags) && plugin->ports[k].num_outputs == 0) + { + FLUID_SNPRINTF(err, err_size, "Output port '%s' on plugin '%s' is not connected\n", + plugin->desc->PortNames[k], plugin->desc->Label); + return FLUID_FAILED; + } + } + } + + /* Check that at least one system input is used */ + has_connections = 0; + for (i = 0; i < 2 * (fx->audio_groups + fx->effects_channels); i++) + { + if (fx->nodes[i]->num_outputs) + { + has_connections = 1; + break; + } + } + if (!has_connections) + { + FLUID_SNPRINTF(err, err_size, "No system input nodes are connected\n"); + return FLUID_FAILED; + } + + num_system_nodes = 2 * (fx->audio_groups + fx->effects_channels + fx->audio_channels); + + /* Check that at least one system output is used */ + has_connections = 0; + for (i = 2 * (fx->audio_groups + fx->effects_channels); i < num_system_nodes; i++) + { + if (fx->nodes[i]->num_inputs) + { + has_connections = 1; + break; + } + } + if (!has_connections) + { + FLUID_SNPRINTF(err, err_size, "No system output nodes are connected\n"); + return FLUID_FAILED; + } + + /* Check that custom audio nodes have both input and output and control nodes have an output */ + for (i = num_system_nodes; i < fx->num_nodes; i++) + { + if (fx->nodes[i]->type == FLUID_LADSPA_NODE_AUDIO && + (fx->nodes[i]->num_inputs == 0 || fx->nodes[i]->num_outputs == 0)) + { + FLUID_SNPRINTF(err, err_size, "Audio node '%s' is not connected on input and output\n", + fx->nodes[i]->name); + return FLUID_FAILED; + } + + if (fx->nodes[i]->type == FLUID_LADSPA_NODE_CONTROL && (fx->nodes[i]->num_outputs == 0)) + { + FLUID_SNPRINTF(err, err_size, "Control node '%s' is not connected\n", fx->nodes[i]->name); + return FLUID_FAILED; + } + } + + return FLUID_OK; +} + + +static FLUID_INLINE void buffer_to_node(fluid_real_t *buffer, fluid_ladspa_node_t *node) +{ + int i; + + /* If the node is not used by any plugin, then we don't need to fill it */ + if (node->num_outputs == 0) + { + return; + } + + for (i = 0; i < FLUID_BUFSIZE; i++) + { + node->buf[i] = (LADSPA_Data)buffer[i]; }; - FxUnit->PluginDescriptorTable[i]->cleanup(FxUnit->PluginInstanceTable[i]); - }; - FxUnit->NumberPlugins=0; +} - L(printf("Clear all nodes")); /* Only after removing plugins! */ - for (i=0; iNumberNodes; i++){ - FLUID_FREE(FxUnit->Nodelist[i]->buf); - FLUID_FREE(FxUnit->Nodelist[i]); - }; - FxUnit->NumberNodes=0; +static FLUID_INLINE void node_to_buffer(fluid_ladspa_node_t *node, fluid_real_t *buffer) +{ + int i; + /* If the node has no inputs, then we don't need to copy it to the node */ + if (node->num_inputs == 0) + { + return; + } - L(printf("Clear all plugin libraries")); - for (i=0; iNumberLibs; i++){ - - assert(FxUnit->ppvPluginLibs[i]); - dlclose(FxUnit->ppvPluginLibs[i]); - - assert(FxUnit->ppvPluginLibNames[i]); - FLUID_FREE(FxUnit->ppvPluginLibNames[i]); - }; - FxUnit->NumberLibs=0; - - L(printf("Clear all command lines")); - for (i=0; iNumberCommands;i++){ - ii=0; - assert(FxUnit->LADSPA_Command_Sequence[i]); - while (FxUnit->LADSPA_Command_Sequence[i][ii]){ - FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i][ii]); - ii++; + for (i = 0; i < FLUID_BUFSIZE; i++) + { + buffer[i] = (fluid_real_t)node->buf[i]; }; - FLUID_FREE(FxUnit->LADSPA_Command_Sequence[i]); - }; - FxUnit->NumberCommands=0; -}; +} -int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ - int i; - //char * Token; - char ** CommandLine; - fluid_LADSPA_FxUnit_t* FxUnit; - assert(synth); - FxUnit=synth->LADSPA_FxUnit; - if (!FxUnit) { - fluid_ostream_printf(out, "ladspa not active!\n"); - return FLUID_FAILED; - } - if (ac>=FLUID_LADSPA_MaxTokens){ - /* Can't be tested. fluidsynth limits the number of tokens. */ - printf("***Error001***\n" - "Too many ports.\nChange FLUID_LADSPA_MaxTokens!"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; - if (ac<2){ - printf("***Error002***\n" - "ladspa_add needs at least two arguments - libname and plugin name!"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; +static void activate_plugin(fluid_ladspa_plugin_t *plugin) +{ + if (!plugin->active) + { + plugin->active = 1; + if (plugin->desc->activate != NULL) + { + plugin->desc->activate(plugin->handle); + } + } +} - if (FxUnit->NumberCommands>=FLUID_LADSPA_MaxPlugins){ - printf("***Error032***\n" - "Too many plugins.\nChange FLUID_LADSPA_MaxPlugins!"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; +static void deactivate_plugin(fluid_ladspa_plugin_t *plugin) +{ + if (plugin->active) + { + plugin->active = 0; + if (plugin->desc->deactivate != NULL) + { + plugin->desc->deactivate(plugin->handle); + } + } +} - /* CommandLine (token sequence) is terminated with NULL. - * Add two more NULLs, so that a chunk of three tokens can be checked later without risk.*/ +/** + * Does a case insensitive 'fuzzy' prefix match. + * + * Checks if haystack starts with needle, ignoring case differences and + * treating spaces and underscores as equal. + * + * @param haystack string to search in + * @param needle string prefix to search for + * @return TRUE if match was found, otherwise FALSE + */ +static int fuzzy_prefix_match(const char *haystack, const char *needle) +{ + unsigned int num_chars; + unsigned int i; + char a; + char b; - CommandLine=FLUID_ARRAY(char*, (ac+3));assert(CommandLine); - for (i=0; iLADSPA_Command_Sequence[FxUnit->NumberCommands]=CommandLine; - FxUnit->NumberCommands++; - return(FLUID_OK); -}; + for (i = 0; i < num_chars; i++) + { + a = haystack[i]; + a = (a == ' ') ? '_' : tolower(a); -int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ - //int i; - //char * Token; - //char ** CommandLine; - char * NodeName; - fluid_real_t NodeValue; - fluid_LADSPA_FxUnit_t* FxUnit; - assert(synth); - FxUnit=synth->LADSPA_FxUnit; - if (!FxUnit) { - fluid_ostream_printf(out, "ladspa not active!\n"); - return FLUID_FAILED; - } + b = needle[i]; + b = (b == ' ') ? '_' : tolower(b); - if (ac<2){ - printf("***Error028***\n" - "ladspa_declnode needs two arguments - node name and value!\n"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; + if (a != b) + { + return FALSE; + } + } - if (FxUnit->NumberUserControlNodes>=FLUID_LADSPA_MaxNodes){ - printf("***Error033***\n" - "Too many user-control nodes.\nChange FLUID_LADSPA_MaxNodes!"); - fluid_LADSPA_clear(FxUnit); - return(PrintErrorMessage); - }; + return TRUE; +} - NodeName=FLUID_STRDUP(av[0]); assert(NodeName); - NodeValue=atof(av[1]); - FxUnit->UserControlNodeNames[FxUnit->NumberUserControlNodes]=NodeName; - FxUnit->UserControlNodeValues[FxUnit->NumberUserControlNodes]=NodeValue; - FxUnit->NumberUserControlNodes++; - return(FLUID_OK); -}; -int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ - //int i; - //char * Token; - char * NodeName; - fluid_real_t NodeValue; - fluid_LADSPA_FxUnit_t* FxUnit; - fluid_LADSPA_Node_t* CurrentNode; - assert(synth); - FxUnit=synth->LADSPA_FxUnit; - if (!FxUnit) { - fluid_ostream_printf(out, "ladspa not active!\n"); - return FLUID_FAILED; - } +/** + * Return a LADSPA node by name. Nodes are searched by exact string comparison. + * + * @param fx LADSPA fx instance + * @param name the node name string + * @return a fluid_ladspa_node_t pointer or NULL if not found + */ +static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name) +{ + int i; - if (ac!=2){ - printf("***Error029***\n" - "ladspa_setnode needs two arguments - node name and value!\n"); - /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ - return(PrintErrorMessage); - }; + for (i = 0; i < fx->num_nodes; i++) + { + if (FLUID_STRCMP(fx->nodes[i]->name, name) == 0) + { + return fx->nodes[i]; + } + } - NodeName=av[0]; assert(NodeName); - NodeValue=atof(av[1]); + return NULL; +} - CurrentNode=fluid_LADSPA_RetrieveNode(FxUnit,NodeName); - if (!CurrentNode){ - printf("***Error030***\n" - "The node %s was not found. Please use the full name of a node, that was\n" - "previously declared with ladspa_declnode.\n",NodeName); - /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ - return(PrintErrorMessage); - }; - if (!(CurrentNode->flags & fluid_LADSPA_node_is_user_ctrl)){ - printf("***Error031***\n" - "The node %s is an ordinary control node.\n" - "Only user control nodes can be modified with ladspa_setnode.\n",NodeName); - /* Do not clear the Fx unit (no fluid_LADSPA_clear). */ - return(PrintErrorMessage); - }; - L(printf("ladspa_setnode: Assigning value %f",NodeValue)); - CurrentNode->buf[0]=NodeValue; - return(FLUID_OK); -}; +/** + * Return a LADSPA plugin port index by name, using a 'fuzzy match'. + * + * Returns the first plugin port which matches the name. If no exact match is + * found, returns the port that starts with the specified name, but only if there is + * only one such match. + * + * @param plugin pointer to fluid_ladspa_plugin_t + * @param name the port name + * @return index of the port in the plugin or -1 on error + */ +static int get_plugin_port_idx(const fluid_ladspa_plugin_t *plugin, const char *name) +{ + unsigned int i; + int port = -1; -int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out){ - fluid_LADSPA_FxUnit_t* FxUnit; - assert(synth); - FxUnit=synth->LADSPA_FxUnit; - if (!FxUnit) { - fluid_ostream_printf(out, "ladspa not active!\n"); - return FLUID_FAILED; - } - fluid_LADSPA_clear(FxUnit); - return(FLUID_OK); -}; + for (i = 0; i < plugin->desc->PortCount; i++) + { + if (fuzzy_prefix_match(plugin->desc->PortNames[i], name)) + { + /* exact match, return immediately */ + if (FLUID_STRLEN(plugin->desc->PortNames[i]) == FLUID_STRLEN(name)) + { + return i; + } + + /* more than one fuzzy match should be treated as not found */ + if (port != -1) + { + return -1; + } + + port = i; + } + } + + return port; +} + +/** + * Return a LADSPA descriptor structure for a plugin in a LADSPA library. + * + * @param lib pointer to fluid_ladspa_lib_t instance + * @param name name (LADSPA Label) of the plugin + * @return pointer to LADSPA_Descriptor, NULL on error or if not found + */ +static const LADSPA_Descriptor *get_plugin_descriptor(const fluid_ladspa_lib_t *lib, const char *name) +{ + const LADSPA_Descriptor *desc; + int i = 0; + + while (1) + { + desc = lib->descriptor(i++); + if (desc == NULL) + { + return NULL; + } + + if (FLUID_STRCMP(desc->Label, name) == 0) + { + return desc; + } + } +} + +/** + * Instantiate a new LADSPA plugin from a library and set up the associated + * control structures needed by the LADSPA fx engine. + * + * Plugins are identified by their "Label" in the plugin descriptor structure. + * + * @param fx LADSPA fx instance + * @param lib pointer to fluid_ladspa_lib_t + * @param name string name of the plugin (the LADSPA Label) + * @return pointer to the new ladspa_plugin_t structure or NULL on error + */ +static fluid_ladspa_plugin_t * +new_fluid_ladspa_plugin(fluid_ladspa_fx_t *fx, const fluid_ladspa_lib_t *lib, const char *name) +{ + fluid_ladspa_plugin_t *plugin; + unsigned int i; + + plugin = FLUID_NEW(fluid_ladspa_plugin_t); + if (plugin == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + plugin->desc = get_plugin_descriptor(lib, name); + if (plugin->desc == NULL) + { + FLUID_FREE(plugin); + return NULL; + } + + plugin->handle = plugin->desc->instantiate(plugin->desc, fx->sample_rate); + if (plugin->handle == NULL) + { + FLUID_FREE(plugin); + FLUID_LOG(FLUID_ERR, "Unable to instantiate plugin '%s' from '%s'", name, lib->filename); + return NULL; + } + + plugin->ports = FLUID_ARRAY(fluid_ladspa_port_state_t, plugin->desc->PortCount); + if (plugin->ports == NULL) + { + plugin->desc->cleanup(plugin->handle); + FLUID_FREE(plugin); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + for (i = 0; i < plugin->desc->PortCount; i++) + { + plugin->ports[i].num_inputs = 0; + plugin->ports[i].num_outputs = 0; + } + + return plugin; +} + +static void delete_fluid_ladspa_plugin(fluid_ladspa_plugin_t *plugin) +{ + FLUID_FREE(plugin->ports); + plugin->desc->cleanup(plugin->handle); + FLUID_FREE(plugin); +} + +static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const char *name, + fluid_ladspa_node_type_t type, int buf_size) +{ + fluid_ladspa_node_t *node; + + /* check if node with this name exits already */ + if (FLUID_STRLEN(name) > 0 && get_node(fx, name) != NULL) + { + return NULL; + } + + if (fx->num_nodes >= FLUID_LADSPA_MAX_NODES) + { + FLUID_LOG(FLUID_ERR, "Maximum number of nodes reached"); + return NULL; + } + + node = FLUID_NEW(fluid_ladspa_node_t); + if (node == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->type = type; + + node->name = FLUID_STRDUP(name); + if (node->name == NULL) + { + FLUID_FREE(node); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + node->buf = FLUID_ARRAY(LADSPA_Data, buf_size); + if (node->buf == NULL) + { + FLUID_FREE(node->name); + FLUID_FREE(node); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + FLUID_MEMSET(node->buf, 0, buf_size * sizeof(LADSPA_Data)); + + node->num_inputs = 0; + node->num_outputs = 0; + + fx->nodes[fx->num_nodes++] = node; + + return node; +} + +static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node) +{ + FLUID_FREE(node->buf); + FLUID_FREE(node->name); + FLUID_FREE(node); +} + +static fluid_ladspa_lib_t *get_ladspa_library(fluid_ladspa_fx_t *fx, const char *filename) +{ + int i; + fluid_ladspa_lib_t *lib; + + /* check if we have loaded this lib before and return it if found */ + for (i = 0; i < fx->num_libs; i++) + { + if (FLUID_STRCMP(fx->libs[i]->filename, filename) == 0) + { + return fx->libs[i]; + } + } + + if (fx->num_libs >= FLUID_LADSPA_MAX_LIBS) + { + FLUID_LOG(FLUID_ERR, "Maximum number of LADSPA libraries reached"); + return NULL; + } + + lib = new_fluid_ladspa_lib(fx, filename); + if (lib == NULL) + { + return NULL; + } + + if (load_plugin_library(lib) != FLUID_OK) + { + delete_fluid_ladspa_lib(lib); + return NULL; + } + + fx->libs[fx->num_libs++] = lib; + + return lib; +} + +static fluid_ladspa_lib_t *new_fluid_ladspa_lib(fluid_ladspa_fx_t *fx, const char *filename) +{ + fluid_ladspa_lib_t *lib; + + lib = FLUID_NEW(fluid_ladspa_lib_t); + if (lib == NULL) + { + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + lib->filename = FLUID_STRDUP(filename); + if (lib->filename == NULL) + { + FLUID_FREE(lib); + FLUID_LOG(FLUID_ERR, "Out of memory"); + return NULL; + } + + return lib; +} + +static void delete_fluid_ladspa_lib(fluid_ladspa_lib_t *lib) +{ + FLUID_FREE(lib->filename); + FLUID_FREE(lib); +} + +static int load_plugin_library(fluid_ladspa_lib_t *lib) +{ + char filepath[FLUID_LADSPA_MAX_PATH_LENGTH]; + char *error; + char *ladspa_path; + + /* If the library name does not contain a slash, then try to load from + * LADSPA_PATH */ + if (FLUID_STRCHR(lib->filename, '/') == NULL) + { + ladspa_path = getenv("LADSPA_PATH"); + if (ladspa_path == NULL) + { + FLUID_LOG(FLUID_ERR, "Unable to load LADSPA plugin '%s'. Use slashes in the " + "LADSPA library filename or set the LADSPA_PATH variable.", + lib->filename); + return FLUID_FAILED; + } + FLUID_SNPRINTF(filepath, FLUID_LADSPA_MAX_PATH_LENGTH, "%s/%s", ladspa_path, lib->filename); + } + else + { + FLUID_SNPRINTF(filepath, FLUID_LADSPA_MAX_PATH_LENGTH, "%s", lib->filename); + } + + dlerror(); + lib->dlib = dlopen(filepath, RTLD_NOW); + error = dlerror(); + if (lib->dlib == NULL || error) + { + if (error == NULL) + { + error = "Unknown error"; + } + FLUID_LOG(FLUID_ERR, "Unable to load LADSPA plugin library '%s': %d", filepath, error); + return FLUID_FAILED; + } + + lib->descriptor = (LADSPA_Descriptor_Function)dlsym(lib->dlib, "ladspa_descriptor"); + error = dlerror(); + if (lib->descriptor == NULL || error) + { + if (error == NULL) + { + error = "Unknown error"; + } + dlclose(lib->dlib); + FLUID_LOG(FLUID_ERR, "Unable to find ladspa_descriptor in '%': %s", filepath, error); + return FLUID_FAILED; + } + + return FLUID_OK; +} + +static void unload_plugin_library(fluid_ladspa_lib_t *lib) +{ + dlclose(lib->dlib); + lib->dlib = NULL; + lib->descriptor = NULL; +} + +/** + * Retrieve a ladspa_plugin_t instance by it's plugin id, as returned from + * fluid_ladspa_add_plugin. + * + * @note The returned pointer is only guaranteed to be valid as long as the + * caller holds the LADSPA API lock. Calles should not store the pointer + * but the plugin id to retrieve it at a later time. + * + * @param fx LADSPA effects instance + * @param id plugin id (integer) + * @return pointer to plugin or NULL if not found + */ +static fluid_ladspa_plugin_t *get_plugin_by_id(fluid_ladspa_fx_t *fx, int id) +{ + int i; + + LADSPA_API_ENTER(fx); + + for (i = 0; i < fx->num_plugins; i++) + { + if (fx->plugins[i]->id == id) + { + LADSPA_API_RETURN(fx, fx->plugins[i]); + } + } + + LADSPA_API_RETURN(fx, NULL); +} -void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit){ - /* The synthesis thread is not running anymore. - * Set the bypass switch, so that fluid_LADSPA_clear can proceed.*/ - FxUnit->Bypass=fluid_LADSPA_Bypassed; - fluid_LADSPA_clear(FxUnit); - pthread_cond_destroy(&FxUnit->cond); /* pro forma */ -}; #endif /*LADSPA*/ diff --git a/src/bindings/fluid_ladspa.h b/src/bindings/fluid_ladspa.h index 581c0bc4..8528b249 100644 --- a/src/bindings/fluid_ladspa.h +++ b/src/bindings/fluid_ladspa.h @@ -21,220 +21,130 @@ /* Author: Markus Nentwig, nentwig@users.sourceforge.net */ - #ifndef _FLUID_LADSPA_H #define _FLUID_LADSPA_H -/*************************************************************** - * - * INCLUDES - */ - +#include "fluid_sys.h" #include "fluidsynth_priv.h" #ifdef LADSPA -#include "fluid_list.h" -#include #include +#include -/*************************************************************** - * - * DEFINES - */ +#define FLUID_LADSPA_MAX_LIBS 100 +#define FLUID_LADSPA_MAX_PLUGINS 100 +#define FLUID_LADSPA_MAX_NODES 100 +#define FLUID_LADSPA_MAX_PATH_LENGTH 512 -/* How many different plugin libraries may be used at the same time? */ -#define FLUID_LADSPA_MaxLibs 100 -/* How many plugin instances may be used at the same time? */ -#define FLUID_LADSPA_MaxPlugins 100 -/* How many nodes are allowed? */ -#define FLUID_LADSPA_MaxNodes 100 -/* How many tokens are allowed in one command line? (for example 152 => max. 50 port plugin allowed) */ -#define FLUID_LADSPA_MaxTokens 152 -/* What is the maximum path length? */ -#define FLUID_LADSPA_MaxPathLength 512 -/*************************************************************** - * - * ENUM - */ +#define FLUID_LADSPA_INACTIVE (0) +#define FLUID_LADSPA_ACTIVE (1) -typedef enum { - fluid_LADSPA_NoMatch, - fluid_LADSPA_PartialMatch, - fluid_LADSPA_FullMatch -} fluid_LADSPA_Stringmatch_t; +typedef enum _fluid_ladspa_dir_t { + FLUID_LADSPA_INPUT, + FLUID_LADSPA_OUTPUT, + FLUID_LADSPA_FIXED -/* Bypass state of the Fx unit */ -typedef enum { - fluid_LADSPA_Active, - fluid_LADSPA_Bypassed, - fluid_LADSPA_BypassRequest -} fluid_LADSPA_BypassState; +} fluid_ladspa_dir_t; -typedef enum { - fluid_LADSPA_node_is_source=1, - fluid_LADSPA_node_is_sink=2, - fluid_LADSPA_node_is_audio=4, - fluid_LADSPA_node_is_control=8, - fluid_LADSPA_node_is_dummy=16, - fluid_LADSPA_node_is_user_ctrl=32 -} fluid_LADSPA_nodeflags; +typedef enum _fluid_ladspa_node_type_t { + FLUID_LADSPA_NODE_AUDIO, + FLUID_LADSPA_NODE_CONTROL, -/* fluid_LADSPA_Node_t - * An internal node of the Fx unit. - * A 'node' is the 'glue' that connects several LADSPA plugins. - * Basically it's a real-valued variable (control node) or a real-valued buffer (audio node). - */ -typedef struct { - LADSPA_Data * buf; /*Either the buffer (Audio node) or a single control value (Control node)*/ - char * Name; /* Unique identifier*/ - int InCount; /* How many sources feed into this node? (0 or 1) */ - int OutCount; /* How many other elements take data out of this node? */ - int flags; -} fluid_LADSPA_Node_t; +} fluid_ladspa_node_type_t; -/* - * fluid_LADSPA_Fx_t - * Fx unit using LADSPA. - * This includes a number of LADSPA plugins, their libraries, nodes etc. - * The Fx unit connects its input to Fluidsynth and its output to the soundcard. - */ -typedef struct { - /* LADSPA-plugins are in shared libraries (for example aw.so). - * Pointers to them are stored here. A library is uniquely identified through - * its filename (full path).*/ - fluid_synth_t* synth; +typedef struct _fluid_ladspa_lib_t +{ + char *filename; + void *dlib; + LADSPA_Descriptor_Function descriptor; - int NumberLibs; - void * ppvPluginLibs[FLUID_LADSPA_MaxLibs]; - char * ppvPluginLibNames[FLUID_LADSPA_MaxLibs]; +} fluid_ladspa_lib_t; - /*List of plugins (descriptor and instance) - * A LADSPA plugin descriptor points to the code, which is executed, when a plugin is run. - * The plugin instance is given as a parameter, when calling. - */ - int NumberPlugins; - const LADSPA_Descriptor * PluginDescriptorTable[FLUID_LADSPA_MaxPlugins]; - LADSPA_Handle * PluginInstanceTable[FLUID_LADSPA_MaxPlugins]; +typedef struct _fluid_ladspa_port_state_t +{ + int num_inputs; + int num_outputs; - /* List of nodes */ - int NumberNodes; - fluid_LADSPA_Node_t * Nodelist[FLUID_LADSPA_MaxNodes]; +} fluid_ladspa_port_state_t; - /* List of Command lines - * During the setup phase, each ladspa_add command creates one command sequence. For example: - * ./aw.so alienwah_stereo Input <- Master_L_Synth Output -> Master_R_Synth Parameter <- $42.0 - * Those lists are stored in LADSPA_Command_Sequence. - * One command line results in one plugin => size MaxPlugins. - */ - int NumberCommands; - char ** LADSPA_Command_Sequence[FLUID_LADSPA_MaxPlugins]; +typedef struct _fluid_ladspa_plugin_t +{ + /* plugin instance id unique to the synth */ + int id; - /* User control nodes - * A user control node is declared at any time before the ladspa_start command. - * It acts as a constant node, but it has a name and can be changed with the ladspa_nodeset command. */ - int NumberUserControlNodes; - char * UserControlNodeNames[FLUID_LADSPA_MaxNodes]; - fluid_real_t UserControlNodeValues[FLUID_LADSPA_MaxNodes]; + const LADSPA_Descriptor *desc; + LADSPA_Handle *handle; - /* Bypass switch - * If set, the LADSPA Fx unit does not touch the signal.*/ - fluid_LADSPA_BypassState Bypass; + volatile int active; - /* Communication between the 'command line' process and the synthesis process. - * A possible conflict situation arises, when fluid_clear is called, and starts to destroy - * the plugins. But the synthesis thread still processes plugins at the same time. The consequences are ugly. - * Therefore ladspa_clear waits for acknowledgement from the synthesis thread, that the Fx unit is bypassed. - * 'cond' is used for the communication, the mutex is required for changing the condition. - */ - pthread_cond_t cond; - pthread_mutex_t mutex; -} fluid_LADSPA_FxUnit_t; + /* Used to keep track of the port connection states */ + fluid_ladspa_port_state_t *ports; -/* - * misc - */ +} fluid_ladspa_plugin_t; -/* Purpose: - * Creates a new Fx unit in bypass mode with default settings. - * It is ready for further calls (add, clear, start). - */ -fluid_LADSPA_FxUnit_t* new_fluid_LADSPA_FxUnit(fluid_synth_t* synth); +typedef struct _fluid_ladspa_node_t +{ + char *name; + fluid_ladspa_node_type_t type; + LADSPA_Data *buf; -/* Purpose: - * Applies the master gain (from command line option --gain or gain command). - * Processes one block of sound data (generated from the synthesizer) through - * the LADSPA Fx unit. - * Acknowledges a bypass request. - */ -void fluid_LADSPA_run(fluid_LADSPA_FxUnit_t* Fx_unit, fluid_real_t* left_buf[], fluid_real_t* right_buf[], fluid_real_t* fx_left_buf[], fluid_real_t* fx_right_buf[]); + char num_inputs; + char num_outputs; -/* Purpose: - * Returns the node belonging to Name or NULL, if not found - */ -fluid_LADSPA_Node_t* fluid_LADSPA_RetrieveNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name); +} fluid_ladspa_node_t; -/* Purpose: - * Creates a new node with the given characteristics. - */ -fluid_LADSPA_Node_t* fluid_LADSPA_CreateNode(fluid_LADSPA_FxUnit_t* FxUnit, char * Name, int flags); +typedef struct _fluid_ladspa_fx_t +{ + int sample_rate; -/* Purpose: - * - Resets LADSPA Fx unit to bypass. - * - Removes all plugins from the reverb unit. - * - Releases all libraries. - * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time - * a plugin is recompiled. - */ -void fluid_LADSPA_clear(fluid_LADSPA_FxUnit_t* FxUnit); + int audio_groups; + int effects_channels; + int audio_channels; -/* Purpose: - * Frees all memory and shuts down the Fx block. - * The synthesis thread must be stopped, when calling. - */ -void fluid_LADSPA_shutdown(fluid_LADSPA_FxUnit_t* FxUnit); + fluid_ladspa_lib_t *libs[FLUID_LADSPA_MAX_LIBS]; + int num_libs; + + fluid_ladspa_node_t *nodes[FLUID_LADSPA_MAX_NODES]; + int num_nodes; + + /* plugins are really plugin instances */ + fluid_ladspa_plugin_t *plugins[FLUID_LADSPA_MAX_PLUGINS]; + int num_plugins; + + /* used to generate the unique plugin ids */ + int next_plugin_id; + + fluid_rec_mutex_t api_mutex; + + volatile int state; + fluid_cond_mutex_t *state_mutex; + fluid_cond_t *state_cond; + +} fluid_ladspa_fx_t; +fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_synth_t *synth); +void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx); -/* - * fluid_handle_LADSPA_XXX - * Those functions are called from fluid_cmd, when a command is entered on the command line. - */ +int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx); +int fluid_ladspa_activate(fluid_ladspa_fx_t *fx, fluid_synth_t *synth); +int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx, fluid_synth_t *synth); +int fluid_ladspa_reset(fluid_ladspa_fx_t *fx); -/* Purpose: - * - Resets LADSPA Fx unit to bypass. - * - Removes all plugins from the reverb unit. - * - Releases all libraries. - * Note: It would be more efficient to keep the libraries. But then the user would have to restart fluidsynth each time - * a plugin is recompiled. - */ -int fluid_LADSPA_handle_clear(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +void fluid_ladspa_run(fluid_ladspa_fx_t *fx, fluid_real_t *left_buf[], fluid_real_t *right_buf[], + fluid_real_t *fx_left_buf[], fluid_real_t *fx_right_buf[]); -/* Purpose: - * Loads the plugins added with 'ladspa_add' and then start the Fx unit. - * Internal processes: - * - load the LADSPA plugin libraries - * - instantiate the plugins - * - connect the plugins - * - set the bypass switch to 'not bypassed' - */ -int fluid_LADSPA_handle_start(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_ladspa_add_plugin(fluid_ladspa_fx_t *fx, const char *lib_name, const char *plugin_name); +int fluid_ladspa_port_exists(fluid_ladspa_fx_t *fx, int plugin_id, const char *name); -/* Purpose: - * Adds one plugin into the list of the LADSPA Fx unit. - * This is only allowed, while the Fx block is in 'bypass' state (after clear). - */ -int fluid_LADSPA_handle_add(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_ladspa_add_audio_node(fluid_ladspa_fx_t *fx, const char *name); +int fluid_ladspa_add_control_node(fluid_ladspa_fx_t *fx, const char *name, fluid_real_t val); +int fluid_ladspa_set_control_node(fluid_ladspa_fx_t *fx, const char *name, fluid_real_t val); +int fluid_ladspa_node_exists(fluid_ladspa_fx_t *fx, const char *name); -/* Purpose: - * Declares a user control node and a value; for further processing in ladspa_start. - */ -int fluid_LADSPA_handle_declnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); - -/* Purpose: - * Assigns a value to the a user control node - */ -int fluid_LADSPA_handle_setnode(fluid_synth_t* synth, int ac, char** av, fluid_ostream_t out); +int fluid_ladspa_connect(fluid_ladspa_fx_t *fx, int plugin_id, fluid_ladspa_dir_t dir, + const char *port_name, const char *node_name); +int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size); #endif /* LADSPA */ - -#endif /* _FLUID_LADSPA_H */ +#endif /* _FLUID_LADSPA_H */ diff --git a/src/rvoice/fluid_rvoice_event.c b/src/rvoice/fluid_rvoice_event.c index 51d5a80f..7c6c710b 100644 --- a/src/rvoice/fluid_rvoice_event.c +++ b/src/rvoice/fluid_rvoice_event.c @@ -121,6 +121,10 @@ fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event) EVENTFUNC_ALL(fluid_rvoice_mixer_set_chorus_params, fluid_rvoice_mixer_t*); EVENTFUNC_R4(fluid_rvoice_mixer_set_reverb_params, fluid_rvoice_mixer_t*); +#ifdef LADSPA + EVENTFUNC_0(fluid_rvoice_mixer_deactivate_ladspa, fluid_rvoice_mixer_t*); +#endif + FLUID_LOG(FLUID_ERR, "fluid_rvoice_event_dispatch: Unknown method %p to dispatch!", event->method); } diff --git a/src/rvoice/fluid_rvoice_mixer.c b/src/rvoice/fluid_rvoice_mixer.c index 9db9a86f..04f8bbbe 100644 --- a/src/rvoice/fluid_rvoice_mixer.c +++ b/src/rvoice/fluid_rvoice_mixer.c @@ -81,7 +81,7 @@ struct _fluid_rvoice_mixer_t { int current_blockcount; /**< Read-only: how many blocks to process this time */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ + fluid_ladspa_fx_t* ladspa_fx; /**< Used by mixer only: Effects unit for LADSPA support. Never created or freed */ #endif #ifdef ENABLE_MIXER_THREADS @@ -142,7 +142,7 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer) #ifdef LADSPA /* Run the signal through the LADSPA Fx unit */ - if (mixer->LADSPA_FxUnit) { + if (mixer->ladspa_fx && mixer->ladspa_fx->state == FLUID_LADSPA_ACTIVE) { int j; FLUID_DECLARE_VLA(fluid_real_t*, left_buf, mixer->buffers.buf_count); FLUID_DECLARE_VLA(fluid_real_t*, right_buf, mixer->buffers.buf_count); @@ -157,8 +157,8 @@ fluid_rvoice_mixer_process_fx(fluid_rvoice_mixer_t* mixer) fx_right_buf[j] = mixer->buffers.fx_right_buf[j]; } for (i=0; i < mixer->current_blockcount * FLUID_BUFSIZE; i += FLUID_BUFSIZE) { - fluid_LADSPA_run(mixer->LADSPA_FxUnit, left_buf, right_buf, fx_left_buf, - fx_right_buf); + fluid_ladspa_run(mixer->ladspa_fx, left_buf, right_buf, fx_left_buf, + fx_right_buf); for (j=0; j < mixer->buffers.buf_count; j++) { left_buf[j] += FLUID_BUFSIZE; right_buf[j] += FLUID_BUFSIZE; @@ -642,10 +642,20 @@ void delete_fluid_rvoice_mixer(fluid_rvoice_mixer_t* mixer) #ifdef LADSPA -void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, - fluid_LADSPA_FxUnit_t* ladspa) +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, fluid_ladspa_fx_t *ladspa_fx) { - mixer->LADSPA_FxUnit = ladspa; + mixer->ladspa_fx = ladspa_fx; +} + +void fluid_rvoice_mixer_deactivate_ladspa(fluid_rvoice_mixer_t *mixer) +{ + if (mixer->ladspa_fx == NULL) return; + mixer->ladspa_fx->state = FLUID_LADSPA_INACTIVE; + + /* Signal LADSPA engine that deactivation has been processed */ + fluid_cond_mutex_lock(mixer->ladspa_fx->state_mutex); + fluid_cond_broadcast(mixer->ladspa_fx->state_cond); + fluid_cond_mutex_unlock(mixer->ladspa_fx->state_mutex); } #endif diff --git a/src/rvoice/fluid_rvoice_mixer.h b/src/rvoice/fluid_rvoice_mixer.h index 026673ef..d43eca57 100644 --- a/src/rvoice/fluid_rvoice_mixer.h +++ b/src/rvoice/fluid_rvoice_mixer.h @@ -69,8 +69,8 @@ void fluid_rvoice_mixer_set_threads(fluid_rvoice_mixer_t* mixer, int thread_coun int prio_level); #ifdef LADSPA -void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, - fluid_LADSPA_FxUnit_t* ladspa); +void fluid_rvoice_mixer_set_ladspa(fluid_rvoice_mixer_t* mixer, fluid_ladspa_fx_t* ladspa_fx); +void fluid_rvoice_mixer_deactivate_ladspa(fluid_rvoice_mixer_t *mixer); #endif #endif diff --git a/src/synth/fluid_synth.c b/src/synth/fluid_synth.c index c2508389..8525689f 100644 --- a/src/synth/fluid_synth.c +++ b/src/synth/fluid_synth.c @@ -711,12 +711,12 @@ new_fluid_synth(fluid_settings_t *settings) /* Create and initialize the Fx unit.*/ fluid_settings_getint(settings, "synth.ladspa.active", &with_ladspa); if (with_ladspa) { - synth->LADSPA_FxUnit = new_fluid_LADSPA_FxUnit(synth); - if(synth->LADSPA_FxUnit == NULL) { + synth->ladspa_fx = new_fluid_ladspa_fx(synth); + if(synth->ladspa_fx == NULL) { FLUID_LOG(FLUID_ERR, "Out of memory"); goto error_recovery; } - fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->LADSPA_FxUnit); + fluid_rvoice_mixer_set_ladspa(synth->eventhandler->mixer, synth->ladspa_fx); } #endif @@ -928,10 +928,9 @@ delete_fluid_synth(fluid_synth_t* synth) fluid_private_free (synth->tuning_iter); #ifdef LADSPA - /* Release the LADSPA Fx unit */ - if (synth->LADSPA_FxUnit) { - fluid_LADSPA_shutdown(synth->LADSPA_FxUnit); - FLUID_FREE(synth->LADSPA_FxUnit); + /* Release the LADSPA effects unit */ + if (synth->ladspa_fx) { + delete_fluid_ladspa_fx(synth->ladspa_fx); } #endif @@ -5269,3 +5268,24 @@ int fluid_synth_set_channel_type(fluid_synth_t* synth, int chan, int type) FLUID_API_RETURN(FLUID_OK); } +#ifdef LADSPA +/** + * Deactivate the LADSPA effects + * + * @param synth FluidSynth instance + * @return FLUID_OK on success, FLUID_FAILED otherwise + */ +int fluid_synth_deactivate_ladspa(fluid_synth_t *synth) +{ + int ret; + + fluid_return_val_if_fail (synth != NULL, FLUID_FAILED); + fluid_synth_api_enter(synth); + + ret = fluid_rvoice_eventhandler_push(synth->eventhandler, + fluid_rvoice_mixer_deactivate_ladspa, + synth->eventhandler->mixer, 0, 0.0f); + + FLUID_API_RETURN(ret); +} +#endif /* LADSPA */ diff --git a/src/synth/fluid_synth.h b/src/synth/fluid_synth.h index 476b006b..63be0023 100644 --- a/src/synth/fluid_synth.h +++ b/src/synth/fluid_synth.h @@ -100,7 +100,7 @@ typedef struct _fluid_sfont_info_t { * ticks_since_start - atomic, set by rendering thread only * cpu_load - atomic, set by rendering thread only * cur, curmax, dither_index - used by rendering thread only - * LADSPA_FxUnit - same instance copied in rendering thread. Synchronising handled internally (I think...?). + * ladspa_fx - same instance copied in rendering thread. Synchronising handled internally. * */ @@ -170,7 +170,7 @@ struct _fluid_synth_t fluid_mod_t* default_mod; /**< the (dynamic) list of default modulators */ #ifdef LADSPA - fluid_LADSPA_FxUnit_t* LADSPA_FxUnit; /**< Effects unit for LADSPA support */ + fluid_ladspa_fx_t* ladspa_fx; /**< Effects unit for LADSPA support */ #endif }; @@ -199,6 +199,10 @@ int fluid_synth_reset_chorus(fluid_synth_t* synth); int fluid_synth_set_chorus_full(fluid_synth_t* synth, int set, int nr, double level, double speed, double depth_ms, int type); +#ifdef LADSPA +int fluid_synth_deactivate_ladspa(fluid_synth_t *synth); +#endif + fluid_sample_timer_t* new_fluid_sample_timer(fluid_synth_t* synth, fluid_timer_callback_t callback, void* data); int delete_fluid_sample_timer(fluid_synth_t* synth, fluid_sample_timer_t* timer);