r860: Merge 2.1:
[cinelerra_cv.git] / plugins / parametric / parametric.C
blob93ad207d26a30537fb2358b6516319f8b48c84f7
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                 double magnitude = plugin->envelope[index];
512                 int y2 = canvas->get_h() * 3 / 4;
514                 if(magnitude > 1)
515                 {
516                         y2 -= (int)(DB::todb(magnitude) * 
517                                 canvas->get_h() * 
518                                 3 / 
519                                 4 / 
520                                 15);
521                 }
522                 else
523                 {
524                         y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
525                 }
526                 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
527                 y1 = y2;
528         }
531 //      for(int i = 0; i < canvas->get_w(); i++)
532 //      {
533 //              int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
534 //              int index = (int)((float)freq / niquist * WINDOW_SIZE / 2);
535 //              double magnitude = plugin->envelope[index];
536 //              int y2 = canvas->get_h() - 
537 //                      (int)((double)canvas->get_h() / normalize * magnitude);
538 //              if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
539 //              y1 = y2;
540 //      }
542         canvas->flash();
551 PLUGIN_THREAD_OBJECT(ParametricEQ, ParametricThread, ParametricWindow)
559 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
560  : CrossfadeFFT()
562         this->plugin = plugin;
565 ParametricFFT::~ParametricFFT()
570 int ParametricFFT::signal_process()
572         for(int i = 0; i < window_size / 2; i++)
573         {
574                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
575                 double angle = atan2(freq_imag[i], freq_real[i]);
576                 freq_real[i] = result * cos(angle);
577                 freq_imag[i] = result * sin(angle);
578         }
580         symmetry(window_size, freq_real, freq_imag);
581         return 0;
584 int ParametricFFT::read_samples(int64_t output_sample, 
585         int samples, 
586         double *buffer)
588         return plugin->read_samples(buffer,
589                 0,
590                 plugin->get_samplerate(),
591                 output_sample,
592                 samples);
602 ParametricEQ::ParametricEQ(PluginServer *server)
603  : PluginAClient(server)
605         PLUGIN_CONSTRUCTOR_MACRO
606         fft = 0;
607         need_reconfigure = 1;
610 ParametricEQ::~ParametricEQ()
612         PLUGIN_DESTRUCTOR_MACRO
614         if(fft) delete fft;
617 NEW_PICON_MACRO(ParametricEQ)
619 SHOW_GUI_MACRO(ParametricEQ, ParametricThread)
621 RAISE_WINDOW_MACRO(ParametricEQ)
623 SET_STRING_MACRO(ParametricEQ)
625 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
628 char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
629 int ParametricEQ::is_realtime() { return 1; }
631 void ParametricEQ::read_data(KeyFrame *keyframe)
633         FileXML input;
634         input.set_shared_string(keyframe->data, strlen(keyframe->data));
636         int result = 0;
637         while(!result)
638         {
639                 result = input.read_tag();
641                 if(!result)
642                 {
643                         if(input.tag.title_is("PARAMETRICEQ"))
644                         {
645                                 config.wetness = input.tag.get_property("WETNESS", config.wetness);
646                         }
647                         else
648                         if(input.tag.title_is("BAND"))
649                         {
650                                 int band = input.tag.get_property("NUMBER", 0);
651                                 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
652                                 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
653                                 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
654                                 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
655                         }
656                 }
657         }
660 void ParametricEQ::save_data(KeyFrame *keyframe)
662         FileXML output;
663         output.set_shared_string(keyframe->data, MESSAGESIZE);
665         output.tag.set_title("PARAMETRICEQ");
666         output.tag.set_property("WETNESS", config.wetness);
667         output.append_tag();
668         output.append_newline();
670         for(int i = 0; i < BANDS; i++)
671         {
672                 output.tag.set_title("BAND");
673                 output.tag.set_property("NUMBER", i);
674                 output.tag.set_property("FREQ", config.band[i].freq);
675                 output.tag.set_property("QUALITY", config.band[i].quality);
676                 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
677                 output.tag.set_property("MODE", config.band[i].mode);
678                 output.append_tag();
679                 output.append_newline();
680         }
682         output.terminate_string();
685 void ParametricEQ::reconfigure()
687         if(!fft)
688         {
689                 fft = new ParametricFFT(this);
690                 fft->initialize(WINDOW_SIZE);
691         }
693 // Reset envelope
695 //printf("ParametricEQ::reconfigure %f\n", wetness);
696         calculate_envelope();
698         for(int i = 0; i < WINDOW_SIZE / 2; i++)
699         {
700                 if(envelope[i] < 0) envelope[i] = 0;
701         }
703         need_reconfigure = 0;
706 double ParametricEQ::calculate_envelope()
708         double wetness = DB::fromdb(config.wetness);
709         int niquist = PluginAClient::project_sample_rate / 2;
710 //printf("ParametricEQ::calculate_envelope %d %d\n", niquist, PluginAClient::project_sample_rate);
713         for(int i = 0; i < WINDOW_SIZE / 2; i++)
714         {
715                 envelope[i] = wetness;
716         }
718         for(int pass = 0; pass < 2; pass++)
719         {
720                 for(int band = 0; band < BANDS; band++)
721                 {
722                         switch(config.band[band].mode)
723                         {
724                                 case ParametricBand::LOWPASS:
725                                         if(pass == 1)
726                                         {
727                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
728                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
729                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
730                                                 {
731                                                         if(i < cutoff) 
732                                                                 envelope[i] += magnitude;
733                                                 }
734                                         }
735                                         break;
737                                 case ParametricBand::HIGHPASS:
738                                         if(pass == 1)
739                                         {
740                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
741                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
742                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
743                                                 {
744                                                         if(i > cutoff) 
745                                                                 envelope[i] += magnitude;
746                                                 }
747                                         }
748                                         break;
750                                 case ParametricBand::BANDPASS:
751                                         if(pass == 0)
752                                         {
753                                                 double magnitude = (config.band[band].magnitude > 0) ? 
754                                                         (DB::fromdb(config.band[band].magnitude) - 1) : 
755                                                         (-1 + DB::fromdb(config.band[band].magnitude));
756                                                 double sigma = (config.band[band].quality < 1) ?
757                                                         (1.0 - config.band[band].quality) :
758                                                         0.01;
759                                                 sigma /= 4;
760                                                 double a = (double)config.band[band].freq / niquist;
761                                                 double normalize = gauss(sigma, 0, 0);
762                                                 if(config.band[band].magnitude <= -MAXMAGNITUDE) 
763                                                         magnitude = -1;
765                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
766                                                         envelope[i] += magnitude * 
767                                                                 gauss(sigma, a, (double)i / (WINDOW_SIZE / 2)) / 
768                                                                 normalize;
769                                         }
770                                         break;
771                         }
772                 }
773         }
774         return 0;
777 double ParametricEQ::gauss(double sigma, double a, double x)
779         if(EQUIV(sigma, 0)) sigma = 0.01;
781         return 1.0 / 
782                 sqrt(2 * M_PI * sigma * sigma) * 
783                 exp(-(x - a) * (x - a) / 
784                         (2 * sigma * sigma));
789 int ParametricEQ::process_buffer(int64_t size, 
790         double *buffer, 
791         int64_t start_position,
792         int sample_rate)
794         need_reconfigure |= load_configuration();
795         if(need_reconfigure) reconfigure();
796         
797         
798         fft->process_buffer(start_position, size, buffer, get_direction());
799         return 0;
810 int ParametricEQ::load_defaults()
812         char directory[BCTEXTLEN], string[BCTEXTLEN];
813         sprintf(directory, "%sparametriceq.rc", BCASTDIR);
814         defaults = new BC_Hash(directory);
815         defaults->load();
816         
817         config.wetness = defaults->get("WETNESS", config.wetness);
818         for(int i = 0; i < BANDS; i++)
819         {
820                 sprintf(string, "FREQ_%d", i);
821                 config.band[i].freq = defaults->get(string, config.band[i].freq);
822                 sprintf(string, "QUALITY_%d", i);
823                 config.band[i].quality = defaults->get(string, config.band[i].quality);
824                 sprintf(string, "MAGNITUDE_%d", i);
825                 config.band[i].magnitude = defaults->get(string, config.band[i].magnitude);
826                 sprintf(string, "MODE_%d", i);
827                 config.band[i].mode = defaults->get(string, config.band[i].mode);
828         }
829         return 0;
832 int ParametricEQ::save_defaults()
834         char string[BCTEXTLEN];
836         defaults->update("WETNESS", config.wetness);
839         for(int i = 0; i < BANDS; i++)
840         {
841                 sprintf(string, "FREQ_%d", i);
842                 defaults->update(string, config.band[i].freq);
843                 sprintf(string, "QUALITY_%d", i);
844                 defaults->update(string, config.band[i].quality);
845                 sprintf(string, "MAGNITUDE_%d", i);
846                 defaults->update(string, config.band[i].magnitude);
847                 sprintf(string, "MODE_%d", i);
848                 defaults->update(string, config.band[i].mode);
849         }
852         defaults->save();
854         return 0;
858 void ParametricEQ::reset()
860         need_reconfigure = 1;
861         thread = 0;
862         fft = 0;
865 void ParametricEQ::update_gui()
867         if(thread)
868         {
869                 load_configuration();
870                 thread->window->lock_window("ParametricEQ::update_gui");
871                 thread->window->update_gui();
872                 thread->window->unlock_window();
873         }