<commit_before>/* FluidSynth - A Software Synthesizer
 *
 * Copyright (C) 2003  <NAME> and others.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

/* This module: 3/2002
 * Author: <NAME>, <EMAIL>
 * 
 * Complete rewrite: 10/2017
 * Author: <NAME>
 */

#include "fluidsynth_priv.h"

#ifdef LADSPA

#include "fluid_ladspa.h"
#include "fluid_sys.h"
#include <dlfcn.h>
#include <math.h>

#define LADSPA_API_ENTER(_fx) (fluid_rec_mutex_lock((_fx)->api_mutex))

#define LADSPA_API_RETURN(_fx, _ret)          \
    fluid_rec_mutex_unlock((_fx)->api_mutex); \
    return (_ret)

static void clear_ladspa(fluid_ladspa_fx_t *fx);

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 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);

static int set_default_port_value(fluid_ladspa_plugin_t *plugin, unsigned int port_idx,
        int sample_rate, LADSPA_Data *data);
static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir,
        fluid_ladspa_plugin_t *plugin, int port_idx);
static int connect_default_control_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_plugin_t *plugin);

/**
 * Creates a new LADSPA effects unit.
 *
 * @param sample_rate sample_rate for the LADSPA plugins
 * @param audio_groups number of input audio channels (stereo)
 * @param effects_channels number of input effects channels (stereo)
 * @param audio_channels number of output audio channels (stereo)
 * @return pointer to the new LADSPA effects unit
 */
fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int audio_groups, int effects_channels, int audio_channels)
{
    fluid_ladspa_fx_t *fx;

    fx = FLUID_NEW(fluid_ladspa_fx_t);
    if (fx == NULL)
    {
        return NULL;
    }
    FLUID_MEMSET(fx, 0, sizeof(fluid_ladspa_fx_t));

    /* Setup recursive mutex to protect access to public API */
    fluid_rec_mutex_init(fx->api_mutex);

    fx->state = FLUID_LADSPA_INACTIVE;

    /* add 0.5 to minimize overall casting error */
    fx->sample_rate = (unsigned long)(sample_rate + 0.5);

    fx->audio_groups = audio_groups;
    fx->effects_channels = effects_channels;
    fx->audio_channels = audio_channels;

    /* Setup mutex and cond used to signal that fluid_ladspa_run has finished */
     fx->run_finished_mutex = new_fluid_cond_mutex();
     if (fx->run_finished_mutex == NULL)
     {
         delete_fluid_ladspa_fx(fx);
         return NULL;
     }
     fx->run_finished_cond = new_fluid_cond();
     if (fx->run_finished_cond == NULL)
     {
         delete_fluid_ladspa_fx(fx);
         return NULL;
     }

    /* Finally, create the nodes that carry audio in and out of the LADSPA unit.
     * 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)
    {
        delete_fluid_ladspa_fx(fx);
        return NULL;
    }

    return fx;
}

/**
 * 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 nobody calls fluid_ladspa_run)
 *
 * @param fx LADSPA effects instance
 */
void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx)
{
    int i;

    clear_ladspa(fx);

    /* clear the remaining input or output nodes */
    for (i = 0; i < fx->num_nodes; i++)
    {
        delete_fluid_ladspa_node(fx->nodes[i]);
    }

    if (fx->run_finished_cond != NULL)
    {
        delete_fluid_cond(fx->run_finished_cond);
    }

    if (fx->run_finished_mutex != NULL)
    {
        delete_fluid_cond_mutex(fx->run_finished_mutex);
    }

    fluid_rec_mutex_destroy(fx->api_mutex);

    FLUID_FREE(fx);
}

/**
 * Set the sample rate of the LADSPA effects.
 *
 * Resets the LADSPA effects if the sample rate is different from the
 * previous sample rate.
 *
 * @param fx LADSPA fx instance
 * @param sample_rate new sample rate
 * @return FLUID_OK on success, otherwise FLUID_FAILED
 */
int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate)
{
    unsigned long new_sample_rate;

    LADSPA_API_ENTER(fx);

    /* Add 0.5 to minimize rounding errors */
    new_sample_rate = (unsigned long)(sample_rate + 0.5);

    if (fx->sample_rate == new_sample_rate)
    {
        LADSPA_API_RETURN(fx, FLUID_OK);
    }

    if (fluid_ladspa_is_active(fx))
    {
        if (fluid_ladspa_reset(fx) != FLUID_OK)
        {
            FLUID_LOG(FLUID_ERR, "Failed to reset LADSPA, unable to change sample rate");
            LADSPA_API_RETURN(fx, FLUID_FAILED);
        }
    }

    fx->sample_rate = new_sample_rate;

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Check if the LADSPA engine is currently used to render audio
 *
 * 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
 * @return TRUE if LADSPA effects engine is active, otherwise FALSE
 */
int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx)
{
    int is_active;

    LADSPA_API_ENTER(fx);

    is_active = (fluid_atomic_int_get(&fx->state) != FLUID_LADSPA_INACTIVE);

    LADSPA_API_RETURN(fx, is_active);
}

