r932: When playing update clocks only, not all zoombar widgets
[cinelerra_cv/mob.git] / plugins / parametric / parametric.C
blobbacac6eb78a9b7f7031f855e96ac66b068850114
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "clip.h"
4 #include "bchash.h"
5 #include "filexml.h"
6 #include "language.h"
7 #include "parametric.h"
8 #include "picon_png.h"
9 #include "units.h"
10 #include "vframe.h"
12 #include <math.h>
13 #include <string.h>
23 REGISTER_PLUGIN(ParametricEQ)
32 ParametricBand::ParametricBand()
34         freq = 440;
35         quality = 1;
36         magnitude = 0;
37         mode = NONE;
41 int ParametricBand::equivalent(ParametricBand &that)
43         if(freq == that.freq && 
44                 EQUIV(quality, that.quality) && 
45                 EQUIV(magnitude, that.magnitude) &&
46                 mode == that.mode)
47         {
48                 return 1;
49         }
50         else
51                 return 0;
55 void ParametricBand::copy_from(ParametricBand &that)
57         freq = that.freq;
58         quality = that.quality;
59         magnitude = that.magnitude;
60         mode = that.mode;
63 void ParametricBand::interpolate(ParametricBand &prev, 
64                 ParametricBand &next, 
65                 double prev_scale, 
66                 double next_scale)
68         freq = (int)(prev.freq * prev_scale + next.freq * next_scale + 0.5);
69         quality = prev.quality * prev_scale + next.quality * next_scale;
70         magnitude = prev.magnitude * prev_scale + next.magnitude * next_scale;
71         mode = prev.mode;
78 ParametricConfig::ParametricConfig()
80         wetness = INFINITYGAIN;
84 int ParametricConfig::equivalent(ParametricConfig &that)
86         for(int i = 0; i < BANDS; i++)
87                 if(!band[i].equivalent(that.band[i])) return 0;
89         if(!EQUIV(wetness, that.wetness)) return 0;
90         return 1;
93 void ParametricConfig::copy_from(ParametricConfig &that)
95         wetness = that.wetness;
96         for(int i = 0; i < BANDS; i++)
97                 band[i].copy_from(that.band[i]);
100 void ParametricConfig::interpolate(ParametricConfig &prev, 
101                 ParametricConfig &next, 
102                 int64_t prev_frame, 
103                 int64_t next_frame, 
104                 int64_t current_frame)
106         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
107         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
108         wetness = prev.wetness;
109         for(int i = 0; i < BANDS; i++)
110         {
111                 band[i].interpolate(prev.band[i], next.band[i], prev_scale, next_scale);
112         }
133 ParametricFreq::ParametricFreq(ParametricEQ *plugin, int x, int y, int band)
134  : BC_QPot(x, y, plugin->config.band[band].freq)
136         this->plugin = plugin;
137         this->band = band;
140 int ParametricFreq::handle_event()
142         plugin->config.band[band].freq = get_value();
143         plugin->send_configure_change();
144         plugin->thread->window->update_canvas();
145         return 1;
155 ParametricQuality::ParametricQuality(ParametricEQ *plugin, int x, int y, int band)
156  : BC_FPot(x, y, plugin->config.band[band].quality, 0, 1)
158         this->plugin = plugin;
159         this->band = band;
160         set_precision(0.01);
163 int ParametricQuality::handle_event()
165         plugin->config.band[band].quality = get_value();
166         plugin->send_configure_change();
167         plugin->thread->window->update_canvas();
168         return 1;
181 ParametricMagnitude::ParametricMagnitude(ParametricEQ *plugin, int x, int y, int band)
182  : BC_FPot(x, y, plugin->config.band[band].magnitude, -MAXMAGNITUDE, MAXMAGNITUDE)
184         this->plugin = plugin;
185         this->band = band;
188 int ParametricMagnitude::handle_event()
190         plugin->config.band[band].magnitude = get_value();
191         plugin->send_configure_change();
192         plugin->thread->window->update_canvas();
193         return 1;
204 ParametricMode::ParametricMode(ParametricEQ *plugin, int x, int y, int band)
205  : BC_PopupMenu(x, 
206                 y, 
207                 150, 
208                 mode_to_text(plugin->config.band[band].mode))
210 //printf("ParametricMode::ParametricMode %d %d\n", band, plugin->config.band[band].mode);
211         this->plugin = plugin;
212         this->band = band;
215 void ParametricMode::create_objects()
217         add_item(new BC_MenuItem(mode_to_text(ParametricBand::LOWPASS)));
218         add_item(new BC_MenuItem(mode_to_text(ParametricBand::HIGHPASS)));
219         add_item(new BC_MenuItem(mode_to_text(ParametricBand::BANDPASS)));
220         add_item(new BC_MenuItem(mode_to_text(ParametricBand::NONE)));
224 int ParametricMode::handle_event()
226         plugin->config.band[band].mode = text_to_mode(get_text());
227         plugin->send_configure_change();
228         plugin->thread->window->update_canvas();
229         return 1;
232 int ParametricMode::text_to_mode(char *text)
234         if(!strcmp(mode_to_text(ParametricBand::LOWPASS), text)) return ParametricBand::LOWPASS;
235         if(!strcmp(mode_to_text(ParametricBand::HIGHPASS), text)) return ParametricBand::HIGHPASS;
236         if(!strcmp(mode_to_text(ParametricBand::BANDPASS), text)) return ParametricBand::BANDPASS;
237         if(!strcmp(mode_to_text(ParametricBand::NONE), text)) return ParametricBand::NONE;
238         return ParametricBand::BANDPASS;
243 char* ParametricMode::mode_to_text(int mode)
245         switch(mode)
246         {
247                 case ParametricBand::LOWPASS:
248                         return _("Lowpass");
249                         break;
250                 case ParametricBand::HIGHPASS:
251                         return _("Highpass");
252                         break;
253                 case ParametricBand::BANDPASS:
254                         return _("Bandpass");
255                         break;
256                 case ParametricBand::NONE:
257                         return _("None");
258                         break;
259         }
260         return "";
273 ParametricBandGUI::ParametricBandGUI(ParametricEQ *plugin, ParametricWindow *window, int x, int y, int band)
275         this->plugin = plugin;
276         this->band = band;
277         this->window = window;
278         this->x = x;
279         this->y = y;
282 ParametricBandGUI::~ParametricBandGUI()
287 #define X1 10
288 #define X2 60
289 #define X3 110
290 #define X4 160
292         
293 void ParametricBandGUI::create_objects()
295         window->add_subwindow(freq = new ParametricFreq(plugin, X1, y, band));
296         window->add_subwindow(quality = new ParametricQuality(plugin, X2, y, band));
297         window->add_subwindow(magnitude = new ParametricMagnitude(plugin, X3, y, band));
298         window->add_subwindow(mode = new ParametricMode(plugin, X4, y, band));
299         mode->create_objects();
302 void ParametricBandGUI::update_gui()
304         freq->update(plugin->config.band[band].freq);
305         quality->update(plugin->config.band[band].quality);
306         magnitude->update(plugin->config.band[band].magnitude);
314 ParametricWetness::ParametricWetness(ParametricEQ *plugin, int x, int y)
315  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
317         this->plugin = plugin;
320 int ParametricWetness::handle_event()
322         plugin->config.wetness = get_value();
323         plugin->send_configure_change();
324         plugin->thread->window->update_canvas();
325         return 1;
333 ParametricWindow::ParametricWindow(ParametricEQ *plugin, int x, int y)
334  : BC_Window(plugin->gui_string, 
335         x, 
336         y, 
337         320, 
338         400, 
339         320, 
340         400,
341         0, 
342         0,
343         1)
345         this->plugin = plugin;
348 ParametricWindow::~ParametricWindow()
350         for(int i = 0; i < BANDS; i++)
351                 delete bands[i];
354 void ParametricWindow::create_objects()
356         int y = 35;
357 SET_TRACE       
358         
359         add_subwindow(new BC_Title(X1, 10, _("Freq")));
360         add_subwindow(new BC_Title(X2, 10, _("Qual")));
361         add_subwindow(new BC_Title(X3, 10, _("Level")));
362         add_subwindow(new BC_Title(X4, 10, _("Mode")));
363         for(int i = 0; i < BANDS; i++)
364         {
365                 bands[i] = new ParametricBandGUI(plugin, this, 10, y, i);
366                 bands[i]->create_objects();
367                 y += 50;
368         }
370 SET_TRACE       
371         add_subwindow(new BC_Title(10, y + 10, _("Wetness:")));
372         add_subwindow(wetness = new ParametricWetness(plugin, 80, y));
373         y += 50;
374         int canvas_x = 30;
375         int canvas_y = y;
376         int canvas_w = get_w() - canvas_x - 10;
377         int canvas_h = get_h() - canvas_y - 30;
378         add_subwindow(canvas = new BC_SubWindow(canvas_x, 
379                 canvas_y, 
380                 canvas_w, 
381                 canvas_h, 
382                 WHITE));
384 SET_TRACE       
385 // Draw canvas titles
386         set_font(SMALLFONT);
387 #define MAJOR_DIVISIONS 4
388 #define MINOR_DIVISIONS 5
389         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
390         {
391                 int y1 = canvas_y + canvas_h - i * (canvas_h / MAJOR_DIVISIONS) - 2;
392                 int y2 = y1 + 3;
393                 int x1 = canvas_x - 25;
394                 int x2 = canvas_x - 10;
395                 int x3 = canvas_x - 2;
397                 char string[BCTEXTLEN];
398                 if(i == 0)
399                         sprintf(string, "oo");
400                 else
401                         sprintf(string, "%d", i * 5 - 5);
403                 set_color(BLACK);
404                 draw_text(x1 + 1, y2 + 1, string);
405                 draw_line(x2 + 1, y1 + 1, x3 + 1, y1 + 1);
406                 set_color(RED);
407                 draw_text(x1, y2, string);
408                 draw_line(x2, y1, x3, y1);
410                 if(i < MAJOR_DIVISIONS)
411                 {
412                         for(int j = 1; j < MINOR_DIVISIONS; j++)
413                         {
414                                 int y3 = y1 - j * (canvas_h / MAJOR_DIVISIONS) / MINOR_DIVISIONS;
415                                 int x4 = x3 - 5;
416                                 set_color(BLACK);
417                                 draw_line(x4 + 1, y3 + 1, x3 + 1, y3 + 1);
418                                 set_color(RED);
419                                 draw_line(x4, y3, x3, y3);
420                         }
421                 }
422         }
424 SET_TRACE       
425 #undef MAJOR_DIVISIONS
426 #define MAJOR_DIVISIONS 5
427         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
428         {
429                 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
430                 int x1 = canvas_x + i * canvas_w / MAJOR_DIVISIONS;
431                 int y1 = canvas_y + canvas_h + 20;
432                 char string[BCTEXTLEN];
433                 sprintf(string, "%d", freq);
434                 int x2 = x1 - get_text_width(SMALLFONT, string);
435                 int y2 = y1 - 10;
436                 int y3 = y2 - 5;
437                 int y4 = canvas_y + canvas_h;
438                 
439                 set_color(BLACK);
440                 draw_text(x2 + 1, y1 + 1, string);
441                 draw_line(x1 + 1, y4 + 1, x1 + 1, y2 + 1);
442                 set_color(RED);
443                 draw_text(x2, y1, string);
444                 draw_line(x1, y4, x1, y2);
446                 if(i < MAJOR_DIVISIONS)
447                 {
448 #undef MINOR_DIVISIONS
449 #define MINOR_DIVISIONS 5
450                         for(int j = 0; j < MINOR_DIVISIONS; j++)
451                         {
452                                 int x3 = (int)(x1 + 
453                                         (canvas_w / MAJOR_DIVISIONS) -
454                                         exp(-(double)j * 0.7) * 
455                                         (canvas_w / MAJOR_DIVISIONS));
456                                 set_color(BLACK);
457                                 draw_line(x3 + 1, y4 + 1, x3 + 1, y3 + 1);
458                                 set_color(RED);
459                                 draw_line(x3, y4, x3, y3);
460                         }
461                 }
462         }
464 SET_TRACE       
465         update_canvas();
466         show_window();
467 SET_TRACE       
470 int ParametricWindow::close_event()
472 // Set result to 1 to indicate a client side close
473         set_done(1);
474         return 1;
477 void ParametricWindow::update_gui()
479         for(int i = 0; i < BANDS; i++)
480                 bands[i]->update_gui();
481         wetness->update(plugin->config.wetness);
482         update_canvas();
486 void ParametricWindow::update_canvas()
488         double scale = 1;
489         int y1 = canvas->get_h() / 2;
490         int niquist = plugin->PluginAClient::project_sample_rate / 2;
491         int wetness = canvas->get_h() -
492                 (int)((plugin->config.wetness - INFINITYGAIN) /
493                         -INFINITYGAIN * 
494                         canvas->get_h() / 
495                         4);
497         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
498 //      canvas->set_color(GREEN);
499 //      canvas->draw_line(0, 
500 //              wetness, 
501 //              canvas->get_w(), 
502 //              wetness);
504         canvas->set_color(BLACK);
506         plugin->calculate_envelope();
507         for(int i = 0; i < canvas->get_w() - 1; i++)
508         {
509                 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
510                 int index = freq * WINDOW_SIZE / 2 / niquist;
511                 if(freq < niquist)
512                 {
513                         double magnitude = plugin->envelope[index];
514                                 int y2 = canvas->get_h() * 3 / 4;
516                                 if(magnitude > 1)
517                         {
518                                         y2 -= (int)(DB::todb(magnitude) * 
519                                         canvas->get_h() * 
520                                         3 / 
521                                         4 / 
522                                         15);
523                                 }
524                         else
525                         {
526                                         y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
527                                 }
528                                 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
529                                 y1 = y2;
530                 }
531                 else
532                 {
533                         canvas->draw_line(i - 1, y1, i, y1);
534                 }
535         }
538 //      for(int i = 0; i < canvas->get_w(); i++)
539 //      {
540 //              int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
541 //              int index = (int)((float)freq / niquist * WINDOW_SIZE / 2);
542 //              double magnitude = plugin->envelope[index];
543 //              int y2 = canvas->get_h() - 
544 //                      (int)((double)canvas->get_h() / normalize * magnitude);
545 //              if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
546 //              y1 = y2;
547 //      }
549         canvas->flash();
558 PLUGIN_THREAD_OBJECT(ParametricEQ, ParametricThread, ParametricWindow)
566 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
567  : CrossfadeFFT()
569         this->plugin = plugin;
572 ParametricFFT::~ParametricFFT()
577 int ParametricFFT::signal_process()
579         for(int i = 0; i < window_size / 2; i++)
580         {
581                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
582                 double angle = atan2(freq_imag[i], freq_real[i]);
583                 freq_real[i] = result * cos(angle);
584                 freq_imag[i] = result * sin(angle);
585         }
587         symmetry(window_size, freq_real, freq_imag);
588         return 0;
591 int ParametricFFT::read_samples(int64_t output_sample, 
592         int samples, 
593         double *buffer)
595         return plugin->read_samples(buffer,
596                 0,
597                 plugin->get_samplerate(),
598                 output_sample,
599                 samples);
609 ParametricEQ::ParametricEQ(PluginServer *server)
610  : PluginAClient(server)
612         PLUGIN_CONSTRUCTOR_MACRO
613         fft = 0;
614         need_reconfigure = 1;
617 ParametricEQ::~ParametricEQ()
619         PLUGIN_DESTRUCTOR_MACRO
621         if(fft) delete fft;
624 NEW_PICON_MACRO(ParametricEQ)
626 SHOW_GUI_MACRO(ParametricEQ, ParametricThread)
628 RAISE_WINDOW_MACRO(ParametricEQ)
630 SET_STRING_MACRO(ParametricEQ)
632 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
635 char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
636 int ParametricEQ::is_realtime() { return 1; }
638 void ParametricEQ::read_data(KeyFrame *keyframe)
640         FileXML input;
641         input.set_shared_string(keyframe->data, strlen(keyframe->data));
643         int result = 0;
644         while(!result)
645         {
646                 result = input.read_tag();
648                 if(!result)
649                 {
650                         if(input.tag.title_is("PARAMETRICEQ"))
651                         {
652                                 config.wetness = input.tag.get_property("WETNESS", config.wetness);
653                         }
654                         else
655                         if(input.tag.title_is("BAND"))
656                         {
657                                 int band = input.tag.get_property("NUMBER", 0);
658                                 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
659                                 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
660                                 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
661                                 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
662                         }
663                 }
664         }
667 void ParametricEQ::save_data(KeyFrame *keyframe)
669         FileXML output;
670         output.set_shared_string(keyframe->data, MESSAGESIZE);
672         output.tag.set_title("PARAMETRICEQ");
673         output.tag.set_property("WETNESS", config.wetness);
674         output.append_tag();
675         output.append_newline();
677         for(int i = 0; i < BANDS; i++)
678         {
679                 output.tag.set_title("BAND");
680                 output.tag.set_property("NUMBER", i);
681                 output.tag.set_property("FREQ", config.band[i].freq);
682                 output.tag.set_property("QUALITY", config.band[i].quality);
683                 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
684                 output.tag.set_property("MODE", config.band[i].mode);
685                 output.append_tag();
686                 output.append_newline();
687         }
689         output.terminate_string();
692 void ParametricEQ::reconfigure()
694         if(!fft)
695         {
696                 fft = new ParametricFFT(this);
697                 fft->initialize(WINDOW_SIZE);
698         }
700 // Reset envelope
702 //printf("ParametricEQ::reconfigure %f\n", wetness);
703         calculate_envelope();
705         for(int i = 0; i < WINDOW_SIZE / 2; i++)
706         {
707                 if(envelope[i] < 0) envelope[i] = 0;
708         }
710         need_reconfigure = 0;
713 double ParametricEQ::calculate_envelope()
715         double wetness = DB::fromdb(config.wetness);
716         int niquist = PluginAClient::project_sample_rate / 2;
717 //printf("ParametricEQ::calculate_envelope %d %d\n", niquist, PluginAClient::project_sample_rate);
720         for(int i = 0; i < WINDOW_SIZE / 2; i++)
721         {
722                 envelope[i] = wetness;
723         }
725         for(int pass = 0; pass < 2; pass++)
726         {
727                 for(int band = 0; band < BANDS; band++)
728                 {
729                         switch(config.band[band].mode)
730                         {
731                                 case ParametricBand::LOWPASS:
732                                         if(pass == 1)
733                                         {
734                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
735                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
736                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
737                                                 {
738                                                         if(i < cutoff) 
739                                                                 envelope[i] += magnitude;
740                                                 }
741                                         }
742                                         break;
744                                 case ParametricBand::HIGHPASS:
745                                         if(pass == 1)
746                                         {
747                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
748                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
749                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
750                                                 {
751                                                         if(i > cutoff) 
752                                                                 envelope[i] += magnitude;
753                                                 }
754                                         }
755                                         break;
757                                 case ParametricBand::BANDPASS:
758                                         if(pass == 0)
759                                         {
760                                                 double magnitude = (config.band[band].magnitude > 0) ? 
761                                                         (DB::fromdb(config.band[band].magnitude) - 1) : 
762                                                         (-1 + DB::fromdb(config.band[band].magnitude));
763                                                 double sigma = (config.band[band].quality < 1) ?
764                                                         (1.0 - config.band[band].quality) :
765                                                         0.01;
766                                                 sigma /= 4;
767                                                 double a = (double)config.band[band].freq / niquist;
768                                                 double normalize = gauss(sigma, 0, 0);
769                                                 if(config.band[band].magnitude <= -MAXMAGNITUDE) 
770                                                         magnitude = -1;
772                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
773                                                         envelope[i] += magnitude * 
774                                                                 gauss(sigma, a, (double)i / (WINDOW_SIZE / 2)) / 
775                                                                 normalize;
776                                         }
777                                         break;
778                         }
779                 }
780         }
781         return 0;
784 double ParametricEQ::gauss(double sigma, double a, double x)
786         if(EQUIV(sigma, 0)) sigma = 0.01;
788         return 1.0 / 
789                 sqrt(2 * M_PI * sigma * sigma) * 
790                 exp(-(x - a) * (x - a) / 
791                         (2 * sigma * sigma));
796 int ParametricEQ::process_buffer(int64_t size, 
797         double *buffer, 
798         int64_t start_position,
799         int sample_rate)
801         need_reconfigure |= load_configuration();
802         if(need_reconfigure) reconfigure();
803         
804         
805         fft->process_buffer(start_position, size, buffer, get_direction());
806         return 0;
817 int ParametricEQ::load_defaults()
819         char directory[BCTEXTLEN], string[BCTEXTLEN];
820         sprintf(directory, "%sparametriceq.rc", BCASTDIR);
821         defaults = new BC_Hash(directory);
822         defaults->load();
823         
824         config.wetness = defaults->get("WETNESS", config.wetness);
825         for(int i = 0; i < BANDS; i++)
826         {
827                 sprintf(string, "FREQ_%d", i);
828                 config.band[i].freq = defaults->get(string, config.band[i].freq);
829                 sprintf(string, "QUALITY_%d", i);
830                 config.band[i].quality = defaults->get(string, config.band[i].quality);
831                 sprintf(string, "MAGNITUDE_%d", i);
832                 config.band[i].magnitude = defaults->get(string, config.band[i].magnitude);
833                 sprintf(string, "MODE_%d", i);
834                 config.band[i].mode = defaults->get(string, config.band[i].mode);
835         }
836         return 0;
839 int ParametricEQ::save_defaults()
841         char string[BCTEXTLEN];
843         defaults->update("WETNESS", config.wetness);
846         for(int i = 0; i < BANDS; i++)
847         {
848                 sprintf(string, "FREQ_%d", i);
849                 defaults->update(string, config.band[i].freq);
850                 sprintf(string, "QUALITY_%d", i);
851                 defaults->update(string, config.band[i].quality);
852                 sprintf(string, "MAGNITUDE_%d", i);
853                 defaults->update(string, config.band[i].magnitude);
854                 sprintf(string, "MODE_%d", i);
855                 defaults->update(string, config.band[i].mode);
856         }
859         defaults->save();
861         return 0;
865 void ParametricEQ::reset()
867         need_reconfigure = 1;
868         thread = 0;
869         fft = 0;
872 void ParametricEQ::update_gui()
874         if(thread)
875         {
876                 load_configuration();
877                 thread->window->lock_window("ParametricEQ::update_gui");
878                 thread->window->update_gui();
879                 thread->window->unlock_window();
880         }