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
24 #include "confirmsave.h"
26 #include "bcsignals.h"
32 #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 100.0
57 PluginClient* new_plugin(PluginServer *server)
59 return new Chorus(server);
64 Chorus::Chorus(PluginServer *server)
65 : PluginAClient(server)
83 for(int i = 0; i < PluginClient::total_in_buffers; i++)
92 for(int i = 0; i < PluginClient::total_in_buffers; i++)
94 delete [] history_buffer[i];
96 delete [] history_buffer;
100 delete [] flanging_table;
103 const char* Chorus::plugin_title() { return N_("Chorus"); }
104 int Chorus::is_realtime() { return 1; }
105 int Chorus::is_multichannel() { return 1; }
106 int Chorus::is_synthesis() { return 1; }
109 int Chorus::process_buffer(int64_t size,
111 int64_t start_position,
114 need_reconfigure |= load_configuration();
115 // printf("Chorus::process_buffer %d start_position=%ld size=%ld need_reconfigure=%d buffer_offset=%d\n",
120 // buffer[0]->get_offset());
125 dsp_in = new double*[PluginClient::total_in_buffers];
126 for(int i = 0; i < PluginClient::total_in_buffers; i++)
132 // reset after seeking & configuring
133 if(last_position != start_position || need_reconfigure)
135 need_reconfigure = 0;
139 delete [] flanging_table;
142 // flanging waveform is a whole number of samples that repeats
143 if(config.rate < MIN_RATE2)
149 table_size = (int)((double)sample_rate / config.rate);
157 // printf("Chorus::process_buffer %d table_size=%d\n",
161 flanging_table = new flange_sample_t[table_size];
162 double depth_samples = config.depth *
164 double half_depth = depth_samples / 2;
165 int half_table = table_size / 2;
166 int quarter_table = table_size / 4;
167 // slow down over time to read from history buffer
168 // double ratio = (double)depth_samples /
170 double coef = (double)half_depth /
171 pow(quarter_table, 2);
173 // printf("Chorus::process_buffer %d %f %f\n",
176 // sample_rate / 2 - depth_samples);
177 for(int i = 0; i <= quarter_table; i++)
179 // double input_sample = -i * ratio;
180 double input_sample = -(coef * pow(i, 2));
181 // printf("Chorus::process_buffer %d i=%d input_sample=%f\n",
185 flanging_table[i].input_sample = input_sample;
188 for(int i = 0; i <= quarter_table; i++)
190 double input_sample = -depth_samples +
191 (coef * pow(quarter_table - i, 2));
192 // printf("Chorus::process_buffer %d i=%d input_sample=%f\n",
194 // quarter_table + i,
196 flanging_table[quarter_table + i].input_sample = input_sample;
199 // rounding error may drop quarter_table * 2
200 flanging_table[half_table].input_sample = -depth_samples;
202 for(int i = 1; i < half_table; i++)
204 flanging_table[half_table + i].input_sample =
205 flanging_table[half_table - i].input_sample;
206 // printf("Chorus::process_buffer %d i=%d input_sample=%f\n",
214 // for(int i = 0; i < table_size; i++)
216 // printf("Chorus::process_buffer %d i=%d input_sample=%f\n",
219 // flanging_table[i].input_sample);
224 history_buffer = new double*[PluginClient::total_in_buffers];
225 for(int i = 0; i < PluginClient::total_in_buffers; i++)
227 history_buffer[i] = 0;
232 // compute the phase position from the keyframe position & the phase offset
233 int64_t prev_position = edl_to_local(
235 get_source_position())->position);
237 if(prev_position == 0)
239 prev_position = get_source_start();
250 voices = new Voice[total_voices()];
253 for(int i = 0; i < total_voices(); i++)
255 Voice *voice = &voices[i];
256 voice->src_channel = i / config.voices;
257 voice->dst_channel = i % PluginClient::total_in_buffers;
259 // randomize the starting phase
260 voice->table_offset = (int64_t)(start_position -
262 i * (table_size / 2) / total_voices()) % (table_size / 2);
263 // (rand() % (table_size / 2))) % (table_size / 2);
264 // printf("Chorus::process_buffer %d i=%d src=%d dst=%d input_sample=%f\n",
267 // voice->src_channel,
268 // voice->dst_channel,
269 // flanging_table[voice->table_offset].input_sample);
273 int starting_offset = (int)(config.offset * sample_rate / 1000);
274 reallocate_dsp(size);
275 // reallocate_history(starting_offset + depth_offset + 1);
276 // always use the maximum history, in case of keyframes
277 reallocate_history((MAX_OFFSET + MAX_DEPTH) * sample_rate / 1000 + 1);
280 for(int i = 0; i < PluginClient::total_in_buffers; i++)
282 read_samples(buffer[i],
292 double wetness = DB::fromdb(config.wetness);
293 if(config.wetness <= INFINITYGAIN)
299 for(int i = 0; i < PluginClient::total_in_buffers; i++)
301 double *output = dsp_in[i];
302 double *input = buffer[i]->get_data();
303 for(int j = 0; j < size; j++)
305 output[j] = input[j] * wetness;
310 for(int i = 0; i < total_voices(); i++)
312 Voice *voice = &voices[i];
313 double *output = dsp_in[voice->dst_channel];
314 double *input = buffer[voice->src_channel]->get_data();
315 double *history = history_buffer[voice->src_channel];
317 // printf("Chorus::process_buffer %d table_offset=%d table=%f\n",
319 // voice->table_offset,
320 // flanging_table[table_size / 2].input_sample);
322 //static int debug = 1;
323 int table_offset = voice->table_offset;
324 for(int j = 0; j < size; j++)
326 flange_sample_t *table = &flanging_table[table_offset];
327 double input_sample = j - starting_offset + table->input_sample;
329 // values to interpolate
332 int input_sample1 = (int)(input_sample);
333 int input_sample2 = (int)(input_sample + 1);
334 double fraction1 = (double)((int)(input_sample + 1)) - input_sample;
335 double fraction2 = 1.0 - fraction1;
336 if(input_sample1 < 0)
338 sample1 = history[history_size + input_sample1];
342 sample1 = input[input_sample1];
345 if(input_sample2 < 0)
347 sample2 = history[history_size + input_sample2];
351 sample2 = input[input_sample2];
353 output[j] += sample1 * fraction1 + sample2 * fraction2;
354 // if(start_position + j > 49600 && start_position + j < 49700)
355 // printf("%ld %d input_sample=%f sample1=%f sample2=%f output=%f\n",
356 // start_position + j, table_offset, input_sample, sample1, sample2, output[j]);
358 if(config.rate >= MIN_RATE2)
361 table_offset %= table_size;
365 voice->table_offset = table_offset;
369 for(int i = 0; i < PluginClient::total_in_buffers; i++)
371 // history is bigger than input buffer. Copy entire input buffer.
372 if(history_size > size)
374 memcpy(history_buffer[i],
375 history_buffer[i] + size,
376 (history_size - size) * sizeof(double));
377 memcpy(history_buffer[i] + (history_size - size),
378 buffer[i]->get_data(),
379 size * sizeof(double));
383 // input is bigger than history buffer. Copy only history size
384 memcpy(history_buffer[i],
385 buffer[i]->get_data() + size - history_size,
386 history_size * sizeof(double));
389 //printf("Chorus::process_buffer %d history_size=%ld\n", __LINE__, history_size);
391 // copy the DSP buffer to the output
392 for(int i = 0; i < PluginClient::total_in_buffers; i++)
394 memcpy(buffer[i]->get_data(), dsp_in[i], size * sizeof(double));
398 if(get_direction() == PLAY_FORWARD)
400 last_position = start_position + size;
404 last_position = start_position - size;
413 int Chorus::total_voices()
415 return PluginClient::total_in_buffers * config.voices;
419 void Chorus::reallocate_dsp(int new_dsp_allocated)
421 if(new_dsp_allocated > dsp_in_allocated)
423 // copy samples already read into the new buffers
424 for(int i = 0; i < PluginClient::total_in_buffers; i++)
426 double *old_dsp = dsp_in[i];
427 double *new_dsp = new double[new_dsp_allocated];
434 dsp_in_allocated = new_dsp_allocated;
438 void Chorus::reallocate_history(int new_size)
440 if(new_size != history_size)
442 // copy samples already read into the new buffers
443 for(int i = 0; i < PluginClient::total_in_buffers; i++)
445 double *old_history = 0;
449 old_history = history_buffer[i];
451 double *new_history = new double[new_size];
452 bzero(new_history, sizeof(double) * new_size);
455 int copy_size = MIN(new_size, history_size);
457 old_history + history_size - copy_size,
458 sizeof(double) * copy_size);
459 delete [] old_history;
461 history_buffer[i] = new_history;
463 history_size = new_size;
471 NEW_WINDOW_MACRO(Chorus, ChorusWindow)
474 LOAD_CONFIGURATION_MACRO(Chorus, ChorusConfig)
477 void Chorus::save_data(KeyFrame *keyframe)
481 // cause xml file to store data directly in text
482 output.set_shared_output(keyframe->xbuf);
484 output.tag.set_title("CHORUS");
485 output.tag.set_property("VOICES", config.voices);
486 output.tag.set_property("OFFSET", config.offset);
487 output.tag.set_property("DEPTH", config.depth);
488 output.tag.set_property("RATE", config.rate);
489 output.tag.set_property("WETNESS", config.wetness);
491 output.append_newline();
493 output.terminate_string();
496 void Chorus::read_data(KeyFrame *keyframe)
499 // cause xml file to read directly from text
500 input.set_shared_input(keyframe->xbuf);
503 result = input.read_tag();
507 if(input.tag.title_is("CHORUS"))
509 config.voices = input.tag.get_property("VOICES", config.voices);
510 config.offset = input.tag.get_property("OFFSET", config.offset);
511 config.depth = input.tag.get_property("DEPTH", config.depth);
512 config.rate = input.tag.get_property("RATE", config.rate);
513 config.wetness = input.tag.get_property("WETNESS", config.wetness);
520 void Chorus::update_gui()
524 if(load_configuration())
526 thread->window->lock_window("Chorus::update_gui 1");
527 ((ChorusWindow*)thread->window)->update();
528 thread->window->unlock_window();
546 ChorusConfig::ChorusConfig()
555 int ChorusConfig::equivalent(ChorusConfig &that)
557 return (voices == that.voices) &&
558 EQUIV(offset, that.offset) &&
559 EQUIV(depth, that.depth) &&
560 EQUIV(rate, that.rate) &&
561 EQUIV(wetness, that.wetness);
564 void ChorusConfig::copy_from(ChorusConfig &that)
566 voices = that.voices;
567 offset = that.offset;
570 wetness = that.wetness;
573 void ChorusConfig::interpolate(ChorusConfig &prev,
577 int64_t current_frame)
582 void ChorusConfig::boundaries()
584 CLAMP(voices, MIN_VOICES, MAX_VOICES);
585 CLAMP(offset, MIN_OFFSET, MAX_OFFSET);
586 CLAMP(depth, MIN_DEPTH, MAX_DEPTH);
587 CLAMP(rate, MIN_RATE, MAX_RATE);
588 CLAMP(wetness, INFINITYGAIN, 0.0);
601 #define WINDOW_W xS(400)
602 #define WINDOW_H yS(165)
604 ChorusWindow::ChorusWindow(Chorus *plugin)
605 : PluginClientWindow(plugin,
612 this->plugin = plugin;
615 ChorusWindow::~ChorusWindow()
624 void ChorusWindow::create_objects()
626 int margin = plugin->get_theme()->widget_border + xS(4);
628 int x2 = xS(200), y = margin -xS(4);
629 int x3 = x2 + BC_Pot::calculate_w() + margin;
630 int x4 = x3 + BC_Pot::calculate_w() + margin;
631 int text_w = get_w() - margin - x4;
632 int height = BC_TextBox::calculate_h(this, MEDIUMFONT, 1, 1) + margin -xS(4);
635 voices = new PluginParam(plugin,
642 &plugin->config.voices, // output_i
645 "Voices per channel:",
648 voices->initialize();
651 offset = new PluginParam(plugin,
659 &plugin->config.offset, // output_f
661 "Phase offset (ms):",
664 offset->set_precision(3);
665 offset->initialize();
669 depth = new PluginParam(plugin,
677 &plugin->config.depth, // output_f
682 depth->set_precision(3);
688 rate = new PluginParam(plugin,
696 &plugin->config.rate, // output_f
701 rate->set_precision(3);
707 wetness = new PluginParam(plugin,
715 &plugin->config.wetness, // output_f
720 wetness->initialize();
726 void ChorusWindow::update()
728 voices->update(0, 0);
729 offset->update(0, 0);
732 wetness->update(0, 0);
735 void ChorusWindow::param_updated()