diff --git a/conf/dialplan/default/ladspa.xml b/conf/dialplan/default/ladspa.xml new file mode 100644 index 0000000000..926c31a87a --- /dev/null +++ b/conf/dialplan/default/ladspa.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_ladspa/Makefile b/src/mod/applications/mod_ladspa/Makefile new file mode 100644 index 0000000000..1a77c52a0d --- /dev/null +++ b/src/mod/applications/mod_ladspa/Makefile @@ -0,0 +1,5 @@ +BASE=../../../.. + +LOCAL_OBJS += load.o +include $(BASE)/build/modmake.rules + diff --git a/src/mod/applications/mod_ladspa/load.c b/src/mod/applications/mod_ladspa/load.c new file mode 100644 index 0000000000..652ecfa0f2 --- /dev/null +++ b/src/mod/applications/mod_ladspa/load.c @@ -0,0 +1,173 @@ +/* load.c + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +/*****************************************************************************/ + +#include +#include +#include +#include + +/*****************************************************************************/ + +#include "ladspa.h" +#include "utils.h" +#include "inttypes.h" +#include "switch.h" +/*****************************************************************************/ + +/* This function provides a wrapping of dlopen(). When the filename is + not an absolute path (i.e. does not begin with / character), this + routine will search the LADSPA_PATH for the file. */ +static void *dlopenLADSPA(const char *pcFilename, int iFlag) +{ + + char *pcBuffer; + const char *pcEnd; + const char *pcLADSPAPath; + const char *pcStart; + int iEndsInSO; + int iNeedSlash; + size_t iFilenameLength; + void *pvResult; + + iFilenameLength = strlen(pcFilename); + pvResult = NULL; + + if (pcFilename[0] == '/') { + + /* The filename is absolute. Assume the user knows what he/she is + doing and simply dlopen() it. */ + + pvResult = dlopen(pcFilename, iFlag); + if (pvResult != NULL) + return pvResult; + + } else { + + /* If the filename is not absolute then we wish to check along the + LADSPA_PATH path to see if we can find the file there. We do + NOT call dlopen() directly as this would find plugins on the + LD_LIBRARY_PATH, whereas the LADSPA_PATH is the correct place + to search. */ + + pcLADSPAPath = getenv("LADSPA_PATH"); + + if (pcLADSPAPath) { + + pcStart = pcLADSPAPath; + while (*pcStart != '\0') { + pcEnd = pcStart; + while (*pcEnd != ':' && *pcEnd != '\0') + pcEnd++; + + pcBuffer = malloc(iFilenameLength + 2 + (pcEnd - pcStart)); + if (pcEnd > pcStart) + strncpy(pcBuffer, pcStart, pcEnd - pcStart); + iNeedSlash = 0; + if (pcEnd > pcStart) + if (*(pcEnd - 1) != '/') { + iNeedSlash = 1; + pcBuffer[pcEnd - pcStart] = '/'; + } + strcpy(pcBuffer + iNeedSlash + (pcEnd - pcStart), pcFilename); + + pvResult = dlopen(pcBuffer, iFlag); + + free(pcBuffer); + if (pvResult != NULL) + return pvResult; + + pcStart = pcEnd; + if (*pcStart == ':') + pcStart++; + } + } + } + + /* As a last ditch effort, check if filename does not end with + ".so". In this case, add this suffix and recurse. */ + iEndsInSO = 0; + if (iFilenameLength > 3) + iEndsInSO = (strcmp(pcFilename + iFilenameLength - 3, ".so") == 0); + if (!iEndsInSO) { + pcBuffer = malloc(iFilenameLength + 4); + strcpy(pcBuffer, pcFilename); + strcat(pcBuffer, ".so"); + pvResult = dlopenLADSPA(pcBuffer, iFlag); + free(pcBuffer); + } + + if (pvResult != NULL) + return pvResult; + + /* If nothing has worked, then at least we can make sure we set the + correct error message - and this should correspond to a call to + dlopen() with the actual filename requested. The dlopen() manual + page does not specify whether the first or last error message + will be kept when multiple calls are made to dlopen(). We've + covered the former case - now we can handle the latter by calling + dlopen() again here. */ + return dlopen(pcFilename, iFlag); +} + +/*****************************************************************************/ + +void *loadLADSPAPluginLibrary(const char *pcPluginFilename) +{ + + void *pvPluginHandle; + + pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW); + if (!pvPluginHandle) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to load plugin \"%s\": %s\n", pcPluginFilename, dlerror()); + } + + return pvPluginHandle; +} + +/*****************************************************************************/ + +void unloadLADSPAPluginLibrary(void *pvLADSPAPluginLibrary) +{ + dlclose(pvLADSPAPluginLibrary); +} + +/*****************************************************************************/ + +const LADSPA_Descriptor *findLADSPAPluginDescriptor(void *pvLADSPAPluginLibrary, const char *pcPluginLibraryFilename, const char *pcPluginLabel) +{ + + const LADSPA_Descriptor *psDescriptor; + LADSPA_Descriptor_Function pfDescriptorFunction; + unsigned long lPluginIndex; + + dlerror(); + pfDescriptorFunction = (LADSPA_Descriptor_Function) (intptr_t)dlsym(pvLADSPAPluginLibrary, "ladspa_descriptor"); + if (!pfDescriptorFunction) { + const char *pcError = dlerror(); + if (pcError) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Unable to find ladspa_descriptor() function in plugin " + "library file \"%s\": %s.\n" "Are you sure this is a LADSPA plugin file?\n", pcPluginLibraryFilename, pcError); + return NULL; + } + } + + for (lPluginIndex = 0;; lPluginIndex++) { + psDescriptor = pfDescriptorFunction(lPluginIndex); + if (psDescriptor == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Unable to find label \"%s\" in plugin library file \"%s\".\n", pcPluginLabel, pcPluginLibraryFilename); + return NULL; + } + if (strcmp(psDescriptor->Label, pcPluginLabel) == 0) + return psDescriptor; + } +} + +/*****************************************************************************/ + +/* EOF */ diff --git a/src/mod/applications/mod_ladspa/mod_ladspa.c b/src/mod/applications/mod_ladspa/mod_ladspa.c new file mode 100644 index 0000000000..871d203646 --- /dev/null +++ b/src/mod/applications/mod_ladspa/mod_ladspa.c @@ -0,0 +1,579 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * mod_ladspa.c -- LADSPA + * + */ +#include +#include "ladspa.h" +#include "utils.h" + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_ladspa_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_ladspa_runtime); +SWITCH_MODULE_LOAD_FUNCTION(mod_ladspa_load); + +/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) + * Defines a switch_loadable_module_function_table_t and a static const char[] modname + */ +SWITCH_MODULE_DEFINITION(mod_ladspa, mod_ladspa_load, mod_ladspa_shutdown, NULL); + +#define MAX_INDEX 256 + +typedef struct { + switch_core_session_t *session; + char *plugin_name; + char *label_name; + void *library_handle; + const LADSPA_Descriptor *ldesc; + LADSPA_Handle handle; + LADSPA_Data config[MAX_INDEX]; + uint8_t has_config[MAX_INDEX]; + int skip; + LADSPA_Data in_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + LADSPA_Data out_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + LADSPA_Data out_ports[MAX_INDEX]; +} switch_ladspa_t; + + + +int check_range(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data val) +{ + if (ldesc->PortRangeHints[i].LowerBound && ldesc->PortRangeHints[i].UpperBound && + (val < ldesc->PortRangeHints[i].LowerBound || val > ldesc->PortRangeHints[i].UpperBound)) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Param %f out of bounds %f-%f\n", + val, ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound); + return 0; + } + + return 1; +} + +int find_default(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data *ptr) + +{ + LADSPA_Data dftval = 0; + int fail = 0; + + LADSPA_PortRangeHintDescriptor port_hint = ldesc->PortRangeHints[i].HintDescriptor; + + switch (port_hint & LADSPA_HINT_DEFAULT_MASK) { + case LADSPA_HINT_DEFAULT_NONE: + break; + case LADSPA_HINT_DEFAULT_MINIMUM: + dftval = ldesc->PortRangeHints[i].LowerBound; + break; + case LADSPA_HINT_DEFAULT_LOW: + if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) { + dftval = exp(log(ldesc->PortRangeHints[i].LowerBound) + * 0.75 + log(ldesc->PortRangeHints[i].UpperBound) + * 0.25); + } else { + dftval = (ldesc->PortRangeHints[i].LowerBound * 0.75 + ldesc->PortRangeHints[i].UpperBound * 0.25); + } + break; + case LADSPA_HINT_DEFAULT_MIDDLE: + if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) { + dftval = sqrt(ldesc->PortRangeHints[i].LowerBound * ldesc->PortRangeHints[i].UpperBound); + } else { + dftval = 0.5 * (ldesc->PortRangeHints[i].LowerBound + ldesc->PortRangeHints[i].UpperBound); + } + break; + case LADSPA_HINT_DEFAULT_HIGH: + if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) { + dftval = exp(log(ldesc->PortRangeHints[i].LowerBound) + * 0.25 + log(ldesc->PortRangeHints[i].UpperBound) + * 0.75); + } else { + dftval = (ldesc->PortRangeHints[i].LowerBound * 0.25 + ldesc->PortRangeHints[i].UpperBound * 0.75); + } + break; + case LADSPA_HINT_DEFAULT_MAXIMUM: + dftval = ldesc->PortRangeHints[i].UpperBound; + break; + case LADSPA_HINT_DEFAULT_0: + dftval = 0; + break; + case LADSPA_HINT_DEFAULT_1: + dftval = 1; + break; + case LADSPA_HINT_DEFAULT_100: + dftval = 100; + break; + case LADSPA_HINT_DEFAULT_440: + dftval = 440; + break; + default: + fail = 1; + break; + } + + if (!fail) { + *ptr = dftval; + } + + return !fail; +} + +static void dump_info(const LADSPA_Descriptor *ldesc) +{ + int i = 0; + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Name: \"%s\"\n", ldesc->Name); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Label: \"%s\"\n", ldesc->Label); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Unique ID: %lu\n", ldesc->UniqueID); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Maker: \"%s\"\n", ldesc->Maker); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Copyright: \"%s\"\n", ldesc->Copyright); + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Must Run Real-Time: "); + if (LADSPA_IS_REALTIME(ldesc->Properties)) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n"); + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has activate() Function: "); + if (ldesc->activate != NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n"); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has deactivate() Function: "); + if (ldesc->deactivate != NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n"); + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has run_adding() Function: "); + if (ldesc->run_adding != NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n"); + + if (ldesc->instantiate == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO INSTANTIATE FUNCTION.\n"); + if (ldesc->connect_port == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CONNECT_PORT FUNCTION.\n"); + if (ldesc->run == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO RUN FUNCTION.\n"); + if (ldesc->run_adding != NULL && ldesc->set_run_adding_gain == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS RUN_ADDING FUNCTION BUT " "NOT SET_RUN_ADDING_GAIN.\n"); + if (ldesc->run_adding == NULL && ldesc->set_run_adding_gain != NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS SET_RUN_ADDING_GAIN FUNCTION BUT " "NOT RUN_ADDING.\n"); + if (ldesc->cleanup == NULL) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CLEANUP FUNCTION.\n"); + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Environment: "); + if (LADSPA_IS_HARD_RT_CAPABLE(ldesc->Properties)) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal or Hard Real-Time\n"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal\n"); + + if (LADSPA_IS_INPLACE_BROKEN(ldesc->Properties)) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "This plugin cannot use in-place processing. " "It will not work with all hosts.\n"); + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Ports:"); + + if (ldesc->PortCount == 0) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\tERROR: PLUGIN HAS NO PORTS.\n"); + + for (i = 0; i < ldesc->PortCount; i++) { + LADSPA_Data dft = 0.0f; + int found = 0; + + if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) { + found = find_default(ldesc, i, &dft); + } + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n \"%s\" ", ldesc->PortNames[i]); + + if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i]) + && LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: INPUT AND OUTPUT"); + else if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "input"); + else if (LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "output"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: NEITHER INPUT NOR OUTPUT"); + + if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i]) + && LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: CONTROL AND AUDIO"); + else if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", control"); + else if (LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i])) + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", audio"); + else + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: NEITHER CONTROL NOR AUDIO"); + + if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) { + if (found) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: %f\n", + ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound, dft); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: none.\n", + ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound); + } + } + + + + } + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n\n"); +} + + + + + +static switch_bool_t ladspa_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) +{ + switch_ladspa_t *pvt = (switch_ladspa_t *) user_data; + //switch_frame_t *frame = NULL; + switch_channel_t *channel = switch_core_session_get_channel(pvt->session); + + switch (type) { + case SWITCH_ABC_TYPE_INIT: + { + switch_codec_implementation_t read_impl = { 0 }; + LADSPA_PortDescriptor port_desc; + int i = 0, j = 0, k = 0; + + switch_core_session_get_read_impl(pvt->session, &read_impl); + + if (!(pvt->library_handle = loadLADSPAPluginLibrary(pvt->plugin_name))) { + return SWITCH_FALSE; + } + + if (!(pvt->ldesc = findLADSPAPluginDescriptor(pvt->library_handle, pvt->plugin_name, pvt->label_name))) { + return SWITCH_FALSE; + } + + + pvt->handle = pvt->ldesc->instantiate(pvt->ldesc, read_impl.actual_samples_per_second); + + dump_info(pvt->ldesc); + + + for (i = 0; i < pvt->ldesc->PortCount; i++) { + port_desc = pvt->ldesc->PortDescriptors[i]; + + if (LADSPA_IS_PORT_CONTROL(port_desc) && LADSPA_IS_PORT_INPUT(port_desc)) { + LADSPA_Data dft = 0.0f; + int found = find_default(pvt->ldesc, i, &dft); + + if (found && !pvt->has_config[j]) { + pvt->config[j] = dft; + pvt->has_config[j] = 1; + } + + if (pvt->has_config[j]) { + if (!check_range(pvt->ldesc, i, pvt->config[j])) { + pvt->config[j] = dft; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_WARNING, "FALLING TO DEFAULT PARAM %d [%s] (%f)\n", + j+1, + pvt->ldesc->PortNames[i], + pvt->config[j]); + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "ADDING PARAM %d [%s] (%f)\n", + j+1, + pvt->ldesc->PortNames[i], + pvt->config[j]); + pvt->ldesc->connect_port(pvt->handle, i, &pvt->config[j++]); + usleep(10000); + } + } + + if (LADSPA_IS_PORT_INPUT(port_desc) && LADSPA_IS_PORT_AUDIO(port_desc)) { + pvt->ldesc->connect_port(pvt->handle, i, pvt->in_buf); + } + + if (LADSPA_IS_PORT_OUTPUT(port_desc)) { + if (LADSPA_IS_PORT_AUDIO(port_desc)) { + pvt->ldesc->connect_port(pvt->handle, i, pvt->out_buf); + } else if (k < MAX_INDEX) { + pvt->ldesc->connect_port(pvt->handle, i, &pvt->out_ports[k++]); + } + } + } + } + + break; + + case SWITCH_ABC_TYPE_CLOSE: + { + + if (pvt->handle && pvt->ldesc) { + pvt->ldesc->cleanup(pvt->handle); + } + + if (pvt->library_handle) { + unloadLADSPAPluginLibrary(pvt->library_handle); + } + } + break; + + case SWITCH_ABC_TYPE_WRITE_REPLACE: + case SWITCH_ABC_TYPE_READ_REPLACE: + { + switch_frame_t *rframe; + int16_t *slin; + + if (type == SWITCH_ABC_TYPE_READ_REPLACE) { + rframe = switch_core_media_bug_get_read_replace_frame(bug); + } else { + rframe = switch_core_media_bug_get_write_replace_frame(bug); + } + + slin = rframe->data; + + if (switch_channel_media_ready(channel)) { + switch_short_to_float(slin, pvt->in_buf, rframe->samples); + pvt->ldesc->run(pvt->handle, rframe->samples); + switch_float_to_short(pvt->out_buf, slin, rframe->samples); + } + + if (type == SWITCH_ABC_TYPE_READ_REPLACE) { + switch_core_media_bug_set_read_replace_frame(bug, rframe); + } else { + switch_core_media_bug_set_write_replace_frame(bug, rframe); + } + + if (pvt->skip && !--pvt->skip) { + return SWITCH_FALSE; + } + + } + break; + case SWITCH_ABC_TYPE_WRITE: + default: + break; + } + + return SWITCH_TRUE; +} + +switch_status_t stop_ladspa_session(switch_core_session_t *session) +{ + switch_media_bug_t *bug; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if ((bug = switch_channel_get_private(channel, "ladspa"))) { + switch_channel_set_private(channel, "ladspa", NULL); + switch_core_media_bug_remove(session, &bug); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + +switch_status_t ladspa_session(switch_core_session_t *session, const char *flags, const char *plugin_name, const char *label, const char *params) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_media_bug_t *bug; + switch_status_t status; + switch_ladspa_t *pvt = { 0 }; + switch_codec_implementation_t read_impl = { 0 }; + int i, bflags = SMBF_READ_REPLACE | SMBF_ANSWER_REQ; + char *pstr; + int argc; + char *argv[50]; + char *dparams = NULL; + + if (zstr(plugin_name)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s INVALID PLUGIN\n", switch_channel_get_name(channel)); + return SWITCH_STATUS_FALSE; + } + + if (zstr(flags)) { + flags = "r"; + } + + if (strchr(flags, 'w')) { + bflags = SMBF_WRITE_REPLACE; + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "FLAGS: %s PLUGIN: %s LABEL: %s PARAMS: %s\n", + flags, plugin_name, label, params); + + switch_core_session_get_read_impl(session, &read_impl); + + pvt = switch_core_session_alloc(session, sizeof(*pvt)); + + pvt->session = session; + if (!zstr(label)) { + pvt->label_name = switch_core_session_strdup(session, label); + } else { + char *p; + pvt->label_name = switch_core_session_strdup(session, plugin_name); + if ((p = strrchr(pvt->label_name, '.'))) { + *p = '\0'; + } + } + + if (strstr(plugin_name, ".so")) { + pvt->plugin_name = switch_core_session_strdup(session, plugin_name); + } else { + pvt->plugin_name = switch_core_session_sprintf(session, "%s.so", plugin_name); + } + + dparams = switch_core_session_strdup(session, params); + + argc = switch_split(dparams, ' ', argv); + + for (i = 0; i < argc; i++) { + pvt->config[i] = atof(argv[i]); + pvt->has_config[i] = 1; + } + + if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + pstr = switch_core_session_sprintf(session, "%s|%s|%s|%s", flags, plugin_name, label, params); + + if ((status = switch_core_media_bug_add(session, "ladspa", pstr, + ladspa_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + switch_channel_set_private(channel, "ladspa", bug); + + return SWITCH_STATUS_SUCCESS; +} + + +static void ladspa_parse(switch_core_session_t *session, const char *data) +{ + char *argv[5] = { 0 }; + int argc; + char *lbuf; + + if (data) { + lbuf = strdup(data); + argc = switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0]))); + ladspa_session(session, argv[0], argv[1], argv[2], argv[3]); + free(lbuf); + } +} + +#define APP_SYNTAX "||