1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 *
28 * mod_ladspa.c -- LADSPA
29 *
30 */
31 #include <switch.h>
32 #include "ladspa.h"
33 #include "utils.h"
34
35 /* Prototypes */
36 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_ladspa_shutdown);
37 SWITCH_MODULE_RUNTIME_FUNCTION(mod_ladspa_runtime);
38 SWITCH_MODULE_LOAD_FUNCTION(mod_ladspa_load);
39
40 /* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime)
41 * Defines a switch_loadable_module_function_table_t and a static const char[] modname
42 */
43 SWITCH_MODULE_DEFINITION(mod_ladspa, mod_ladspa_load, mod_ladspa_shutdown, NULL);
44
45 #define MAX_INDEX 256
46
47 typedef struct {
48 switch_core_session_t *session;
49 char *plugin_name;
50 char *label_name;
51 void *library_handle;
52 const LADSPA_Descriptor *ldesc;
53 LADSPA_Handle handle;
54 LADSPA_Data config[MAX_INDEX];
55 int num_idx;
56 char *str_config[MAX_INDEX];
57 int str_idx;
58 uint8_t has_config[MAX_INDEX];
59 int skip;
60 LADSPA_Data in_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
61 LADSPA_Data file_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
62 LADSPA_Data out_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
63 LADSPA_Data out_ports[MAX_INDEX];
64 switch_file_handle_t fh;
65 } switch_ladspa_t;
66
67
68
check_range(const LADSPA_Descriptor * ldesc,int i,LADSPA_Data val)69 int check_range(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data val)
70 {
71 if (ldesc->PortRangeHints[i].LowerBound && ldesc->PortRangeHints[i].UpperBound &&
72 (val < ldesc->PortRangeHints[i].LowerBound || val > ldesc->PortRangeHints[i].UpperBound)) {
73 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Param %f out of bounds %f-%f\n",
74 val, ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound);
75 return 0;
76 }
77
78 return 1;
79 }
80
find_default(const LADSPA_Descriptor * ldesc,int i,LADSPA_Data * ptr)81 int find_default(const LADSPA_Descriptor *ldesc, int i, LADSPA_Data *ptr)
82
83 {
84 LADSPA_Data dftval = 0;
85 int fail = 0;
86
87 LADSPA_PortRangeHintDescriptor port_hint = ldesc->PortRangeHints[i].HintDescriptor;
88
89 switch (port_hint & LADSPA_HINT_DEFAULT_MASK) {
90 case LADSPA_HINT_DEFAULT_NONE:
91 break;
92 case LADSPA_HINT_DEFAULT_MINIMUM:
93 dftval = ldesc->PortRangeHints[i].LowerBound;
94 break;
95 case LADSPA_HINT_DEFAULT_LOW:
96 if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
97 dftval = exp(log(ldesc->PortRangeHints[i].LowerBound)
98 * 0.75 + log(ldesc->PortRangeHints[i].UpperBound)
99 * 0.25);
100 } else {
101 dftval = (ldesc->PortRangeHints[i].LowerBound * 0.75 + ldesc->PortRangeHints[i].UpperBound * 0.25);
102 }
103 break;
104 case LADSPA_HINT_DEFAULT_MIDDLE:
105 if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
106 dftval = sqrt(ldesc->PortRangeHints[i].LowerBound * ldesc->PortRangeHints[i].UpperBound);
107 } else {
108 dftval = 0.5 * (ldesc->PortRangeHints[i].LowerBound + ldesc->PortRangeHints[i].UpperBound);
109 }
110 break;
111 case LADSPA_HINT_DEFAULT_HIGH:
112 if (LADSPA_IS_HINT_LOGARITHMIC(port_hint)) {
113 dftval = exp(log(ldesc->PortRangeHints[i].LowerBound)
114 * 0.25 + log(ldesc->PortRangeHints[i].UpperBound)
115 * 0.75);
116 } else {
117 dftval = (ldesc->PortRangeHints[i].LowerBound * 0.25 + ldesc->PortRangeHints[i].UpperBound * 0.75);
118 }
119 break;
120 case LADSPA_HINT_DEFAULT_MAXIMUM:
121 dftval = ldesc->PortRangeHints[i].UpperBound;
122 break;
123 case LADSPA_HINT_DEFAULT_0:
124 dftval = 0;
125 break;
126 case LADSPA_HINT_DEFAULT_1:
127 dftval = 1;
128 break;
129 case LADSPA_HINT_DEFAULT_100:
130 dftval = 100;
131 break;
132 case LADSPA_HINT_DEFAULT_440:
133 dftval = 440;
134 break;
135 default:
136 fail = 1;
137 break;
138 }
139
140 if (!fail) {
141 *ptr = dftval;
142 }
143
144 return !fail;
145 }
146
dump_info(const LADSPA_Descriptor * ldesc)147 static void dump_info(const LADSPA_Descriptor *ldesc)
148 {
149 int i = 0;
150
151 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Name: \"%s\"\n", ldesc->Name);
152 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Label: \"%s\"\n", ldesc->Label);
153 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Plugin Unique ID: %lu\n", ldesc->UniqueID);
154 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Maker: \"%s\"\n", ldesc->Maker);
155 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Copyright: \"%s\"\n", ldesc->Copyright);
156
157 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Must Run Real-Time: ");
158 if (LADSPA_IS_REALTIME(ldesc->Properties))
159 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
160 else
161 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
162
163 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has activate() Function: ");
164 if (ldesc->activate != NULL)
165 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
166 else
167 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
168 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has deactivate() Function: ");
169 if (ldesc->deactivate != NULL)
170 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
171 else
172 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
173 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Has run_adding() Function: ");
174 if (ldesc->run_adding != NULL)
175 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Yes\n");
176 else
177 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "No\n");
178
179 if (ldesc->instantiate == NULL)
180 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO INSTANTIATE FUNCTION.\n");
181 if (ldesc->connect_port == NULL)
182 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CONNECT_PORT FUNCTION.\n");
183 if (ldesc->run == NULL)
184 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO RUN FUNCTION.\n");
185 if (ldesc->run_adding != NULL && ldesc->set_run_adding_gain == NULL)
186 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS RUN_ADDING FUNCTION BUT " "NOT SET_RUN_ADDING_GAIN.\n");
187 if (ldesc->run_adding == NULL && ldesc->set_run_adding_gain != NULL)
188 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS SET_RUN_ADDING_GAIN FUNCTION BUT " "NOT RUN_ADDING.\n");
189 if (ldesc->cleanup == NULL)
190 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: PLUGIN HAS NO CLEANUP FUNCTION.\n");
191
192 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Environment: ");
193 if (LADSPA_IS_HARD_RT_CAPABLE(ldesc->Properties))
194 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal or Hard Real-Time\n");
195 else
196 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Normal\n");
197
198 if (LADSPA_IS_INPLACE_BROKEN(ldesc->Properties))
199 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");
200
201 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Ports:");
202
203 if (ldesc->PortCount == 0)
204 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\tERROR: PLUGIN HAS NO PORTS.\n");
205
206 for (i = 0; i < ldesc->PortCount; i++) {
207 LADSPA_Data dft = 0.0f;
208 int found = 0;
209
210 if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) {
211 found = find_default(ldesc, i, &dft);
212 }
213
214 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n \"%s\" ", ldesc->PortNames[i]);
215
216 if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i])
217 && LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i]))
218 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: INPUT AND OUTPUT");
219 else if (LADSPA_IS_PORT_INPUT(ldesc->PortDescriptors[i]))
220 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "input");
221 else if (LADSPA_IS_PORT_OUTPUT(ldesc->PortDescriptors[i]))
222 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "output");
223 else
224 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "ERROR: NEITHER INPUT NOR OUTPUT");
225
226 if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])
227 && LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i]))
228 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: CONTROL AND AUDIO");
229 else if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i]))
230 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", control");
231 else if (LADSPA_IS_PORT_AUDIO(ldesc->PortDescriptors[i]))
232 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", audio");
233 else
234 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, ", ERROR: NEITHER CONTROL NOR AUDIO");
235
236 if (LADSPA_IS_PORT_CONTROL(ldesc->PortDescriptors[i])) {
237 if (found) {
238 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: %f\n",
239 ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound, dft);
240 } else {
241 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n RANGE: %f-%f DEFAULT: none.\n",
242 ldesc->PortRangeHints[i].LowerBound, ldesc->PortRangeHints[i].UpperBound);
243 }
244 }
245
246
247
248 }
249
250 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "\n\n");
251 }
252
253
254
255
256
ladspa_callback(switch_media_bug_t * bug,void * user_data,switch_abc_type_t type)257 static switch_bool_t ladspa_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
258 {
259 switch_ladspa_t *pvt = (switch_ladspa_t *) user_data;
260 //switch_frame_t *frame = NULL;
261 switch_channel_t *channel = switch_core_session_get_channel(pvt->session);
262
263 switch (type) {
264 case SWITCH_ABC_TYPE_INIT:
265 {
266 switch_codec_implementation_t read_impl = { 0 };
267 LADSPA_PortDescriptor port_desc;
268 int i = 0, j = 0, k = 0, str_idx = 0;
269
270 switch_core_session_get_read_impl(pvt->session, &read_impl);
271
272 if (!(pvt->library_handle = loadLADSPAPluginLibrary(pvt->plugin_name))) {
273 return SWITCH_FALSE;
274 }
275
276 if (!(pvt->ldesc = findLADSPAPluginDescriptor(pvt->library_handle, pvt->plugin_name, pvt->label_name))) {
277 return SWITCH_FALSE;
278 }
279
280
281 pvt->handle = pvt->ldesc->instantiate(pvt->ldesc, read_impl.actual_samples_per_second);
282
283 dump_info(pvt->ldesc);
284
285
286 for (i = 0; i < pvt->ldesc->PortCount; i++) {
287 port_desc = pvt->ldesc->PortDescriptors[i];
288
289 if (LADSPA_IS_PORT_CONTROL(port_desc) && LADSPA_IS_PORT_INPUT(port_desc)) {
290 LADSPA_Data dft = 0.0f;
291 int found = find_default(pvt->ldesc, i, &dft);
292
293 if (found && !pvt->has_config[j]) {
294 pvt->config[j] = dft;
295 pvt->has_config[j] = 1;
296 }
297
298 if (pvt->has_config[j]) {
299 if (!check_range(pvt->ldesc, i, pvt->config[j])) {
300 pvt->config[j] = dft;
301 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_WARNING, "FALLING TO DEFAULT PARAM %d [%s] (%f)\n",
302 j+1,
303 pvt->ldesc->PortNames[i],
304 pvt->config[j]);
305 }
306
307 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "ADDING PARAM %d [%s] (%f)\n",
308 j+1,
309 pvt->ldesc->PortNames[i],
310 pvt->config[j]);
311 pvt->ldesc->connect_port(pvt->handle, i, &pvt->config[j++]);
312 usleep(10000);
313 }
314 }
315
316 if (LADSPA_IS_PORT_INPUT(port_desc) && LADSPA_IS_PORT_AUDIO(port_desc)) {
317 int mapped = 0;
318
319 if (pvt->str_idx && !zstr(pvt->str_config[str_idx])) {
320
321 if (!strcasecmp(pvt->str_config[str_idx], "none")) {
322 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT NOTHING to port: %s\n",
323 pvt->ldesc->PortNames[i]
324 );
325 mapped = 1;
326 } else if (!strncasecmp(pvt->str_config[str_idx], "file:", 5)) {
327 char *file = pvt->str_config[str_idx] + 5;
328
329 if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
330 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session),
331 SWITCH_LOG_ERROR, "CAN'T CONNECT FILE [%s] File already mapped\n", file);
332 } else {
333 if (switch_core_file_open(&pvt->fh,
334 file,
335 read_impl.number_of_channels,
336 read_impl.actual_samples_per_second,
337 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
338 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot open file: %s\n", file);
339 return SWITCH_FALSE;
340 }
341
342
343 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT FILE [%s] to port: %s\n",
344 file,
345 pvt->ldesc->PortNames[i]
346 );
347
348 pvt->ldesc->connect_port(pvt->handle, i, pvt->file_buf);
349 mapped = 1;
350 }
351 }
352
353 str_idx++;
354 }
355
356 if (!mapped) {
357 pvt->ldesc->connect_port(pvt->handle, i, pvt->in_buf);
358 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "CONNECT CHANNEL AUDIO to port: %s\n",
359 pvt->ldesc->PortNames[i]
360 );
361 }
362
363 }
364
365 if (LADSPA_IS_PORT_OUTPUT(port_desc)) {
366 if (LADSPA_IS_PORT_AUDIO(port_desc)) {
367 pvt->ldesc->connect_port(pvt->handle, i, pvt->out_buf);
368 } else if (k < MAX_INDEX) {
369 pvt->ldesc->connect_port(pvt->handle, i, &pvt->out_ports[k++]);
370 }
371 }
372 }
373 }
374
375 break;
376
377 case SWITCH_ABC_TYPE_CLOSE:
378 {
379
380 if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
381 switch_core_file_close(&pvt->fh);
382 }
383
384 if (pvt->handle && pvt->ldesc) {
385 pvt->ldesc->cleanup(pvt->handle);
386 }
387
388 if (pvt->library_handle) {
389 unloadLADSPAPluginLibrary(pvt->library_handle);
390 }
391 }
392 break;
393
394 case SWITCH_ABC_TYPE_WRITE_REPLACE:
395 case SWITCH_ABC_TYPE_READ_REPLACE:
396 {
397 switch_frame_t *rframe;
398 int16_t *slin, abuf[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
399 switch_size_t olen = 0;
400
401
402 if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
403 rframe = switch_core_media_bug_get_read_replace_frame(bug);
404 } else {
405 rframe = switch_core_media_bug_get_write_replace_frame(bug);
406 }
407
408 slin = rframe->data;
409
410 if (switch_channel_media_ready(channel)) {
411 switch_short_to_float(slin, pvt->in_buf, rframe->samples);
412
413 if (switch_test_flag((&pvt->fh), SWITCH_FILE_OPEN)) {
414 olen = rframe->samples;
415 if (switch_core_file_read(&pvt->fh, abuf, &olen) != SWITCH_STATUS_SUCCESS) {
416 switch_codec_implementation_t read_impl = { 0 };
417 char *file = switch_core_session_strdup(pvt->session, pvt->fh.file_path);
418 switch_core_session_get_read_impl(pvt->session, &read_impl);
419
420 switch_core_file_close(&pvt->fh);
421
422 if (switch_core_file_open(&pvt->fh,
423 file,
424 read_impl.number_of_channels,
425 read_impl.actual_samples_per_second,
426 SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL) != SWITCH_STATUS_SUCCESS) {
427 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot open file: %s\n", file);
428 return SWITCH_FALSE;
429 }
430
431 olen = rframe->samples;
432 if (switch_core_file_read(&pvt->fh, abuf, &olen) != SWITCH_STATUS_SUCCESS) {
433 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_ERROR, "Cannot READ file: %s\n", file);
434 return SWITCH_FALSE;
435 }
436 }
437
438 switch_short_to_float(abuf, pvt->file_buf, olen);
439 }
440
441 pvt->ldesc->run(pvt->handle, rframe->samples);
442
443 switch_float_to_short(pvt->out_buf, slin, rframe->samples);
444 }
445
446 if (type == SWITCH_ABC_TYPE_READ_REPLACE) {
447 switch_core_media_bug_set_read_replace_frame(bug, rframe);
448 } else {
449 switch_core_media_bug_set_write_replace_frame(bug, rframe);
450 }
451
452 if (pvt->skip && !--pvt->skip) {
453 return SWITCH_FALSE;
454 }
455
456 }
457 break;
458 case SWITCH_ABC_TYPE_WRITE:
459 default:
460 break;
461 }
462
463 return SWITCH_TRUE;
464 }
465
stop_ladspa_session(switch_core_session_t * session)466 switch_status_t stop_ladspa_session(switch_core_session_t *session)
467 {
468 switch_media_bug_t *bug;
469 switch_channel_t *channel = switch_core_session_get_channel(session);
470
471 if ((bug = switch_channel_get_private(channel, "ladspa"))) {
472 switch_channel_set_private(channel, "ladspa", NULL);
473 switch_core_media_bug_remove(session, &bug);
474 return SWITCH_STATUS_SUCCESS;
475 }
476
477 return SWITCH_STATUS_FALSE;
478 }
479
ladspa_session(switch_core_session_t * session,const char * flags,const char * plugin_name,const char * label,const char * params)480 switch_status_t ladspa_session(switch_core_session_t *session, const char *flags, const char *plugin_name, const char *label, const char *params)
481 {
482 switch_channel_t *channel = switch_core_session_get_channel(session);
483 switch_media_bug_t *bug;
484 switch_status_t status;
485 switch_ladspa_t *pvt = { 0 };
486 switch_codec_implementation_t read_impl = { 0 };
487 int i, bflags = SMBF_READ_REPLACE | SMBF_ANSWER_REQ;
488 char *pstr;
489 int argc;
490 char *argv[50];
491 char *dparams = NULL;
492
493 if (zstr(plugin_name)) {
494 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "%s INVALID PLUGIN\n", switch_channel_get_name(channel));
495 return SWITCH_STATUS_FALSE;
496 }
497
498 if (zstr(flags)) {
499 flags = "r";
500 }
501
502 if (strchr(flags, 'w')) {
503 bflags = SMBF_WRITE_REPLACE;
504 }
505
506 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "FLAGS: %s PLUGIN: %s LABEL: %s PARAMS: %s\n",
507 flags, plugin_name, label, params);
508
509 switch_core_session_get_read_impl(session, &read_impl);
510
511 pvt = switch_core_session_alloc(session, sizeof(*pvt));
512
513 pvt->session = session;
514 if (!zstr(label)) {
515 pvt->label_name = switch_core_session_strdup(session, label);
516 } else {
517 char *p;
518 pvt->label_name = switch_core_session_strdup(session, plugin_name);
519 if ((p = strrchr(pvt->label_name, '.'))) {
520 *p = '\0';
521 }
522 }
523
524 if (strstr(plugin_name, ".so")) {
525 pvt->plugin_name = switch_core_session_strdup(session, plugin_name);
526 } else {
527 pvt->plugin_name = switch_core_session_sprintf(session, "%s.so", plugin_name);
528 }
529
530 dparams = switch_core_session_strdup(session, params);
531
532 argc = switch_split(dparams, ' ', argv);
533
534 for (i = 0; i < argc; i++) {
535 if (switch_is_number(argv[i])) {
536 if (pvt->num_idx < MAX_INDEX) {
537 pvt->config[pvt->num_idx] = atof(argv[i]);
538 pvt->has_config[pvt->num_idx] = 1;
539 pvt->num_idx++;
540 }
541 } else {
542 if (pvt->str_idx < MAX_INDEX) {
543 pvt->str_config[pvt->str_idx++] = switch_core_session_strdup(session, argv[i]);
544 }
545 }
546 }
547
548 if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
549 return SWITCH_STATUS_FALSE;
550 }
551
552 pstr = switch_core_session_sprintf(session, "%s|%s|%s|%s", flags, plugin_name, label, params);
553
554 if ((status = switch_core_media_bug_add(session, "ladspa", pstr,
555 ladspa_callback, pvt, 0, bflags | SMBF_NO_PAUSE, &bug)) != SWITCH_STATUS_SUCCESS) {
556 return status;
557 }
558
559 switch_channel_set_private(channel, "ladspa", bug);
560
561 return SWITCH_STATUS_SUCCESS;
562 }
563
564
ladspa_parse(switch_core_session_t * session,const char * data)565 static void ladspa_parse(switch_core_session_t *session, const char *data)
566 {
567 char *argv[5] = { 0 };
568 char *lbuf;
569
570 if (data) {
571 lbuf = strdup(data);
572 switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0])));
573 ladspa_session(session, argv[0], argv[1], argv[2], argv[3]);
574 free(lbuf);
575 }
576 }
577
578 #define APP_SYNTAX "<flags>|<plugin>|<label>|<params>"
SWITCH_STANDARD_APP(ladspa_run_function)579 SWITCH_STANDARD_APP(ladspa_run_function)
580 {
581 ladspa_parse(session, data);
582 }
583
584
585
586 #define API_SYNTAX "<uuid> [start|stop] <flags>|<plugin>|<label>|<params>"
SWITCH_STANDARD_API(ladspa_api)587 SWITCH_STANDARD_API(ladspa_api)
588 {
589 char *uuid = NULL;
590 char *data = NULL;
591 char *p, *action;
592 switch_core_session_t *ksession = NULL;
593
594 if (!cmd) {
595 stream->write_function(stream, "-ERR Operation Failed\n");
596 goto done;
597 }
598
599 data = strdup(cmd);
600
601 if ((p = strchr(data, ' '))) {
602 uuid = data;
603 *p++ = '\0';
604
605 if (!(ksession = switch_core_session_locate(uuid))) {
606 stream->write_function(stream, "-ERR non-existant UUID\n");
607 goto done;
608 }
609
610 if ((action = strstr(cmd, "stop"))) {
611 stop_ladspa_session(ksession);
612 switch_core_session_rwunlock(ksession);
613 stream->write_function(stream, "+OK\n");
614 goto done;
615 } else if ((action = strstr(cmd, "start"))) {
616 // Advance past 'start' conditional keyword and move on
617 p += 5 * sizeof(char);
618 *p++ = '\0';
619 switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, "Tried to remove 'start' and now the string is \"%s\"\n", p);
620 }
621
622 ladspa_parse(ksession, p);
623 stream->write_function(stream, "+OK\n");
624 } else {
625 stream->write_function(stream, "-ERR Usage %s\n", API_SYNTAX);
626 }
627
628 done:
629
630 switch_safe_free(data);
631
632 return SWITCH_STATUS_SUCCESS;
633 }
634
635
636 /* Macro expands to: switch_status_t mod_ladspa_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */
SWITCH_MODULE_LOAD_FUNCTION(mod_ladspa_load)637 SWITCH_MODULE_LOAD_FUNCTION(mod_ladspa_load)
638 {
639 switch_application_interface_t *app_interface;
640 switch_api_interface_t *api_interface;
641 char *path = getenv("LADSPA_PATH");
642
643 if (zstr(path)) {
644 if (switch_directory_exists("/usr/lib64/ladspa/", pool) == SWITCH_STATUS_SUCCESS) {
645 setenv("LADSPA_PATH", "/usr/lib64/ladspa/:/usr/local/lib/ladspa", 1);
646 } else if (switch_directory_exists("/usr/lib/ladspa/", pool) == SWITCH_STATUS_SUCCESS) {
647 setenv("LADSPA_PATH", "/usr/lib/ladspa/:/usr/local/lib/ladspa", 1);
648 } else if (switch_directory_exists("/usr/local/lib/ladspa/", pool) == SWITCH_STATUS_SUCCESS) {
649 setenv("LADSPA_PATH", "/usr/local/lib/ladspa", 1);
650 }
651 }
652
653 /* connect my internal structure to the blank pointer passed to me */
654 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
655
656 SWITCH_ADD_APP(app_interface, "ladspa_run", "ladspa_run", NULL, ladspa_run_function, APP_SYNTAX, SAF_NONE);
657 SWITCH_ADD_API(api_interface, "uuid_ladspa", "ladspa", ladspa_api, API_SYNTAX);
658
659 switch_console_set_complete("add uuid_ladspa ::console::list_uuid");
660
661
662 /* indicate that the module should continue to be loaded */
663 return SWITCH_STATUS_SUCCESS;
664 }
665
666 /*
667 Called when the system shuts down
668 Macro expands to: switch_status_t mod_ladspa_shutdown() */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_ladspa_shutdown)669 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_ladspa_shutdown)
670 {
671 /* Cleanup dynamically allocated config settings */
672
673 return SWITCH_STATUS_SUCCESS;
674 }
675
676 /* For Emacs:
677 * Local Variables:
678 * mode:c
679 * indent-tabs-mode:t
680 * tab-width:4
681 * c-basic-offset:4
682 * End:
683 * For VIM:
684 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
685 */
686