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