a19ff7ff3464e2e71ec9f9a1cd2cbb8dedc11eae
[goodguy/history.git] / cinelerra-5.1 / plugins / parametric / parametric.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "parametric.h"
29 #include "samples.h"
30 #include "theme.h"
31 #include "units.h"
32 #include "vframe.h"
33
34 #include <math.h>
35 #include <string.h>
36 #include <unistd.h>
37
38
39
40
41
42
43
44
45
46 REGISTER_PLUGIN(ParametricEQ)
47
48
49
50
51
52
53
54
55 ParametricBand::ParametricBand()
56 {
57         freq = 440;
58         quality = 1;
59         magnitude = 0;
60         mode = NONE;
61 }
62
63
64 int ParametricBand::equivalent(ParametricBand &that)
65 {
66         if(freq == that.freq &&
67                 EQUIV(quality, that.quality) &&
68                 EQUIV(magnitude, that.magnitude) &&
69                 mode == that.mode)
70         {
71                 return 1;
72         }
73         else
74                 return 0;
75 }
76
77
78 void ParametricBand::copy_from(ParametricBand &that)
79 {
80         freq = that.freq;
81         quality = that.quality;
82         magnitude = that.magnitude;
83         mode = that.mode;
84 }
85
86 void ParametricBand::interpolate(ParametricBand &prev,
87                 ParametricBand &next,
88                 double prev_scale,
89                 double next_scale)
90 {
91         freq = (int)(prev.freq * prev_scale + next.freq * next_scale + 0.5);
92         quality = prev.quality * prev_scale + next.quality * next_scale;
93         magnitude = prev.magnitude * prev_scale + next.magnitude * next_scale;
94         mode = prev.mode;
95 }
96
97
98
99
100
101 ParametricConfig::ParametricConfig()
102 {
103         wetness = INFINITYGAIN;
104         window_size = 4096;
105 }
106
107
108 int ParametricConfig::equivalent(ParametricConfig &that)
109 {
110         for(int i = 0; i < BANDS; i++)
111                 if(!band[i].equivalent(that.band[i])) return 0;
112
113         if(!EQUIV(wetness, that.wetness) ||
114                 window_size != that.window_size) return 0;
115         return 1;
116 }
117
118 void ParametricConfig::copy_from(ParametricConfig &that)
119 {
120         wetness = that.wetness;
121         window_size = that.window_size;
122         for(int i = 0; i < BANDS; i++)
123                 band[i].copy_from(that.band[i]);
124 }
125
126 void ParametricConfig::interpolate(ParametricConfig &prev,
127                 ParametricConfig &next,
128                 int64_t prev_frame,
129                 int64_t next_frame,
130                 int64_t current_frame)
131 {
132         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
133         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
134         wetness = prev.wetness;
135         window_size = prev.window_size;
136         for(int i = 0; i < BANDS; i++)
137         {
138                 band[i].interpolate(prev.band[i], next.band[i], prev_scale, next_scale);
139         }
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160 ParametricFreq::ParametricFreq(ParametricEQ *plugin, int x, int y, int band)
161  : BC_QPot(x, y, plugin->config.band[band].freq)
162 {
163         this->plugin = plugin;
164         this->band = band;
165 }
166
167 int ParametricFreq::handle_event()
168 {
169         plugin->config.band[band].freq = get_value();
170         plugin->send_configure_change();
171         ((ParametricWindow*)plugin->thread->window)->update_canvas();
172         return 1;
173 }
174
175
176
177
178
179
180
181
182 ParametricQuality::ParametricQuality(ParametricEQ *plugin, int x, int y, int band)
183  : BC_FPot(x, y, plugin->config.band[band].quality, 0, 1)
184 {
185         this->plugin = plugin;
186         this->band = band;
187         set_precision(0.01);
188 }
189
190 int ParametricQuality::handle_event()
191 {
192         plugin->config.band[band].quality = get_value();
193         plugin->send_configure_change();
194         ((ParametricWindow*)plugin->thread->window)->update_canvas();
195         return 1;
196 }
197
198
199
200
201
202
203
204
205
206
207
208 ParametricMagnitude::ParametricMagnitude(ParametricEQ *plugin, int x, int y, int band)
209  : BC_FPot(x, y, plugin->config.band[band].magnitude, -MAXMAGNITUDE, MAXMAGNITUDE)
210 {
211         this->plugin = plugin;
212         this->band = band;
213 }
214
215 int ParametricMagnitude::handle_event()
216 {
217         plugin->config.band[band].magnitude = get_value();
218         plugin->send_configure_change();
219         ((ParametricWindow*)plugin->thread->window)->update_canvas();
220         return 1;
221 }
222
223
224
225
226
227
228
229
230
231 ParametricMode::ParametricMode(ParametricEQ *plugin, int x, int y, int band)
232  : BC_PopupMenu(x,
233                 y,
234                 150,
235                 mode_to_text(plugin->config.band[band].mode))
236 {
237 //printf("ParametricMode::ParametricMode %d %d\n", band, plugin->config.band[band].mode);
238         this->plugin = plugin;
239         this->band = band;
240 }
241
242 void ParametricMode::create_objects()
243 {
244         add_item(new BC_MenuItem(mode_to_text(ParametricBand::LOWPASS)));
245         add_item(new BC_MenuItem(mode_to_text(ParametricBand::HIGHPASS)));
246         add_item(new BC_MenuItem(mode_to_text(ParametricBand::BANDPASS)));
247         add_item(new BC_MenuItem(mode_to_text(ParametricBand::NONE)));
248 }
249
250
251 int ParametricMode::handle_event()
252 {
253         plugin->config.band[band].mode = text_to_mode(get_text());
254         plugin->send_configure_change();
255         ((ParametricWindow*)plugin->thread->window)->update_canvas();
256         return 1;
257 }
258
259 int ParametricMode::text_to_mode(char *text)
260 {
261         if(!strcmp(mode_to_text(ParametricBand::LOWPASS), text)) return ParametricBand::LOWPASS;
262         if(!strcmp(mode_to_text(ParametricBand::HIGHPASS), text)) return ParametricBand::HIGHPASS;
263         if(!strcmp(mode_to_text(ParametricBand::BANDPASS), text)) return ParametricBand::BANDPASS;
264         if(!strcmp(mode_to_text(ParametricBand::NONE), text)) return ParametricBand::NONE;
265         return ParametricBand::BANDPASS;
266 }
267
268
269
270 const char* ParametricMode::mode_to_text(int mode)
271 {
272         switch(mode)
273         {
274                 case ParametricBand::LOWPASS:
275                         return _("Lowpass");
276                         break;
277                 case ParametricBand::HIGHPASS:
278                         return _("Highpass");
279                         break;
280                 case ParametricBand::BANDPASS:
281                         return _("Bandpass");
282                         break;
283                 case ParametricBand::NONE:
284                         return _("None");
285                         break;
286         }
287         return "";
288 }
289
290
291
292
293
294
295
296
297
298
299
300 ParametricBandGUI::ParametricBandGUI(ParametricEQ *plugin, ParametricWindow *window, int x, int y, int band)
301 {
302         this->plugin = plugin;
303         this->band = band;
304         this->window = window;
305         this->x = x;
306         this->y = y;
307 }
308
309 ParametricBandGUI::~ParametricBandGUI()
310 {
311 }
312
313
314 #define X1 10
315 #define X2 60
316 #define X3 110
317 #define X4 160
318
319
320 void ParametricBandGUI::create_objects()
321 {
322         window->add_subwindow(freq = new ParametricFreq(plugin, X1, y, band));
323         window->add_subwindow(quality = new ParametricQuality(plugin, X2, y, band));
324         window->add_subwindow(magnitude = new ParametricMagnitude(plugin, X3, y, band));
325         window->add_subwindow(mode = new ParametricMode(plugin, X4, y, band));
326         mode->create_objects();
327 }
328
329 void ParametricBandGUI::update_gui()
330 {
331         freq->update(plugin->config.band[band].freq);
332         quality->update(plugin->config.band[band].quality);
333         magnitude->update(plugin->config.band[band].magnitude);
334         mode->set_text(ParametricMode::mode_to_text(plugin->config.band[band].mode));
335 }
336
337
338
339
340
341
342 ParametricWetness::ParametricWetness(ParametricEQ *plugin, int x, int y)
343  : BC_FPot(x, y, plugin->config.wetness, INFINITYGAIN, 0)
344 {
345         this->plugin = plugin;
346 }
347
348 int ParametricWetness::handle_event()
349 {
350         plugin->config.wetness = get_value();
351         plugin->send_configure_change();
352         ((ParametricWindow*)plugin->thread->window)->update_canvas();
353         return 1;
354 }
355
356
357
358
359
360
361
362 ParametricSize::ParametricSize(ParametricWindow *window, ParametricEQ *plugin, int x, int y)
363  : BC_PopupMenu(x, y, 100, "4096", 1)
364 {
365         this->plugin = plugin;
366         this->window = window;
367 }
368
369 int ParametricSize::handle_event()
370 {
371         plugin->config.window_size = atoi(get_text());
372         plugin->send_configure_change();
373
374         window->update_canvas();
375         return 1;
376 }
377
378 void ParametricSize::create_objects()
379 {
380         add_item(new BC_MenuItem("2048"));
381         add_item(new BC_MenuItem("4096"));
382         add_item(new BC_MenuItem("8192"));
383         add_item(new BC_MenuItem("16384"));
384         add_item(new BC_MenuItem("32768"));
385         add_item(new BC_MenuItem("65536"));
386         add_item(new BC_MenuItem("131072"));
387         add_item(new BC_MenuItem("262144"));
388 }
389
390 void ParametricSize::update(int size)
391 {
392         char string[BCTEXTLEN];
393         sprintf(string, "%d", size);
394         set_text(string);
395 }
396
397
398
399
400
401
402 ParametricWindow::ParametricWindow(ParametricEQ *plugin)
403  : PluginClientWindow(plugin,
404         350,
405         400,
406         350,
407         400,
408         0)
409 {
410         this->plugin = plugin;
411 }
412
413 ParametricWindow::~ParametricWindow()
414 {
415         for(int i = 0; i < BANDS; i++)
416                 delete bands[i];
417 }
418
419 void ParametricWindow::create_objects()
420 {
421         int y = 35;
422 SET_TRACE
423
424         add_subwindow(new BC_Title(X1, 10, _("Freq")));
425         add_subwindow(new BC_Title(X2, 10, _("Qual")));
426         add_subwindow(new BC_Title(X3, 10, _("Level")));
427         add_subwindow(new BC_Title(X4, 10, _("Mode")));
428         for(int i = 0; i < BANDS; i++)
429         {
430                 bands[i] = new ParametricBandGUI(plugin, this, 10, y, i);
431                 bands[i]->create_objects();
432                 y += 50;
433         }
434
435 SET_TRACE
436         BC_Title *title;
437         int x = plugin->get_theme()->widget_border;
438         add_subwindow(title = new BC_Title(x, y + 10, _("Wetness:")));
439         x += title->get_w() + plugin->get_theme()->widget_border;
440         add_subwindow(wetness = new ParametricWetness(plugin,
441                 x,
442                 y));
443         x += wetness->get_w() + plugin->get_theme()->widget_border;
444
445         add_subwindow(title = new BC_Title(x, y + 10, _("Window:")));
446         x += title->get_w() + plugin->get_theme()->widget_border;
447         add_subwindow(size = new ParametricSize(this,
448                 plugin,
449                 x,
450                 y + 10));
451         size->create_objects();
452         size->update(plugin->config.window_size);
453
454
455
456         y += 50;
457         int canvas_x = 30;
458         int canvas_y = y;
459         int canvas_w = get_w() - canvas_x - 10;
460         int canvas_h = get_h() - canvas_y - 30;
461         add_subwindow(canvas = new BC_SubWindow(canvas_x,
462                 canvas_y,
463                 canvas_w,
464                 canvas_h,
465                 BLACK));
466
467 SET_TRACE
468 // Draw canvas titles
469         set_font(SMALLFONT);
470 #define MAJOR_DIVISIONS 4
471 #define MINOR_DIVISIONS 5
472         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
473         {
474                 int y1 = canvas_y + canvas_h - i * (canvas_h / MAJOR_DIVISIONS) - 2;
475                 int y2 = y1 + 3;
476                 int x1 = canvas_x - 25;
477                 int x2 = canvas_x - 10;
478                 int x3 = canvas_x - 2;
479
480                 char string[BCTEXTLEN];
481                 if(i == 0)
482                         sprintf(string, "oo");
483                 else
484                         sprintf(string, "%d", i * 5 - 5);
485
486                 set_color(BLACK);
487                 draw_text(x1 + 1, y2 + 1, string);
488                 draw_line(x2 + 1, y1 + 1, x3 + 1, y1 + 1);
489                 set_color(RED);
490                 draw_text(x1, y2, string);
491                 draw_line(x2, y1, x3, y1);
492
493                 if(i < MAJOR_DIVISIONS)
494                 {
495                         for(int j = 1; j < MINOR_DIVISIONS; j++)
496                         {
497                                 int y3 = y1 - j * (canvas_h / MAJOR_DIVISIONS) / MINOR_DIVISIONS;
498                                 int x4 = x3 - 5;
499                                 set_color(BLACK);
500                                 draw_line(x4 + 1, y3 + 1, x3 + 1, y3 + 1);
501                                 set_color(RED);
502                                 draw_line(x4, y3, x3, y3);
503                         }
504                 }
505         }
506
507 SET_TRACE
508 #undef MAJOR_DIVISIONS
509 #define MAJOR_DIVISIONS 5
510         for(int i = 0; i <= MAJOR_DIVISIONS; i++)
511         {
512                 int freq = Freq::tofreq(i * TOTALFREQS / MAJOR_DIVISIONS);
513                 int x1 = canvas_x + i * canvas_w / MAJOR_DIVISIONS;
514                 int y1 = canvas_y + canvas_h + 20;
515                 char string[BCTEXTLEN];
516                 sprintf(string, "%d", freq);
517                 int x2 = x1 - get_text_width(SMALLFONT, string);
518                 int y2 = y1 - 10;
519                 int y3 = y2 - 5;
520                 int y4 = canvas_y + canvas_h;
521
522                 set_color(BLACK);
523                 draw_text(x2 + 1, y1 + 1, string);
524                 draw_line(x1 + 1, y4 + 1, x1 + 1, y2 + 1);
525                 set_color(RED);
526                 draw_text(x2, y1, string);
527                 draw_line(x1, y4, x1, y2);
528
529                 if(i < MAJOR_DIVISIONS)
530                 {
531 #undef MINOR_DIVISIONS
532 #define MINOR_DIVISIONS 5
533                         for(int j = 0; j < MINOR_DIVISIONS; j++)
534                         {
535                                 int x3 = (int)(x1 +
536                                         (canvas_w / MAJOR_DIVISIONS) -
537                                         exp(-(double)j * 0.7) *
538                                         (canvas_w / MAJOR_DIVISIONS));
539                                 set_color(BLACK);
540                                 draw_line(x3 + 1, y4 + 1, x3 + 1, y3 + 1);
541                                 set_color(RED);
542                                 draw_line(x3, y4, x3, y3);
543                         }
544                 }
545         }
546
547 SET_TRACE
548         update_canvas();
549         show_window();
550 SET_TRACE
551 }
552
553
554
555 void ParametricWindow::update_gui()
556 {
557         for(int i = 0; i < BANDS; i++)
558                 bands[i]->update_gui();
559         wetness->update(plugin->config.wetness);
560         size->update(plugin->config.window_size);
561         update_canvas();
562 }
563
564
565 void ParametricWindow::update_canvas()
566 {
567         int y1 = canvas->get_h() / 2;
568         int niquist = plugin->PluginAClient::project_sample_rate / 2;
569
570         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
571
572
573
574 // Draw spectrogram
575         int total_frames = plugin->get_gui_update_frames();
576         ParametricGUIFrame *frame = (ParametricGUIFrame*)plugin->get_gui_frame();
577
578         if(frame)
579         {
580                 delete plugin->last_frame;
581                 plugin->last_frame = frame;
582         }
583         else
584         {
585                 frame = plugin->last_frame;
586         }
587
588 // Draw most recent frame
589         if(frame)
590         {
591                 canvas->set_color(MEGREY);
592                 int y1 = 0;
593                 int y2 = 0;
594                 for(int i = 0; i < canvas->get_w(); i++)
595                 {
596                         int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
597                         int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
598                         if(index < frame->window_size / 2)
599                         {
600                                 double magnitude = frame->data[index] /
601                                         frame->freq_max *
602                                         frame->time_max;
603                                 y2 = (int)(canvas->get_h() -
604                                         (DB::todb(magnitude) - INFINITYGAIN) *
605                                         canvas->get_h() /
606                                         -INFINITYGAIN);
607                                 CLAMP(y2, 0, canvas->get_h() - 1);
608                                 if(i > 0)
609                                 {
610                                         canvas->draw_line(i - 1, y1, i, y2);
611                                 }
612                                 y1 = y2;
613                         }
614                 }
615
616                 total_frames--;
617         }
618
619
620
621
622
623
624 // Delete remaining frames
625         while(total_frames > 0)
626         {
627                 PluginClientFrame *frame = plugin->get_gui_frame();
628
629                 if(frame) delete frame;
630                 total_frames--;
631         }
632
633
634
635
636
637 //      canvas->set_color(GREEN);
638 //      canvas->draw_line(0, wetness, canvas->get_w(), wetness);
639 //      canvas->draw_line(0,
640 //              wetness,
641 //              canvas->get_w(),
642 //              wetness);
643
644         canvas->set_color(WHITE);
645         canvas->set_line_width(2);
646
647         plugin->calculate_envelope();
648         for(int i = 0; i < canvas->get_w(); i++)
649         {
650                 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
651                 int index = (int64_t)freq * (int64_t)plugin->config.window_size / 2 / niquist;
652                 if(freq < niquist && index < plugin->config.window_size / 2)
653                 {
654 //printf("ParametricWindow::update_canvas %d %d\n", __LINE__, index);
655                         double magnitude = plugin->envelope[index];
656                         int y2 = canvas->get_h() * 3 / 4;
657
658                         if(magnitude > 1)
659                         {
660                                 y2 -= (int)(DB::todb(magnitude) *
661                                         canvas->get_h() *
662                                         3 /
663                                         4 /
664                                         15);
665                         }
666                         else
667                         {
668                                 y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
669                         }
670                         if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
671                         y1 = y2;
672                 }
673                 else
674                 {
675                         canvas->draw_line(i - 1, y1, i, y1);
676                 }
677         }
678         canvas->set_line_width(1);
679
680
681 //      for(int i = 0; i < canvas->get_w(); i++)
682 //      {
683 //              int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
684 //              int index = (int)((float)freq / niquist * plugin->config.window_size / 2);
685 //              double magnitude = plugin->envelope[index];
686 //              int y2 = canvas->get_h() -
687 //                      (int)((double)canvas->get_h() / normalize * magnitude);
688 //              if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
689 //              y1 = y2;
690 //      }
691
692         canvas->flash(1);
693 }
694
695
696
697
698
699
700
701 ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate)
702  : PluginClientFrame(window_size / 2, window_size / 2, sample_rate)
703 {
704         this->window_size = window_size;
705         data = new double[window_size / 2];
706         freq_max = 0;
707         time_max = 0;
708 }
709
710 ParametricGUIFrame::~ParametricGUIFrame()
711 {
712         delete [] data;
713 }
714
715
716
717
718
719
720
721
722
723
724 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
725  : CrossfadeFFT()
726 {
727         this->plugin = plugin;
728 }
729
730 ParametricFFT::~ParametricFFT()
731 {
732 }
733
734
735 int ParametricFFT::signal_process()
736 {
737 // Create new frame for updating GUI
738         frame = new ParametricGUIFrame(window_size,
739                 plugin->PluginAClient::project_sample_rate);
740         plugin->add_gui_frame(frame);
741
742         double freq_max = 0;
743         for(int i = 0; i < window_size / 2; i++)
744         {
745
746 // if(i == 10) printf("ParametricFFT::signal_process %d %f\n",
747 // __LINE__,
748 // plugin->envelope[i]);
749
750                 double result = plugin->envelope[i] *
751                         sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
752                 double angle = atan2(freq_imag[i], freq_real[i]);
753                 freq_real[i] = result * cos(angle);
754                 freq_imag[i] = result * sin(angle);
755
756                 frame->data[i] = result;
757                 if(result > freq_max) freq_max = result;
758         }
759         frame->freq_max = freq_max;
760
761
762         symmetry(window_size, freq_real, freq_imag);
763         return 0;
764 }
765
766 int ParametricFFT::post_process()
767 {
768         double time_max = 0;
769         for(int i = 0; i < window_size; i++)
770         {
771                 if(output_real[i] > time_max) time_max = output_real[i];
772         }
773         frame->time_max = time_max;
774         return 0;
775 }
776
777
778
779 int ParametricFFT::read_samples(int64_t output_sample,
780         int samples,
781         Samples *buffer)
782 {
783         return plugin->read_samples(buffer,
784                 0,
785                 plugin->get_samplerate(),
786                 output_sample,
787                 samples);
788 }
789
790
791
792
793
794
795
796
797 ParametricEQ::ParametricEQ(PluginServer *server)
798  : PluginAClient(server)
799 {
800
801         fft = 0;
802         need_reconfigure = 1;
803         envelope = 0;
804         last_frame = 0;
805 }
806
807 ParametricEQ::~ParametricEQ()
808 {
809         delete last_frame;
810         delete [] envelope;
811         if(fft) delete fft;
812 }
813
814 NEW_WINDOW_MACRO(ParametricEQ, ParametricWindow)
815
816 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
817
818
819 const char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
820 int ParametricEQ::is_realtime() { return 1; }
821
822 void ParametricEQ::read_data(KeyFrame *keyframe)
823 {
824         FileXML input;
825         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
826
827         int result = 0;
828         while(!result)
829         {
830                 result = input.read_tag();
831
832                 if(!result)
833                 {
834                         if(input.tag.title_is("PARAMETRICEQ"))
835                         {
836                                 config.wetness = input.tag.get_property("WETNESS", config.wetness);
837                                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
838                         }
839                         else
840                         if(input.tag.title_is("BAND"))
841                         {
842                                 int band = input.tag.get_property("NUMBER", 0);
843                                 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
844                                 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
845                                 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
846                                 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
847                         }
848                 }
849         }
850 }
851
852 void ParametricEQ::save_data(KeyFrame *keyframe)
853 {
854         FileXML output;
855         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
856
857         output.tag.set_title("PARAMETRICEQ");
858         output.tag.set_property("WETNESS", config.wetness);
859         output.tag.set_property("WINDOW_SIZE", config.window_size);
860         output.append_tag();
861         output.append_newline();
862
863         for(int i = 0; i < BANDS; i++)
864         {
865                 output.tag.set_title("BAND");
866                 output.tag.set_property("NUMBER", i);
867                 output.tag.set_property("FREQ", config.band[i].freq);
868                 output.tag.set_property("QUALITY", config.band[i].quality);
869                 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
870                 output.tag.set_property("MODE", config.band[i].mode);
871                 output.append_tag();
872                 output.tag.set_title("/BAND");
873                 output.append_tag();
874                 output.append_newline();
875         }
876
877         output.tag.set_title("/PARAMETRICEQ");
878         output.append_tag();
879         output.append_newline();
880         output.terminate_string();
881 }
882
883 void ParametricEQ::reconfigure()
884 {
885         if(fft &&
886                 config.window_size != fft->window_size)
887         {
888 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
889                 delete fft;
890                 fft = 0;
891         }
892
893         if(!fft)
894         {
895 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
896                 fft = new ParametricFFT(this);
897                 fft->initialize(config.window_size);
898         }
899
900 // Reset envelope
901
902 //printf("ParametricEQ::reconfigure %f\n", wetness);
903         calculate_envelope();
904
905         for(int i = 0; i < config.window_size / 2; i++)
906         {
907                 if(envelope[i] < 0) envelope[i] = 0;
908         }
909
910         need_reconfigure = 0;
911 }
912
913 double ParametricEQ::calculate_envelope()
914 {
915         double wetness = DB::fromdb(config.wetness);
916         if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0;
917         int niquist = PluginAClient::project_sample_rate / 2;
918
919         if(!envelope) envelope = new double[MAX_WINDOW / 2];
920
921 //printf("ParametricEQ::calculate_envelope %d %f\n", __LINE__, wetness);
922         for(int i = 0; i < config.window_size / 2; i++)
923         {
924                 envelope[i] = wetness;
925         }
926
927         for(int pass = 0; pass < 2; pass++)
928         {
929                 for(int band = 0; band < BANDS; band++)
930                 {
931                         switch(config.band[band].mode)
932                         {
933                                 case ParametricBand::LOWPASS:
934                                         if(pass == 1)
935                                         {
936                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
937                                                 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
938                                                 for(int i = 0; i < config.window_size / 2; i++)
939                                                 {
940                                                         if(i < cutoff)
941                                                                 envelope[i] += magnitude;
942                                                 }
943                                         }
944                                         break;
945
946                                 case ParametricBand::HIGHPASS:
947                                         if(pass == 1)
948                                         {
949                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
950                                                 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
951                                                 for(int i = 0; i < config.window_size / 2; i++)
952                                                 {
953                                                         if(i > cutoff)
954                                                                 envelope[i] += magnitude;
955                                                 }
956                                         }
957                                         break;
958
959                                 case ParametricBand::BANDPASS:
960                                         if(pass == 0)
961                                         {
962                                                 double magnitude = (config.band[band].magnitude > 0) ?
963                                                         (DB::fromdb(config.band[band].magnitude) - 1) :
964                                                         (-1 + DB::fromdb(config.band[band].magnitude));
965                                                 double sigma = (config.band[band].quality < 1) ?
966                                                         (1.0 - config.band[band].quality) :
967                                                         0.01;
968                                                 sigma /= 4;
969                                                 double center = (double)Freq::fromfreq(config.band[band].freq) /
970                                                         TOTALFREQS;
971                                                 double normalize = gauss(sigma, 0, 0);
972                                                 if(config.band[band].magnitude <= -MAXMAGNITUDE)
973                                                         magnitude = -1;
974
975                                                 for(int i = 0; i < config.window_size / 2; i++)
976                                                 {
977                                                         int freq = i * niquist / (config.window_size / 2);
978                                                         int current_slot = Freq::fromfreq(freq);
979                                                         envelope[i] += magnitude *
980                                                                 gauss(sigma, center, (double)current_slot / TOTALFREQS) /
981                                                                 normalize;
982 // printf("freq=%d magnitude=%f gauss=%f normalize=%f envelope[i]=%f\n",
983 // freq,
984 // magnitude,
985 // gauss(sigma, center, (double)current_slot / TOTALFREQS),
986 // normalize,
987 // envelope[i]);
988                                                 }
989                                         }
990                                         break;
991                         }
992                 }
993         }
994
995         return 0;
996 }
997
998 double ParametricEQ::gauss(double sigma, double center, double x)
999 {
1000         if(EQUIV(sigma, 0)) sigma = 0.01;
1001
1002         double result = 1.0 /
1003                 sqrt(2 * M_PI * sigma * sigma) *
1004                 exp(-(x - center) * (x - center) /
1005                         (2 * sigma * sigma));
1006
1007
1008         return result;
1009 }
1010
1011
1012
1013 int ParametricEQ::process_buffer(int64_t size,
1014         Samples *buffer,
1015         int64_t start_position,
1016         int sample_rate)
1017 {
1018         need_reconfigure |= load_configuration();
1019         if(need_reconfigure) reconfigure();
1020
1021
1022         fft->process_buffer(start_position,
1023                 size,
1024                 buffer,
1025                 get_direction());
1026
1027
1028
1029         return 0;
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 void ParametricEQ::reset()
1043 {
1044         need_reconfigure = 1;
1045         thread = 0;
1046         fft = 0;
1047 }
1048
1049 void ParametricEQ::update_gui()
1050 {
1051         if(thread)
1052         {
1053                 if(load_configuration())
1054                 {
1055                         ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
1056                         calculate_envelope();
1057                         ((ParametricWindow*)thread->window)->update_gui();
1058                         ((ParametricWindow*)thread->window)->unlock_window();
1059                 }
1060                 else
1061                 {
1062                         int total_frames = get_gui_update_frames();
1063 //printf("ParametricEQ::update_gui %d %d\n", __LINE__, total_frames);
1064                         if(total_frames)
1065                         {
1066                                 ((ParametricWindow*)thread->window)->lock_window("ParametricEQ::update_gui");
1067                                 ((ParametricWindow*)thread->window)->update_canvas();
1068                                 ((ParametricWindow*)thread->window)->unlock_window();
1069                         }
1070                 }
1071         }
1072 }
1073
1074
1075