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, 100, "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, 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 + 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 + 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);
459 int canvas_w = get_w() - canvas_x - 10;
460 int canvas_h = get_h() - canvas_y - 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 - 25;
477 int x2 = canvas_x - 10;
478 int x3 = canvas_x - 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 + 20;
515 char string[BCTEXTLEN];
516 sprintf(string, "%d", freq);
517 int x2 = x1 - get_text_width(SMALLFONT, string);
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());
575 int total_frames = plugin->get_gui_update_frames();
576 ParametricGUIFrame *frame = (ParametricGUIFrame*)plugin->get_gui_frame();
580 delete plugin->last_frame;
581 plugin->last_frame = frame;
585 frame = plugin->last_frame;
588 // Draw most recent frame
591 canvas->set_color(MEGREY);
594 for(int i = 0; i < canvas->get_w(); i++)
596 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
597 int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
598 if(index < frame->window_size / 2)
600 double magnitude = frame->data[index] /
603 y2 = (int)(canvas->get_h() -
604 (DB::todb(magnitude) - INFINITYGAIN) *
607 CLAMP(y2, 0, canvas->get_h() - 1);
610 canvas->draw_line(i - 1, y1, i, y2);
624 // Delete remaining frames
625 while(total_frames > 0)
627 PluginClientFrame *frame = plugin->get_gui_frame();
629 if(frame) delete frame;
637 // canvas->set_color(GREEN);
638 // canvas->draw_line(0, wetness, canvas->get_w(), wetness);
639 // canvas->draw_line(0,
644 canvas->set_color(WHITE);
645 canvas->set_line_width(2);
647 plugin->calculate_envelope();
648 for(int i = 0; i < canvas->get_w(); i++)
650 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
651 int index = (int64_t)freq * (int64_t)plugin->config.window_size / 2 / niquist;
652 if(freq < niquist && index < plugin->config.window_size / 2)
654 //printf("ParametricWindow::update_canvas %d %d\n", __LINE__, index);
655 double magnitude = plugin->envelope[index];
656 int y2 = canvas->get_h() * 3 / 4;
660 y2 -= (int)(DB::todb(magnitude) *
668 y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
670 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
675 canvas->draw_line(i - 1, y1, i, y1);
678 canvas->set_line_width(1);
681 // for(int i = 0; i < canvas->get_w(); i++)
683 // int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
684 // int index = (int)((float)freq / niquist * plugin->config.window_size / 2);
685 // double magnitude = plugin->envelope[index];
686 // int y2 = canvas->get_h() -
687 // (int)((double)canvas->get_h() / normalize * magnitude);
688 // if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
701 ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate)
702 : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
704 this->window_size = window_size;
705 data = new double[window_size / 2];
710 ParametricGUIFrame::~ParametricGUIFrame()
724 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
727 this->plugin = plugin;
730 ParametricFFT::~ParametricFFT()
735 int ParametricFFT::signal_process()
737 // Create new frame for updating GUI
738 frame = new ParametricGUIFrame(window_size,
739 plugin->PluginAClient::project_sample_rate);
740 plugin->add_gui_frame(frame);
743 for(int i = 0; i < window_size / 2; i++)
746 // if(i == 10) printf("ParametricFFT::signal_process %d %f\n",
748 // plugin->envelope[i]);
750 double result = plugin->envelope[i] *
751 sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
752 double angle = atan2(freq_imag[i], freq_real[i]);
753 freq_real[i] = result * cos(angle);
754 freq_imag[i] = result * sin(angle);
756 frame->data[i] = result;
757 if(result > freq_max) freq_max = result;
759 frame->freq_max = freq_max;
762 symmetry(window_size, freq_real, freq_imag);
766 int ParametricFFT::post_process()
769 for(int i = 0; i < window_size; i++)
771 if(output_real[i] > time_max) time_max = output_real[i];
773 frame->time_max = time_max;
779 int ParametricFFT::read_samples(int64_t output_sample,
783 return plugin->read_samples(buffer,
785 plugin->get_samplerate(),
797 ParametricEQ::ParametricEQ(PluginServer *server)
798 : PluginAClient(server)
802 need_reconfigure = 1;
807 ParametricEQ::~ParametricEQ()
814 NEW_WINDOW_MACRO(ParametricEQ, ParametricWindow)
816 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
819 const char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
820 int ParametricEQ::is_realtime() { return 1; }
822 void ParametricEQ::read_data(KeyFrame *keyframe)
825 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
830 result = input.read_tag();
834 if(input.tag.title_is("PARAMETRICEQ"))
836 config.wetness = input.tag.get_property("WETNESS", config.wetness);
837 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
840 if(input.tag.title_is("BAND"))
842 int band = input.tag.get_property("NUMBER", 0);
843 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
844 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
845 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
846 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
852 void ParametricEQ::save_data(KeyFrame *keyframe)
855 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
857 output.tag.set_title("PARAMETRICEQ");
858 output.tag.set_property("WETNESS", config.wetness);
859 output.tag.set_property("WINDOW_SIZE", config.window_size);
861 output.append_newline();
863 for(int i = 0; i < BANDS; i++)
865 output.tag.set_title("BAND");
866 output.tag.set_property("NUMBER", i);
867 output.tag.set_property("FREQ", config.band[i].freq);
868 output.tag.set_property("QUALITY", config.band[i].quality);
869 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
870 output.tag.set_property("MODE", config.band[i].mode);
872 output.tag.set_title("/BAND");
874 output.append_newline();
877 output.tag.set_title("/PARAMETRICEQ");
879 output.append_newline();
880 output.terminate_string();
883 void ParametricEQ::reconfigure()
886 config.window_size != fft->window_size)
888 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
895 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
896 fft = new ParametricFFT(this);
897 fft->initialize(config.window_size);
902 //printf("ParametricEQ::reconfigure %f\n", wetness);
903 calculate_envelope();
905 for(int i = 0; i < config.window_size / 2; i++)
907 if(envelope[i] < 0) envelope[i] = 0;
910 need_reconfigure = 0;
913 double ParametricEQ::calculate_envelope()
915 double wetness = DB::fromdb(config.wetness);
916 if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0;
917 int niquist = PluginAClient::project_sample_rate / 2;
919 if(!envelope) envelope = new double[MAX_WINDOW / 2];
921 //printf("ParametricEQ::calculate_envelope %d %f\n", __LINE__, wetness);
922 for(int i = 0; i < config.window_size / 2; i++)
924 envelope[i] = wetness;
927 for(int pass = 0; pass < 2; pass++)
929 for(int band = 0; band < BANDS; band++)
931 switch(config.band[band].mode)
933 case ParametricBand::LOWPASS:
936 double magnitude = DB::fromdb(config.band[band].magnitude);
937 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
938 for(int i = 0; i < config.window_size / 2; i++)
941 envelope[i] += magnitude;
946 case ParametricBand::HIGHPASS:
949 double magnitude = DB::fromdb(config.band[band].magnitude);
950 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
951 for(int i = 0; i < config.window_size / 2; i++)
954 envelope[i] += magnitude;
959 case ParametricBand::BANDPASS:
962 double magnitude = (config.band[band].magnitude > 0) ?
963 (DB::fromdb(config.band[band].magnitude) - 1) :
964 (-1 + DB::fromdb(config.band[band].magnitude));
965 double sigma = (config.band[band].quality < 1) ?
966 (1.0 - config.band[band].quality) :
969 double center = (double)Freq::fromfreq(config.band[band].freq) /
971 double normalize = gauss(sigma, 0, 0);
972 if(config.band[band].magnitude <= -MAXMAGNITUDE)
975 for(int i = 0; i < config.window_size / 2; i++)
977 int freq = i * niquist / (config.window_size / 2);
978 int current_slot = Freq::fromfreq(freq);
979 envelope[i] += magnitude *
980 gauss(sigma, center, (double)current_slot / TOTALFREQS) /
982 // printf("freq=%d magnitude=%f gauss=%f normalize=%f envelope[i]=%f\n",
985 // gauss(sigma, center, (double)current_slot / TOTALFREQS),
998 double ParametricEQ::gauss(double sigma, double center, double x)
1000 if(EQUIV(sigma, 0)) sigma = 0.01;
1002 double result = 1.0 /
1003 sqrt(2 * M_PI * sigma * sigma) *
1004 exp(-(x - center) * (x - center) /
1005 (2 * sigma * sigma));
1013 int ParametricEQ::process_buffer(int64_t size,
1015 int64_t start_position,
1018 need_reconfigure |= load_configuration();
1019 if(need_reconfigure) reconfigure();
1022 fft->process_buffer(start_position,
1042 void ParametricEQ::reset()
1044 need_reconfigure = 1;
1049 void ParametricEQ::update_gui()
1053 if(load_configuration())
1055 ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
1056 calculate_envelope();
1057 ((ParametricWindow*)thread->window)->update_gui();
1058 ((ParametricWindow*)thread->window)->unlock_window();
1062 int total_frames = get_gui_update_frames();
1063 //printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
1066 ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
1067 ((ParametricWindow*)thread->window)->update_canvas();
1068 ((ParametricWindow*)thread->window)->unlock_window();