ffmpeg versioning mods from Andrew
[goodguy/cinelerra.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                 xS(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 xS(10)
315 #define X2 xS(60)
316 #define X3 xS(110)
317 #define X4 xS(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, xS(120), "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         xS(350),
405         yS(400),
406         xS(350),
407         yS(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 = yS(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, xS(10), y, i);
431                 bands[i]->create_objects();
432                 y += yS(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 + yS(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 + yS(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 + yS(10)));
451         size->create_objects();
452         size->update(plugin->config.window_size);
453
454
455
456         y += yS(50);
457         int canvas_x = xS(30);
458         int canvas_y = y;
459         int canvas_w = get_w() - canvas_x - xS(10);
460         int canvas_h = get_h() - canvas_y - yS(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 + yS(3);
476                 int x1 = canvas_x - xS(25);
477                 int x2 = canvas_x - xS(10);
478                 int x3 = canvas_x - xS(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 - xS(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 + yS(20);
515                 char string[BCTEXTLEN];
516                 sprintf(string, "%d", freq);
517                 int x2 = x1 - get_text_width(SMALLFONT, string);
518                 int y2 = y1 - yS(10);
519                 int y3 = y2 - yS(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 // Draw spectrogram
573         double tracking_position = plugin->get_tracking_position();
574         ParametricGUIFrame *frame = (ParametricGUIFrame *)
575                 plugin->get_gui_frame(tracking_position, 1);
576 // Draw most recent frame
577         if( frame ) {
578                 int y1 = 0, y2 = 0;
579                 canvas->set_color(MEGREY);
580
581                 for(int i = 0; i < canvas->get_w(); i++) {
582                         int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
583                         int index = (int64_t)freq * (int64_t)frame->window_size / 2 / niquist;
584                         if(index < frame->window_size / 2) {
585                                 double magnitude = frame->data[index] /
586                                         frame->freq_max * frame->time_max;
587                                 y2 = (int)(canvas->get_h() -
588                                         (DB::todb(magnitude) - INFINITYGAIN) *
589                                         canvas->get_h() / -INFINITYGAIN);
590                                 CLAMP(y2, 0, canvas->get_h() - 1);
591                                 if(i > 0)
592                                         canvas->draw_line(i - 1, y1, i, y2);
593                                 y1 = y2;
594                         }
595                 }
596                 delete frame;
597         }
598
599 //      canvas->set_color(GREEN);
600 //      canvas->draw_line(0, wetness, canvas->get_w(), wetness);
601 //      canvas->draw_line(0,
602 //              wetness,
603 //              canvas->get_w(),
604 //              wetness);
605
606         canvas->set_color(WHITE);
607         canvas->set_line_width(2);
608
609         plugin->calculate_envelope();
610         for(int i = 0; i < canvas->get_w(); i++)
611         {
612                 int freq = Freq::tofreq(i * TOTALFREQS / canvas->get_w());
613                 int index = (int64_t)freq * (int64_t)plugin->config.window_size / 2 / niquist;
614                 if(freq < niquist && index < plugin->config.window_size / 2)
615                 {
616 //printf("ParametricWindow::update_canvas %d %d\n", __LINE__, index);
617                         double magnitude = plugin->envelope[index];
618                         int y2 = canvas->get_h() * 3 / 4;
619
620                         if(magnitude > 1)
621                         {
622                                 y2 -= (int)(DB::todb(magnitude) *
623                                         canvas->get_h() *
624                                         3 /
625                                         4 /
626                                         15);
627                         }
628                         else
629                         {
630                                 y2 += (int)((1 - magnitude) * canvas->get_h() / 4);
631                         }
632                         if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
633                         y1 = y2;
634                 }
635                 else
636                 {
637                         canvas->draw_line(i - 1, y1, i, y1);
638                 }
639         }
640         canvas->set_line_width(1);
641
642
643 //      for(int i = 0; i < canvas->get_w(); i++)
644 //      {
645 //              int freq = Freq::tofreq((int)((float)i / canvas->get_w() * TOTALFREQS));
646 //              int index = (int)((float)freq / niquist * plugin->config.window_size / 2);
647 //              double magnitude = plugin->envelope[index];
648 //              int y2 = canvas->get_h() -
649 //                      (int)((double)canvas->get_h() / normalize * magnitude);
650 //              if(i > 0) canvas->draw_line(i - 1, y1, i, y2);
651 //              y1 = y2;
652 //      }
653
654         canvas->flash(1);
655 }
656
657
658 ParametricGUIFrame::ParametricGUIFrame(int window_size, int sample_rate)
659  : PluginClientFrame()
660 {
661         this->window_size = window_size;
662         data_size = window_size / 2;
663         data = new double[data_size];
664         freq_max = 0;
665         time_max = 0;
666 }
667
668 ParametricGUIFrame::~ParametricGUIFrame()
669 {
670         delete [] data;
671 }
672
673
674 ParametricFFT::ParametricFFT(ParametricEQ *plugin)
675  : CrossfadeFFT()
676 {
677         this->plugin = plugin;
678 }
679
680 ParametricFFT::~ParametricFFT()
681 {
682 }
683
684
685 int ParametricFFT::signal_process()
686 {
687 // Create new frame for updating GUI
688         frame = new ParametricGUIFrame(window_size,
689                 plugin->PluginAClient::project_sample_rate);
690         plugin->add_gui_frame(frame);
691
692         double freq_max = 0;
693         for(int i = 0; i < window_size / 2; i++)
694         {
695
696 // if(i == 10) printf("ParametricFFT::signal_process %d %f\n",
697 // __LINE__,
698 // plugin->envelope[i]);
699
700                 double result = plugin->envelope[i] *
701                         sqrt(freq_real[i] * freq_real[i] + freq_imag[i] * freq_imag[i]);
702                 double angle = atan2(freq_imag[i], freq_real[i]);
703                 freq_real[i] = result * cos(angle);
704                 freq_imag[i] = result * sin(angle);
705
706                 frame->data[i] = result;
707                 if(result > freq_max) freq_max = result;
708         }
709         frame->freq_max = freq_max;
710
711
712         symmetry(window_size, freq_real, freq_imag);
713         return 0;
714 }
715
716 int ParametricFFT::post_process()
717 {
718         double time_max = 0;
719         for(int i = 0; i < window_size; i++)
720         {
721                 if(output_real[i] > time_max) time_max = output_real[i];
722         }
723         frame->time_max = time_max;
724         return 0;
725 }
726
727
728
729 int ParametricFFT::read_samples(int64_t output_sample,
730         int samples,
731         Samples *buffer)
732 {
733         return plugin->read_samples(buffer,
734                 0,
735                 plugin->get_samplerate(),
736                 output_sample,
737                 samples);
738 }
739
740
741
742
743
744
745
746
747 ParametricEQ::ParametricEQ(PluginServer *server)
748  : PluginAClient(server)
749 {
750
751         fft = 0;
752         need_reconfigure = 1;
753         envelope = 0;
754         last_frame = 0;
755 }
756
757 ParametricEQ::~ParametricEQ()
758 {
759         delete last_frame;
760         delete [] envelope;
761         if(fft) delete fft;
762 }
763
764 NEW_WINDOW_MACRO(ParametricEQ, ParametricWindow)
765
766 LOAD_CONFIGURATION_MACRO(ParametricEQ, ParametricConfig)
767
768
769 const char* ParametricEQ::plugin_title() { return N_("EQ Parametric"); }
770 int ParametricEQ::is_realtime() { return 1; }
771
772 void ParametricEQ::read_data(KeyFrame *keyframe)
773 {
774         FileXML input;
775         input.set_shared_input(keyframe->xbuf);
776
777         int result = 0;
778         while(!result)
779         {
780                 result = input.read_tag();
781
782                 if(!result)
783                 {
784                         if(input.tag.title_is("PARAMETRICEQ"))
785                         {
786                                 config.wetness = input.tag.get_property("WETNESS", config.wetness);
787                                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
788                         }
789                         else
790                         if(input.tag.title_is("BAND"))
791                         {
792                                 int band = input.tag.get_property("NUMBER", 0);
793                                 config.band[band].freq = input.tag.get_property("FREQ", config.band[band].freq);
794                                 config.band[band].quality = input.tag.get_property("QUALITY", config.band[band].quality);
795                                 config.band[band].magnitude = input.tag.get_property("MAGNITUDE", config.band[band].magnitude);
796                                 config.band[band].mode = input.tag.get_property("MODE", config.band[band].mode);
797                         }
798                 }
799         }
800 }
801
802 void ParametricEQ::save_data(KeyFrame *keyframe)
803 {
804         FileXML output;
805         output.set_shared_output(keyframe->xbuf);
806
807         output.tag.set_title("PARAMETRICEQ");
808         output.tag.set_property("WETNESS", config.wetness);
809         output.tag.set_property("WINDOW_SIZE", config.window_size);
810         output.append_tag();
811         output.append_newline();
812
813         for(int i = 0; i < BANDS; i++)
814         {
815                 output.tag.set_title("BAND");
816                 output.tag.set_property("NUMBER", i);
817                 output.tag.set_property("FREQ", config.band[i].freq);
818                 output.tag.set_property("QUALITY", config.band[i].quality);
819                 output.tag.set_property("MAGNITUDE", config.band[i].magnitude);
820                 output.tag.set_property("MODE", config.band[i].mode);
821                 output.append_tag();
822                 output.tag.set_title("/BAND");
823                 output.append_tag();
824                 output.append_newline();
825         }
826
827         output.tag.set_title("/PARAMETRICEQ");
828         output.append_tag();
829         output.append_newline();
830         output.terminate_string();
831 }
832
833 void ParametricEQ::reconfigure()
834 {
835         if(fft &&
836                 config.window_size != fft->window_size)
837         {
838 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
839                 delete fft;
840                 fft = 0;
841         }
842
843         if(!fft)
844         {
845 //printf("ParametricEQ::reconfigure %d %d\n", __LINE__, config.window_size);
846                 fft = new ParametricFFT(this);
847                 fft->initialize(config.window_size);
848         }
849
850 // Reset envelope
851
852 //printf("ParametricEQ::reconfigure %f\n", wetness);
853         calculate_envelope();
854
855         for(int i = 0; i < config.window_size / 2; i++)
856         {
857                 if(envelope[i] < 0) envelope[i] = 0;
858         }
859
860         need_reconfigure = 0;
861 }
862
863 double ParametricEQ::calculate_envelope()
864 {
865         double wetness = DB::fromdb(config.wetness);
866         if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0;
867         int niquist = PluginAClient::project_sample_rate / 2;
868
869         if(!envelope) envelope = new double[MAX_WINDOW / 2];
870
871 //printf("ParametricEQ::calculate_envelope %d %f\n", __LINE__, wetness);
872         for(int i = 0; i < config.window_size / 2; i++)
873         {
874                 envelope[i] = wetness;
875         }
876
877         for(int pass = 0; pass < 2; pass++)
878         {
879                 for(int band = 0; band < BANDS; band++)
880                 {
881                         switch(config.band[band].mode)
882                         {
883                                 case ParametricBand::LOWPASS:
884                                         if(pass == 1)
885                                         {
886                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
887                                                 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
888                                                 for(int i = 0; i < config.window_size / 2; i++)
889                                                 {
890                                                         if(i < cutoff)
891                                                                 envelope[i] += magnitude;
892                                                 }
893                                         }
894                                         break;
895
896                                 case ParametricBand::HIGHPASS:
897                                         if(pass == 1)
898                                         {
899                                                 double magnitude = DB::fromdb(config.band[band].magnitude);
900                                                 int cutoff = (int)((double)config.band[band].freq / niquist * config.window_size / 2);
901                                                 for(int i = 0; i < config.window_size / 2; i++)
902                                                 {
903                                                         if(i > cutoff)
904                                                                 envelope[i] += magnitude;
905                                                 }
906                                         }
907                                         break;
908
909                                 case ParametricBand::BANDPASS:
910                                         if(pass == 0)
911                                         {
912                                                 double magnitude = (config.band[band].magnitude > 0) ?
913                                                         (DB::fromdb(config.band[band].magnitude) - 1) :
914                                                         (-1 + DB::fromdb(config.band[band].magnitude));
915                                                 double sigma = (config.band[band].quality < 1) ?
916                                                         (1.0 - config.band[band].quality) :
917                                                         0.01;
918                                                 sigma /= 4;
919                                                 double center = (double)Freq::fromfreq(config.band[band].freq) /
920                                                         TOTALFREQS;
921                                                 double normalize = gauss(sigma, 0, 0);
922                                                 if(config.band[band].magnitude <= -MAXMAGNITUDE)
923                                                         magnitude = -1;
924
925                                                 for(int i = 0; i < config.window_size / 2; i++)
926                                                 {
927                                                         int freq = i * niquist / (config.window_size / 2);
928                                                         int current_slot = Freq::fromfreq(freq);
929                                                         envelope[i] += magnitude *
930                                                                 gauss(sigma, center, (double)current_slot / TOTALFREQS) /
931                                                                 normalize;
932 // printf("freq=%d magnitude=%f gauss=%f normalize=%f envelope[i]=%f\n",
933 // freq,
934 // magnitude,
935 // gauss(sigma, center, (double)current_slot / TOTALFREQS),
936 // normalize,
937 // envelope[i]);
938                                                 }
939                                         }
940                                         break;
941                         }
942                 }
943         }
944
945         return 0;
946 }
947
948 double ParametricEQ::gauss(double sigma, double center, double x)
949 {
950         if(EQUIV(sigma, 0)) sigma = 0.01;
951
952         double result = 1.0 /
953                 sqrt(2 * M_PI * sigma * sigma) *
954                 exp(-(x - center) * (x - center) /
955                         (2 * sigma * sigma));
956
957
958         return result;
959 }
960
961
962
963 int ParametricEQ::process_buffer(int64_t size,
964         Samples *buffer,
965         int64_t start_position,
966         int sample_rate)
967 {
968         need_reconfigure |= load_configuration();
969         if(need_reconfigure) reconfigure();
970
971
972         fft->process_buffer(start_position,
973                 size,
974                 buffer,
975                 get_direction());
976
977
978
979         return 0;
980 }
981
982 void ParametricEQ::reset()
983 {
984         need_reconfigure = 1;
985         thread = 0;
986         fft = 0;
987 }
988
989 void ParametricEQ::update_gui()
990 {
991         if( !thread ) return;
992         ParametricWindow *window = (ParametricWindow*)thread->window;
993         window->lock_window("ParametricEQ::update_gui");
994         if( load_configuration() ) {
995                 calculate_envelope();
996                 window->update_gui();
997         }
998         else if( pending_gui_frame() ) {
999                 window->update_canvas();
1000         }
1001         window->unlock_window();
1002 }
1003