add sliders to cwdw proj/cam tools, edit id tweaks, deltrk shortcut tweak
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / tremolo / tremolo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2017-2019 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program 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  * This program 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 this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include "clip.h"
23 #include "confirmsave.h"
24 #include "bchash.h"
25 #include "bcsignals.h"
26 #include "errorbox.h"
27 #include "filexml.h"
28 #include "language.h"
29 #include "samples.h"
30 #include "theme.h"
31 #include "transportque.inc"
32 #include "tremolo.h"
33 #include "units.h"
34
35 #include "vframe.h"
36
37 #include <math.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41
42
43 // min rate for the GUI
44 #define MIN_RATE 0.0
45 // min rate to avoid division by zero
46 #define MIN_RATE2 0.10
47 #define MAX_RATE 10.0
48 #define MIN_OFFSET 0.0
49 #define MAX_OFFSET 100.0
50 #define MIN_DEPTH 0.0
51 #define MAX_DEPTH (-INFINITYGAIN)
52
53
54
55 PluginClient* new_plugin(PluginServer *server)
56 {
57         return new Tremolo(server);
58 }
59
60
61
62 Tremolo::Tremolo(PluginServer *server)
63  : PluginAClient(server)
64 {
65         need_reconfigure = 1;
66     table = 0;
67     table_size = 0;
68     table_offset = 0;
69     last_position = 0;
70 }
71
72 Tremolo::~Tremolo()
73 {
74     delete [] table;
75 }
76
77 const char* Tremolo::plugin_title() { return N_("Tremolo"); }
78 int Tremolo::is_realtime() { return 1; }
79 int Tremolo::is_multichannel() { return 0; }
80 int Tremolo::is_synthesis() { return 0; }
81
82
83 int Tremolo::process_buffer(int64_t size, 
84         Samples *buffer, 
85         int64_t start_position,
86         int sample_rate)
87 {
88     need_reconfigure |= load_configuration();
89 // printf("Tremolo::process_buffer %d start_position=%ld size=%ld need_reconfigure=%d\n",
90 // __LINE__,
91 // start_position, 
92 // size,
93 // need_reconfigure);
94
95 // reset after configuring
96     if(last_position != start_position ||
97         need_reconfigure)
98     {
99         need_reconfigure = 0;
100
101         if(table)
102         {
103             delete [] table;
104         }
105
106 // waveform is a whole number of samples that repeats
107         if(config.rate < MIN_RATE2)
108         {
109             table_size = 256;
110         }
111         else
112         {
113             table_size = (int)((double)sample_rate / config.rate);
114         }
115
116
117         table = new double[table_size];
118         double depth = 1.0 - DB::fromdb(-config.depth);
119
120 // printf("Tremolo::process_buffer %d table_size=%d depth=%f\n", 
121 // __LINE__, 
122 // table_size,
123 // depth);
124
125         for(int i = 0; i < table_size; i++)
126         {
127             double value = 0;
128             
129             switch(config.waveform)
130             {
131                 case SINE:
132                     value = (sin((double)i * 2 * M_PI / table_size + 3.0 * M_PI / 2) + 1) / 2;
133                     break;
134                 case SAWTOOTH:
135                     value = (double)(table_size - i) / table_size;
136                     break;
137                 case SAWTOOTH2:
138                     value = (double)i / table_size;
139                     break;
140                 case SQUARE:
141                     if(i < table_size / 2)
142                     {
143                         value = 0;
144                     }
145                     else
146                     {
147                         value = 1;
148                     }
149                     break;
150                 case TRIANGLE:
151                     if(i < table_size / 2)
152                     {
153                         value = (double)(i * 2) / table_size;
154                     }
155                     else
156                     {
157                         value = 1.0 - 
158                             (double)(i - table_size / 2) / 
159                             (table_size / 2);
160                     }
161                     break;
162             }
163 // value is -1 ... 0
164             value = 1.0 - value * depth;
165 // printf("Tremolo::process_buffer %d i=%d value=%f\n", 
166 // __LINE__, 
167 // i, 
168 // value);
169             table[i] = value;
170         }
171
172
173 // compute the phase position from the keyframe position & the phase offset
174                 int64_t prev_position = edl_to_local(
175                         get_prev_keyframe(
176                                 get_source_position())->position);
177
178                 if(prev_position == 0)
179                 {
180                         prev_position = get_source_start();
181                 }
182
183         int64_t starting_offset = (int64_t)(config.offset * table_size / 100);
184         table_offset = (int64_t)(start_position - 
185             prev_position +
186             starting_offset) %
187             table_size;
188 // printf("Tremolo::process_buffer %d table_offet=%d table_size=%d\n",
189 // __LINE__,
190 // table_offset,
191 // table_size);
192
193 // printf("Tremolo::process_buffer %d i=%d src=%d dst=%d input_sample=%f\n",
194 // __LINE__,
195 // i,
196 // voice->src_channel,
197 // voice->dst_channel,
198 // flanging_table[voice->table_offset].input_sample);
199     }
200
201
202 // read the input
203         read_samples(buffer,
204                 0,
205                 sample_rate,
206                 start_position,
207                 size);
208
209 // input signal
210     double *in = buffer->get_data();
211     double *out = buffer->get_data();
212     for(int j = 0; j < size; j++)
213     {
214         out[j] = in[j] * table[table_offset++];
215         table_offset %= table_size;
216     }
217
218
219
220     if(get_direction() == PLAY_FORWARD)
221     {
222         last_position = start_position + size;
223     }
224     else
225     {
226         last_position = start_position - size;
227     }
228 //printf("Tremolo::process_buffer %d\n", __LINE__);
229
230     
231
232     return 0;
233 }
234
235
236
237 NEW_WINDOW_MACRO(Tremolo, TremoloWindow)
238
239
240 LOAD_CONFIGURATION_MACRO(Tremolo, TremoloConfig)
241
242
243 void Tremolo::save_data(KeyFrame *keyframe)
244 {
245         FileXML output;
246
247 // cause xml file to store data directly in text
248         output.set_shared_output(keyframe->xbuf);
249
250         output.tag.set_title("TREMOLO");
251         output.tag.set_property("OFFSET", config.offset);
252         output.tag.set_property("DEPTH", config.depth);
253         output.tag.set_property("RATE", config.rate);
254         output.tag.set_property("WAVEFORM", config.waveform);
255         output.append_tag();
256         output.append_newline();
257
258         output.terminate_string();
259 }
260
261 void Tremolo::read_data(KeyFrame *keyframe)
262 {
263         FileXML input;
264 // cause xml file to read directly from text
265         input.set_shared_input(keyframe->xbuf);
266         int result = 0;
267
268         result = input.read_tag();
269
270         if(!result)
271         {
272                 if(input.tag.title_is("TREMOLO"))
273                 {
274                         config.offset = input.tag.get_property("OFFSET", config.offset);
275                         config.depth = input.tag.get_property("DEPTH", config.depth);
276                         config.rate = input.tag.get_property("RATE", config.rate);
277                         config.waveform = input.tag.get_property("WAVEFORM", config.waveform);
278                 }
279         }
280
281         config.boundaries();
282 }
283
284 void Tremolo::update_gui()
285 {
286         if(thread)
287         {
288                 if(load_configuration())
289                 {
290                         thread->window->lock_window("Tremolo::update_gui 1");
291             ((TremoloWindow*)thread->window)->update();
292                         thread->window->unlock_window();
293         }
294         }
295 }
296
297
298
299
300
301
302
303
304
305 TremoloConfig::TremoloConfig()
306 {
307         offset = 0.00;
308         depth = 10.0;
309         rate = 0.20;
310         waveform = SINE;
311 }
312
313 int TremoloConfig::equivalent(TremoloConfig &that)
314 {
315         return EQUIV(offset, that.offset) &&
316                 EQUIV(depth, that.depth) &&
317                 EQUIV(rate, that.rate) &&
318                 waveform == that.waveform;
319 }
320
321 void TremoloConfig::copy_from(TremoloConfig &that)
322 {
323         offset = that.offset;
324         depth = that.depth;
325         rate = that.rate;
326         waveform = that.waveform;
327 }
328
329 void TremoloConfig::interpolate(TremoloConfig &prev, 
330         TremoloConfig &next, 
331         int64_t prev_frame, 
332         int64_t next_frame, 
333         int64_t current_frame)
334 {
335         copy_from(prev);
336 }
337
338 void TremoloConfig::boundaries()
339 {
340         CLAMP(offset, MIN_OFFSET, MAX_OFFSET);
341         CLAMP(depth, MIN_DEPTH, MAX_DEPTH);
342         CLAMP(rate, MIN_RATE, MAX_RATE);
343         CLAMP(waveform, 0, TOTAL_WAVEFORMS - 1);
344 }
345
346
347
348
349
350
351
352
353
354
355
356 #define WINDOW_W xS(400)
357 #define WINDOW_H yS(140)
358
359 TremoloWindow::TremoloWindow(Tremolo *plugin)
360  : PluginClientWindow(plugin, 
361         WINDOW_W, 
362         WINDOW_H, 
363         WINDOW_W, 
364         WINDOW_H, 
365         0)
366
367         this->plugin = plugin; 
368 }
369
370 TremoloWindow::~TremoloWindow()
371 {
372     delete offset;
373     delete depth;
374     delete rate;
375     delete waveform;
376 }
377
378 void TremoloWindow::create_objects()
379 {
380         int margin = plugin->get_theme()->widget_border + xS(4);
381     int x1 = margin;
382         int x2 = xS(200), y = margin - xS(4);
383     int x3 = x2 + BC_Pot::calculate_w() + margin;
384     int x4 = x3 + BC_Pot::calculate_w() + margin;
385     int text_w = get_w() - margin - x4;
386     int height = BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin - xS(4);
387
388
389     offset = new PluginParam(plugin,
390         this,
391         x1, 
392         x3,
393         x4,
394         y, 
395         text_w,
396         0,  // output_i
397         &plugin->config.offset, // output_f
398         0, // output_q
399         "Phase offset (%):",
400         MIN_OFFSET, // min
401         MAX_OFFSET); // max
402     offset->set_precision(3);
403     offset->initialize();
404     y += height;
405
406
407     depth = new PluginParam(plugin,
408         this,
409         x1, 
410         x2,
411         x4,
412         y, 
413         text_w,
414         0,  // output_i
415         &plugin->config.depth, // output_f
416         0, // output_q
417         "Depth (dB):",
418         MIN_DEPTH, // min
419         MAX_DEPTH); // max
420     depth->set_precision(3);
421     depth->initialize();
422     y += height;
423
424
425
426     rate = new PluginParam(plugin,
427         this,
428         x1, 
429         x3,
430         x4,
431         y, 
432         text_w,
433         0,  // output_i
434         &plugin->config.rate, // output_f
435         0, // output_q
436         "Rate (Hz):",
437         MIN_RATE, // min
438         MAX_RATE); // max
439     rate->set_precision(3);
440     rate->initialize();
441     y += height;
442
443     char string[BCTEXTLEN];
444     int y2 = y + BC_Pot::calculate_h() / 2;
445     add_subwindow(new BC_Title(x1, y2, _("Waveform:")));
446     add_tool(waveform = new TremoloWaveForm(plugin,
447         x2, 
448         y2,
449         waveform_to_text(string, plugin->config.waveform)));
450     waveform->create_objects();
451
452         show_window();
453 }
454
455 void TremoloWindow::update()
456 {
457     offset->update(0, 0);
458     depth->update(0, 0);
459     rate->update(0, 0);
460     char string[BCTEXTLEN];
461         waveform->set_text(waveform_to_text(string, plugin->config.waveform));
462 }
463
464
465
466 char* TremoloWindow::waveform_to_text(char *text, int waveform)
467 {
468         switch(waveform)
469         {
470                 case SINE:            sprintf(text, _("Sine"));           break;
471                 case SAWTOOTH:        sprintf(text, _("Sawtooth"));       break;
472                 case SAWTOOTH2:        sprintf(text, _("Rev Sawtooth"));       break;
473                 case SQUARE:          sprintf(text, _("Square"));         break;
474                 case TRIANGLE:        sprintf(text, _("Triangle"));       break;
475         }
476         return text;
477 }
478
479
480 void TremoloWindow::param_updated()
481 {
482 }
483
484
485
486
487
488 TremoloWaveForm::TremoloWaveForm(Tremolo *plugin, int x, int y, char *text)
489  : BC_PopupMenu(x, y, xS(120), text)
490 {
491     this->plugin = plugin;
492 }
493
494
495 TremoloWaveForm::~TremoloWaveForm()
496 {
497 }
498
499
500 void TremoloWaveForm::create_objects()
501 {
502     char string[BCTEXTLEN];
503         add_item(new TremoloWaveFormItem(plugin, 
504         TremoloWindow::waveform_to_text(string, SINE), 
505         SINE));
506         add_item(new TremoloWaveFormItem(plugin, 
507         TremoloWindow::waveform_to_text(string, SAWTOOTH), 
508         SAWTOOTH));
509         add_item(new TremoloWaveFormItem(plugin, 
510         TremoloWindow::waveform_to_text(string, SAWTOOTH2), 
511         SAWTOOTH2));
512         add_item(new TremoloWaveFormItem(plugin, 
513         TremoloWindow::waveform_to_text(string, SQUARE), 
514         SQUARE));
515         add_item(new TremoloWaveFormItem(plugin, 
516         TremoloWindow::waveform_to_text(string, TRIANGLE), 
517         TRIANGLE));
518 }
519
520
521
522 TremoloWaveFormItem::TremoloWaveFormItem(Tremolo *plugin, char *text, int value)
523  : BC_MenuItem(text)
524 {
525     this->plugin = plugin;
526     this->value = value;
527 }
528
529 TremoloWaveFormItem::~TremoloWaveFormItem()
530 {
531 }
532
533 int TremoloWaveFormItem::handle_event()
534 {
535     plugin->config.waveform = value;
536     get_popup_menu()->set_text(get_text());
537     plugin->send_configure_change();
538     return 1;
539 }