4 * Copyright (C) 2011 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
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
28 #include "parametric.h"
46 REGISTER_PLUGIN(ParametricEQ)
55 ParametricBand::ParametricBand()
64 int ParametricBand::equivalent(ParametricBand &that)
66 if(freq == that.freq &&
67 EQUIV(quality, that.quality) &&
68 EQUIV(magnitude, that.magnitude) &&
78 void ParametricBand::copy_from(ParametricBand &that)
81 quality = that.quality;
82 magnitude = that.magnitude;
86 void ParametricBand::interpolate(ParametricBand &prev,
91 freq = (int)(prev.freq * prev_scale + next.freq * next_scale + 0.5);
92 quality = prev.quality * prev_scale + next.quality * next_scale;
93 magnitude = prev.magnitude * prev_scale + next.magnitude * next_scale;
101 ParametricConfig::ParametricConfig()
103 wetness = INFINITYGAIN;
108 int ParametricConfig::equivalent(ParametricConfig &that)
110 for(int i = 0; i < BANDS; i++)
111 if(!band[i].equivalent(that.band[i])) return 0;
113 if(!EQUIV(wetness, that.wetness) ||
114 window_size != that.window_size) return 0;
118 void ParametricConfig::copy_from(ParametricConfig &that)
120 wetness = that.wetness;
121 window_size = that.window_size;
122 for(int i = 0; i < BANDS; i++)
123 band[i].copy_from(that.band[i]);
126 void ParametricConfig::interpolate(ParametricConfig &prev,
127 ParametricConfig &next,
130 int64_t current_frame)
132 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
133 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
134 wetness = prev.wetness;
135 window_size = prev.window_size;
136 for(int i = 0; i < BANDS; i++)
138 band[i].interpolate(prev.band[i], next.band[i], prev_scale, next_scale);
160 ParametricFreq::ParametricFreq(ParametricEQ *plugin, int x, int y, int band)
161 : BC_QPot(x, y, plugin->config.band[band].freq)
163 this->plugin = plugin;
167 int ParametricFreq::handle_event()
169 plugin->config.band[band].freq = get_value();
170 plugin->send_configure_change();
171 ((ParametricWindow*)plugin->thread->window)->update_canvas();
182 ParametricQuality::ParametricQuality(ParametricEQ *plugin, int x, int y, int band)
183 : BC_FPot(x, y, plugin->config.band[band].quality, 0, 1)
185 this->plugin = plugin;
190 int ParametricQuality::handle_event()
192 plugin->config.band[band].quality = get_value();
193 plugin->send_configure_change();
194 ((ParametricWindow*)plugin->thread->window)->update_canvas();
208 ParametricMagnitude::ParametricMagnitude(ParametricEQ *plugin, int x, int y, int band)
209 : BC_FPot(x, y, plugin->config.band[band].magnitude, -MAXMAGNITUDE, MAXMAGNITUDE)
211 this->plugin = plugin;
215 int ParametricMagnitude::handle_event()
217 plugin->config.band[band].magnitude = get_value();
218 plugin->send_configure_change();
219 ((ParametricWindow*)plugin->thread->window)->update_canvas();
231 ParametricMode::ParametricMode(ParametricEQ *plugin, int x, int y, int band)
235 mode_to_text(plugin->config.band[band].mode))
237 //printf("ParametricMode::ParametricMode %d %d\n", band, plugin->config.band[band].mode);
238 this->plugin = plugin;
242 void ParametricMode::create_objects()
244 add_item(new BC_MenuItem(mode_to_text(ParametricBand::LOWPASS)));
245 add_item(new BC_MenuItem(mode_to_text(ParametricBand::HIGHPASS)));
246 add_item(new BC_MenuItem(mode_to_text(ParametricBand::BANDPASS)));
247 add_item(new BC_MenuItem(mode_to_text(ParametricBand::NONE)));
251 int ParametricMode::handle_event()
253 plugin->config.band[band].mode = text_to_mode(get_text());
254 plugin->send_configure_change();
255 ((ParametricWindow*)plugin->thread->window)->update_canvas();
259 int ParametricMode::text_to_mode(char *text)
261 if(!strcmp(mode_to_text(ParametricBand::LOWPASS), text)) return ParametricBand::LOWPASS;
262 if(!strcmp(mode_to_text(ParametricBand::HIGHPASS), text)) return ParametricBand::HIGHPASS;
263 if(!strcmp(mode_to_text(ParametricBand::BANDPASS), text)) return ParametricBand::BANDPASS;
264 if(!strcmp(mode_to_text(ParametricBand::NONE), text)) return ParametricBand::NONE;
265 return ParametricBand::BANDPASS;
270 const char* ParametricMode::mode_to_text(int mode)
274 case ParametricBand::LOWPASS:
277 case ParametricBand::HIGHPASS:
278 return _("Highpass");
280 case ParametricBand::BANDPASS:
281 return _("Bandpass");
283 case ParametricBand::NONE:
300 ParametricBandGUI::ParametricBandGUI(ParametricEQ *plugin, ParametricWindow *window, int x, int y, int band)
302 this->plugin = plugin;
304 this->window = window;
309 ParametricBandGUI::~ParametricBandGUI()
320 void ParametricBandGUI::create_objects()
322 window->add_subwindow(freq = new ParametricFreq(plugin, X1, y, band));
323 window->add_subwindow(quality = new ParametricQuality(plugin, X2, y, band));
324 window->add_subwindow(magnitude = new ParametricMagnitude(plugin, X3, y, band));
325 window->add_subwindow(mode = new ParametricMode(plugin, X4, y, band));
326 mode->create_objects();
329 void ParametricBandGUI::update_gui()
331 freq->update(plugin->config.band[band].freq);
332 quality->update(plugin->config.band[band].quality);
333 magnitude->update(plugin->config.band[band].magnitude);
334 mode->set_text(ParametricMode::mode_to_text(plugin->config.band[band].mode));
342 ParametricWetness::ParametricWetness(ParametricEQ *plugin, int x, int y)
343 : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
345 this->plugin = plugin;
348 int ParametricWetness::handle_event()
350 plugin->config.wetness = get_value();
351 plugin->send_configure_change();
352 ((ParametricWindow*)plugin->thread->window)->update_canvas();
362 ParametricSize::ParametricSize(ParametricWindow *window, ParametricEQ *plugin, int x, int y)
363 : BC_PopupMenu(x, y, xS(120), "4096", 1)
365 this->plugin = plugin;
366 this->window = window;
369 int ParametricSize::handle_event()
371 plugin->config.window_size = atoi(get_text());
372 plugin->send_configure_change();
374 window->update_canvas();
378 void ParametricSize::create_objects()
380 add_item(new BC_MenuItem("2048"));
381 add_item(new BC_MenuItem("4096"));
382 add_item(new BC_MenuItem("8192"));
383 add_item(new BC_MenuItem("16384"));
384 add_item(new BC_MenuItem("32768"));
385 add_item(new BC_MenuItem("65536"));
386 add_item(new BC_MenuItem("131072"));
387 add_item(new BC_MenuItem("262144"));
390 void ParametricSize::update(int size)
392 char string[BCTEXTLEN];
393 sprintf(string, "%d", size);
402 ParametricWindow::ParametricWindow(ParametricEQ *plugin)
403 : PluginClientWindow(plugin,
410 this->plugin = plugin;
413 ParametricWindow::~ParametricWindow()
415 for(int i = 0; i < BANDS; i++)
419 void ParametricWindow::create_objects()
424 add_subwindow(new BC_Title(X1, 10, _("Freq")));
425 add_subwindow(new BC_Title(X2, 10, _("Qual")));
426 add_subwindow(new BC_Title(X3, 10, _("Level")));
427 add_subwindow(new BC_Title(X4, 10, _("Mode")));
428 for(int i = 0; i < BANDS; i++)
430 bands[i] = new ParametricBandGUI(plugin, this, xS(10), y, i);
431 bands[i]->create_objects();
437 int x = plugin->get_theme()->widget_border;
438 add_subwindow(title = new BC_Title(x, y + yS(10), _("Wetness:")));
439 x += title->get_w() + plugin->get_theme()->widget_border;
440 add_subwindow(wetness = new ParametricWetness(plugin,
443 x += wetness->get_w() + plugin->get_theme()->widget_border;
445 add_subwindow(title = new BC_Title(x, y + yS(10), _("Window:")));
446 x += title->get_w() + plugin->get_theme()->widget_border;
447 add_subwindow(size = new ParametricSize(this,
451 size->create_objects();
452 size->update(plugin->config.window_size);
457 int canvas_x = xS(30);
459 int canvas_w = get_w() - canvas_x - xS(10);
460 int canvas_h = get_h() - canvas_y - yS(30);
461 add_subwindow(canvas = new BC_SubWindow(canvas_x,
468 // Draw canvas titles
470 #define MAJOR_DIVISIONS 4
471 #define MINOR_DIVISIONS 5
472 for(int i = 0; i <= MAJOR_DIVISIONS; i++)
474 int y1 = canvas_y + canvas_h - i * (canvas_h / MAJOR_DIVISIONS) - 2;
476 int x1 = canvas_x - xS(25);
477 int x2 = canvas_x - xS(10);
478 int x3 = canvas_x - xS(2);
480 char string[BCTEXTLEN];
482 sprintf(string, "oo");
484 sprintf(string, "%d", i * 5 - 5);
487 draw_text(x1 + 1, y2 + 1, string);
488 draw_line(x2 + 1, y1 + 1, x3 + 1, y1 + 1);
490 draw_text(x1, y2, string);
491 draw_line(x2, y1, x3, y1);
493 if(i < MAJOR_DIVISIONS)
495 for(int j = 1; j < MINOR_DIVISIONS; j++)
497 int y3 = y1 - j * (canvas_h / MAJOR_DIVISIONS) / MINOR_DIVISIONS;
500 draw_line(x4 + 1, y3 + 1, x3 + 1, y3 + 1);
502 draw_line(x4, y3, x3, y3);
508 #undef MAJOR_DIVISIONS
509 #define MAJOR_DIVISIONS 5
510 for(int i = 0; i <= MAJOR_DIVISIONS; i++)
512 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
513 int x1 = canvas_x + i * canvas_w / MAJOR_DIVISIONS;
514 int y1 = canvas_y + canvas_h + yS(20);
515 char string[BCTEXTLEN];
516 sprintf(string, "%d", freq);
517 int x2 = x1 - get_text_width(SMALLFONT, string);
518 int y2 = y1 - yS(10);
520 int y4 = canvas_y + canvas_h;
523 draw_text(x2 + 1, y1 + 1, string);
524 draw_line(x1 + 1, y4 + 1, x1 + 1, y2 + 1);
526 draw_text(x2, y1, string);
527 draw_line(x1, y4, x1, y2);
529 if(i < MAJOR_DIVISIONS)
531 #undef MINOR_DIVISIONS
532 #define MINOR_DIVISIONS 5
533 for(int j = 0; j < MINOR_DIVISIONS; j++)
536 (canvas_w / MAJOR_DIVISIONS) -
537 exp(-(double)j * 0.7) *
538 (canvas_w / MAJOR_DIVISIONS));
540 draw_line(x3+1, y4+1, x3+1, y3+1);
542 draw_line(x3, y4, x3, y3);
555 void ParametricWindow::update_gui()
557 for(int i = 0; i < BANDS; i++)
558 bands[i]->update_gui();
559 wetness->update(plugin->config.wetness);
560 size->update(plugin->config.window_size);
565 void ParametricWindow::update_canvas()
567 int y1 = canvas->get_h() / 2;
568 int niquist = plugin->PluginAClient::project_sample_rate / 2;
570 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
573 double tracking_position = plugin->get_tracking_position();
574 ParametricGUIFrame *frame = (ParametricGUIFrame *)
575 plugin->get_gui_frame(tracking_position, 1);
576 // Draw most recent frame
579 canvas->set_color(MEGREY);
581 for(int i = 0; i < canvas->get_w(); i++) {
582 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
583 int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
584 if(index < frame->window_size / 2) {
585 double magnitude = frame->data[index] /
586 frame->freq_max * frame->time_max;
587 y2 = (int)(canvas->get_h() -
588 (DB::todb(magnitude) - INFINITYGAIN) *
589 canvas->get_h() / -INFINITYGAIN);
590 CLAMP(y2, 0, canvas->get_h() - 1);
592 canvas->draw_line(i - 1, y1, i, y2);
599 // canvas->set_color(GREEN);
600 // canvas->draw_line(0, wetness, canvas->get_w(), wetness);
601 // canvas->draw_line(0,
606 canvas->set_color(WHITE);
607 canvas->set_line_width(2);
609 plugin->calculate_envelope();
610 for(int i = 0; i < canvas->get_w(); i++)
612 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
613 int index = (int64_t)freq * (int64_t)plugin->config.window_size / 2 / niquist;
614 if(freq < niquist && index < plugin->config.window_size / 2)
616 //printf("ParametricWindow::update_canvas %d %d\n", __LINE__, index);
617 double magnitude = plugin->envelope[index];
618 int y2 = canvas->get_h() * 3 / 4;
622 y2 -= (int)(DB::todb(magnitude) *
630 y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
632 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
637 canvas->draw_line(i - 1, y1, i, y1);
640 canvas->set_line_width(1);
643 // for(int i = 0; i < canvas->get_w(); i++)
645 // int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
646 // int index = (int)((float)freq / niquist * plugin->config.window_size / 2);
647 // double magnitude = plugin->envelope[index];
648 // int y2 = canvas->get_h() -
649 // (int)((double)canvas->get_h() / normalize * magnitude);
650 // if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
658 ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate)
659 : PluginClientFrame()
661 this->window_size = window_size;
662 data_size = window_size / 2;
663 data = new double[data_size];
668 ParametricGUIFrame::~ParametricGUIFrame()
674 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
677 this->plugin = plugin;
680 ParametricFFT::~ParametricFFT()
685 int ParametricFFT::signal_process()
687 // Create new frame for updating GUI
688 frame = new ParametricGUIFrame(window_size,
689 plugin->PluginAClient::project_sample_rate);
690 plugin->add_gui_frame(frame);
693 for(int i = 0; i < window_size / 2; i++)
696 // if(i == 10) printf("ParametricFFT::signal_process %d %f\n",
698 // plugin->envelope[i]);
700 double result = plugin->envelope[i] *
701 sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
702 double angle = atan2(freq_imag[i], freq_real[i]);
703 freq_real[i] = result * cos(angle);
704 freq_imag[i] = result * sin(angle);
706 frame->data[i] = result;
707 if(result > freq_max) freq_max = result;
709 frame->freq_max = freq_max;
712 symmetry(window_size, freq_real, freq_imag);
716 int ParametricFFT::post_process()
719 for(int i = 0; i < window_size; i++)
721 if(output_real[i] > time_max) time_max = output_real[i];
723 frame->time_max = time_max;
729 int ParametricFFT::read_samples(int64_t output_sample,
733 return plugin->read_samples(buffer,
735 plugin->get_samplerate(),
747 ParametricEQ::ParametricEQ(PluginServer *server)
748 : PluginAClient(server)
752 need_reconfigure = 1;
757 ParametricEQ::~ParametricEQ()
764 NEW_WINDOW_MACRO(ParametricEQ, ParametricWindow)
766 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
769 const char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
770 int ParametricEQ::is_realtime() { return 1; }
772 void ParametricEQ::read_data(KeyFrame *keyframe)
775 input.set_shared_input(keyframe->xbuf);
780 result = input.read_tag();
784 if(input.tag.title_is("PARAMETRICEQ"))
786 config.wetness = input.tag.get_property("WETNESS", config.wetness);
787 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
790 if(input.tag.title_is("BAND"))
792 int band = input.tag.get_property("NUMBER", 0);
793 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
794 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
795 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
796 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
802 void ParametricEQ::save_data(KeyFrame *keyframe)
805 output.set_shared_output(keyframe->xbuf);
807 output.tag.set_title("PARAMETRICEQ");
808 output.tag.set_property("WETNESS", config.wetness);
809 output.tag.set_property("WINDOW_SIZE", config.window_size);
811 output.append_newline();
813 for(int i = 0; i < BANDS; i++)
815 output.tag.set_title("BAND");
816 output.tag.set_property("NUMBER", i);
817 output.tag.set_property("FREQ", config.band[i].freq);
818 output.tag.set_property("QUALITY", config.band[i].quality);
819 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
820 output.tag.set_property("MODE", config.band[i].mode);
822 output.tag.set_title("/BAND");
824 output.append_newline();
827 output.tag.set_title("/PARAMETRICEQ");
829 output.append_newline();
830 output.terminate_string();
833 void ParametricEQ::reconfigure()
836 config.window_size != fft->window_size)
838 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
845 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
846 fft = new ParametricFFT(this);
847 fft->initialize(config.window_size);
852 //printf("ParametricEQ::reconfigure %f\n", wetness);
853 calculate_envelope();
855 for(int i = 0; i < config.window_size / 2; i++)
857 if(envelope[i] < 0) envelope[i] = 0;
860 need_reconfigure = 0;
863 double ParametricEQ::calculate_envelope()
865 double wetness = DB::fromdb(config.wetness);
866 if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0;
867 int niquist = PluginAClient::project_sample_rate / 2;
869 if(!envelope) envelope = new double[MAX_WINDOW / 2];
871 //printf("ParametricEQ::calculate_envelope %d %f\n", __LINE__, wetness);
872 for(int i = 0; i < config.window_size / 2; i++)
874 envelope[i] = wetness;
877 for(int pass = 0; pass < 2; pass++)
879 for(int band = 0; band < BANDS; band++)
881 switch(config.band[band].mode)
883 case ParametricBand::LOWPASS:
886 double magnitude = DB::fromdb(config.band[band].magnitude);
887 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
888 for(int i = 0; i < config.window_size / 2; i++)
891 envelope[i] += magnitude;
896 case ParametricBand::HIGHPASS:
899 double magnitude = DB::fromdb(config.band[band].magnitude);
900 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
901 for(int i = 0; i < config.window_size / 2; i++)
904 envelope[i] += magnitude;
909 case ParametricBand::BANDPASS:
912 double magnitude = (config.band[band].magnitude > 0) ?
913 (DB::fromdb(config.band[band].magnitude) - 1) :
914 (-1 + DB::fromdb(config.band[band].magnitude));
915 double sigma = (config.band[band].quality < 1) ?
916 (1.0 - config.band[band].quality) :
919 double center = (double)Freq::fromfreq(config.band[band].freq) /
921 double normalize = gauss(sigma, 0, 0);
922 if(config.band[band].magnitude <= -MAXMAGNITUDE)
925 for(int i = 0; i < config.window_size / 2; i++)
927 int freq = i * niquist / (config.window_size / 2);
928 int current_slot = Freq::fromfreq(freq);
929 envelope[i] += magnitude *
930 gauss(sigma, center, (double)current_slot / TOTALFREQS) /
932 // printf("freq=%d magnitude=%f gauss=%f normalize=%f envelope[i]=%f\n",
935 // gauss(sigma, center, (double)current_slot / TOTALFREQS),
948 double ParametricEQ::gauss(double sigma, double center, double x)
950 if(EQUIV(sigma, 0)) sigma = 0.01;
952 double result = 1.0 /
953 sqrt(2 * M_PI * sigma * sigma) *
954 exp(-(x - center) * (x - center) /
955 (2 * sigma * sigma));
963 int ParametricEQ::process_buffer(int64_t size,
965 int64_t start_position,
968 need_reconfigure |= load_configuration();
969 if(need_reconfigure) reconfigure();
972 fft->process_buffer(start_position,
982 void ParametricEQ::reset()
984 need_reconfigure = 1;
989 void ParametricEQ::update_gui()
991 if( !thread ) return;
992 ParametricWindow *window = (ParametricWindow*)thread->window;
993 window->lock_window("ParametricEQ::update_gui");
994 if( load_configuration() ) {
995 calculate_envelope();
996 window->update_gui();
998 else if( pending_gui_frame() ) {
999 window->update_canvas();
1001 window->unlock_window();