4 * Copyright (C) 2017-2019 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #include "confirmsave.h"
25 #include "bcsignals.h"
31 #include "transportque.inc"
43 // min rate for the GUI
45 // min rate to avoid division by zero
46 #define MIN_RATE2 0.10
48 #define MIN_OFFSET 0.0
49 #define MAX_OFFSET 100.0
51 #define MAX_DEPTH (-INFINITYGAIN)
55 PluginClient* new_plugin(PluginServer *server)
57 return new Tremolo(server);
62 Tremolo::Tremolo(PluginServer *server)
63 : PluginAClient(server)
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; }
83 int Tremolo::process_buffer(int64_t size,
85 int64_t start_position,
88 need_reconfigure |= load_configuration();
89 // printf("Tremolo::process_buffer %d start_position=%ld size=%ld need_reconfigure=%d\n",
95 // reset after configuring
96 if(last_position != start_position ||
106 // waveform is a whole number of samples that repeats
107 if(config.rate < MIN_RATE2)
113 table_size = (int)((double)sample_rate / config.rate);
117 table = new double[table_size];
118 double depth = 1.0 - DB::fromdb(-config.depth);
120 // printf("Tremolo::process_buffer %d table_size=%d depth=%f\n",
125 for(int i = 0; i < table_size; i++)
129 switch(config.waveform)
132 value = (sin((double)i * 2 * M_PI / table_size + 3.0 * M_PI / 2) + 1) / 2;
135 value = (double)(table_size - i) / table_size;
138 value = (double)i / table_size;
141 if(i < table_size / 2)
151 if(i < table_size / 2)
153 value = (double)(i * 2) / table_size;
158 (double)(i - table_size / 2) /
164 value = 1.0 - value * depth;
165 // printf("Tremolo::process_buffer %d i=%d value=%f\n",
173 // compute the phase position from the keyframe position & the phase offset
174 int64_t prev_position = edl_to_local(
176 get_source_position())->position);
178 if(prev_position == 0)
180 prev_position = get_source_start();
183 int64_t starting_offset = (int64_t)(config.offset * table_size / 100);
184 table_offset = (int64_t)(start_position -
188 // printf("Tremolo::process_buffer %d table_offet=%d table_size=%d\n",
193 // printf("Tremolo::process_buffer %d i=%d src=%d dst=%d input_sample=%f\n",
196 // voice->src_channel,
197 // voice->dst_channel,
198 // flanging_table[voice->table_offset].input_sample);
210 double *in = buffer->get_data();
211 double *out = buffer->get_data();
212 for(int j = 0; j < size; j++)
214 out[j] = in[j] * table[table_offset++];
215 table_offset %= table_size;
220 if(get_direction() == PLAY_FORWARD)
222 last_position = start_position + size;
226 last_position = start_position - size;
228 //printf("Tremolo::process_buffer %d\n", __LINE__);
237 NEW_WINDOW_MACRO(Tremolo, TremoloWindow)
240 LOAD_CONFIGURATION_MACRO(Tremolo, TremoloConfig)
243 void Tremolo::save_data(KeyFrame *keyframe)
247 // cause xml file to store data directly in text
248 output.set_shared_output(keyframe->xbuf);
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);
256 output.append_newline();
258 output.terminate_string();
261 void Tremolo::read_data(KeyFrame *keyframe)
264 // cause xml file to read directly from text
265 input.set_shared_input(keyframe->xbuf);
268 result = input.read_tag();
272 if(input.tag.title_is("TREMOLO"))
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);
284 void Tremolo::update_gui()
288 if(load_configuration())
290 thread->window->lock_window("Tremolo::update_gui 1");
291 ((TremoloWindow*)thread->window)->update();
292 thread->window->unlock_window();
305 TremoloConfig::TremoloConfig()
313 int TremoloConfig::equivalent(TremoloConfig &that)
315 return EQUIV(offset, that.offset) &&
316 EQUIV(depth, that.depth) &&
317 EQUIV(rate, that.rate) &&
318 waveform == that.waveform;
321 void TremoloConfig::copy_from(TremoloConfig &that)
323 offset = that.offset;
326 waveform = that.waveform;
329 void TremoloConfig::interpolate(TremoloConfig &prev,
333 int64_t current_frame)
338 void TremoloConfig::boundaries()
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);
356 #define WINDOW_W xS(400)
357 #define WINDOW_H yS(140)
359 TremoloWindow::TremoloWindow(Tremolo *plugin)
360 : PluginClientWindow(plugin,
367 this->plugin = plugin;
370 TremoloWindow::~TremoloWindow()
378 void TremoloWindow::create_objects()
380 int margin = plugin->get_theme()->widget_border + xS(4);
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);
389 offset = new PluginParam(plugin,
397 &plugin->config.offset, // output_f
402 offset->set_precision(3);
403 offset->initialize();
407 depth = new PluginParam(plugin,
415 &plugin->config.depth, // output_f
420 depth->set_precision(3);
426 rate = new PluginParam(plugin,
434 &plugin->config.rate, // output_f
439 rate->set_precision(3);
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,
449 waveform_to_text(string, plugin->config.waveform)));
450 waveform->create_objects();
455 void TremoloWindow::update()
457 offset->update(0, 0);
460 char string[BCTEXTLEN];
461 waveform->set_text(waveform_to_text(string, plugin->config.waveform));
466 char* TremoloWindow::waveform_to_text(char *text, int waveform)
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;
480 void TremoloWindow::param_updated()
488 TremoloWaveForm::TremoloWaveForm(Tremolo *plugin, int x, int y, char *text)
489 : BC_PopupMenu(x, y, xS(120), text)
491 this->plugin = plugin;
495 TremoloWaveForm::~TremoloWaveForm()
500 void TremoloWaveForm::create_objects()
502 char string[BCTEXTLEN];
503 add_item(new TremoloWaveFormItem(plugin,
504 TremoloWindow::waveform_to_text(string, SINE),
506 add_item(new TremoloWaveFormItem(plugin,
507 TremoloWindow::waveform_to_text(string, SAWTOOTH),
509 add_item(new TremoloWaveFormItem(plugin,
510 TremoloWindow::waveform_to_text(string, SAWTOOTH2),
512 add_item(new TremoloWaveFormItem(plugin,
513 TremoloWindow::waveform_to_text(string, SQUARE),
515 add_item(new TremoloWaveFormItem(plugin,
516 TremoloWindow::waveform_to_text(string, TRIANGLE),
522 TremoloWaveFormItem::TremoloWaveFormItem(Tremolo *plugin, char *text, int value)
525 this->plugin = plugin;
529 TremoloWaveFormItem::~TremoloWaveFormItem()
533 int TremoloWaveFormItem::handle_event()
535 plugin->config.waveform = value;
536 get_popup_menu()->set_text(get_text());
537 plugin->send_configure_change();