Adding stereo plugins, fixed LADSPA menus, ignoring NON-RT plugins - Alex
[terminatorX.git] / src / tX_ladspa_class.cc
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2003  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19     File: tX_ladspa_class.cc
20 */
21
22 #include "tX_ladspa_class.h"
23 #include "tX_global.h"
24 #include <dirent.h>
25 #include <dlfcn.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #ifdef USE_LRDF
31 #include <lrdf.h>
32 #endif
33
34 LADSPA_Class * LADSPA_Class::root=NULL;
35 LADSPA_Class * LADSPA_Class::unclassified=NULL;
36 std::list <char *> LADSPA_Class::rdf_files;
37 vtt_class *LADSPA_Class::current_vtt;
38 bool LADSPA_Class::liblrdf_error=false;
39
40 /* Why do have to code this myself? */
41 static int compare(const char *a, const char *b) 
42 {
43         int lena, lenb, i;
44
45         if (!a && !b) return 0;
46         if (!a) return 2;
47         if (!b) return 1;
48         
49         lena=strlen(a);
50         lenb=strlen(b);
51         
52         for (i=0; (i<lena) && (i<lenb); i++) {
53                 if (a[i]>b[i]) {
54                         return 2;
55                 } else if (a[i]<b[i]) {
56                         return 1;
57                 }
58         }
59         
60         if (lena>lenb) return 1;
61         else if (lenb>lena) return 2;
62         return 0;
63 }
64
65 void LADSPA_Class::init() {
66         char *start, *end, *buffer;
67
68 #ifdef USE_LRDF
69         /* Scanning every dir in path */
70         start = globals.lrdf_path;
71         
72         while (*start != '\0') {
73                 end = start;
74                 while (*end != ':' && *end != '\0') end++;
75     
76                 buffer = (char *) malloc(1 + end - start);
77                 if (end > start) strncpy(buffer, start, end - start);
78                         
79                 buffer[end - start] = '\0';
80                 LADSPA_Class::scandir(buffer);
81                 free (buffer); 
82     
83         start = end;
84         if (*start == ':') start++;
85         }
86         
87         if (rdf_files.size() > 0) {
88                 char *uris[rdf_files.size()+1];
89                 std::list <char *> :: iterator i;
90                 int t;
91                 
92                 for (i=rdf_files.begin(), t=0; i!=rdf_files.end(); i++, t++) {
93                         uris[t]=(*i);
94                 }
95                 uris[t]=NULL;
96                 
97                 lrdf_init();
98         
99                 if (lrdf_read_files((const char **) uris)) {
100                         tX_error("liblrdf had problems reading the rdf files - cannot provide structured menu");
101                         liblrdf_error=true;
102         }
103 #endif
104                 root=new LADSPA_Class("http://ladspa.org/ontology#Plugin");
105 #ifdef USE_LRDF         
106                 lrdf_cleanup();
107         } else {
108                 tX_error("No RDF files found");
109         }
110 #endif
111         
112         unclassified=new LADSPA_Class();
113         /* This is the last class to accpet all plugins not accepted by other classes. */
114         root->subclasses.push_back(unclassified);
115 }
116
117 void LADSPA_Class::scandir(char *dirname) {
118         int dirlen=strlen(dirname);
119         int needslash=0;
120         DIR * dir;
121         struct dirent * entry;
122         char *filename;
123         
124         if (!dirlen) { tX_error("LADSPA_Class::scandir() Empty directory name"); return; };
125
126         if (dirname[dirlen - 1] != '/') needslash=1;
127         
128         dir = opendir(dirname);
129         
130         if (!dir) { tX_error("LADSPA_Class::scandir() couldn't access directory \"%s\"", dirname); return; };
131         
132         while (1) {
133                 entry=readdir(dir);             
134                 if (!entry) { closedir(dir); return; }
135                 
136                 if ((strcmp(entry->d_name, ".")==0) ||
137                         (strcmp(entry->d_name, "..")==0)) continue;
138                 
139                 filename = (char *) malloc (dirlen + strlen(entry->d_name) + 10 + needslash);
140                 
141                 strcpy(filename, "file:");
142                 strcat(filename, dirname);
143                 if (needslash) strcat(filename, "/");
144                 strcat(filename, entry->d_name);
145                 
146                 tX_debug("Found RDF file: %s", filename);
147                 rdf_files.push_back(filename);          
148         }       
149 }
150
151 void LADSPA_Class::insert_class(LADSPA_Class *cls) {
152         std::list <LADSPA_Class *> :: iterator i;
153         
154         for (i=subclasses.begin(); i!=subclasses.end(); i++) {
155                 LADSPA_Class *a_class=(*i);
156                 int res=compare(cls->label, a_class->label);
157                 
158                 if (res < 2) {
159                         subclasses.insert(i, cls);
160                         return;
161                 }
162         }
163         
164         subclasses.push_back(cls);
165 }
166
167 LADSPA_Class :: LADSPA_Class (char *uri) : label(NULL), accept_all(false) {
168 #ifdef USE_LRDF 
169         lrdf_uris *ulist;
170         char *urilabel;
171         int i;
172
173         if (!liblrdf_error) {
174                 urilabel=lrdf_get_label(uri);
175                 
176                 if (urilabel) {
177                         label=strdup(urilabel);
178                 }
179                 
180                 /* Finding subclasses... */
181                 ulist = lrdf_get_subclasses(uri);
182                 
183                 for (i = 0; ulist && i < ulist->count; i++) {
184                         insert_class(new LADSPA_Class(ulist->items[i]));
185                 }
186         
187                 lrdf_free_uris(ulist);
188         
189                 /* Finding instances... */
190                 ulist=lrdf_get_instances(uri);
191                 
192                 for (i = 0; ulist && i < ulist->count; i++) {
193                         registered_ids.push_back(lrdf_get_uid(ulist->items[i]));
194                 }
195         
196                 lrdf_free_uris(ulist);
197         }
198 #endif  
199 }
200
201 LADSPA_Class :: LADSPA_Class() : label("Unclassified"), accept_all(true) {
202 }
203
204 bool LADSPA_Class :: add_plugin(LADSPA_Plugin *plugin) {
205         return root->add_plugin_instance(plugin, MONO);
206 }
207
208 bool LADSPA_Class :: add_stereo_plugin(LADSPA_Stereo_Plugin *plugin) {
209         return root->add_plugin_instance(plugin, STEREO);
210 }
211
212 bool LADSPA_Class :: add_plugin_instance(LADSPA_Plugin *plugin, LADSPA_Plugin_Type type) {
213         if (accept_all) {
214                 insert_plugin(plugin, type);
215                 return true;
216         }
217         
218         long id=plugin->getUniqueID();
219         std::list <long> :: iterator i;
220         
221         /* Is this plugin an instance of this class? */
222         
223         for (i=registered_ids.begin(); i!=registered_ids.end(); i++) {
224                 if ((*i)==id) {
225                         /* The plugin belongs to this class... */
226                         insert_plugin(plugin, type);
227                         return true;
228                 }
229         }
230         
231         /* Try to insert the plugin in subclasses */
232         std::list <LADSPA_Class *> :: iterator cls;
233         
234         for (cls=subclasses.begin(); cls!=subclasses.end(); cls++) {
235                 LADSPA_Class *lrdf_class=(*cls);
236                 
237                 if (lrdf_class->add_plugin_instance(plugin, type)) return true;
238         }
239         
240         /* Giving up... */
241         
242         return false;
243 }
244
245 void LADSPA_Class::insert_plugin(LADSPA_Plugin *plugin, LADSPA_Plugin_Type type) {
246         std::list <LADSPA_Plugin *> :: iterator i;
247         std::list <LADSPA_Plugin *> *list;
248         
249         if (type==MONO) {
250                 list=&plugins;
251         } else {
252                 list=(std::list <LADSPA_Plugin *> *) &stereo_plugins;
253         }
254         
255         for (i=list->begin(); i!=list->end(); i++) {
256                 LADSPA_Plugin *a_plug=(*i);
257                 int res=compare(plugin->getName(), a_plug->getName());
258                 
259                 if (res < 2) {
260                         list->insert(i, plugin);
261                         return;
262                 }
263         }
264         
265         list->push_back(plugin);
266 }
267
268 void LADSPA_Class::list(char *buffer) {
269         strcat(buffer, "\t");
270         
271         printf("%s class %s {\n", buffer, label);
272
273         std::list <LADSPA_Plugin *> :: iterator i;      
274         
275         for (i=plugins.begin(); i!=plugins.end(); i++) {
276                 printf("%s - plugin: %s\n", buffer, (*i)->getName());
277         }
278         
279         std::list <LADSPA_Stereo_Plugin *> :: iterator s;       
280         for (s=stereo_plugins.begin(); s!=stereo_plugins.end(); s++) {
281                 printf("%s - stereo plugin: %s\n", buffer, (*s)->getName());
282         }
283         
284         std::list <LADSPA_Class *> :: iterator c;
285         
286         for (c=subclasses.begin(); c!=subclasses.end(); c++) (*c)->list(buffer);
287         
288         printf("%s}\n", buffer);
289         
290         buffer[strlen(buffer)-1]=0;
291 }
292
293 void LADSPA_Class::dump() {
294         char buffer[256]="";
295         root->list(buffer);
296 }
297
298 static void menu_callback(GtkWidget *wid, LADSPA_Plugin *plugin) {
299         vtt_class *vtt=LADSPA_Class::get_current_vtt();
300         
301         if (vtt) {
302                 vtt->add_effect(plugin);
303         } else {
304                 tX_error("LADSPA_Class::menu_callback() no vtt");
305         }
306 }
307
308 static void stereo_menu_callback(GtkWidget *wid, LADSPA_Stereo_Plugin *plugin) {
309         vtt_class *vtt=LADSPA_Class::get_current_vtt();
310         
311         if (vtt) {
312                 vtt->add_stereo_effect(plugin);
313         } else {
314                 tX_error("LADSPA_Class::menu_callback() no vtt");
315         }
316 }
317
318 int LADSPA_Class :: plugins_in_class(LADSPA_Plugin_Type type) {
319         std::list <LADSPA_Class *> :: iterator cls;
320         std::list <LADSPA_Plugin *> *list;
321         int counter=0;
322         
323         if (type==MONO) {
324                 list=&plugins;
325         } else {
326                 list=(std::list <LADSPA_Plugin *> *) &stereo_plugins;
327         }
328         counter=list->size();
329         
330         for (cls=subclasses.begin(); cls!=subclasses.end(); cls++) {
331                 counter+=(*cls)->plugins_in_class(type);
332         }
333         
334         return counter;
335 }
336
337 GtkWidget * LADSPA_Class :: get_menu(LADSPA_Plugin_Type type) {
338         std::list <LADSPA_Class *> :: iterator cls;
339         GtkWidget *menu=gtk_menu_new();
340         GtkWidget *item;
341         bool need_separator=false;
342         
343         for (cls=subclasses.begin(); cls!=subclasses.end(); cls++) {
344                 LADSPA_Class *c=(*cls);
345                 
346                 if (c->plugins_in_class(type)>0) {
347                         item=gtk_menu_item_new_with_label(c->label);
348                         GtkWidget *submenu=c->get_menu(type);
349                         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
350                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
351                         gtk_widget_show(item);
352                         need_separator=true;
353                 }
354         }
355         
356         std::list <LADSPA_Plugin *> *list;
357         
358         if (type==MONO) {
359                 list=&plugins;
360         } else {
361                 list=(std::list <LADSPA_Plugin *> *) &stereo_plugins;
362         }
363
364         if (need_separator && list->size()) {
365                 item = gtk_menu_item_new();
366                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
367                 gtk_widget_set_sensitive (item, FALSE);
368                 gtk_widget_show (item);
369         }
370         
371         std::list <LADSPA_Plugin *> :: iterator plugin;
372         
373         for (plugin=list->begin(); plugin!=list->end(); plugin++) {
374                 char buffer[512];
375                 LADSPA_Plugin *p=(*plugin);
376                 
377                 sprintf(buffer, "%s - (%s, %li)", p->getName(), p->getLabel(), p->getUniqueID());
378                 item=gtk_menu_item_new_with_label(buffer);
379                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
380                 g_signal_connect(G_OBJECT(item), "activate", (type == MONO) ? G_CALLBACK(menu_callback) : G_CALLBACK(stereo_menu_callback), p);         
381                 gtk_widget_show(item);
382         }
383         
384         return menu;
385 }
386
387 GtkWidget * LADSPA_Class :: get_ladspa_menu() {
388         return root->get_menu(MONO);
389 }
390
391 GtkWidget * LADSPA_Class :: get_stereo_ladspa_menu() {
392         return root->get_menu(STEREO);
393 }