r370: Heroine Virutal's official release 1.2.1
[cinelerra_cv/mob.git] / hvirtual / plugins / parametric / parametric.C
blob8466bc9adaa82aeb21490ec0d2e777e5ded2af64
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "defaults.h"
4 #include "filexml.h"
5 #include "language.h"
6 #include "parametric.h"
7 #include "picon_png.h"
8 #include "units.h"
9 #include "vframe.h"
11 #include <math.h>
12 #include <string.h>
22 REGISTER_PLUGIN(ParametricEQ)
31 ParametricBand::ParametricBand()
33         freq = 440;
34         quality = 1;
35         magnitude = 0;
36         mode = NONE;
40 int ParametricBand::equivalent(ParametricBand &that)
42         if(freq == that.freq && 
43                 EQUIV(quality, that.quality) && 
44                 EQUIV(magnitude, that.magnitude) &&
45                 mode == that.mode)
46         {
47                 return 1;
48         }
49         else
50                 return 0;
54 void ParametricBand::copy_from(ParametricBand &that)
56         freq = that.freq;
57         quality = that.quality;
58         magnitude = that.magnitude;
59         mode = that.mode;
62 void ParametricBand::interpolate(ParametricBand &prev, 
63                 ParametricBand &next, 
64                 double prev_scale, 
65                 double next_scale)
67         freq = (int)(prev.freq * prev_scale + next.freq * next_scale + 0.5);
68         quality = prev.quality * prev_scale + next.quality * next_scale;
69         magnitude = prev.magnitude * prev_scale + next.magnitude * next_scale;
70         mode = prev.mode;
77 ParametricConfig::ParametricConfig()
79         wetness = INFINITYGAIN;
83 int ParametricConfig::equivalent(ParametricConfig &that)
85         for(int i = 0; i < BANDS; i++)
86                 if(!band[i].equivalent(that.band[i])) return 0;
88         if(!EQUIV(wetness, that.wetness)) return 0;
89         return 1;
92 void ParametricConfig::copy_from(ParametricConfig &that)
94         wetness = that.wetness;
95         for(int i = 0; i < BANDS; i++)
96                 band[i].copy_from(that.band[i]);
99 void ParametricConfig::interpolate(ParametricConfig &prev, 
100                 ParametricConfig &next, 
101                 int64_t prev_frame, 
102                 int64_t next_frame, 
103                 int64_t current_frame)
105         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
106         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
107         wetness = prev.wetness;
108         for(int i = 0; i < BANDS; i++)
109         {
110                 band[i].interpolate(prev.band[i], next.band[i], prev_scale, next_scale);
111         }
132 ParametricFreq::ParametricFreq(ParametricEQ *plugin, int x, int y, int band)
133  : BC_QPot(x, y, plugin->config.band[band].freq)
135         this->plugin = plugin;
136         this->band = band;
139 int ParametricFreq::handle_event()
141         plugin->config.band[band].freq = get_value();
142         plugin->send_configure_change();
143         plugin->thread->window->update_canvas();
144         return 1;
154 ParametricQuality::ParametricQuality(ParametricEQ *plugin, int x, int y, int band)
155  : BC_FPot(x, y, plugin->config.band[band].quality, 0, 1)
157         this->plugin = plugin;
158         this->band = band;
159         set_precision(0.01);
162 int ParametricQuality::handle_event()
164         plugin->config.band[band].quality = get_value();
165         plugin->send_configure_change();
166         plugin->thread->window->update_canvas();
167         return 1;
180 ParametricMagnitude::ParametricMagnitude(ParametricEQ *plugin, int x, int y, int band)
181  : BC_FPot(x, y, plugin->config.band[band].magnitude, -MAXMAGNITUDE, MAXMAGNITUDE)
183         this->plugin = plugin;
184         this->band = band;
187 int ParametricMagnitude::handle_event()
189         plugin->config.band[band].magnitude = get_value();
190         plugin->send_configure_change();
191         plugin->thread->window->update_canvas();
192         return 1;
203 ParametricMode::ParametricMode(ParametricEQ *plugin, int x, int y, int band)
204  : BC_PopupMenu(x, 
205                 y, 
206                 150, 
207                 mode_to_text(plugin->config.band[band].mode))
209 //printf("ParametricMode::ParametricMode %d %d\n", band, plugin->config.band[band].mode);
210         this->plugin = plugin;
211         this->band = band;
214 void ParametricMode::create_objects()
216         add_item(new BC_MenuItem(mode_to_text(ParametricBand::LOWPASS)));
217         add_item(new BC_MenuItem(mode_to_text(ParametricBand::HIGHPASS)));
218         add_item(new BC_MenuItem(mode_to_text(ParametricBand::BANDPASS)));
219         add_item(new BC_MenuItem(mode_to_text(ParametricBand::NONE)));
223 int ParametricMode::handle_event()
225         plugin->config.band[band].mode = text_to_mode(get_text());
226         plugin->send_configure_change();
227         plugin->thread->window->update_canvas();
228         return 1;
231 int ParametricMode::text_to_mode(char *text)
233         if(!strcmp(mode_to_text(ParametricBand::LOWPASS), text)) return ParametricBand::LOWPASS;
234         if(!strcmp(mode_to_text(ParametricBand::HIGHPASS), text)) return ParametricBand::HIGHPASS;
235         if(!strcmp(mode_to_text(ParametricBand::BANDPASS), text)) return ParametricBand::BANDPASS;
236         if(!strcmp(mode_to_text(ParametricBand::NONE), text)) return ParametricBand::NONE;
237         return ParametricBand::BANDPASS;
242 char* ParametricMode::mode_to_text(int mode)
244         switch(mode)
245         {
246                 case ParametricBand::LOWPASS:
247                         return _("Lowpass");
248                         break;
249                 case ParametricBand::HIGHPASS:
250                         return _("Highpass");
251                         break;
252                 case ParametricBand::BANDPASS:
253                         return _("Bandpass");
254                         break;
255                 case ParametricBand::NONE:
256                         return _("None");
257                         break;
258         }
259         return "";
272 ParametricBandGUI::ParametricBandGUI(ParametricEQ *plugin, ParametricWindow *window, int x, int y, int band)
274         this->plugin = plugin;
275         this->band = band;
276         this->window = window;
277         this->x = x;
278         this->y = y;
281 ParametricBandGUI::~ParametricBandGUI()
286 #define X1 10
287 #define X2 60
288 #define X3 110
289 #define X4 160
291         
292 void ParametricBandGUI::create_objects()
294         window->add_subwindow(freq = new ParametricFreq(plugin, X1, y, band));
295         window->add_subwindow(quality = new ParametricQuality(plugin, X2, y, band));
296         window->add_subwindow(magnitude = new ParametricMagnitude(plugin, X3, y, band));
297         window->add_subwindow(mode = new ParametricMode(plugin, X4, y, band));
298         mode->create_objects();
301 void ParametricBandGUI::update_gui()
303         freq->update(plugin->config.band[band].freq);
304         quality->update(plugin->config.band[band].quality);
305         magnitude->update(plugin->config.band[band].magnitude);
313 ParametricWetness::ParametricWetness(ParametricEQ *plugin, int x, int y)
314  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
316         this->plugin = plugin;
319 int ParametricWetness::handle_event()
321         plugin->config.wetness = get_value();
322         plugin->send_configure_change();
323         plugin->thread->window->update_canvas();
324         return 1;
332 ParametricWindow::ParametricWindow(ParametricEQ *plugin, int x, int y)
333  : BC_Window(plugin->gui_string, 
334         x, 
335         y, 
336         320, 
337         400, 
338         320, 
339         400,
340         0, 
341         0,
342         1)
344         this->plugin = plugin;
347 ParametricWindow::~ParametricWindow()
349         for(int i = 0; i < BANDS; i++)
350                 delete bands[i];
353 void ParametricWindow::create_objects()
355         int y = 35;
356         
357         
358         add_subwindow(new BC_Title(X1, 10, _("Freq")));
359         add_subwindow(new BC_Title(X2, 10, _("Qual")));
360         add_subwindow(new BC_Title(X3, 10, _("Level")));
361         add_subwindow(new BC_Title(X4, 10, _("Mode")));
362         for(int i = 0; i < BANDS; i++)
363         {
364                 bands[i] = new ParametricBandGUI(plugin, this, 10, y, i);
365                 bands[i]->create_objects();
366                 y += 50;
367         }
369         add_subwindow(new BC_Title(10, y + 10, _("Wetness:")));
370         add_subwindow(wetness = new ParametricWetness(plugin, 80, y));
371         y += 50;
372         int canvas_x = 30;
373         int canvas_y = y;
374         int canvas_w = get_w() - canvas_x - 10;
375         int canvas_h = get_h() - canvas_y - 30;
376         add_subwindow(canvas = new BC_SubWindow(canvas_x, 
377                 canvas_y, 
378                 canvas_w, 
379                 canvas_h, 
380                 WHITE));
382 // Draw canvas titles
383         set_font(SMALLFONT);
384 #define MAJOR_DIVISIONS 4
385 #define MINOR_DIVISIONS 5
386         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
387         {
388                 int y1 = canvas_y + canvas_h - i * (canvas_h / MAJOR_DIVISIONS) - 2;
389                 int y2 = y1 + 3;
390                 int x1 = canvas_x - 25;
391                 int x2 = canvas_x - 10;
392                 int x3 = canvas_x - 2;
394                 char string[BCTEXTLEN];
395                 if(i == 0)
396                         sprintf(string, "oo");
397                 else
398                         sprintf(string, "%d", i * 5 - 5);
400                 set_color(BLACK);
401                 draw_text(x1 + 1, y2 + 1, string);
402                 draw_line(x2 + 1, y1 + 1, x3 + 1, y1 + 1);
403                 set_color(RED);
404                 draw_text(x1, y2, string);
405                 draw_line(x2, y1, x3, y1);
407                 if(i < MAJOR_DIVISIONS)
408                 {
409                         for(int j = 1; j < MINOR_DIVISIONS; j++)
410                         {
411                                 int y3 = y1 - j * (canvas_h / MAJOR_DIVISIONS) / MINOR_DIVISIONS;
412                                 int x4 = x3 - 5;
413                                 set_color(BLACK);
414                                 draw_line(x4 + 1, y3 + 1, x3 + 1, y3 + 1);
415                                 set_color(RED);
416                                 draw_line(x4, y3, x3, y3);
417                         }
418                 }
419         }
421 #undef MAJOR_DIVISIONS
422 #define MAJOR_DIVISIONS 5
423         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
424         {
425                 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
426                 int x1 = canvas_x + i * canvas_w / MAJOR_DIVISIONS;
427                 int y1 = canvas_y + canvas_h + 20;
428                 char string[BCTEXTLEN];
429                 sprintf(string, "%d", freq);
430                 int x2 = x1 - get_text_width(SMALLFONT, string);
431                 int y2 = y1 - 10;
432                 int y3 = y2 - 5;
433                 int y4 = canvas_y + canvas_h;
434                 
435                 set_color(BLACK);
436                 draw_text(x2 + 1, y1 + 1, string);
437                 draw_line(x1 + 1, y4 + 1, x1 + 1, y2 + 1);
438                 set_color(RED);
439                 draw_text(x2, y1, string);
440                 draw_line(x1, y4, x1, y2);
442                 if(i < MAJOR_DIVISIONS)
443                 {
444 #undef MINOR_DIVISIONS
445 #define MINOR_DIVISIONS 5
446                         for(int j = 0; j < MINOR_DIVISIONS; j++)
447                         {
448                                 int x3 = (int)(x1 + 
449                                         (canvas_w / MAJOR_DIVISIONS) -
450                                         exp(-(double)j * 0.7) * 
451                                         (canvas_w / MAJOR_DIVISIONS));
452                                 set_color(BLACK);
453                                 draw_line(x3 + 1, y4 + 1, x3 + 1, y3 + 1);
454                                 set_color(RED);
455                                 draw_line(x3, y4, x3, y3);
456                         }
457                 }
458         }
460         update_canvas();
461         show_window();
462         flush();
465 int ParametricWindow::close_event()
467 // Set result to 1 to indicate a client side close
468         set_done(1);
469         return 1;
472 void ParametricWindow::update_gui()
474         for(int i = 0; i < BANDS; i++)
475                 bands[i]->update_gui();
476         wetness->update(plugin->config.wetness);
477         update_canvas();
481 void ParametricWindow::update_canvas()
483         double scale = 1;
484         int y1 = canvas->get_h() / 2;
485         int niquist = plugin->PluginAClient::project_sample_rate / 2;
486         int wetness = canvas->get_h() -
487                 (int)((plugin->config.wetness - INFINITYGAIN) /
488                         -INFINITYGAIN * 
489                         canvas->get_h() / 
490                         4);
492         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
493 //      canvas->set_color(GREEN);
494 //      canvas->draw_line(0, 
495 //              wetness, 
496 //              canvas->get_w(), 
497 //              wetness);
499         canvas->set_color(BLACK);
501         plugin->calculate_envelope();
502         for(int i = 0; i < canvas->get_w() - 1; i++)
503         {
504                 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
505                 int index = freq * WINDOW_SIZE / 2 / niquist;
506                 double magnitude = plugin->envelope[index];
507                 int y2 = canvas->get_h() * 3 / 4;
509                 if(magnitude > 1)
510                 {
511                         y2 -= (int)(DB::todb(magnitude) * 
512                                 canvas->get_h() * 
513                                 3 / 
514                                 4 / 
515                                 15);
516                 }
517                 else
518                 {
519                         y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
520 //                      y2 += (int)(DB::todb(magnitude) / 
521 //                              INFINITYGAIN * 
522 //                              canvas->get_h() / 
523 //                              4);
524                 }
525                 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
526                 y1 = y2;
527         }
530 //      for(int i = 0; i < canvas->get_w(); i++)
531 //      {
532 //              int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
533 //              int index = (int)((float)freq / niquist * WINDOW_SIZE / 2);
534 //              double magnitude = plugin->envelope[index];
535 //              int y2 = canvas->get_h() - 
536 //                      (int)((double)canvas->get_h() / normalize * magnitude);
537 //              if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
538 //              y1 = y2;
539 //      }
541         canvas->flash();
542         flush();
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 Defaults(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         }