/**
 * Activate the LADSPA fx instance and each configured plugin.
 *
 * @param fx LADSPA fx instance
 * @return FLUID_OK if activation succeeded or already active, otherwise FLUID_FAILED
 */
int fluid_ladspa_activate(fluid_ladspa_fx_t *fx)
{
    int i;

    LADSPA_API_ENTER(fx);

    if (fluid_ladspa_is_active(fx))
    {
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    if (fluid_ladspa_check(fx, NULL, 0) != FLUID_OK)
    {
        FLUID_LOG(FLUID_ERR, "LADSPA check failed, unable to activate effects");
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    for (i = 0; i < fx->num_plugins; i++)
    {
        activate_plugin(fx->plugins[i]);
    }

    if (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_INACTIVE, FLUID_LADSPA_ACTIVE))
    {
        for (i = 0; i < fx->num_plugins; i++)
        {
            deactivate_plugin(fx->plugins[i]);
        }
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Deactivate a LADSPA fx instance and all configured plugins.
 *
 * @note This function may sleep.
 *
 * @param fx LADSPA fx instance
 * @return FLUID_OK if deactivation succeeded, otherwise FLUID_FAILED
 */
int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx)
{
    int i;

    LADSPA_API_ENTER(fx);

    /* If we are already inactive, then simply return success */
    if (fluid_atomic_int_get(&fx->state) == FLUID_LADSPA_INACTIVE)
    {
        LADSPA_API_RETURN(fx, FLUID_OK);
    }

    /* Notify fluid_ladspa_run that we would like to deactivate and that it should
     * send us a signal when its done if it is currently running */
    fluid_atomic_int_set(&fx->pending_deactivation, 1);

    fluid_cond_mutex_lock(fx->run_finished_mutex);
    while (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_INACTIVE))
    {
        fluid_cond_wait(fx->run_finished_cond, fx->run_finished_mutex);
    }
    fluid_cond_mutex_unlock(fx->run_finished_mutex);

    /* Now that we're inactive, deactivate all plugins and return success */
    for (i = 0; i < fx->num_plugins; i++)
    {
        deactivate_plugin(fx->plugins[i]);
    }

    fluid_atomic_int_set(&fx->pending_deactivation, 0);

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Reset the LADSPA effects engine: Deactivate LADSPA if currently active, remove all
 * plugin instances, remove all user nodes and unload all libraries.
 *
 * @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))
    {
        if (fluid_ladspa_deactivate(fx) != FLUID_OK)
        {
            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.
 *
 * FluidSynth calls this function during main 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;

    /* Somebody wants to deactivate the engine, so let's give them a chance to do that.
     * And check that there is at least one plugin loaded, to avoid the overhead of the
     * atomic compare and exchange on an unconfigured LADSPA engine. */
    if (fluid_atomic_int_get(&fx->pending_deactivation) || fx->num_plugins == 0)
    {
        return;
    }

    /* Inform the engine that we are now running pluings, and bail out if it's not active */
    if (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_RUNNING))
    {
        return;
    }

    /* The input and output nodes are always first in the fx->nodes array, in the order:
     * main inputs, effect inputs, main outputs.
     * All inputs and outputs use two nodes (stereo), left and right channels interleaved. */
    n = 0;

    /* Incoming main audio data */
    for (i = 0; i < fx->audio_groups; i++)<commit_msg>Correct documentation for fluid_ladspa_run<commit_after>/* FluidSynth - A Software Synthesizer
 *
 * Copyright (C) 2003  <NAME> and others.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

/* This module: 3/2002
 * Author: <NAME>, <EMAIL>
 * 
 * Complete rewrite: 10/2017
 * Author: <NAME>
 */

#include "fluidsynth_priv.h"

#ifdef LADSPA

#include "fluid_ladspa.h"
#include "fluid_sys.h"
#include <dlfcn.h>
#include <math.h>

#define LADSPA_API_ENTER(_fx) (fluid_rec_mutex_lock((_fx)->api_mutex))

#define LADSPA_API_RETURN(_fx, _ret)          \
    fluid_rec_mutex_unlock((_fx)->api_mutex); \
    return (_ret)

static void clear_ladspa(fluid_ladspa_fx_t *fx);

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 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);

