Merge pull request #795 from mawe42/ladspa-improvements

Improvements to LADSPA subsystem
This commit is contained in:
Tom M 2021-03-07 12:50:03 +01:00 committed by GitHub
commit 5d1078f7b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -26,14 +26,13 @@
*/
#include "fluid_ladspa.h"
#include "fluid_list.h"
#if defined(LADSPA) || defined(__DOXYGEN__)
#include <math.h>
#include <ladspa.h>
#define FLUID_LADSPA_MAX_EFFECTS 100
#define FLUID_LADSPA_MAX_NODES 100
typedef enum _fluid_ladspa_state_t
{
@ -109,19 +108,11 @@ struct _fluid_ladspa_fx_t
/* The buffer size for all audio buffers (in samples) */
int buffer_size;
fluid_ladspa_node_t *nodes[FLUID_LADSPA_MAX_NODES];
int num_nodes;
fluid_list_t *host_nodes;
/* Pointers to host nodes in nodes array */
fluid_ladspa_node_t *host_nodes[FLUID_LADSPA_MAX_NODES];
int num_host_nodes;
fluid_list_t *user_nodes;
/* Pointers to user audio nodes in nodes array */
fluid_ladspa_node_t *audio_nodes[FLUID_LADSPA_MAX_NODES];
int num_audio_nodes;
fluid_ladspa_effect_t *effects[FLUID_LADSPA_MAX_EFFECTS];
int num_effects;
fluid_list_t *effects;
fluid_rec_mutex_t api_mutex;
@ -240,16 +231,20 @@ error_recovery:
*/
void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx)
{
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
fluid_return_if_fail(fx != NULL);
clear_ladspa(fx);
/* clear the remaining input or output nodes */
for(i = 0; i < fx->num_nodes; i++)
/* Delete the remaining host nodes */
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
delete_fluid_ladspa_node(fx->nodes[i]);
node = (fluid_ladspa_node_t *) fluid_list_get(list);
delete_fluid_ladspa_node(node);
}
delete_fluid_list(fx->host_nodes);
if(fx->run_finished_cond != NULL)
{
@ -285,6 +280,7 @@ int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix,
{
int i;
char name[99];
fluid_ladspa_node_t *node;
LADSPA_API_ENTER(fx);
@ -306,12 +302,15 @@ int fluid_ladspa_add_host_ports(fluid_ladspa_fx_t *fx, const char *prefix,
FLUID_STRNCPY(name, prefix, sizeof(name));
}
if(new_fluid_ladspa_node(fx, name,
FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_HOST,
&buffers[i * buf_stride]) == NULL)
node = new_fluid_ladspa_node(fx, name,
FLUID_LADSPA_NODE_AUDIO | FLUID_LADSPA_NODE_HOST,
&buffers[i * buf_stride]);
if (node == NULL)
{
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
fx->host_nodes = fluid_list_append(fx->host_nodes, node);
}
LADSPA_API_RETURN(fx, FLUID_OK);
@ -389,7 +388,8 @@ int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx)
*/
int fluid_ladspa_activate(fluid_ladspa_fx_t *fx)
{
int i;
fluid_list_t *list;
fluid_ladspa_effect_t *effect;
fluid_return_val_if_fail(fx != NULL, FLUID_FAILED);
@ -406,16 +406,18 @@ int fluid_ladspa_activate(fluid_ladspa_fx_t *fx)
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
activate_effect(fx->effects[i]);
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
activate_effect(effect);
}
if(!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_INACTIVE, FLUID_LADSPA_ACTIVE))
{
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
deactivate_effect(fx->effects[i]);
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
deactivate_effect(effect);
}
LADSPA_API_RETURN(fx, FLUID_FAILED);
@ -434,7 +436,8 @@ int fluid_ladspa_activate(fluid_ladspa_fx_t *fx)
*/
int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx)
{
int i;
fluid_list_t *list;
fluid_ladspa_effect_t *effect;
fluid_return_val_if_fail(fx != NULL, FLUID_FAILED);
@ -460,9 +463,10 @@ int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx)
fluid_cond_mutex_unlock(fx->run_finished_mutex);
/* Now that we're inactive, deactivate all effects and return success */
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
deactivate_effect(fx->effects[i]);
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
deactivate_effect(effect);
}
fx->pending_deactivation = 0;
@ -513,14 +517,15 @@ int fluid_ladspa_reset(fluid_ladspa_fx_t *fx)
*/
void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size)
{
int i;
int num_samples;
fluid_list_t *list;
fluid_ladspa_node_t *node;
fluid_ladspa_effect_t *effect;
/* Somebody wants to deactivate the engine, so let's give them a chance to do that.
* And check that there is at least one effect, to avoid the overhead of the
* atomic compare and exchange on an unconfigured LADSPA engine. */
if(fx->pending_deactivation || fx->num_effects == 0)
if(fx->pending_deactivation || fx->effects == NULL)
{
return;
}
@ -537,15 +542,17 @@ void fluid_ladspa_run(fluid_ladspa_fx_t *fx, int block_count, int block_size)
copy_host_to_effect_buffers(fx, num_samples);
#endif
for(i = 0; i < fx->num_audio_nodes; i++)
for(list = fx->user_nodes; list; list = fluid_list_next(list))
{
FLUID_MEMSET(fx->audio_nodes[i]->effect_buffer, 0, fx->buffer_size * sizeof(LADSPA_Data));
node = (fluid_ladspa_node_t *) fluid_list_get(list);
FLUID_MEMSET(node->effect_buffer, 0, fx->buffer_size * sizeof(LADSPA_Data));
}
/* Run each effect in the order that they were added */
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
effect = fx->effects[i];
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
if(effect->mix)
{
@ -651,41 +658,39 @@ int fluid_ladspa_effect_set_mix(fluid_ladspa_fx_t *fx, const char *name, int mix
static void clear_ladspa(fluid_ladspa_fx_t *fx)
{
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
fluid_ladspa_effect_t *effect;
/* Deactivate and free all effects */
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
deactivate_effect(fx->effects[i]);
delete_fluid_ladspa_effect(fx->effects[i]);
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
deactivate_effect(effect);
delete_fluid_ladspa_effect(effect);
}
delete_fluid_list(fx->effects);
fx->effects = NULL;
fx->num_effects = 0;
/* Delete all nodes (but not the host audio nodes) */
for(i = 0; i < fx->num_nodes; i++)
/* Delete all user nodes */
for(list = fx->user_nodes; list; list = fluid_list_next(list))
{
if((fx->nodes[i]->type & FLUID_LADSPA_NODE_HOST) &&
(fx->nodes[i]->type & FLUID_LADSPA_NODE_AUDIO))
{
continue;
}
node = (fluid_ladspa_node_t *) fluid_list_get(list);
delete_fluid_ladspa_node(fx->nodes[i]);
delete_fluid_ladspa_node(node);
}
delete_fluid_list(fx->user_nodes);
fx->user_nodes = NULL;
/* Fill the list with the host nodes and reset the connection counts */
for(i = 0; i < fx->num_host_nodes; i++)
/* Mark all host nodes as unconnected */
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
fx->host_nodes[i]->num_inputs = 0;
fx->host_nodes[i]->num_outputs = 0;
fx->nodes[i] = fx->host_nodes[i];
node = (fluid_ladspa_node_t *) fluid_list_get(list);
node->num_inputs = 0;
node->num_outputs = 0;
}
fx->num_nodes = fx->num_host_nodes;
/* Reset list of user audio nodes */
fx->num_audio_nodes = 0;
}
/**
@ -810,6 +815,8 @@ int fluid_ladspa_add_buffer(fluid_ladspa_fx_t *fx, const char *name)
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
fx->user_nodes = fluid_list_append(fx->user_nodes, node);
LADSPA_API_RETURN(fx, FLUID_OK);
}
@ -891,12 +898,6 @@ int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name,
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
if(fx->num_effects >= FLUID_LADSPA_MAX_EFFECTS)
{
FLUID_LOG(FLUID_ERR, "Maximum number of LADSPA effects reached");
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
effect = new_fluid_ladspa_effect(fx, lib_name, plugin_name);
if(effect == NULL)
@ -919,17 +920,17 @@ int fluid_ladspa_add_effect(fluid_ladspa_fx_t *fx, const char *effect_name,
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
fx->effects[fx->num_effects++] = effect;
fx->effects = fluid_list_append(fx->effects, effect);
LADSPA_API_RETURN(fx, FLUID_OK);
}
/**
* Connect an effect port to a host port or buffer
* Connect an effect audio port to a host port or buffer
*
* @param fx LADSPA effects instance
* @param effect_name name of the effect
* @param port_name the port name to connect to (case-insensitive prefix match)
* @param port_name the audio port name to connect to (case-insensitive prefix match)
* @param name the host port or buffer to connect to (case-insensitive)
* @return FLUID_OK on success, otherwise FLUID_FAILED
*
@ -973,27 +974,25 @@ int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name,
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
port_flags = effect->desc->PortDescriptors[port_idx];
if(!LADSPA_IS_PORT_AUDIO(port_flags))
{
FLUID_LOG(FLUID_ERR, "Only audio effect ports can be linked to buffers or host ports");
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
node = get_node(fx, name);
if(node == NULL)
{
FLUID_LOG(FLUID_ERR, "Node '%s' not found", name);
FLUID_LOG(FLUID_ERR, "Link target '%s' not found", name);
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
port_flags = effect->desc->PortDescriptors[port_idx];
/* Check that requested port type matches the node type */
if(LADSPA_IS_PORT_CONTROL(port_flags) && !(node->type & FLUID_LADSPA_NODE_CONTROL))
if(!(node->type & FLUID_LADSPA_NODE_AUDIO))
{
FLUID_LOG(FLUID_ERR, "Control port '%s' on effect '%s' can only connect to "
"other control ports", port_name, effect_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, "Audio port '%s' on effect '%s' can only connect to"
"other audio port or buffer", port_name, effect_name);
FLUID_LOG(FLUID_ERR, "Link target '%s' needs to be an audio port or buffer", name);
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
@ -1024,9 +1023,9 @@ int fluid_ladspa_effect_link(fluid_ladspa_fx_t *fx, const char *effect_name,
*/
int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size)
{
int i;
const char *str;
const char *str2;
fluid_list_t *list;
fluid_ladspa_effect_t *effect;
fluid_return_val_if_fail(fx != NULL, FLUID_FAILED);
@ -1035,15 +1034,15 @@ int fluid_ladspa_check(fluid_ladspa_fx_t *fx, char *err, int err_size)
LADSPA_API_ENTER(fx);
/* Check that there is at least one effect */
if(fx->num_effects == 0)
if(fx->effects == NULL)
{
FLUID_SNPRINTF(err, err_size, "No effects configured\n");
LADSPA_API_RETURN(fx, FLUID_FAILED);
}
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
effect = fx->effects[i];
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
if(check_all_ports_connected(effect, &str) == FLUID_FAILED)
{
@ -1128,13 +1127,26 @@ static void deactivate_effect(fluid_ladspa_effect_t *effect)
*/
static fluid_ladspa_node_t *get_node(fluid_ladspa_fx_t *fx, const char *name)
{
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
for(i = 0; i < fx->num_nodes; i++)
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
if(FLUID_STRCASECMP(fx->nodes[i]->name, name) == 0)
node = (fluid_ladspa_node_t *) fluid_list_get(list);
if(FLUID_STRCASECMP(node->name, name) == 0)
{
return fx->nodes[i];
return node;
}
}
for(list = fx->user_nodes; list; list = fluid_list_next(list))
{
node = (fluid_ladspa_node_t *) fluid_list_get(list);
if(FLUID_STRCASECMP(node->name, name) == 0)
{
return node;
}
}
@ -1304,8 +1316,25 @@ new_fluid_ladspa_effect(fluid_ladspa_fx_t *fx, const char *lib_name, const char
static void delete_fluid_ladspa_effect(fluid_ladspa_effect_t *effect)
{
unsigned int i;
fluid_ladspa_node_t *node;
fluid_return_if_fail(effect != NULL);
/* Control nodes are created automatically when the effect is instantiated and
* are private to this effect, so we can safely remove them here. Nodes connected
* to audio ports might be connected to other effects as well, so we simply remove
* any pointers to them from the effect. */
for(i = 0; i < effect->desc->PortCount; i++)
{
node = (fluid_ladspa_node_t *) effect->port_nodes[i];
if(node && node->type & FLUID_LADSPA_NODE_CONTROL)
{
delete_fluid_ladspa_node(node);
}
}
FLUID_FREE(effect->port_nodes);
if(effect->handle != NULL && effect->desc != NULL && effect->desc->cleanup != NULL)
@ -1334,12 +1363,6 @@ static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const c
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)
@ -1396,19 +1419,6 @@ static fluid_ladspa_node_t *new_fluid_ladspa_node(fluid_ladspa_fx_t *fx, const c
FLUID_MEMSET(node->effect_buffer, 0, buffer_size * sizeof(LADSPA_Data));
}
fx->nodes[fx->num_nodes++] = node;
/* Host and user audio nodes are also noted in separate lists to access them
* quickly during fluid_ladspa_run */
if((type & FLUID_LADSPA_NODE_AUDIO) && (type & FLUID_LADSPA_NODE_HOST))
{
fx->host_nodes[fx->num_host_nodes++] = node;
}
else if((type & FLUID_LADSPA_NODE_AUDIO) && (type & FLUID_LADSPA_NODE_USER))
{
fx->audio_nodes[fx->num_audio_nodes++] = node;
}
return node;
}
@ -1436,15 +1446,18 @@ static void delete_fluid_ladspa_node(fluid_ladspa_node_t *node)
*/
static fluid_ladspa_effect_t *get_effect(fluid_ladspa_fx_t *fx, const char *name)
{
int i;
fluid_list_t *list;
fluid_ladspa_effect_t *effect;
LADSPA_API_ENTER(fx);
for(i = 0; i < fx->num_effects; i++)
for(list = fx->effects; list; list = fluid_list_next(list))
{
if(FLUID_STRNCASECMP(fx->effects[i]->name, name, FLUID_STRLEN(name)) == 0)
effect = (fluid_ladspa_effect_t *) fluid_list_get(list);
if(FLUID_STRNCASECMP(effect->name, name, FLUID_STRLEN(name)) == 0)
{
LADSPA_API_RETURN(fx, fx->effects[i]);
LADSPA_API_RETURN(fx, effect);
}
}
@ -1689,11 +1702,14 @@ static int check_no_inplace_broken(fluid_ladspa_effect_t *effect, const char **n
*/
static int check_host_output_used(fluid_ladspa_fx_t *fx)
{
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
for(i = 0; i < fx->num_host_nodes; i++)
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
if(fx->host_nodes[i]->num_inputs)
node = (fluid_ladspa_node_t *) fluid_list_get(list);
if(node->num_inputs)
{
return FLUID_OK;
}
@ -1711,13 +1727,16 @@ static int check_host_output_used(fluid_ladspa_fx_t *fx)
*/
static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **name)
{
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
for(i = 0; i < fx->num_audio_nodes; i++)
for(list = fx->user_nodes; list; list = fluid_list_next(list))
{
if(fx->audio_nodes[i]->num_inputs == 0 || fx->audio_nodes[i]->num_outputs == 0)
node = (fluid_ladspa_node_t *) fluid_list_get(list);
if(node->num_inputs == 0 || node->num_outputs == 0)
{
*name = fx->audio_nodes[i]->name;
*name = node->name;
return FLUID_FAILED;
}
}
@ -1732,12 +1751,13 @@ static int check_all_audio_nodes_connected(fluid_ladspa_fx_t *fx, const char **n
*/
static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int num_samples)
{
int i, n;
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
for(n = 0; n < fx->num_host_nodes; n++)
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
node = fx->host_nodes[n];
node = (fluid_ladspa_node_t *) fluid_list_get(list);
/* Only copy host nodes that have at least one output or output, i.e.
* that are connected to at least one effect port. */
@ -1757,12 +1777,13 @@ static FLUID_INLINE void copy_host_to_effect_buffers(fluid_ladspa_fx_t *fx, int
*/
static FLUID_INLINE void copy_effect_to_host_buffers(fluid_ladspa_fx_t *fx, int num_samples)
{
int i, n;
int i;
fluid_list_t *list;
fluid_ladspa_node_t *node;
for(n = 0; n < fx->num_host_nodes; n++)
for(list = fx->host_nodes; list; list = fluid_list_next(list))
{
node = fx->host_nodes[n];
node = (fluid_ladspa_node_t *) fluid_list_get(list);
/* Only copy effect nodes that have at least one input, i.e. that are connected to
* at least one effect output */