Adding stereo plugins, fixed LADSPA menus, ignoring NON-RT plugins - Alex
[terminatorX.git] / src / tX_vttfx.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_vttfx.cc
20  
21     Description: This handles the effects in the per vtt fx chain. Supports the
22                  buitlin echo/lowpass effects and ladspa plugins.
23 */
24
25 #include "tX_vttfx.h"
26 #include <stdio.h>
27 #include <glib.h>
28 #include "tX_vtt.h"
29 #define myvtt ((vtt_class *) vtt)
30 #include "tX_global.h"
31
32 float ladspa_dummy_output_port;
33
34 void vtt_fx :: reconnect_buffer()
35 {
36         /* NOP */
37 }
38
39 vtt_fx :: ~vtt_fx() {}
40 void vtt_fx::toggle_drywet() {}
41 bool vtt_fx::is_stereo() { return false; }
42 tX_drywet_type vtt_fx::has_drywet_feature() { return NOT_DRYWET_CAPABLE; }
43
44 /******************* builtin fx ***/
45
46 /* lowpass */ 
47 void vtt_fx_lp :: activate() { myvtt->lp_reset(); }
48 void vtt_fx_lp :: deactivate() { /* NOP */ }
49 void vtt_fx_lp :: run() { myvtt->render_lp(); }
50 int vtt_fx_lp :: isEnabled() { return myvtt->lp_enable; }
51
52 void vtt_fx_lp :: save (FILE *rc, gzFile rz, char *indent) { 
53         tX_store("%s<cutoff/>\n", indent);
54 }
55
56
57 const char *vtt_fx_lp :: get_info_string()
58 {
59         return "TerminatorX built-in resonant lowpass filter.";
60 }
61
62
63 /* echo */
64 void vtt_fx_ec :: activate() { /* NOP */ }
65 void vtt_fx_ec :: deactivate() { myvtt->ec_clear_buffer(); }
66 void vtt_fx_ec :: run() { myvtt->render_ec(); }
67 int vtt_fx_ec :: isEnabled() { return myvtt->ec_enable; }
68
69 void vtt_fx_ec :: save (FILE *rc, gzFile rz, char *indent) { 
70         tX_store("%s<lowpass/>\n", indent);     
71 }
72
73 const char *vtt_fx_ec :: get_info_string()
74 {
75         return "TerminatorX built-in echo effect.";
76 }
77
78 /******************** LADSPA fx ***/
79 /* short cut "cpd" macro to current port descriptor */
80
81 #define cpd plugin->getDescriptor()->PortDescriptors[port]
82 #define cpn plugin->getDescriptor()->PortNames[port]
83 #define cph plugin->getDescriptor()->PortRangeHints[port]
84
85 void vtt_fx_ladspa :: reconnect_buffer()
86 {
87         plugin->getDescriptor()->connect_port(instance, input_port, myvtt->output_buffer);
88         if (wet_buffer) {
89                 plugin->getDescriptor()->connect_port(instance, output_port, wet_buffer);       
90         } else {
91                 plugin->getDescriptor()->connect_port(instance, output_port, myvtt->output_buffer);     
92         }
93 }
94
95 static void wrapstr(char *str)
96 {
97         char temp[256]="";
98         char target[2048]="";
99         char *token;
100
101         token=strtok(str, " "); 
102         
103         while(token)
104         {
105                 if (strlen(token)+strlen(temp)<10)
106                 {
107                         if (strlen(temp)) strcat(temp, " ");
108                         strcat(temp, token);
109                 }
110                 else
111                 {
112                         if (strlen(temp))
113                         {
114                                 if(strlen(target)) strcat(target, "\n");
115                                 if(strlen(temp)>10)
116                                 {
117                                         temp[8]='.';
118                                         temp[9]='.';
119                                         temp[10]='.';
120                                         temp[11]=0;
121                                 }
122                                 strcat(target, temp);
123                                 strcpy(temp,token);
124                         }
125                 }
126                 token=strtok(NULL, " ");
127         }
128
129        if (strlen(temp))
130        {
131                if(strlen(target)) strcat(target, "\n");
132                strcat(target, temp);
133        }
134
135         strcpy(str, target);
136  }
137
138 vtt_fx_ladspa :: vtt_fx_ladspa(LADSPA_Plugin *p, void *v)
139 {
140         int port;
141         float min, max;
142         char buffer[2048];
143         char buffer2[2048];
144         
145         tX_seqpar_vttfx *sp;
146
147         plugin=p; vtt=v;
148         
149         instance=(LADSPA_Handle *) plugin->getDescriptor()->instantiate(plugin->getDescriptor(), 44100);
150         
151         if (!instance)
152         {
153                 fprintf (stderr, "tX: Fatal Error: failed to instantiate plugin \"%s\".\n", plugin->getDescriptor()->Name);
154                 /* How to handle this ? */
155         }
156         
157         sp = sp_enable = new tX_seqpar_vttfx_bool();
158         sp->set_mapping_parameters(1, 0, 0, 0);
159         sprintf(buffer, "%s: Enable", plugin->getName());
160         sp->set_name(buffer, "Enable");
161         sp->set_vtt(vtt);
162         controls.push_back(sp); 
163
164         sp_wet=NULL;
165         wet_buffer=NULL;
166         
167         /* connecting ports */
168         for (port=0; port < plugin->getPortCount(); port++) {
169                 if (LADSPA_IS_PORT_AUDIO(cpd)) {
170                         if (LADSPA_IS_PORT_INPUT(cpd)) input_port=port;
171                         else if (LADSPA_IS_PORT_OUTPUT(cpd)) output_port=port;
172                 } else if ((LADSPA_IS_PORT_CONTROL(cpd)) && (LADSPA_IS_PORT_INPUT(cpd))) {
173                         min=-22100;
174                         max=+22100;
175                         
176                         if (LADSPA_IS_HINT_BOUNDED_BELOW(cph.HintDescriptor)) min=cph.LowerBound;
177                         if (LADSPA_IS_HINT_BOUNDED_ABOVE(cph.HintDescriptor)) max=cph.UpperBound;
178                         
179                         if (LADSPA_IS_HINT_SAMPLE_RATE(cph.HintDescriptor)) {
180                                 min*=44100; max*=44100;
181                         }
182                         
183                         if (LADSPA_IS_HINT_TOGGLED(cph.HintDescriptor)) {
184                                 sp=new tX_seqpar_vttfx_bool();
185                                 sp->set_mapping_parameters(max, min, 0, 0);
186                         } else if (LADSPA_IS_HINT_INTEGER(cph.HintDescriptor)) {
187                                 sp=new tX_seqpar_vttfx_int();
188                                 sp->set_mapping_parameters(max, min, 0, 0);
189                         } else {
190                                 sp=new tX_seqpar_vttfx_float();
191                                 sp->set_mapping_parameters(max, min, (max-min)/100.0, 1);
192                         }
193                         
194                         sprintf(buffer, "%s: %s", plugin->getLabel(), cpn);
195                         strcpy(buffer2, cpn);
196                         wrapstr(buffer2);
197                         
198                         sp->set_name(buffer, buffer2);
199                         sp->set_vtt(vtt);
200                         plugin->getDescriptor()->connect_port(instance, port, sp->get_value_ptr());
201                         controls.push_back(sp);
202                 } else if ((LADSPA_IS_PORT_CONTROL(cpd)) && (LADSPA_IS_PORT_OUTPUT(cpd))) {
203                         plugin->getDescriptor()->connect_port(instance, port, &ladspa_dummy_output_port);
204                 }
205         }
206 }
207
208 void vtt_fx_ladspa :: realloc_drywet() 
209 {
210         free_drywet();
211         wet_buffer=(f_prec *) malloc(sizeof(float)*vtt_class::samples_in_mix_buffer);
212 }
213
214 void vtt_fx_ladspa :: free_drywet()
215 {
216         if (wet_buffer) {
217                 free(wet_buffer);
218                 wet_buffer=NULL;
219         }
220 }
221
222 void vtt_fx_ladspa :: activate()
223 {
224         if (sp_wet) {
225                 realloc_drywet();
226         }
227         reconnect_buffer(); // we always have to reconnect...
228         if (plugin->getDescriptor()->activate) plugin->getDescriptor()->activate(instance);
229 }
230
231 void vtt_fx_ladspa :: deactivate()
232 {
233         if (plugin->getDescriptor()->deactivate) plugin->getDescriptor()->deactivate(instance);
234         
235         free_drywet();
236 }
237
238 void vtt_fx_ladspa :: run()
239 {
240         plugin->getDescriptor()->run(instance, (vtt_class::samples_in_mix_buffer)>>1);
241         
242         if (wet_buffer) {
243                 f_prec wet=sp_wet->get_value();
244                 f_prec dry=1.0-wet;
245                 
246                 for (int sample=0; sample < (vtt_class::samples_in_mix_buffer)>>1; sample++) {
247                         myvtt->output_buffer[sample]=dry*myvtt->output_buffer[sample]+wet*wet_buffer[sample];
248                 }
249         }
250 }
251
252 int vtt_fx_ladspa :: isEnabled()
253 {
254         return (int) sp_enable->get_value();
255 }
256
257 const char *vtt_fx_ladspa :: get_info_string()
258 {
259         return plugin->get_info_string();
260 }
261
262 vtt_fx_ladspa :: ~vtt_fx_ladspa()
263 {
264         list <tX_seqpar_vttfx *> :: iterator sp;
265         
266         while (controls.size()) {
267                 sp=controls.begin();
268                 controls.remove((*sp));
269                 
270                 delete (*sp);
271         }               
272         plugin->getDescriptor()->cleanup(instance);
273         
274         if (wet_buffer) free(wet_buffer);
275         delete panel;
276 }
277
278
279 void vtt_fx_ladspa :: save (FILE *rc, gzFile rz, char *indent) {
280         long ID=plugin->getUniqueID();
281         list <tX_seqpar_vttfx *> :: iterator sp;
282         
283         tX_store("%s<ladspa_plugin>\n", indent);
284         strcat (indent, "\t");
285         
286         store_int("ladspa_id", ID);
287         store_bool("has_drywet", (sp_wet!=NULL));
288         
289         for (sp=controls.begin(); sp!=controls.end(); sp++) {
290                 store_float_sp("param", (*sp)->get_value(), (*(*sp)));
291         }
292         
293         store_bool("panel_hidden", panel->is_hidden());
294         
295         indent[strlen(indent)-1]=0;
296         tX_store("%s</ladspa_plugin>\n", indent);
297 }
298
299 void vtt_fx_ladspa :: load(xmlDocPtr doc, xmlNodePtr node) {
300         int dummy;
301         bool hidden=false;
302         list <tX_seqpar_vttfx *> :: iterator sp=controls.begin();
303         int elementFound;
304         double val;
305         
306         for (xmlNodePtr cur=node->xmlChildrenNode; cur!=NULL; cur=cur->next) {
307                 if (cur->type == XML_ELEMENT_NODE) {
308                         bool drywet=false;
309                         elementFound=0;
310                         
311                         restore_int("ladspa_id", dummy);
312                         restore_bool("panel_hidden", hidden);
313                         restore_bool("has_drywet", drywet);
314                         if (drywet) add_drywet();
315                         
316                         if ((!elementFound) && (xmlStrcmp(cur->name, (xmlChar *) "param")==0)) {
317                                 val=0;
318                                 elementFound=0;
319                                 double dvalue;
320                         
321                                 if (sp==controls.end()) {
322                                         tX_warning("found unexpected parameters for ladspa plugin [%i].", dummy);
323                                 } else {
324                                         restore_float_id("param", val, (*(*sp)), (*sp)->do_exec(val));                                  
325                                         (*sp)->do_update_graphics();
326                                         sp++;
327                                 }
328                         }
329                         
330                         if (!elementFound) {
331                                 tX_warning("unhandled ladspa_plugin element %s.", cur->name);
332                         }
333                 }
334         }
335         
336         panel->hide(hidden);
337 }
338
339 void vtt_fx_ladspa :: toggle_drywet() {
340         if (sp_wet) {
341                 remove_drywet();
342         } else {
343                 add_drywet();
344         }
345 }
346
347 void vtt_fx_ladspa :: add_drywet() {
348         char buffer[1024];
349         
350         sp_wet=new tX_seqpar_vttfx_float();
351         sp_wet->set_mapping_parameters(1.0, 0, 0.01, 1);
352         sprintf(buffer, "%s: Dry/Wet", plugin->getLabel());
353         sp_wet->set_name(buffer, "Dry/Wet");
354         sp_wet->set_vtt(vtt);
355         panel->add_client_widget(sp_wet->get_widget());
356         
357         pthread_mutex_lock(&vtt_class::render_lock);
358         controls.push_back(sp_wet);
359         deactivate();
360         activate();
361         pthread_mutex_unlock(&vtt_class::render_lock);
362 }
363
364 void vtt_fx_ladspa :: remove_drywet() {
365         pthread_mutex_lock(&vtt_class::render_lock);
366         deactivate();
367
368         controls.remove(sp_wet);
369         delete sp_wet;
370         sp_wet=NULL;
371         
372         activate();
373         pthread_mutex_unlock(&vtt_class::render_lock);
374 }
375
376 tX_drywet_type vtt_fx_ladspa::has_drywet_feature()
377
378         if (sp_wet) return DRYWET_ACTIVE;
379         else return DRYWET_AVAILABLE;
380 }
381
382 /****** STEREO plugins **********/
383
384 vtt_fx_stereo_ladspa::vtt_fx_stereo_ladspa(LADSPA_Stereo_Plugin *p, void *v):vtt_fx_ladspa(p,v)
385 {
386         input_port=input2_port=-1; 
387         output_port=output2_port=-1; 
388         wet_buffer2=NULL;
389         
390         for (int port=0; port < plugin->getPortCount(); port++) {
391                 if (LADSPA_IS_PORT_AUDIO(cpd)) {
392                         if (LADSPA_IS_PORT_INPUT(cpd)) {
393                                 if (input_port<0) { input_port=port; }
394                                 else if (input2_port<0) { input2_port=port; }
395                                 else { tX_error("Extra input port for plugin %s?", plugin->getName()); }
396                         } else if (LADSPA_IS_PORT_OUTPUT(cpd)) {
397                                 if (output_port<0) { output_port=port; }
398                                 else if (output2_port<0) { output2_port=port; }
399                                 else { tX_error("Extra output port for plugin %s?", plugin->getName()); }
400                         }
401                 }
402         }
403 };
404
405 void vtt_fx_stereo_ladspa :: reconnect_buffer()
406 {
407         plugin->getDescriptor()->connect_port(instance, input_port, myvtt->output_buffer);
408         plugin->getDescriptor()->connect_port(instance, input2_port, myvtt->output_buffer2);
409
410         if (wet_buffer) {
411                 plugin->getDescriptor()->connect_port(instance, output_port, wet_buffer);       
412                 plugin->getDescriptor()->connect_port(instance, output2_port, wet_buffer2);
413         } else {
414                 plugin->getDescriptor()->connect_port(instance, output_port, myvtt->output_buffer);     
415                 plugin->getDescriptor()->connect_port(instance, output2_port, myvtt->output_buffer2);   
416         }
417 }
418
419 void vtt_fx_stereo_ladspa :: realloc_drywet() 
420 {
421         free_drywet();
422         wet_buffer=(f_prec *) malloc(sizeof(float)*vtt_class::samples_in_mix_buffer);
423         wet_buffer2=(f_prec *) malloc(sizeof(float)*vtt_class::samples_in_mix_buffer);
424 }
425
426 void vtt_fx_stereo_ladspa :: free_drywet()
427 {
428         if (wet_buffer) {
429                 free(wet_buffer);
430                 wet_buffer=NULL;
431         }
432         if (wet_buffer2) {
433                 free(wet_buffer2);
434                 wet_buffer2=NULL;
435         }
436 }
437
438 void vtt_fx_stereo_ladspa :: run()
439 {
440         plugin->getDescriptor()->run(instance, (vtt_class::samples_in_mix_buffer)>>1);
441         
442         if (wet_buffer) {
443                 f_prec wet=sp_wet->get_value();
444                 f_prec dry=1.0-wet;
445                 
446                 for (int sample=0; sample < (vtt_class::samples_in_mix_buffer)>>1; sample++) {
447                         myvtt->output_buffer[sample]=dry*myvtt->output_buffer[sample]+wet*wet_buffer[sample];
448                         myvtt->output_buffer2[sample]=dry*myvtt->output_buffer2[sample]+wet*wet_buffer2[sample];
449                 }
450         }
451 }
452
453 vtt_fx_stereo_ladspa :: ~vtt_fx_stereo_ladspa()
454 {
455         // rest should be handeld in parent's destrucutor.
456         if (wet_buffer2) free(wet_buffer2);
457 }
458
459 bool vtt_fx_stereo_ladspa::is_stereo() { return true; }