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