static int set_default_port_value(fluid_ladspa_plugin_t *plugin, unsigned int port_idx,
        int sample_rate, LADSPA_Data *data);
static void connect_node_to_port(fluid_ladspa_node_t *node, fluid_ladspa_dir_t dir,
        fluid_ladspa_plugin_t *plugin, int port_idx);
static int connect_default_control_nodes(fluid_ladspa_fx_t *fx, fluid_ladspa_plugin_t *plugin);

/**
 * Creates a new LADSPA effects unit.
 *
 * @param sample_rate sample_rate for the LADSPA plugins
 * @param audio_groups number of input audio channels (stereo)
 * @param effects_channels number of input effects channels (stereo)
 * @param audio_channels number of output audio channels (stereo)
 * @return pointer to the new LADSPA effects unit
 */
fluid_ladspa_fx_t *new_fluid_ladspa_fx(fluid_real_t sample_rate, int audio_groups, int effects_channels, int audio_channels)
{
    fluid_ladspa_fx_t *fx;

    fx = FLUID_NEW(fluid_ladspa_fx_t);
    if (fx == NULL)
    {
        return NULL;
    }
    FLUID_MEMSET(fx, 0, sizeof(fluid_ladspa_fx_t));

    /* Setup recursive mutex to protect access to public API */
    fluid_rec_mutex_init(fx->api_mutex);

    fx->state = FLUID_LADSPA_INACTIVE;

    /* add 0.5 to minimize overall casting error */
    fx->sample_rate = (unsigned long)(sample_rate + 0.5);

    fx->audio_groups = audio_groups;
    fx->effects_channels = effects_channels;
    fx->audio_channels = audio_channels;

    /* Setup mutex and cond used to signal that fluid_ladspa_run has finished */
     fx->run_finished_mutex = new_fluid_cond_mutex();
     if (fx->run_finished_mutex == NULL)
     {
         delete_fluid_ladspa_fx(fx);
         return NULL;
     }
     fx->run_finished_cond = new_fluid_cond();
     if (fx->run_finished_cond == NULL)
     {
         delete_fluid_ladspa_fx(fx);
         return NULL;
     }

    /* Finally, create the nodes that carry audio in and out of the LADSPA unit.
     * 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)
    {
        delete_fluid_ladspa_fx(fx);
        return NULL;
    }

    return fx;
}

/**
 * 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 nobody calls fluid_ladspa_run)
 *
 * @param fx LADSPA effects instance
 */
void delete_fluid_ladspa_fx(fluid_ladspa_fx_t *fx)
{
    int i;

    clear_ladspa(fx);

    /* clear the remaining input or output nodes */
    for (i = 0; i < fx->num_nodes; i++)
    {
        delete_fluid_ladspa_node(fx->nodes[i]);
    }

    if (fx->run_finished_cond != NULL)
    {
        delete_fluid_cond(fx->run_finished_cond);
    }

    if (fx->run_finished_mutex != NULL)
    {
        delete_fluid_cond_mutex(fx->run_finished_mutex);
    }

    fluid_rec_mutex_destroy(fx->api_mutex);

    FLUID_FREE(fx);
}

/**
 * Set the sample rate of the LADSPA effects.
 *
 * Resets the LADSPA effects if the sample rate is different from the
 * previous sample rate.
 *
 * @param fx LADSPA fx instance
 * @param sample_rate new sample rate
 * @return FLUID_OK on success, otherwise FLUID_FAILED
 */
int fluid_ladspa_set_sample_rate(fluid_ladspa_fx_t *fx, fluid_real_t sample_rate)
{
    unsigned long new_sample_rate;

    LADSPA_API_ENTER(fx);

    /* Add 0.5 to minimize rounding errors */
    new_sample_rate = (unsigned long)(sample_rate + 0.5);

    if (fx->sample_rate == new_sample_rate)
    {
        LADSPA_API_RETURN(fx, FLUID_OK);
    }

    if (fluid_ladspa_is_active(fx))
    {
        if (fluid_ladspa_reset(fx) != FLUID_OK)
        {
            FLUID_LOG(FLUID_ERR, "Failed to reset LADSPA, unable to change sample rate");
            LADSPA_API_RETURN(fx, FLUID_FAILED);
        }
    }

    fx->sample_rate = new_sample_rate;

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Check if the LADSPA engine is currently used to render audio
 *
 * 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
 * @return TRUE if LADSPA effects engine is active, otherwise FALSE
 */
int fluid_ladspa_is_active(fluid_ladspa_fx_t *fx)
{
    int is_active;

    LADSPA_API_ENTER(fx);

    is_active = (fluid_atomic_int_get(&fx->state) != FLUID_LADSPA_INACTIVE);

    LADSPA_API_RETURN(fx, is_active);
}

/**
 * Activate the LADSPA fx instance and each configured plugin.
 *
 * @param fx LADSPA fx instance
 * @return FLUID_OK if activation succeeded or already active, otherwise FLUID_FAILED
 */
int fluid_ladspa_activate(fluid_ladspa_fx_t *fx)
{
    int i;

    LADSPA_API_ENTER(fx);

    if (fluid_ladspa_is_active(fx))
    {
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    if (fluid_ladspa_check(fx, NULL, 0) != FLUID_OK)
    {
        FLUID_LOG(FLUID_ERR, "LADSPA check failed, unable to activate effects");
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    for (i = 0; i < fx->num_plugins; i++)
    {
        activate_plugin(fx->plugins[i]);
    }

    if (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_INACTIVE, FLUID_LADSPA_ACTIVE))
    {
        for (i = 0; i < fx->num_plugins; i++)
        {
            deactivate_plugin(fx->plugins[i]);
        }
        LADSPA_API_RETURN(fx, FLUID_FAILED);
    }

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Deactivate a LADSPA fx instance and all configured plugins.
 *
 * @note This function may sleep.
 *
 * @param fx LADSPA fx instance
 * @return FLUID_OK if deactivation succeeded, otherwise FLUID_FAILED
 */
int fluid_ladspa_deactivate(fluid_ladspa_fx_t *fx)
{
    int i;

    LADSPA_API_ENTER(fx);

    /* If we are already inactive, then simply return success */
    if (fluid_atomic_int_get(&fx->state) == FLUID_LADSPA_INACTIVE)
    {
        LADSPA_API_RETURN(fx, FLUID_OK);
    }

    /* Notify fluid_ladspa_run that we would like to deactivate and that it should
     * send us a signal when its done if it is currently running */
    fluid_atomic_int_set(&fx->pending_deactivation, 1);

    fluid_cond_mutex_lock(fx->run_finished_mutex);
    while (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_INACTIVE))
    {
        fluid_cond_wait(fx->run_finished_cond, fx->run_finished_mutex);
    }
    fluid_cond_mutex_unlock(fx->run_finished_mutex);

    /* Now that we're inactive, deactivate all plugins and return success */
    for (i = 0; i < fx->num_plugins; i++)
    {
        deactivate_plugin(fx->plugins[i]);
    }

    fluid_atomic_int_set(&fx->pending_deactivation, 0);

    LADSPA_API_RETURN(fx, FLUID_OK);
}

/**
 * Reset the LADSPA effects engine: Deactivate LADSPA if currently active, remove all
 * plugin instances, remove all user nodes and unload all libraries.
 *
 * @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))
    {
        if (fluid_ladspa_deactivate(fx) != FLUID_OK)
        {
            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.
 *
 * FluidSynth calls this function during main 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 left_buf array of pointers into the left audio group buffers
 * @param right_buf array of pointers into the right audio group buffers
 * @param fx_left_buf array of pointers into the left effects buffers
 * @param fx_right_buf array of pointers into the right effects 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;

    /* Somebody wants to deactivate the engine, so let's give them a chance to do that.
     * And check that there is at least one plugin loaded, to avoid the overhead of the
     * atomic compare and exchange on an unconfigured LADSPA engine. */
    if (fluid_atomic_int_get(&fx->pending_deactivation) || fx->num_plugins == 0)
    {
        return;
    }

    /* Inform the engine that we are now running pluings, and bail out if it's not active */
    if (!fluid_atomic_int_compare_and_exchange(&fx->state, FLUID_LADSPA_ACTIVE, FLUID_LADSPA_RUNNING))
    {
        return;
    }

    /* The input and output nodes are always first in the fx->nodes array, in the order:
     * main inputs, effect inputs, main outputs.
     * All inputs and outputs use two nodes (stereo), left and right channels interleaved. */
    n = 0;

    /* Incoming main audio data */
    for (i = 0; i < fx->audio_groups; i++)
{'fraction_non_alphanumeric': 0.08577111129526808, 'fraction_numerical': 0.00389492002983343, 'mean_word_length': 3.7416502946954813, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 101, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6409415', 'n_tokens_mistral': 8504, 'n_tokens_neox': 8170, 'n_words': 2282}