dynamic keyframes, textbox rework, andrea ffmpeg.opts, perpetual chkpt undo, lv2...
[goodguy/history.git] / cinelerra-5.1 / plugins / echocancel / echocancel.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-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 "clip.h"
24 #include "cursors.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "bccolors.h"
29 #include "samples.h"
30 #include "echocancel.h"
31 #include "theme.h"
32 #include "transportque.inc"
33 #include "units.h"
34 #include "vframe.h"
35
36
37 #include <string.h>
38 #define TMPDIR "/tmp/echocancel/"
39
40
41 REGISTER_PLUGIN(EchoCancel)
42
43
44 EchoCancelConfig::EchoCancelConfig()
45 {
46         level = 0.0;
47         normalize = 0;
48         xzoom = MIN_XZOOM;
49         peaks = MIN_PEAKS;
50         damp = MIN_DAMP;
51         cutoff = (MIN_CUTOFF+MAX_CUTOFF)/2;
52         gain = 0;
53         offset = 0;
54         window_size = 0;
55         mode = CANCEL_OFF;
56         history_size = 4;
57 }
58
59 int EchoCancelConfig::equivalent(EchoCancelConfig &that)
60 {
61         return EQUIV(level, that.level) &&
62                 normalize == that.normalize &&
63                 xzoom == that.xzoom &&
64                 damp == that.damp &&
65                 cutoff == that.cutoff &&
66                 window_size == that.window_size &&
67                 mode == that.mode &&
68                 history_size == that.history_size;
69 }
70
71 void EchoCancelConfig::copy_from(EchoCancelConfig &that)
72 {
73         level = that.level;
74         normalize = that.normalize;
75         xzoom = that.xzoom;
76         peaks = that.peaks;
77         damp = that.damp;
78         cutoff = that.cutoff;
79         gain = that.gain;
80         offset = that.offset;
81         window_size = that.window_size;
82         mode = that.mode;
83         history_size = that.history_size;
84
85         if( window_size ) CLAMP(window_size, MIN_WINDOW, MAX_WINDOW);
86         CLAMP(history_size, MIN_HISTORY, MAX_HISTORY);
87         CLAMP(xzoom, MIN_XZOOM, MAX_XZOOM);
88         CLAMP(peaks, MIN_PEAKS, MAX_PEAKS);
89         CLAMP(damp, MIN_DAMP, MAX_DAMP);
90         CLAMP(cutoff, MIN_CUTOFF, MAX_CUTOFF);
91 }
92
93 void EchoCancelConfig::interpolate(EchoCancelConfig &prev,
94         EchoCancelConfig &next,
95         int64_t prev_frame,
96         int64_t next_frame,
97         int64_t current_frame)
98 {
99         copy_from(prev);
100 }
101
102
103
104 EchoCancelFrame::EchoCancelFrame(int n)
105 {
106         this->len = n;
107         data = new float[n + 1];
108         data[0] = 1;
109 }
110
111 EchoCancelFrame::~EchoCancelFrame()
112 {
113         delete [] data;
114 }
115
116
117
118 EchoCancelLevel::EchoCancelLevel(EchoCancel *plugin, int x, int y)
119  : BC_FPot(x, y, plugin->config.level, INFINITYGAIN, MAXGAIN)
120 {
121         this->plugin = plugin;
122 }
123
124 int EchoCancelLevel::handle_event()
125 {
126         plugin->config.level = get_value();
127         plugin->send_configure_change();
128         return 1;
129 }
130
131
132
133
134
135 EchoCancelMode::EchoCancelMode(EchoCancel *plugin, int x, int y)
136  : BC_PopupMenu(x, y, 120, to_text(plugin->config.mode))
137 {
138         this->plugin = plugin;
139 }
140
141 void EchoCancelMode::create_objects()
142 {
143         add_item(new BC_MenuItem(to_text(CANCEL_OFF)));
144         add_item(new BC_MenuItem(to_text(CANCEL_ON)));
145         add_item(new BC_MenuItem(to_text(CANCEL_MAN)));
146 }
147
148 int EchoCancelMode::handle_event()
149 {
150         int new_mode = to_mode(get_text());
151         if( plugin->config.mode != new_mode ) {
152                 plugin->config.mode = new_mode;
153                 plugin->send_configure_change();
154         }
155         return 1;
156 }
157
158 const char* EchoCancelMode::to_text(int mode)
159 {
160         switch(mode) {
161         case CANCEL_ON: return _("ON");
162         case CANCEL_MAN: return _("MAN");
163         }
164         return _("OFF");
165 }
166
167 int EchoCancelMode::to_mode(const char *text)
168 {
169         if(!strcmp(to_text(CANCEL_ON), text)) return CANCEL_ON;
170         if(!strcmp(to_text(CANCEL_MAN), text)) return CANCEL_MAN;
171         return CANCEL_OFF;
172 }
173
174
175
176
177 EchoCancelHistory::EchoCancelHistory(EchoCancel *plugin,
178         int x,
179         int y)
180  : BC_IPot(x, y, plugin->config.history_size, MIN_HISTORY, MAX_HISTORY)
181 {
182         this->plugin = plugin;
183 }
184
185 int EchoCancelHistory::handle_event()
186 {
187         plugin->config.history_size = get_value();
188         plugin->send_configure_change();
189         return 1;
190 }
191
192
193
194
195
196
197 EchoCancelWindowSize::EchoCancelWindowSize(EchoCancel *plugin, int x, int y, const char *text)
198  : BC_PopupMenu(x, y, 80, text)
199 {
200         this->plugin = plugin;
201 }
202
203 int EchoCancelWindowSize::handle_event()
204 {
205         plugin->config.window_size = atoi(get_text());
206         plugin->send_configure_change();
207         return 1;
208 }
209
210 const char *EchoCancelWindowSize::to_text(int size)
211 {
212         if( !size ) return _("default");
213         static char string[BCSTRLEN];
214         sprintf(string, "%d", size);
215         return string;
216 }
217
218 int EchoCancelWindowSize::to_size(const char *text)
219 {
220         return !strcmp(to_text(0),text) ? 0 : strtol(text,0,0);
221 }
222
223
224
225 EchoCancelWindowSizeTumbler::EchoCancelWindowSizeTumbler(EchoCancel *plugin, int x, int y)
226  : BC_Tumbler(x, y)
227 {
228         this->plugin = plugin;
229 }
230
231 int EchoCancelWindowSizeTumbler::handle_up_event()
232 {
233         if( !plugin->config.window_size )
234                 plugin->config.window_size = MIN_WINDOW;
235         else if( (plugin->config.window_size *= 2) > MAX_WINDOW )
236                 plugin->config.window_size = MAX_WINDOW;
237         EchoCancelWindowSize *window_size =
238                 ((EchoCancelWindow *)plugin->get_thread()->get_window())->window_size;
239         const char *wsp = EchoCancelWindowSize::to_text(plugin->config.window_size);
240         window_size->set_text(wsp);
241         plugin->send_configure_change();
242         return 0;
243 }
244
245 int EchoCancelWindowSizeTumbler::handle_down_event()
246 {
247         plugin->config.window_size /= 2;
248         if(plugin->config.window_size < MIN_WINDOW)
249                 plugin->config.window_size = 0;
250         EchoCancelWindowSize *window_size =
251                 ((EchoCancelWindow *)plugin->get_thread()->get_window())->window_size;
252         const char *wsp = EchoCancelWindowSize::to_text(plugin->config.window_size);
253         window_size->set_text(wsp);
254         plugin->send_configure_change();
255         return 1;
256 }
257
258
259
260
261
262 EchoCancelNormalize::EchoCancelNormalize(EchoCancel *plugin, int x, int y)
263  : BC_CheckBox(x, y, plugin->config.normalize, _("Normalize"))
264 {
265         this->plugin = plugin;
266 }
267
268 int EchoCancelNormalize::handle_event()
269 {
270         plugin->config.normalize = get_value();
271         plugin->send_configure_change();
272         return 1;
273 }
274
275
276
277
278 EchoCancelXZoom::EchoCancelXZoom(EchoCancel *plugin, int x, int y)
279  : BC_IPot(x, y, plugin->config.xzoom, MIN_XZOOM, MAX_XZOOM)
280 {
281         this->plugin = plugin;
282 }
283
284 int EchoCancelXZoom::handle_event()
285 {
286         plugin->config.xzoom = get_value();
287         plugin->send_configure_change();
288         return 1;
289 }
290
291
292
293 EchoCancelPeaks::EchoCancelPeaks(EchoCancel *plugin, int x, int y)
294  : BC_IPot(x, y, plugin->config.peaks, MIN_PEAKS, MAX_PEAKS)
295 {
296         this->plugin = plugin;
297 }
298
299 int EchoCancelPeaks::handle_event()
300 {
301         plugin->config.peaks = get_value();
302         plugin->send_configure_change();
303         return 1;
304 }
305
306
307
308 EchoCancelDamp::EchoCancelDamp(EchoCancel *plugin, int x, int y)
309  : BC_IPot(x, y, plugin->config.damp, MIN_DAMP, MAX_DAMP)
310 {
311         this->plugin = plugin;
312 }
313
314 int EchoCancelDamp::handle_event()
315 {
316         plugin->config.damp = get_value();
317         plugin->send_configure_change();
318         return 1;
319 }
320
321
322
323 EchoCancelCutoff::EchoCancelCutoff(EchoCancel *plugin, int x, int y)
324  : BC_IPot(x, y, plugin->config.cutoff, MIN_CUTOFF, MAX_CUTOFF)
325 {
326         this->plugin = plugin;
327 }
328
329 int EchoCancelCutoff::handle_event()
330 {
331         plugin->config.cutoff = get_value();
332         plugin->send_configure_change();
333         return 1;
334 }
335
336
337
338 EchoCancelCanvas::EchoCancelCanvas(EchoCancel *plugin, int x, int y, int w, int h)
339  : BC_SubWindow(x, y, w, h, BLACK)
340 {
341         this->plugin = plugin;
342         current_operation = NONE;
343 }
344
345 int EchoCancelCanvas::button_press_event()
346 {
347         if(is_event_win() && cursor_inside())
348         {
349                 calculate_point(1);
350                 current_operation = DRAG;
351                 plugin->send_configure_change();
352                 return 1;
353         }
354         return 0;
355 }
356
357 int EchoCancelCanvas::button_release_event()
358 {
359         if(current_operation == DRAG)
360         {
361                 calculate_point(2);
362                 current_operation = NONE;
363                 return 1;
364         }
365         return 0;
366 }
367
368 int EchoCancelCanvas::cursor_motion_event()
369 {
370         if(current_operation == DRAG)
371         {
372                 calculate_point(3);
373         }
374         return 0;
375 }
376
377
378 void EchoCancelCanvas::calculate_point(int do_overlay)
379 {
380         int x = get_cursor_x();
381         int y = get_cursor_y();
382         CLAMP(x, 0, get_w() - 1);
383         CLAMP(y, 0, get_h() - 1);
384
385         EchoCancelWindow *window = (EchoCancelWindow *)plugin->thread->window;
386         window->calculate_frequency(x, y, do_overlay);
387 }
388
389 void EchoCancelCanvas::draw_overlay()
390 {
391         EchoCancelWindow *window = (EchoCancelWindow*)plugin->thread->window;
392         if(window->probe_x >= 0 || window->probe_y >= 0)
393         {
394                 set_color(GREEN);
395                 set_inverse();
396                 char string[BCTEXTLEN];
397                 sprintf(string, "%d, %f", plugin->config.offset, plugin->config.gain);
398                 draw_text(window->probe_x, window->probe_y, string);
399                 draw_line(0, window->probe_y, get_w(), window->probe_y);
400                 draw_line(window->probe_x, 0, window->probe_x, get_h());
401                 set_opaque();
402         }
403 }
404
405
406
407
408
409
410
411
412 EchoCancelWindow::EchoCancelWindow(EchoCancel *plugin)
413  : PluginClientWindow(plugin, plugin->w, plugin->h, 320, 320, 1)
414 {
415         this->plugin = plugin;
416         probe_x = probe_y = -1;
417 }
418
419 EchoCancelWindow::~EchoCancelWindow()
420 {
421 }
422
423 void EchoCancelWindow::create_objects()
424 {
425         int pad = plugin->get_theme()->widget_border;
426         add_subwindow(canvas = new EchoCancelCanvas(plugin, 0, 0,
427                 get_w(), get_h() - 2*BC_Pot::calculate_h() - 3*pad));
428         canvas->set_cursor(CROSS_CURSOR, 0, 0);
429
430         int x = pad, y = canvas->get_y() + canvas->get_h() + pad;
431         int x1 = x, y1 = y;
432
433         add_subwindow(level_title = new BC_Title(x, y, _("Level:")));
434         x += level_title->get_w() + pad;
435         add_subwindow(level = new EchoCancelLevel(plugin, x, y));
436         x += level->get_w() + pad;
437         y += level->get_h() + pad;
438
439         x = x1;
440         add_subwindow(normalize = new EchoCancelNormalize(plugin, x, y));
441         x += normalize->get_w() + 3*pad;
442         y += normalize->get_h() - BC_Title::calculate_h(this,_("Gain: "));
443         add_subwindow(gain_title = new EchoCancelTitle(x, y, _("Gain: "), 0.));
444         x += gain_title->get_w() + 2*pad;
445         add_subwindow(offset_title = new EchoCancelTitle(x, y, _("Offset: "), 0));
446
447         x = x1 + level_title->get_w() + level->get_w() + 2*pad;
448         x1 = x;
449         y = y1;
450
451         add_subwindow(mode_title = new BC_Title(x, y, _("Mode:")));
452         x += mode_title->get_w() + pad;
453         add_subwindow(mode = new EchoCancelMode(plugin, x, y));
454         mode->create_objects();
455
456         x = x1;
457         y += mode->get_h() + pad;
458
459         add_subwindow(window_size_title = new BC_Title(x, y, _("Window size:")));
460         x += window_size_title->get_w() + pad;
461         const char *wsp = EchoCancelWindowSize::to_text(plugin->config.window_size);
462         add_subwindow(window_size = new EchoCancelWindowSize(plugin, x, y, wsp));
463         x += window_size->get_w();
464         add_subwindow(window_size_tumbler = new EchoCancelWindowSizeTumbler(plugin, x, y));
465
466         window_size->add_item(new BC_MenuItem(EchoCancelWindowSize::to_text(0)));
467         for( int i=MIN_WINDOW; i<=MAX_WINDOW; i*=2 ) {
468                 window_size->add_item(new BC_MenuItem(EchoCancelWindowSize::to_text(i)));
469         }
470
471         x = x1;
472         y += window_size->get_h() + pad;
473
474         x = x1 = window_size_tumbler->get_x() + window_size_tumbler->get_w() + pad;
475         y = y1;
476         add_subwindow(history_title = new BC_Title(x, y, _("History:")));
477         x += history_title->get_w() + pad;
478         add_subwindow(history = new EchoCancelHistory(plugin, x, y));
479
480         x = x1;
481         int y2 = y;
482         y += history->get_h() + pad;
483         add_subwindow(xzoom_title = new BC_Title(x, y, _("X Zoom:")));
484         x += xzoom_title->get_w() + pad;
485         add_subwindow(xzoom = new EchoCancelXZoom(plugin, x, y));
486         x += xzoom->get_w() + pad;
487         x1 = x;
488         add_subwindow(damp_title = new BC_Title(x, y, _("Damp:")));
489         x += damp_title->get_w() + pad;
490         add_subwindow(damp = new EchoCancelDamp(plugin, x, y));
491         x += damp->get_w() + pad;
492         add_subwindow(cutoff_title = new BC_Title(x, y, _("Cutoff Hz:")));
493         x += cutoff_title->get_w() + pad;
494         add_subwindow(cutoff = new EchoCancelCutoff(plugin, x, y));
495         int x2 = x - BC_Title::calculate_w(this, _("Peaks:")) - pad;
496         add_subwindow(peaks_title = new BC_Title(x2, y2, _("Peaks:")));
497         add_subwindow(peaks = new EchoCancelPeaks(plugin, x, y2));
498
499         x = x1 + 3*pad;
500         y = y1;
501         add_subwindow(freq_title = new BC_Title(x, y, _("0 Hz")));
502         y += freq_title->get_h() + pad;
503         add_subwindow(amplitude_title = new BC_Title(x, y, _("Amplitude: 0 dB")));
504
505         show_window();
506 }
507
508 int EchoCancelWindow::resize_event(int w, int h)
509 {
510         int canvas_h = canvas->get_h();
511         int canvas_difference = get_h() - canvas_h;
512
513         canvas->reposition_window(0, 0, w, h - canvas_difference);
514         canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
515         probe_x = probe_y = -1;
516
517         int dx = 0, dy = canvas->get_h() - canvas_h;
518
519 // Remove all columns which may be a different size.
520         plugin->frame_buffer.remove_all_objects();
521         plugin->frame_history.remove_all_objects();
522
523         level_title->reposition_window_relative(dx, dy);
524         level->reposition_window_relative(dx, dy);
525         mode_title->reposition_window_relative(dx, dy);
526         mode->reposition_window_relative(dx, dy);
527         window_size_title->reposition_window_relative(dx, dy);
528         window_size->reposition_window_relative(dx, dy);
529         window_size_tumbler->reposition_window_relative(dx, dy);
530         gain_title->reposition_window_relative(dx, dy);
531         offset_title->reposition_window_relative(dx, dy);
532         normalize->reposition_window_relative(dx, dy);
533         history_title->reposition_window_relative(dx, dy);
534         history->reposition_window_relative(dx, dy);
535         xzoom_title->reposition_window_relative(dx, dy);
536         xzoom->reposition_window_relative(dx, dy);
537         peaks_title->reposition_window_relative(dx, dy);
538         peaks->reposition_window_relative(dx, dy);
539         damp_title->reposition_window_relative(dx, dy);
540         damp->reposition_window_relative(dx, dy);
541         cutoff_title->reposition_window_relative(dx, dy);
542         cutoff->reposition_window_relative(dx, dy);
543         freq_title->reposition_window_relative(dx, dy);
544         amplitude_title->reposition_window_relative(dx, dy);
545
546         flush();
547         plugin->w = w;
548         plugin->h = h;
549         return 0;
550 }
551
552
553 void EchoCancelWindow::calculate_frequency(int x, int y, int do_overlay)
554 {
555         if( !do_overlay && probe_x == x && probe_y == y ) return;
556         if( do_overlay & 2 ) canvas->draw_overlay();
557         probe_x = x;  probe_y = y;
558
559 // Convert to coordinates in frame history
560         int need_config_update = 0;
561         double gain = plugin->config.gain;
562         double offset = plugin->config.offset;
563
564         if( x >= 0 && plugin->frame_history.size() ) {
565                 int ww = canvas->get_w();
566                 int freq_pixel = x;
567                 if( freq_pixel < 0 ) freq_pixel = 0;
568                 if( freq_pixel > ww ) freq_pixel = ww;
569                 int time_pixel = 0;
570                 int idx = plugin->frame_history.size()-1 - time_pixel;
571                 EchoCancelFrame *frm = plugin->frame_history[idx];
572                 int pixels = canvas->get_w();
573                 int half_window = plugin->header.window_size / 2;
574                 offset = (double)half_window * freq_pixel/(pixels * plugin->config.xzoom);
575                 if( offset <= 1e-10 ) offset = 1;
576                 int freq = plugin->header.sample_rate / offset;
577                 int msecs = 1000. / freq;
578                 char string[BCTEXTLEN];
579                 sprintf(string, _("%d Hz, %d ms (%d))"), freq, msecs, (int)offset);
580                 freq_title->update(string);
581                 int frm_sz1 = frm->size()-1;
582                 if( freq_pixel > frm_sz1 ) freq_pixel = frm_sz1;
583                 float *frame_data = frm->samples();
584                 double level = frame_data[freq_pixel];
585                 double scale = frm->scale();
586                 sprintf(string, _("Amplitude: %.3f (%.6g)"), level, scale);
587                 amplitude_title->update(string);
588         }
589         if( y >= 0 ) {
590                 int hh = canvas->get_h()/2;
591                 int gain_pixel = hh - y;
592                 if( gain_pixel < 0 ) gain_pixel = 0;
593                 if( gain_pixel > hh ) gain_pixel = hh;
594                 gain = (double)gain_pixel / hh;
595         }
596         if( plugin->config.gain != gain ) {
597                 gain_title->update(plugin->config.gain = gain);
598                 need_config_update = 1;
599         }
600         if( offset > 0 && plugin->config.offset != offset ) {
601                 offset_title->update(plugin->config.offset = offset);
602                 need_config_update = 1;
603         }
604         if( need_config_update )
605                 plugin->send_configure_change();
606
607         if( do_overlay & 1 ) canvas->draw_overlay();
608         if( do_overlay ) canvas->flash();
609 }
610
611 void EchoCancelWindow::update_gui()
612 {
613         level->update(plugin->config.level);
614         char string[BCTEXTLEN];
615         sprintf(string, "%d", plugin->config.window_size);
616         window_size->set_text(string);
617         mode->set_text(EchoCancelMode::to_text(plugin->config.mode));
618         history->update(plugin->config.history_size);
619         normalize->set_value(plugin->config.normalize);
620         gain_title->update(plugin->config.gain);
621         offset_title->update(plugin->config.offset);
622 }
623
624 EchoCancel::EchoCancel(PluginServer *server)
625  : PluginAClient(server)
626 {
627         reset();
628         timer = new Timer;
629         w = 640;
630         h = 480;
631 }
632
633 EchoCancel::~EchoCancel()
634 {
635         delete fft;
636         delete audio_buffer;
637         delete data;
638         delete_buffers();
639         frame_buffer.remove_all_objects();
640         frame_history.remove_all_objects();
641         delete timer;
642 }
643
644
645 void EchoCancel::reset()
646 {
647         thread = 0;
648         window_size = 0;
649         half_window = 0;
650         interrupted = 0;
651         fft = 0;
652         audio_buffer = 0;
653         data = 0;
654         cor = 0;
655         aud_real = 0;  aud_imag = 0;
656         env_real = 0;  env_imag = 0;
657         envelope = 0;  env_data = 0;
658         time_frame = 0;
659         memset(&header, 0, sizeof(header));
660 }
661
662 void DataBuffer::
663 set_buffer(int ofs, int len)
664 {
665         int needed = ofs + len;
666         if( needed > allocated ) {
667                 DataHeader *new_header = new_data_header(needed);
668                 if( data_header ) {
669                         int old_size = sizeof(DataHeader) + allocated*sizeof(float);
670                         memcpy(new_header, data_header, old_size);
671                         delete [] (char *)data_header;
672                 }
673                 else
674                         memset(new_header, 0, sizeof(*new_header));
675                 data_header = new_header;
676                 allocated = needed;
677         }
678         sample_data = &data_header->samples[ofs];
679         data_len = len;
680 }
681
682 void AudioBuffer::
683 set_buffer(int ofs, int len)
684 {
685         int needed = ofs + len;
686         if( needed > allocated ) {
687                 Samples *new_samples = new Samples(needed);
688                 if( samples ) {
689                         double *old_data = samples->get_data();
690                         double *new_data = new_samples->get_data();
691                         memcpy(new_data, old_data, allocated*sizeof(old_data[0]));
692                         delete samples;
693                 }
694                 samples = new_samples;
695                 allocated = needed;
696         }
697         sample_data = samples->get_data() + ofs;
698         data_len = len;
699 }
700
701 void AudioBuffer::
702 append(double *bfr, int len)
703 {
704         set_buffer(buffer_size(), len);
705         memcpy(get_data(), bfr, len*sizeof(sample_data[0]));
706         data_len = len;
707 }
708
709 void AudioBuffer::
710 remove(int len)
711 {
712         double *data = samples->get_data();
713         int move_len = buffer_size() - len;
714         memmove(data, data+len, move_len*sizeof(double));
715         if( move_len < data_len ) {
716                 sample_data = data;
717                 data_len = move_len;
718         }
719         else
720                 sample_data -= len;
721 }
722
723 const char* EchoCancel::plugin_title() { return N_("EchoCancel"); }
724 int EchoCancel::is_realtime() { return 1; }
725
726 static inline double sqr(double v) { return v*v; }
727
728 static inline void cx_product(int n, int sf, double *rp, double *ip,
729                 double *arp, double *aip, double *brp, double *bip)
730 {
731         int m = !sf ? n : n/2, i = 0;
732         while( i <= m ) {
733                 double ar = arp[i], ai = aip[i];
734                 double br = brp[i], bi = bip[i];
735                 rp[i] = ar*br - ai*bi;  // complex a*ib
736                 ip[i] = ar*bi + ai*br;
737                 ++i;
738         }
739         if( !sf ) return;
740         while( --m > 0 ) { rp[i] = rp[m];  ip[i] = -ip[m];  ++i; }
741 }
742
743 static inline void cj_product(int n, int sf, double *rp, double *ip,
744                 double *arp, double *aip, double *brp, double *bip)
745 {
746         int m = !sf ? n-1 : n/2, i = 0;
747         while( i <= m ) {
748                 double ar = arp[i], ai = aip[i];
749                 double br = brp[i], bi = -bip[i];
750                 rp[i] = ar*br - ai*bi;  // complex a*ib'
751                 ip[i] = ar*bi + ai*br;
752                 ++i;
753         }
754         if( !sf ) return;
755         while( --m > 0 ) { rp[i] = rp[m];  ip[i] = -ip[m];  ++i; }
756 }
757
758 static inline void log_power(int n, int sf, double *rp, double *arp, double *aip)
759 {
760         int m = !sf ? n : n/2, i = 0;
761         while( i <= m ) {
762                 double sr = arp[i], si = aip[i];
763                 double ss = sqr(sr) + sqr(si);
764                 rp[i] = ss > 1e-20 ? log(ss) : -46;
765                 ++i;
766         }
767         if( !sf ) return;
768         while( --m > 0 ) { rp[i] = rp[m];  ++i; }
769 }
770
771 /* Adapted from Marple: Digital Spectral Analysis with Applications
772  *   Solves linear simultaneous equations by the Levinson algorithm.
773  *      TX = Z
774  * Input:
775  *   T[m,m] a nonsymmetric Toeplitz matrix,
776  *     tc[0..m-1] left column, tr[0..m-1] top row
777  *   Z[m]   known right-hand-side column,
778  * Output:
779  *   X[m]   solution vector
780  * Returns: 1 if singular, 0 on success
781  */
782
783 static inline int toeplitz(int m, double *tc, double *tr, double *z, double *x)
784 {
785         double a[m], b[m], p = tc[0];
786         if( p != tr[0] || fabs(p) < 1e-20 ) return 1;
787         x[0] = z[0] / p;
788         for( int k0=0,k=1; k<m; k0=k++ ) {
789                 double sc = tc[k], sr = tr[k], xc = x[0] * sc;
790                 for( int i=1,j=k0; i<k; ++i,--j ) {
791                         sc += a[i] * tc[j];
792                         sr += b[i] * tr[j];
793                         xc += x[i] * tc[j];
794                 }
795                 double ac = -sc / p, br = -sr / p;
796                 p *= 1 - ac * br;
797                 if( fabs(p) < 1e-20 ) return 1;
798                 a[k] = ac;      b[k] = br;
799                 x[k] = (z[k] - xc) / p;
800                 for( int i=1,j=k0; j>0; ++i,--j ) {
801                         sc = a[i];      sr = b[j];
802                         a[i] += ac * sr;
803                         b[j] += br * sc;
804                 }
805                 double xk = x[k];
806                 for( int i=0,j=k; j>0; ++i,--j )
807                         x[i] += xk * b[j];
808         }
809         return 0;
810 }
811
812
813 void EchoCancel::cepstrum(double *audio)
814 {
815 //dfile_dump(TMPDIR "audio",audio,window_size);
816         fft->do_fft(window_size, 0, audio, 0, aud_real, aud_imag);
817 //dfile_dump(TMPDIR "aud_real",aud_real,window_size);
818 //dfile_dump(TMPDIR "aud_imag",aud_imag,window_size);
819         // half zero, half window of audio
820         int k = 0;
821         for( ; k<half_window; ++k ) cor[k] = 0;
822         for( ; k<window_size; ++k ) cor[k] = audio[k];
823         fft->do_fft(window_size, 0, cor, 0, env_real, env_imag);
824 //dfile_dump(TMPDIR "env_real",env_real,window_size);
825 //dfile_dump(TMPDIR "env_imag",env_imag,window_size);
826         cj_product(window_size, 1, env_real, env_imag,
827                 env_real, env_imag, aud_real, aud_imag);
828 //dfile_dump(TMPDIR "prd_real",env_real,window_size);
829 //dfile_dump(TMPDIR "prd_imag",env_imag,window_size);
830         log_power(window_size, 1, aud_real, env_real, env_imag);
831         // a = zeros, last half audio buffer, b = audio buffer
832         // COR = fft(a)*conj(fft(b)), cor = IFFT(COR)
833         // cepstrum = IFFT(log(mag(COR)**2))
834         double *cept_real = aud_real, *cept_imag = aud_imag; // do in place
835         fft->do_fft(window_size, 1, aud_real, 0, cept_real, cept_imag);
836         fft->do_fft(window_size, 1, env_real, env_imag, cor, aud_imag);
837 //dfile_dump(TMPDIR "cor",cor,half_window);
838         int damp = config.damp; // apply dampening
839         if( ++time_frames < damp ) damp = time_frames;
840         float wt = 1./damp, wt1 = 1. - wt;
841         float *dp = data->get_buffer();
842         for( int i=0; i<half_window; ++i ) {
843                 double v = cept_real[i] * cor[i];
844                 dp[i] = time_frame[i] = (wt1*time_frame[i] + wt*v);
845         }
846 //ffile_dump(TMPDIR "time_frame",time_frame,half_window);
847 }
848
849 void EchoCancel::dfile_dump(const char *fn, double *dp, int n)
850 {
851         FILE *fp = fopen(fn,"w");
852         if( !fp ) return;
853         for( int i=0; i<n; ++i ) fprintf(fp,"%f\n",dp[i]);
854         fclose(fp);
855 }
856
857 void EchoCancel::ffile_dump(const char *fn, float *dp, int n)
858 {
859         FILE *fp = fopen(fn,"w");
860         if( !fp ) return;
861         for( int i=0; i<n; ++i ) fprintf(fp,"%f\n",dp[i]);
862         fclose(fp);
863 }
864
865 void EchoCancel::calculate_envelope(int sample_rate, int peaks, int bsz)
866 {
867         int cutoff = sample_rate/config.cutoff;
868         bsz |= 1; // should be odd
869         int bsz2 = bsz/2+1;
870         double power = 0;
871         for( int i=bsz2; --i>0; ) power += 2*cor[i];
872         power += cor[0];
873         int damp = config.damp; // apply dampening
874         if( ++time_frames < damp ) damp = time_frames;
875         float wt = 1./damp, wt1 = 1. - wt;
876         for( int i=0; i<half_window; ++i ) envelope[i] *= wt1;
877         if( power < 1e-12 ) return;
878         double *dp = env_real, *ep = dp;
879         for( int i=0; i<half_window; ++i ) *ep++ = time_frame[i];
880 //printf("envelope %.4f:\n", power);
881         while( --peaks >= 0 ) {
882                 int x = cutoff;
883                 double *sp = dp+x, *mp = sp, *xp = sp;
884                 double sx = 0;  // first bsz band
885                 for( int i=bsz; --i>=0; ) sx += *xp++;
886                 double mx = sx;
887                 while( xp < ep ) {
888                         sx += *xp++ - *sp++;
889                         if( sx > mx ) { mx = sx;  mp = sp; }
890                 }
891                 x = mp - dp;
892                 double echo_signal = 0, *cp = cor+x;
893                 for( int i=0; i<bsz; ++i ) echo_signal += cp[i];
894 //printf("   %d(%.3f/%.3f):\n", x, mx, echo_signal/power);
895                 // positive reflections only, not too small
896                 if( echo_signal/power < 0.001 ) break;
897                 double gain[bsz];
898                 int ret = toeplitz(bsz, cor, cor, cp, gain);
899                 if( !ret ) {
900                         double ss = 0;
901                         for( int i=0; i<bsz; ++i ) ss += sqr(gain[i]);
902                         double rms = sqrt(ss / bsz);
903 //printf("rms=%f ",rms);
904                         if( rms > 2 ) ret = 1;   // too wacky
905                 }
906                 if( ret ) { // misbehaved, use single pulse in band center
907 //printf("** ");
908                         memset(gain, 0, bsz*sizeof(gain[0]));
909                         gain[bsz2] = echo_signal / power;
910                 }
911 //double g=0;
912                 for( int i=0; i<bsz; ++i,++x ) {
913                         envelope[x] += wt * gain[i];
914 //printf("[%d]=%.3f,", x, envelope[x]);  g += envelope[x];
915                 }
916 //printf(" : %.4f\n",g);
917                 if( !peaks ) break;
918                 // remove echo from search, prevent periodic reverbs
919                 for(int k=x; k<half_window; k+=x ) {
920                         // clear this band and adj bands
921                         int j = k - bsz, n = 3*bsz;
922                         if( j < 0 ) { n += j;  j = 0; }
923                         if( j+n > half_window ) n = half_window - j;
924                         for( int i=n; --i>=0; ++j ) dp[j] = 0;
925                 }
926         }
927 //printf(" ==\n");
928 }
929
930 void EchoCancel::create_envelope(int sample_rate)
931 {
932         memset(envelope,0,half_window*sizeof(envelope[0]));
933         int offset = config.offset;
934         if( offset < 0 || offset >= half_window ) return;
935         double gain = config.gain;
936         if( gain < 0 || gain >= 1 ) return;
937         envelope[offset] = gain;
938 }
939
940 int EchoCancel::cancel_echo(double *bp, int size)
941 {
942         int n = half_window + size;
943         if( n < window_size ) n = window_size;
944         if( audio_buffer->buffer_size() < n ) return 1;
945         int k = 0;
946         // a = audio data, t = env data: convolution = ifft(fft(a)*conj(fft(t))),
947         // but ifft(fft(a)*conj(fft(t)))==ifft(fft(a)*fft([t[0]]+t[:0:-1]))
948         // removing conj reverses t, as echos are from the past
949         if( config.mode == CANCEL_MAN ) create_envelope(sample_rate);
950         for( int i=0; i<half_window; ++i ) env_data[k++] = envelope[i];
951         while( k < window_size ) env_data[k++] = 0;
952         fft->do_fft(window_size, 0, env_data, 0, env_real, env_imag);
953
954         n = half_window + size;
955         if( n < window_size ) n = window_size;
956         double *ap = audio_buffer->get_buffer(-n);
957         fft->do_fft(window_size, 0, ap, 0, aud_real, aud_imag);
958         cx_product(window_size, 1, aud_real, aud_imag,
959                 aud_real, aud_imag, env_real, env_imag);
960         fft->do_fft(window_size, 1, aud_real, aud_imag);
961         n = size;
962         if( n > half_window ) n = half_window;
963         double *sp = &aud_real[window_size - n];
964         for( int i=n; --i>=0; ++bp,++sp ) *bp -= *sp;
965
966         return 0;
967 }
968
969 void EchoCancel::delete_buffers()
970 {
971         delete [] cor;          cor = 0;
972         delete [] aud_real;     aud_real = 0;
973         delete [] aud_imag;     aud_imag = 0;
974         delete [] envelope;     envelope = 0;
975         delete [] env_real;     env_real = 0;
976         delete [] env_imag;     env_real = 0;
977         delete [] env_data;     env_data = 0;
978         delete [] time_frame;   time_frame = 0;
979 }
980
981 void EchoCancel::alloc_buffers(int fft_sz)
982 {
983         window_size = fft_sz;
984         half_window = window_size / 2;
985         cor = new double[window_size];
986         aud_real = new double[window_size];
987         aud_imag = new double[window_size];
988         envelope = new double[half_window];
989         env_real = new double[window_size];
990         env_imag = new double[window_size];
991         env_data = new double[window_size];
992         time_frame = new float[half_window];
993 }
994
995 int EchoCancel::process_buffer(int64_t size, Samples *buffer,
996                 int64_t start_position, int sample_rate)
997 {
998 // Pass through
999         read_samples(buffer, 0, sample_rate, start_position, size);
1000 // Reset audio buffer
1001         load_configuration();
1002         int fft_sz = config.window_size;
1003         if( !fft_sz ) fft_sz = 1<<FFT::samples_to_bits(sample_rate);
1004         if( window_size != fft_sz ) {
1005                 render_stop();
1006                 delete_buffers();
1007                 alloc_buffers(fft_sz);
1008                 time_frames = 0;
1009         }
1010         if( !time_frames ) {
1011                 memset(envelope, 0, half_window*sizeof(envelope[0]));
1012                 memset(time_frame, 0, half_window*sizeof(time_frame[0]));
1013         }
1014         if( !data ) data = new DataBuffer(window_size);
1015         if( !audio_buffer ) audio_buffer = new AudioBuffer(window_size+2*size);
1016         if( !fft ) fft = new FFT;
1017
1018 // Accumulate audio
1019         int past_audio = audio_buffer->buffer_size() - window_size;
1020         if( past_audio > 0 ) audio_buffer->remove(past_audio);
1021         audio_buffer->append(buffer->get_data(), size);
1022
1023 // process half_windows
1024         double *bp = buffer->get_data();
1025         int total_windows = 0, audio_size = audio_buffer->buffer_size();
1026         while( audio_size >= window_size ) {
1027                 int sample_offset = half_window * total_windows++;
1028                 data->set_buffer(sample_offset, half_window);
1029                 double *audio = audio_buffer->get_buffer(-audio_size);
1030                 cepstrum(audio);
1031                 if( config.mode == CANCEL_ON )
1032                         calculate_envelope(sample_rate, config.peaks, 7);
1033                 if( config.mode != CANCEL_OFF && size > 0 )
1034                         cancel_echo(bp, size);
1035                 bp += half_window;
1036                 size -= half_window;
1037                 audio_size -= half_window;
1038         }
1039
1040         DataHeader *data_header = data->get_header();
1041         data_header->window_size = window_size;
1042         data_header->interrupted = interrupted;
1043         data_header->sample_rate = sample_rate;
1044         data_header->total_windows = total_windows;
1045         data_header->level = DB::fromdb(config.level); // Linear output level
1046         send_render_gui(data_header, data_header->size());
1047         interrupted = 0;
1048         return 0;
1049 }
1050
1051 void EchoCancel::render_stop()
1052 {
1053         interrupted = 1;
1054         if( audio_buffer ) audio_buffer->set_buffer(0, 0);
1055         if( data ) { delete data;  data = 0; }
1056 }
1057
1058
1059
1060 NEW_WINDOW_MACRO(EchoCancel, EchoCancelWindow)
1061
1062
1063 void EchoCancelCanvas::
1064 draw_frame(float *data, int len, int hh)
1065 {
1066         int w = get_w(), h = get_h();
1067         float y = data[0];
1068         int h1 = h-1;
1069         int y1 = hh - hh*y;
1070         for(int x1=0,x2=1; x2<w; ++x2 ) {
1071                 if( x2 < len ) y = data[x2];
1072                 int y2 = hh - hh*y;
1073                 CLAMP(y2, 0, h1);
1074                 draw_line(x1, y1, x2, y2);
1075                 x1 = x2;  y1 = y2;
1076         }
1077 }
1078
1079 void EchoCancel::update_gui()
1080 {
1081         if( !thread ) return;
1082         EchoCancelWindow *window = (EchoCancelWindow*)thread->get_window();
1083         window->lock_window("EchoCancel::update_gui");
1084         if( load_configuration() )
1085                 window->update_gui();
1086 // Shift frames into history
1087         int new_frames = 0;
1088         while( frame_buffer.size() > 0 ) {
1089                 while( frame_history.size() >= config.history_size )
1090                         frame_history.remove_object_number(0);
1091                 frame_history.append(frame_buffer[0]);
1092                 frame_buffer.remove_number(0);
1093                 ++new_frames;
1094         }
1095
1096         if( new_frames > 0 ) {
1097                 int last_frame = frame_history.size()-1;
1098 // Draw frames from history
1099                 EchoCancelCanvas *canvas = (EchoCancelCanvas*)window->canvas;
1100                 canvas->clear_box(0, 0, canvas->get_w(), canvas->get_h());
1101                 canvas->set_line_width(1);
1102                 for( int frame=0; frame<last_frame; ++frame ) {
1103                         int luma = 0x80 * (frame+1)/last_frame + 0x40;
1104                         canvas->set_color(luma*0x010101);
1105                         EchoCancelFrame *frm = frame_history[frame];
1106                         canvas->draw_frame(frm->samples(), frm->size(), h/2);
1107                 }
1108                 canvas->set_line_width(2);
1109                 canvas->set_color(WHITE);
1110                 EchoCancelFrame *frm = frame_history[last_frame];
1111                 canvas->draw_frame(frm->samples(), frm->size(), h/2);
1112                 canvas->set_color(RED);
1113                 canvas->draw_line(frm->cut_offset,0, frm->cut_offset,10);
1114 // Recompute probe level
1115                 int do_overlay = canvas->is_dragging() ? 1 : 0;
1116                 window->calculate_frequency(window->probe_x, window->probe_y, do_overlay);
1117                 canvas->flash();
1118         }
1119
1120         window->unlock_window();
1121 }
1122
1123 void EchoCancel::render_gui(void *data, int size)
1124 {
1125         if( !thread ) return;
1126         DataHeader *data_header = (DataHeader *)data;
1127         memcpy(&header, data_header, sizeof(header));
1128         if( window_size != data_header->window_size ) {
1129                 window_size = data_header->window_size;
1130                 half_window = window_size / 2;
1131                 frame_buffer.remove_all_objects();
1132                 frame_history.remove_all_objects();
1133         }
1134         if( data_header->interrupted )
1135                 interrupted = 1;
1136         EchoCancelWindow *window = (EchoCancelWindow *)thread->get_window();
1137         int pixels = window->canvas->get_w();
1138         float window_scale = (double)half_window / (pixels * config.xzoom);
1139         int nwin = data_header->total_windows;
1140         int iwin = nwin - config.history_size;
1141         if( iwin < 0 ) iwin = 0;
1142         for( ; iwin < nwin; ++iwin ) {
1143                 float *samples = data_header->samples + half_window*iwin;
1144                 EchoCancelFrame *frm = new EchoCancelFrame(pixels);
1145                 float *frm_data = frm->samples();
1146                 double cut_period = (double)data_header->sample_rate / config.cutoff;
1147                 frm->cut_offset = cut_period / window_scale;
1148
1149                 for(int i = 0; i < pixels; i++) {
1150                         float start = i*window_scale, stop = start+window_scale;
1151                         int istart = (int)start, istop = (int)stop;
1152                         float fstart = start-istart, fstop = stop-istop;
1153                         fstart = istart == istop ? -fstart : 1 - fstart;
1154                         float sum = samples[istart++] * fstart;
1155                         while( istart < istop ) sum += samples[istart++];
1156                         if( fstop > 1e-10 ) sum += samples[istop] * fstop;
1157                         frm_data[i] = sum/window_scale;
1158                 }
1159 // Normalize
1160                 float scale = data_header->level;
1161                 if( config.normalize ) {
1162                         float max = 0;
1163                         for( int i=frm->cut_offset; i<pixels; ++i ) {
1164                                 float dat = fabsf(frm_data[i]);
1165                                 if( dat > max ) max = dat;
1166                         }
1167                         scale = max > 1e-10 ? scale / max : 1;
1168                 }
1169                 if( scale != 1 ) frm->rescale(scale);
1170                 while( frame_buffer.size() >= config.history_size )
1171                                 frame_buffer.remove_object_number(0);
1172                 frame_buffer.append(frm);
1173         }
1174
1175         timer->update();
1176 }
1177
1178 int EchoCancel::load_configuration()
1179 {
1180         KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
1181         EchoCancelConfig old_config;
1182         old_config.copy_from(config);
1183         read_data(prev_keyframe);
1184         return !old_config.equivalent(config) ? 1 : 0;
1185 }
1186
1187 void EchoCancel::read_data(KeyFrame *keyframe)
1188 {
1189         int result;
1190         FileXML input;
1191         input.set_shared_input(keyframe->xbuf);
1192
1193         while(!(result = input.read_tag()) ) {
1194                 if( !input.tag.title_is("ECHOCANCEL")) continue;
1195                 config.level = input.tag.get_property("LEVEL", config.level);
1196                 config.normalize = input.tag.get_property("NORMALIZE", config.normalize);
1197                 config.window_size = input.tag.get_property("WINDOW_SIZE", config.window_size);
1198                 config.xzoom = input.tag.get_property("XZOOM", config.xzoom);
1199                 config.peaks = input.tag.get_property("PEAKS", config.peaks);
1200                 config.mode = input.tag.get_property("MODE", config.mode);
1201                 config.damp = input.tag.get_property("DAMP", config.damp);
1202                 config.history_size = input.tag.get_property("HISTORY_SIZE", config.history_size);
1203                 config.gain = input.tag.get_property("GAIN", config.gain);
1204                 config.offset = input.tag.get_property("OFFSET", config.offset);
1205                 if( !is_defaults() ) continue;
1206                 w = input.tag.get_property("W", w);
1207                 h = input.tag.get_property("H", h);
1208         }
1209 }
1210
1211 void EchoCancel::save_data(KeyFrame *keyframe)
1212 {
1213         FileXML output;
1214         output.set_shared_output(keyframe->xbuf);
1215
1216         output.tag.set_title("ECHOCANCEL");
1217         output.tag.set_property("LEVEL", config.level);
1218         output.tag.set_property("NORMALIZE", config.normalize);
1219         output.tag.set_property("WINDOW_SIZE", config.window_size);
1220         output.tag.set_property("MODE", config.mode);
1221         output.tag.set_property("XZOOM", config.xzoom);
1222         output.tag.set_property("PEAKS", config.peaks);
1223         output.tag.set_property("DAMP", config.damp);
1224         output.tag.set_property("HISTORY_SIZE", config.history_size);
1225         output.tag.set_property("GAIN", config.gain);
1226         output.tag.set_property("OFFSET", config.offset);
1227         output.tag.set_property("W", w);
1228         output.tag.set_property("H", h);
1229         output.append_tag();
1230         output.tag.set_title("/ECHOCANCEL");
1231         output.append_tag();
1232         output.append_newline();
1233         output.terminate_string();
1234 }
1235
1236
1237
1238
1239