dynamic keyframes, textbox rework, andrea ffmpeg.opts, perpetual chkpt undo, lv2...
[goodguy/history.git] / cinelerra-5.1 / plugins / echo / echo.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 "echo.h"
31 #include "theme.h"
32 #include "transportque.inc"
33 #include "units.h"
34 #include "vframe.h"
35
36
37 #include <string.h>
38
39
40 REGISTER_PLUGIN(Echo)
41
42
43 EchoConfig::EchoConfig()
44 {
45         level = 0.0;
46         atten = -6.0;
47         offset = 100;
48 }
49
50 int EchoConfig::equivalent(EchoConfig &that)
51 {
52         return EQUIV(level, that.level) &&
53                 atten == that.atten &&
54                 offset == that.offset;
55 }
56
57 void EchoConfig::copy_from(EchoConfig &that)
58 {
59         level = that.level;
60         atten = that.atten;
61         offset = that.offset;
62 }
63
64 void EchoConfig::interpolate(EchoConfig &prev, EchoConfig &next,
65         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
66 {
67         double frames = next_frame - prev_frame;
68         double next_scale = (current_frame - prev_frame) / frames;
69         double prev_scale = (next_frame - current_frame) / frames;
70         level  = prev.level  * prev_scale + next.level  * next_scale;
71         atten   = prev.atten   * prev_scale + next.atten   * next_scale;
72         offset = prev.offset * prev_scale + next.offset * next_scale;
73 }
74
75
76
77
78 EchoWindow::EchoWindow(Echo *plugin)
79  : PluginClientWindow(plugin, 250, 100, 250, 100, 0)
80 {
81         this->plugin = plugin;
82 }
83
84 EchoWindow::~EchoWindow()
85 {
86 }
87
88
89 EchoLevel::EchoLevel(EchoWindow *window, int x, int y)
90  : BC_FPot(x, y, window->plugin->config.level, INFINITYGAIN, MAXGAIN)
91 {
92         this->window = window;
93 }
94 EchoLevel::~EchoLevel()
95 {
96 }
97 int EchoLevel::handle_event()
98 {
99         Echo *plugin = (Echo *)window->plugin;
100         plugin->config.level = get_value();
101         plugin->send_configure_change();
102         double l = plugin->db.fromdb(plugin->config.level);
103         window->level_title->update(l);
104         return 1;
105 }
106
107 EchoAtten::EchoAtten(EchoWindow *window, int x, int y)
108  : BC_FPot(x, y, window->plugin->config.atten,INFINITYGAIN,-0.1)
109 {
110         this->window = window;
111 }
112 EchoAtten::~EchoAtten()
113 {
114 }
115 int EchoAtten::handle_event()
116 {
117         Echo *plugin = (Echo *)window->plugin;
118         plugin->config.atten = get_value();
119         plugin->send_configure_change();
120         double g = plugin->db.fromdb(plugin->config.atten);
121         window->atten_title->update(g);
122         return 1;
123 }
124
125 EchoOffset::EchoOffset(EchoWindow *window, int x, int y)
126  : BC_FPot(x, y, window->plugin->config.offset, 1, 999)
127 {
128         this->window = window;
129 }
130 EchoOffset::~EchoOffset()
131 {
132 }
133 int EchoOffset::handle_event()
134 {
135         Echo *plugin = (Echo *)window->plugin;
136         plugin->config.offset = get_value();
137         plugin->send_configure_change();
138         window->offset_title->update((int)plugin->config.offset);
139         return 1;
140 }
141
142 void EchoWindow::create_objects()
143 {
144         int x = 170, y = 10;
145         add_subwindow(level_title=new EchoTitle(5, y + 10, _("Level: "),
146                 plugin->db.fromdb(plugin->config.level)));
147         add_subwindow(level = new EchoLevel(this, x, y)); y += 25;
148         add_subwindow(atten_title=new EchoTitle(5, y + 10, _("Atten: "),
149                 plugin->db.fromdb(plugin->config.atten)));
150         add_subwindow(atten = new EchoAtten(this, x + 35, y)); y += 25;
151         add_subwindow(offset_title=new EchoTitle(5, y + 10, _("Offset: "),
152                 (int)plugin->config.offset));
153         add_subwindow(offset = new EchoOffset(this, x, y)); y += 25;
154         show_window();
155 }
156
157 int EchoWindow::resize_event(int w, int h)
158 {
159         return 0;
160 }
161
162 void EchoWindow::update_gui()
163 {
164         level->update(plugin->config.level);
165         double l = plugin->db.fromdb(plugin->config.level);
166         level_title->update(l);
167         atten->update(plugin->config.atten);
168         double g = plugin->db.fromdb(plugin->config.atten);
169         atten->update(g);
170         offset->update(plugin->config.offset);
171         int ofs = plugin->config.offset;
172         offset_title->update(ofs);
173 }
174
175
176 void Echo::update_gui()
177 {
178         if( !thread ) return;
179         EchoWindow *window = (EchoWindow*)thread->get_window();
180         window->lock_window("Echo::update_gui");
181         if( load_configuration() )
182                 window->update_gui();
183         window->unlock_window();
184 }
185
186 Echo::Echo(PluginServer *server)
187  : PluginAClient(server)
188 {
189         reset();
190 }
191
192 Echo::~Echo()
193 {
194         delete_buffers();
195 }
196
197
198 void Echo::reset()
199 {
200         thread = 0;
201         rbfr = 0;
202         rpos = 0;
203         ibfr = 0;
204         nch = 0;
205         isz = 0;
206         rsz = 0;
207         bfr_pos = 0;
208 }
209
210 const char* Echo::plugin_title() { return N_("Echo"); }
211 int Echo::is_realtime() { return 1; }
212 int Echo::is_multichannel() { return 1; }
213
214 void Echo::delete_buffers()
215 {
216         for( int ch=0; ch<nch; ++ch ) {
217                 delete [] ibfr[ch];
218                 delete rbfr[ch];
219         }
220         delete [] ibfr;  ibfr = 0;
221         delete [] rbfr;  rbfr = 0;
222         isz = rsz = nch = 0;
223 }
224
225 void Echo::alloc_buffers(int nch, int isz, int rsz)
226 {
227         // allocate reflection buffers
228         ibfr = new double *[nch];
229         rbfr = new ring_buffer *[nch];
230         for( int ch=0; ch<nch; ++ch ) {
231                 ibfr[ch] = new double[isz];
232                 rbfr[ch] = new ring_buffer(rsz);
233         }
234         this->nch = nch;
235         this->rsz = rsz;
236         this->isz = isz;
237 }
238
239 int Echo::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate)
240 {
241         load_configuration();
242
243         double level = db.fromdb(config.level);
244         double atten = db.fromdb(config.atten);
245         int ofs = config.offset * sample_rate/1000.;
246         int len = size * ((ofs + size-1) / size);
247
248         if( nch != total_in_buffers || isz != size || rsz != len )
249                 delete_buffers();
250         if( !ibfr ) {
251                 alloc_buffers(total_in_buffers, size, len);
252                 bfr_pos = -1;
253         }
254         if( bfr_pos != start_position ) {       // reset reflection
255                 bfr_pos = start_position;
256                 for( int ch=0; ch<nch; ++ch ) rbfr[ch]->clear();
257                 rpos = 0;
258         }
259
260         for( int ch=0; ch<nch; ++ch ) {         // read samples * level
261                 read_samples(buffer[ch], ch, sample_rate, start_position, size);
262                 double *bp = buffer[ch]->get_data(), *cp = ibfr[ch];
263                 for( int ch=0; ch<size; ++ch ) *cp++ = *bp++ * level;
264         }
265         bfr_pos += size;
266         int n = len - ofs;
267
268         for( int ch=0; ch<nch; ++ch ) {
269                 ring_buffer *rp = rbfr[ch];
270                 rp->iseek(rpos);  rp->oseek(rpos+ofs);
271                 double *bp = ibfr[ch];
272                 for( int i=n; --i>=0; )         // add front to end reflection
273                         rp->onxt() = atten * (rp->inxt() + *bp++);
274
275                 double *op = buffer[ch]->get_data(), *ip = ibfr[ch];
276                 rp->iseek(rpos);
277                 for( int i=size; --i>=0; )      // output reflection + input
278                         *op++ = rp->inxt() + *ip++;
279
280                 rp->iseek(rpos+n);
281                 for( int i=size-n; --i>=0; )    // add end to front reflection
282                         rp->onxt() = atten * (rp->inxt() + *bp++);
283         }
284         rpos += size;
285
286         return 0;
287 }
288
289
290
291 NEW_WINDOW_MACRO(Echo, EchoWindow)
292
293
294 int Echo::load_configuration()
295 {
296         KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
297         EchoConfig old_config;
298         old_config.copy_from(config);
299         read_data(prev_keyframe);
300         return !old_config.equivalent(config) ? 1 : 0;
301 }
302
303 void Echo::read_data(KeyFrame *keyframe)
304 {
305         int result;
306         FileXML input;
307         input.set_shared_input(keyframe->xbuf);
308
309         while(!(result = input.read_tag()) ) {
310                 if( !input.tag.title_is("ECHO")) continue;
311                 config.level = input.tag.get_property("LEVEL", config.level);
312                 config.atten = input.tag.get_property("ATTEN", config.atten);
313                 config.offset = input.tag.get_property("OFFSET", config.offset);
314                 if( !is_defaults() ) continue;
315         }
316 }
317
318 void Echo::save_data(KeyFrame *keyframe)
319 {
320         FileXML output;
321         output.set_shared_output(keyframe->xbuf);
322
323         output.tag.set_title("ECHO");
324         output.tag.set_property("LEVEL", config.level);
325         output.tag.set_property("ATTEN", config.atten);
326         output.tag.set_property("OFFSET", config.offset);
327         output.append_tag();
328         output.tag.set_title("/ECHO");
329         output.append_tag();
330         output.append_newline();
331         output.terminate_string();
332 }
333
334
335