r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / parametric / parametric.C
blob3d35d856562265a862beb605986c1e9e8a72f22b
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "defaults.h"
4 #include "filexml.h"
5 #include "parametric.h"
6 #include "picon_png.h"
7 #include "units.h"
8 #include "vframe.h"
10 #include <math.h>
11 #include <string.h>
13 #include <libintl.h>
14 #define _(String) gettext(String)
15 #define gettext_noop(String) String
16 #define N_(String) gettext_noop (String)
25 PluginClient* new_plugin(PluginServer *server)
27         return new ParametricEQ(server);
37 ParametricBand::ParametricBand()
39         freq = 440;
40         quality = 1;
41         magnitude = 0;
42         mode = NONE;
46 int ParametricBand::equivalent(ParametricBand &that)
48         if(freq == that.freq && 
49                 EQUIV(quality, that.quality) && 
50                 EQUIV(magnitude, that.magnitude) &&
51                 mode == that.mode)
52         {
53                 return 1;
54         }
55         else
56                 return 0;
60 void ParametricBand::copy_from(ParametricBand &that)
62         freq = that.freq;
63         quality = that.quality;
64         magnitude = that.magnitude;
65         mode = that.mode;
68 void ParametricBand::interpolate(ParametricBand &prev, 
69                 ParametricBand &next, 
70                 double prev_scale, 
71                 double next_scale)
73         freq = (int)(prev.freq * prev_scale + next.freq * next_scale + 0.5);
74         quality = prev.quality * prev_scale + next.quality * next_scale;
75         magnitude = prev.magnitude * prev_scale + next.magnitude * next_scale;
76         mode = prev.mode;
83 ParametricConfig::ParametricConfig()
85         wetness = INFINITYGAIN;
89 int ParametricConfig::equivalent(ParametricConfig &that)
91         for(int i = 0; i < BANDS; i++)
92                 if(!band[i].equivalent(that.band[i])) return 0;
94         if(!EQUIV(wetness, that.wetness)) return 0;
95         return 1;
98 void ParametricConfig::copy_from(ParametricConfig &that)
100         wetness = that.wetness;
101         for(int i = 0; i < BANDS; i++)
102                 band[i].copy_from(that.band[i]);
105 void ParametricConfig::interpolate(ParametricConfig &prev, 
106                 ParametricConfig &next, 
107                 int64_t prev_frame, 
108                 int64_t next_frame, 
109                 int64_t current_frame)
111         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
112         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
113         wetness = prev.wetness;
114         for(int i = 0; i < BANDS; i++)
115         {
116                 band[i].interpolate(prev.band[i], next.band[i], prev_scale, next_scale);
117         }
138 ParametricFreq::ParametricFreq(ParametricEQ *plugin, int x, int y, int band)
139  : BC_QPot(x, y, plugin->config.band[band].freq)
141         this->plugin = plugin;
142         this->band = band;
145 int ParametricFreq::handle_event()
147         plugin->config.band[band].freq = get_value();
148         plugin->send_configure_change();
149         plugin->thread->window->update_canvas();
150         return 1;
160 ParametricQuality::ParametricQuality(ParametricEQ *plugin, int x, int y, int band)
161  : BC_FPot(x, y, plugin->config.band[band].quality, 0, 1)
163         this->plugin = plugin;
164         this->band = band;
165         set_precision(0.01);
168 int ParametricQuality::handle_event()
170         plugin->config.band[band].quality = get_value();
171         plugin->send_configure_change();
172         plugin->thread->window->update_canvas();
173         return 1;
186 ParametricMagnitude::ParametricMagnitude(ParametricEQ *plugin, int x, int y, int band)
187  : BC_FPot(x, y, plugin->config.band[band].magnitude, -MAXMAGNITUDE, MAXMAGNITUDE)
189         this->plugin = plugin;
190         this->band = band;
193 int ParametricMagnitude::handle_event()
195         plugin->config.band[band].magnitude = get_value();
196         plugin->send_configure_change();
197         plugin->thread->window->update_canvas();
198         return 1;
209 ParametricMode::ParametricMode(ParametricEQ *plugin, int x, int y, int band)
210  : BC_PopupMenu(x, 
211                 y, 
212                 150, 
213                 mode_to_text(plugin->config.band[band].mode))
215 //printf("ParametricMode::ParametricMode %d %d\n", band, plugin->config.band[band].mode);
216         this->plugin = plugin;
217         this->band = band;
220 void ParametricMode::create_objects()
222         add_item(new BC_MenuItem(mode_to_text(ParametricBand::LOWPASS)));
223         add_item(new BC_MenuItem(mode_to_text(ParametricBand::HIGHPASS)));
224         add_item(new BC_MenuItem(mode_to_text(ParametricBand::BANDPASS)));
225         add_item(new BC_MenuItem(mode_to_text(ParametricBand::NONE)));
229 int ParametricMode::handle_event()
231         plugin->config.band[band].mode = text_to_mode(get_text());
232         plugin->send_configure_change();
233         plugin->thread->window->update_canvas();
234         return 1;
237 int ParametricMode::text_to_mode(char *text)
239         if(!strcmp(mode_to_text(ParametricBand::LOWPASS), text)) return ParametricBand::LOWPASS;
240         if(!strcmp(mode_to_text(ParametricBand::HIGHPASS), text)) return ParametricBand::HIGHPASS;
241         if(!strcmp(mode_to_text(ParametricBand::BANDPASS), text)) return ParametricBand::BANDPASS;
242         if(!strcmp(mode_to_text(ParametricBand::NONE), text)) return ParametricBand::NONE;
243         return ParametricBand::BANDPASS;
248 char* ParametricMode::mode_to_text(int mode)
250         switch(mode)
251         {
252                 case ParametricBand::LOWPASS:
253                         return _("Lowpass");
254                         break;
255                 case ParametricBand::HIGHPASS:
256                         return _("Highpass");
257                         break;
258                 case ParametricBand::BANDPASS:
259                         return _("Bandpass");
260                         break;
261                 case ParametricBand::NONE:
262                         return _("None");
263                         break;
264         }
265         return "";
278 ParametricBandGUI::ParametricBandGUI(ParametricEQ *plugin, ParametricWindow *window, int x, int y, int band)
280         this->plugin = plugin;
281         this->band = band;
282         this->window = window;
283         this->x = x;
284         this->y = y;
287 ParametricBandGUI::~ParametricBandGUI()
292 #define X1 10
293 #define X2 60
294 #define X3 110
295 #define X4 160
297         
298 void ParametricBandGUI::create_objects()
300         window->add_subwindow(freq = new ParametricFreq(plugin, X1, y, band));
301         window->add_subwindow(quality = new ParametricQuality(plugin, X2, y, band));
302         window->add_subwindow(magnitude = new ParametricMagnitude(plugin, X3, y, band));
303         window->add_subwindow(mode = new ParametricMode(plugin, X4, y, band));
304         mode->create_objects();
307 void ParametricBandGUI::update_gui()
309         freq->update(plugin->config.band[band].freq);
310         quality->update(plugin->config.band[band].quality);
311         magnitude->update(plugin->config.band[band].magnitude);
319 ParametricWetness::ParametricWetness(ParametricEQ *plugin, int x, int y)
320  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
322         this->plugin = plugin;
325 int ParametricWetness::handle_event()
327         plugin->config.wetness = get_value();
328         plugin->send_configure_change();
329         plugin->thread->window->update_canvas();
330         return 1;
338 ParametricWindow::ParametricWindow(ParametricEQ *plugin, int x, int y)
339  : BC_Window(plugin->gui_string, 
340         x, 
341         y, 
342         320, 
343         400, 
344         320, 
345         400,
346         0, 
347         0,
348         1)
350         this->plugin = plugin;
353 ParametricWindow::~ParametricWindow()
355         for(int i = 0; i < BANDS; i++)
356                 delete bands[i];
359 void ParametricWindow::create_objects()
361         int y = 35;
362         
363         
364         add_subwindow(new BC_Title(X1, 10, _("Freq")));
365         add_subwindow(new BC_Title(X2, 10, _("Qual")));
366         add_subwindow(new BC_Title(X3, 10, _("Level")));
367         add_subwindow(new BC_Title(X4, 10, _("Mode")));
368         for(int i = 0; i < BANDS; i++)
369         {
370                 bands[i] = new ParametricBandGUI(plugin, this, 10, y, i);
371                 bands[i]->create_objects();
372                 y += 50;
373         }
375         add_subwindow(new BC_Title(10, y + 10, _("Wetness:")));
376         add_subwindow(wetness = new ParametricWetness(plugin, 80, y));
377         y += 50;
378         add_subwindow(canvas = new BC_SubWindow(10, y, get_w() - 20, get_h() - y - 10, WHITE));
379         
380         
381         update_canvas();
382         show_window();
383         flush();
386 int ParametricWindow::close_event()
388 // Set result to 1 to indicate a client side close
389         set_done(1);
390         return 1;
393 void ParametricWindow::update_gui()
395         for(int i = 0; i < BANDS; i++)
396                 bands[i]->update_gui();
397         wetness->update(plugin->config.wetness);
398         update_canvas();
402 void ParametricWindow::update_canvas()
404 //printf("ParametricWindow::update_canvas 1\n");
405         double scale = 1;
406         int y1 = canvas->get_h() / 2;
407         double normalize = DB::fromdb(MAXMAGNITUDE);
408         int niquist = plugin->PluginAClient::project_sample_rate / 2;
410         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
411         canvas->set_color(GREEN);
412         canvas->draw_line(0, 
413                 canvas->get_h() - (int)((double)canvas->get_h() / normalize), 
414                 canvas->get_w(), 
415                 canvas->get_h() - (int)((double)canvas->get_h() / normalize));
417         canvas->set_color(BLACK);
419         plugin->calculate_envelope();
420         for(int i = 0; i < canvas->get_w(); i++)
421         {
422                 int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
423                 int index = (int)((float)freq / niquist * WINDOW_SIZE / 2);
424                 double magnitude = plugin->envelope[index];
425                 int y2 = canvas->get_h() - 
426                         (int)((double)canvas->get_h() / normalize * magnitude);
427                 if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
428                 y1 = y2;
429         }
431         canvas->flash();
432         flush();
441 PLUGIN_THREAD_OBJECT(ParametricEQ, ParametricThread, ParametricWindow)
449 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
450  : CrossfadeFFT()
452         this->plugin = plugin;
455 ParametricFFT::~ParametricFFT()
460 int ParametricFFT::signal_process()
462 //printf("ParametricFFT::signal_process 1\n");
463         for(int i = 0; i < window_size / 2; i++)
464         {
465                 double result = plugin->envelope[i] * sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
466                 double angle = atan2(freq_imag[i], freq_real[i]);
467                 freq_real[i] = result * cos(angle);
468                 freq_imag[i] = result * sin(angle);
469         }
470 //printf("ParametricFFT::signal_process 2\n");
472         symmetry(window_size, freq_real, freq_imag);
473 //printf("ParametricFFT::signal_process 3\n");
474         return 0;
486 ParametricEQ::ParametricEQ(PluginServer *server)
487  : PluginAClient(server)
489 //printf("ParametricEQ::ParametricEQ 1\n");
490         reset();
491         load_defaults();
492 //printf("ParametricEQ::ParametricEQ 2\n");
495 ParametricEQ::~ParametricEQ()
497         if(thread)
498         {
499                 thread->window->set_done(0);
500                 thread->completion.lock();
501                 delete thread;
502         }
503         
504         save_defaults();
505         delete defaults;
507         if(fft) delete fft;
510 NEW_PICON_MACRO(ParametricEQ)
512 SHOW_GUI_MACRO(ParametricEQ, ParametricThread)
514 RAISE_WINDOW_MACRO(ParametricEQ)
516 SET_STRING_MACRO(ParametricEQ)
518 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
521 char* ParametricEQ::plugin_title()
523         return _("EQ Parametric");
526 int ParametricEQ::is_realtime()
528         return 1;
531 void ParametricEQ::read_data(KeyFrame *keyframe)
533         FileXML input;
534         input.set_shared_string(keyframe->data, strlen(keyframe->data));
536         int result = 0;
537         while(!result)
538         {
539                 result = input.read_tag();
541                 if(!result)
542                 {
543                         if(input.tag.title_is("PARAMETRICEQ"))
544                         {
545                                 config.wetness = input.tag.get_property("WETNESS", config.wetness);
546                         }
547                         else
548                         if(input.tag.title_is("BAND"))
549                         {
550                                 int band = input.tag.get_property("NUMBER", 0);
551                                 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
552                                 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
553                                 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
554                                 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
555                         }
556                 }
557         }
560 void ParametricEQ::save_data(KeyFrame *keyframe)
562         FileXML output;
563         output.set_shared_string(keyframe->data, MESSAGESIZE);
565         output.tag.set_title("PARAMETRICEQ");
566         output.tag.set_property("WETNESS", config.wetness);
567         output.append_tag();
568         output.append_newline();
570         for(int i = 0; i < BANDS; i++)
571         {
572                 output.tag.set_title("BAND");
573                 output.tag.set_property("NUMBER", i);
574                 output.tag.set_property("FREQ", config.band[i].freq);
575                 output.tag.set_property("QUALITY", config.band[i].quality);
576                 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
577                 output.tag.set_property("MODE", config.band[i].mode);
578                 output.append_tag();
579                 output.append_newline();
580         }
582         output.terminate_string();
585 void ParametricEQ::reconfigure()
587         if(!fft)
588         {
589                 fft = new ParametricFFT(this);
590                 fft->initialize(WINDOW_SIZE);
591         }
593 // Reset envelope
595 //printf("ParametricEQ::reconfigure %f\n", wetness);
596         calculate_envelope();
598         for(int i = 0; i < WINDOW_SIZE / 2; i++)
599         {
600                 if(envelope[i] < 0) envelope[i] = 0;
601         }
603         need_reconfigure = 0;
606 double ParametricEQ::calculate_envelope()
608         double wetness = DB::fromdb(config.wetness);
609         int niquist = PluginAClient::project_sample_rate / 2;
610 //printf("ParametricEQ::calculate_envelope %d %d\n", niquist, PluginAClient::project_sample_rate);
613         for(int i = 0; i < WINDOW_SIZE / 2; i++)
614         {
615                 envelope[i] = wetness;
616         }
618         for(int pass = 0; pass < 2; pass++)
619         {
620                 for(int band = 0; band < BANDS; band++)
621                 {
622                         switch(config.band[band].mode)
623                         {
624                                 case ParametricBand::LOWPASS:
625                                         if(pass == 1)
626                                         {
627                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
628                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
629                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
630                                                 {
631                                                         if(i < cutoff) 
632                                                                 envelope[i] += magnitude;
633                                                 }
634                                         }
635                                         break;
637                                 case ParametricBand::HIGHPASS:
638                                         if(pass == 1)
639                                         {
640                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
641                                                 int cutoff = (int)((float)config.band[band].freq / niquist * WINDOW_SIZE / 2);
642                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
643                                                 {
644                                                         if(i > cutoff) 
645                                                                 envelope[i] += magnitude;
646                                                 }
647                                         }
648                                         break;
650                                 case ParametricBand::BANDPASS:
651                                         if(pass == 0)
652                                         {
653                                                 double magnitude = (config.band[band].magnitude > 0) ? 
654                                                         (DB::fromdb(config.band[band].magnitude) - 1) : 
655                                                         (-1 + DB::fromdb(config.band[band].magnitude));
656                                                 double sigma = (config.band[band].quality < 1) ?
657                                                         (1.0 - config.band[band].quality) :
658                                                         0.01;
659                                                 sigma /= 4;
660                                                 double a = (double)config.band[band].freq / niquist;
661                                                 double normalize = gauss(sigma, 0, 0);
662                                                 if(config.band[band].magnitude <= -MAXMAGNITUDE) 
663                                                         magnitude = -1;
665                                                 for(int i = 0; i < WINDOW_SIZE / 2; i++)
666                                                         envelope[i] += magnitude * 
667                                                                 gauss(sigma, a, (double)i / (WINDOW_SIZE / 2)) / 
668                                                                 normalize;
669                                         }
670                                         break;
671                         }
672                 }
673         }
674         return 0;
677 double ParametricEQ::gauss(double sigma, double a, double x)
679         if(EQUIV(sigma, 0)) sigma = 0.01;
681         return 1 / 
682                 sqrt(2 * M_PI * sigma * sigma) * 
683                 exp(-(x - a) * (x - a) / 
684                         (2 * sigma * sigma));
687 int ParametricEQ::process_realtime(int64_t size, 
688         double *input_ptr, 
689         double *output_ptr)
691         need_reconfigure |= load_configuration();
692         if(need_reconfigure) reconfigure();
693         
694         
695         fft->process_fifo(size, input_ptr, output_ptr);
706 int ParametricEQ::load_defaults()
708         char directory[BCTEXTLEN], string[BCTEXTLEN];
709         sprintf(directory, "%sparametriceq.rc", BCASTDIR);
710         defaults = new Defaults(directory);
711         defaults->load();
712         
713         config.wetness = defaults->get("WETNESS", config.wetness);
714         for(int i = 0; i < BANDS; i++)
715         {
716                 sprintf(string, "FREQ_%d", i);
717                 config.band[i].freq = defaults->get(string, config.band[i].freq);
718                 sprintf(string, "QUALITY_%d", i);
719                 config.band[i].quality = defaults->get(string, config.band[i].quality);
720                 sprintf(string, "MAGNITUDE_%d", i);
721                 config.band[i].magnitude = defaults->get(string, config.band[i].magnitude);
722                 sprintf(string, "MODE_%d", i);
723                 config.band[i].mode = defaults->get(string, config.band[i].mode);
724         }
725         return 0;
728 int ParametricEQ::save_defaults()
730         char string[BCTEXTLEN];
732         defaults->update("WETNESS", config.wetness);
735         for(int i = 0; i < BANDS; i++)
736         {
737                 sprintf(string, "FREQ_%d", i);
738                 defaults->update(string, config.band[i].freq);
739                 sprintf(string, "QUALITY_%d", i);
740                 defaults->update(string, config.band[i].quality);
741                 sprintf(string, "MAGNITUDE_%d", i);
742                 defaults->update(string, config.band[i].magnitude);
743                 sprintf(string, "MODE_%d", i);
744                 defaults->update(string, config.band[i].mode);
745         }
748         defaults->save();
750         return 0;
754 void ParametricEQ::reset()
756         need_reconfigure = 1;
757         thread = 0;
758         fft = 0;
761 void ParametricEQ::update_gui()
763         if(thread)
764         {
765                 load_configuration();
766                 thread->window->lock_window();
767                 thread->window->update_gui();
768                 thread->window->unlock_window();
769         }