1 /*
2 * Copyright (C) 2004 2005 2007, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <config.h>
22
23 #ifdef HAVE_LADSPA
24
25 #include <dlfcn.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <locale.h>
30 #include "um.h"
31 #include "ladspacore.h"
32 #include "gettext.h"
33
34 static GHashTable *effect_list;
35
descriptor_to_index(LADSPA_PortDescriptor pdesc)36 static int descriptor_to_index(LADSPA_PortDescriptor pdesc)
37 {
38 int j;
39 if ((pdesc & LADSPA_PORT_INPUT) != 0) j=0;
40 else if ((pdesc & LADSPA_PORT_OUTPUT) != 0) j=1;
41 else return -1;
42 if ((pdesc & LADSPA_PORT_CONTROL) != 0) return j;
43 else if ((pdesc & LADSPA_PORT_AUDIO) != 0) return j+2;
44 else return -1;
45 }
46
scan_file(gchar * filename,gchar * basename,gboolean do_check)47 static void scan_file(gchar *filename, gchar *basename, gboolean do_check)
48 {
49 void *p;
50 LADSPA_Descriptor_Function func;
51 gchar *msg,*c;
52 gint i,j;
53 int k[4];
54 unsigned long l;
55 const LADSPA_Descriptor *desc;
56 LadspaEffect *eff;
57
58 p = dlopen(filename,RTLD_NOW);
59 if (p == NULL) {
60 console_message(dlerror());
61 return;
62 }
63 func = dlsym(p,"ladspa_descriptor");
64 if (func == NULL) {
65 msg = g_strdup_printf("%s: ladspa_descriptor: %s",
66 basename,dlerror());
67 console_message(msg);
68 g_free(msg);
69 dlclose(p);
70 return;
71 }
72 i=0;
73 while (1) {
74 outer:
75 desc = func(i);
76 if (desc == NULL) break;
77 c = g_strdup_printf("%ld_%s",(long int)desc->UniqueID,basename);
78 if (do_check &&
79 g_hash_table_lookup(effect_list,c) != NULL) {
80 g_free(c);
81 i++;
82 continue;
83 }
84 eff = g_malloc0(sizeof(*eff));
85 eff->id = c;
86 eff->filename = g_strdup(filename);
87 eff->effect_number = i++;
88 eff->name = g_strdup(desc->Name);
89 eff->maker = g_strdup(desc->Maker);
90 eff->copyright = g_strdup(desc->Copyright);
91 for (l=0; l<desc->PortCount; l++) {
92 j = descriptor_to_index(desc->PortDescriptors[l]);
93 if (j < 0) {
94 msg = g_strdup_printf(_("Effect %s contains "
95 "invalid port %s"),eff->name,
96 desc->PortNames[l]);
97 console_message(msg);
98 g_free(msg);
99 g_free(eff->id);
100 g_free(eff->filename);
101 g_free(eff->name);
102 g_free(eff->maker);
103 g_free(eff->copyright);
104 g_free(eff);
105 goto outer;
106 }
107 eff->numports[j]++;
108 }
109 for (j=0; j<4; j++)
110 if (eff->numports[j] > 0)
111 eff->ports[j] = g_malloc(eff->numports[j] *
112 sizeof(LadspaPort));
113 k[0] = k[1] = k[2] = k[3] = 0;
114 for (l=0; l<desc->PortCount; l++) {
115 j = descriptor_to_index(desc->PortDescriptors[l]);
116 if (j < 0) continue;
117 eff->ports[j][k[j]].number = l;
118 eff->ports[j][k[j]].name = g_strdup(desc->PortNames[l]);
119 memcpy(&(eff->ports[j][k[j]].prh),
120 &(desc->PortRangeHints[l]),
121 sizeof(LADSPA_PortRangeHint));
122 k[j] ++;
123 }
124 g_hash_table_insert(effect_list,eff->id,eff);
125 }
126 dlclose(p);
127 }
128
scan_directory(gchar * dir,gpointer dummy)129 static void scan_directory(gchar *dir, gpointer dummy)
130 {
131 DIR *d;
132 struct dirent *de;
133 int i;
134 gchar *fn,*msg;
135 char *cur_lc_numeric;
136
137 d = opendir(dir);
138 if (d == NULL) {
139 if (errno != ENOENT) {
140 msg = g_strdup_printf(_("Ladspa: Error scanning %s"),dir);
141 console_perror(msg);
142 g_free(msg);
143 }
144 return;
145 }
146
147 /* Some LADSPA plugins resets LC_NUMERIC setting,
148 * so we need to save it. */
149 cur_lc_numeric = setlocale(LC_NUMERIC, NULL);
150
151 while (1) {
152 de = readdir(d);
153 if (de == NULL) break;
154 i = strlen(de->d_name);
155 if (i<4 || strcmp(de->d_name + i-3, ".so")) continue;
156 fn = g_strdup_printf("%s/%s",dir,de->d_name);
157
158 scan_file(fn,de->d_name,FALSE);
159
160 g_free(fn);
161 }
162 closedir(d);
163
164 /* Restore LC_NUMERIC setting. */
165 setlocale(LC_NUMERIC, cur_lc_numeric);
166 }
167
ladspa_cleanup_func(gpointer key,gpointer value,gpointer user_data)168 static gboolean ladspa_cleanup_func(gpointer key, gpointer value,
169 gpointer user_data)
170 {
171 int i,j;
172 LadspaEffect *eff;
173 eff = (LadspaEffect *)value;
174 g_free(eff->id);
175 g_free(eff->name);
176 g_free(eff->filename);
177 g_free(eff->maker);
178 g_free(eff->copyright);
179
180 for (i=0; i<4; i++) {
181 for (j=0; j<eff->numports[i]; j++)
182 g_free(eff->ports[i][j].name);
183 g_free(eff->ports[i]);
184 }
185
186 g_free(eff);
187 /* eff->id == value, so no need to free value again */
188 return TRUE;
189 }
190
ladspa_cleanup(void)191 static void ladspa_cleanup(void)
192 {
193 if (effect_list == NULL) return;
194 g_hash_table_foreach_remove(effect_list, ladspa_cleanup_func, NULL);
195 g_hash_table_destroy(effect_list);
196 effect_list = NULL;
197 }
198
ladspa_path_foreach(void (* function)(gchar * dirname,gpointer user_data),gpointer user_data)199 static void ladspa_path_foreach(void (*function)(gchar *dirname,
200 gpointer user_data),
201 gpointer user_data)
202 {
203 gchar *p,*c;
204
205 p = getenv("LADSPA_PATH");
206 if (p == NULL) {
207 p = DEFAULT_LADSPA_PATH;
208 /* console_message(_("Environment variable LADSPA_PATH not set.\n"
209 "LADSPA support is disabled."));
210 return; */
211 }
212 p = g_strdup(p);
213 c = strtok(p,":");
214 while (c != NULL) {
215 function(c,user_data);
216 c = strtok(NULL,":");
217 }
218 free(p);
219
220 }
221
ladspa_rescan(void)222 void ladspa_rescan(void)
223 {
224 ladspa_cleanup();
225 effect_list = g_hash_table_new(g_str_hash,g_str_equal);
226
227 ladspa_path_foreach(scan_directory,NULL);
228 }
229
foreach_func(gpointer key,gpointer value,gpointer user_data)230 static void foreach_func(gpointer key, gpointer value, gpointer user_data)
231 {
232 void (*func)(void *) = (void (*)(void *))user_data;
233 func(value);
234 }
235
ladspa_foreach_effect(void (* func)(LadspaEffect * eff))236 void ladspa_foreach_effect(void (*func)(LadspaEffect *eff))
237 {
238 g_hash_table_foreach(effect_list,foreach_func,func);
239 }
240
find_effect_func(gchar * dirname,gpointer user_data)241 void find_effect_func(gchar *dirname, gpointer user_data)
242 {
243 gchar *fn = (gchar *)user_data;
244 gchar *c;
245 c = g_strdup_printf("%s/%s",dirname,fn);
246 if (file_exists(c))
247 scan_file(c,fn,TRUE);
248 g_free(c);
249 }
250
ladspa_find_effect(gchar * id)251 LadspaEffect *ladspa_find_effect(gchar *id)
252 {
253 LadspaEffect *eff;
254 gchar *c,*d,*e;
255 unsigned long unid;
256
257 if (effect_list == NULL)
258 effect_list = g_hash_table_new(g_str_hash,g_str_equal);
259 eff = (LadspaEffect *)g_hash_table_lookup(effect_list,id);
260 if (eff != NULL) return eff;
261
262 /* Parse the ID to get uniqueID and filename */
263 c = g_strdup(id);
264 d = strchr(c,'_');
265 if (d == NULL) {
266 g_free(c);
267 return NULL;
268 }
269 *d = 0;
270 d++;
271 /* c should now be UniqueID, d filename. g_free(c) frees both c & d */
272 unid = strtoul(c,&e,10);
273 if (*e != 0) {
274 g_free(c);
275 return NULL;
276 }
277
278 /* Search for the file and add it's effects if found */
279 ladspa_path_foreach(find_effect_func,d);
280 g_free(c);
281
282 /* Try again */
283 eff = (LadspaEffect *)g_hash_table_lookup(effect_list,id);
284
285 return eff;
286
287 }
288
289 static LadspaEffect *processing_effect;
290 static const LADSPA_Descriptor *processing_desc;
291 static LADSPA_Handle processing_handle;
292 static sample_t *processing_outbuf;
293
ladspa_filter_proc(void * in,guint sample_size,chunk_writeout_func out_func,WriteoutID id,Dataformat * format)294 static gboolean ladspa_filter_proc(void *in, guint sample_size,
295 chunk_writeout_func out_func, WriteoutID id,
296 Dataformat *format)
297 {
298 sample_t *s = (sample_t *)in;
299 guint chans = format->channels;
300 guint frames = sample_size / (sizeof(sample_t)*chans);
301 guint i,j,k,m;
302 int l;
303 float *b;
304 for (i=0; i<frames; i+=j) {
305 j = MIN(frames-i, 1024);
306 /* Set input */
307 for (k=0; k < processing_effect->numports[2]; k++) {
308 l = processing_effect->ports[2][k].map;
309 if (l < 0) continue;
310 b = processing_effect->ports[2][k].buffer;
311 #ifndef USE_DOUBLE_SAMPLES
312 if (chans == 1) {
313 memcpy(b,&s[i],j*sizeof(float));
314 continue;
315 }
316 #endif
317 for (m=0; m<j; m++)
318 b[m] = (float) s[chans*(i+m) + l];
319 }
320 /* Run plugin */
321 processing_desc->run(processing_handle,j);
322 /* Get output */
323 /* Copy the input to use as default (a little inefficient,
324 I know..) */
325 if (processing_effect->keep)
326 memcpy(processing_outbuf,&s[i],j*chans*sizeof(sample_t));
327 else
328 memset(processing_outbuf,0,j*chans*sizeof(sample_t));
329 for (k=0; k < processing_effect->numports[3]; k++) {
330 l = processing_effect->ports[3][k].map;
331 if (l < 0) continue;
332 b = processing_effect->ports[3][k].buffer;
333 #ifndef USE_DOUBLE_SAMPLES
334 if (chans == 1) {
335 memcpy(processing_outbuf,b,j*sizeof(float));
336 continue;
337 }
338 #endif
339 for (m=0; m<j; m++)
340 processing_outbuf[chans*m + l] = b[m];
341 }
342 /* Write output */
343 if (out_func(id,processing_outbuf,j*chans*sizeof(sample_t)))
344 return TRUE;
345 }
346 return FALSE;
347 }
348
ladspa_run_effect(Chunk * chunk,StatusBar * bar,LadspaEffect * eff,int dither_mode)349 Chunk *ladspa_run_effect(Chunk *chunk, StatusBar *bar, LadspaEffect *eff,
350 int dither_mode)
351 {
352 void *p;
353 gchar *c;
354 int i,j,k;
355 Chunk *r;
356 float f;
357 LADSPA_Descriptor_Function func;
358 const LADSPA_Descriptor *desc;
359 LADSPA_Handle hand;
360 char *cur_lc_numeric;
361
362 /* Save LC_NUMERIC setting. */
363 cur_lc_numeric = setlocale(LC_NUMERIC, NULL);
364
365 /* Open plugin */
366 p = dlopen(eff->filename,RTLD_LAZY);
367 if (p == NULL) {
368 user_error(dlerror());
369 return NULL;
370 }
371 func = dlsym(p,"ladspa_descriptor");
372 if (func == NULL) {
373 c = g_strdup_printf("%s: ladspa_descriptor: %s",eff->filename,
374 dlerror());
375 user_error(c);
376 g_free(c);
377 dlclose(p);
378 setlocale(LC_NUMERIC, cur_lc_numeric);
379 return NULL;
380 }
381 desc = func(eff->effect_number);
382 g_assert(desc != NULL);
383 hand = desc->instantiate(desc,chunk->format.samplerate);
384 /* Connect ports */
385 for (i=0; i<2; i++)
386 for (j=0; j<eff->numports[i]; j++) {
387 f = eff->ports[i][j].value;
388 desc->connect_port(hand,eff->ports[i][j].number,
389 &(eff->ports[i][j].value));
390 eff->ports[i][j].value = f;
391 }
392 for (i=2; i<4; i++)
393 for (j=0; j<eff->numports[i]; j++) {
394 eff->ports[i][j].buffer = g_malloc(1024*sizeof(float));
395 desc->connect_port(hand,eff->ports[i][j].number,
396 eff->ports[i][j].buffer);
397 if (i == 2 && eff->ports[i][j].map == -1)
398 for (k=0; k<1024; k++)
399 eff->ports[i][j].buffer[k] =
400 eff->ports[i][j].value;
401 }
402 /* Apply effect */
403 processing_effect = eff;
404 processing_desc = desc;
405 processing_handle = hand;
406 processing_outbuf = g_malloc(1024*chunk->format.channels*
407 sizeof(sample_t));
408 if (desc->activate != NULL) desc->activate(hand);
409 c = g_strdup_printf(_("Applying effect '%s'"),eff->name);
410 r = chunk_filter(chunk, ladspa_filter_proc, NULL, CHUNK_FILTER_MANY,
411 TRUE, dither_mode, bar, c);
412 g_free(c);
413 if (desc->deactivate != NULL) desc->deactivate(hand);
414 if (desc->cleanup != NULL) desc->cleanup(hand);
415 dlclose(p);
416 g_free(processing_outbuf);
417 for (i=2; i<4; i++)
418 for (j=0; j<eff->numports[i]; j++)
419 g_free(eff->ports[i][j].buffer);
420
421 /* Restore LC_NUMERIC setting. */
422 setlocale(LC_NUMERIC, cur_lc_numeric);
423 return r;
424 }
425
426 #endif /* HAVE_LADSPA */
427