bg/clr color tweaks, clear borders rework, fc31 depends
[goodguy/cinelerra.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, xS(250), yS(100), xS(250), yS(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 xs5 = xS(5), xs35 = xS(35);
145         int ys10 = yS(10), ys25 = yS(25);
146         int x = xS(170), y = yS(10);
147         add_subwindow(level_title=new EchoTitle(xs5, y + ys10, _("Level: "),
148                 plugin->db.fromdb(plugin->config.level)));
149         add_subwindow(level = new EchoLevel(this, x, y)); y += ys25;
150         add_subwindow(atten_title=new EchoTitle(xs5, y + ys10, _("Atten: "),
151                 plugin->db.fromdb(plugin->config.atten)));
152         add_subwindow(atten = new EchoAtten(this, x + xs35, y)); y += ys25;
153         add_subwindow(offset_title=new EchoTitle(xs5, y + ys10, _("Offset: "),
154                 (int)plugin->config.offset));
155         add_subwindow(offset = new EchoOffset(this, x, y)); y += ys25;
156         show_window();
157 }
158
159 int EchoWindow::resize_event(int w, int h)
160 {
161         return 0;
162 }
163
164 void EchoWindow::update_gui()
165 {
166         level->update(plugin->config.level);
167         double l = plugin->db.fromdb(plugin->config.level);
168         level_title->update(l);
169         atten->update(plugin->config.atten);
170         double g = plugin->db.fromdb(plugin->config.atten);
171         atten->update(g);
172         offset->update(plugin->config.offset);
173         int ofs = plugin->config.offset;
174         offset_title->update(ofs);
175 }
176
177
178 void Echo::update_gui()
179 {
180         if( !thread ) return;
181         EchoWindow *window = (EchoWindow*)thread->get_window();
182         window->lock_window("Echo::update_gui");
183         if( load_configuration() )
184                 window->update_gui();
185         window->unlock_window();
186 }
187
188 Echo::Echo(PluginServer *server)
189  : PluginAClient(server)
190 {
191         reset();
192 }
193
194 Echo::~Echo()
195 {
196         delete_buffers();
197 }
198
199
200 void Echo::reset()
201 {
202         thread = 0;
203         rbfr = 0;
204         rpos = 0;
205         ibfr = 0;
206         nch = 0;
207         isz = 0;
208         rsz = 0;
209         bfr_pos = 0;
210 }
211
212 const char* Echo::plugin_title() { return N_("Echo"); }
213 int Echo::is_realtime() { return 1; }
214 int Echo::is_multichannel() { return 1; }
215
216 void Echo::delete_buffers()
217 {
218         for( int ch=0; ch<nch; ++ch ) {
219                 delete [] ibfr[ch];
220                 delete rbfr[ch];
221         }
222         delete [] ibfr;  ibfr = 0;
223         delete [] rbfr;  rbfr = 0;
224         isz = rsz = nch = 0;
225 }
226
227 void Echo::alloc_buffers(int nch, int isz, int rsz)
228 {
229         // allocate reflection buffers
230         ibfr = new double *[nch];
231         rbfr = new ring_buffer *[nch];
232         for( int ch=0; ch<nch; ++ch ) {
233                 ibfr[ch] = new double[isz];
234                 rbfr[ch] = new ring_buffer(rsz);
235         }
236         this->nch = nch;
237         this->rsz = rsz;
238         this->isz = isz;
239 }
240
241 int Echo::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate)
242 {
243         load_configuration();
244
245         double level = db.fromdb(config.level);
246         double atten = db.fromdb(config.atten);
247         int ofs = config.offset * sample_rate/1000.;
248         int len = size * ((ofs + size-1) / size);
249
250         if( nch != total_in_buffers || isz != size || rsz != len )
251                 delete_buffers();
252         if( !ibfr ) {
253                 alloc_buffers(total_in_buffers, size, len);
254                 bfr_pos = -1;
255         }
256         if( bfr_pos != start_position ) {       // reset reflection
257                 bfr_pos = start_position;
258                 for( int ch=0; ch<nch; ++ch ) rbfr[ch]->clear();
259                 rpos = 0;
260         }
261
262         for( int ch=0; ch<nch; ++ch ) {         // read samples * level
263                 read_samples(buffer[ch], ch, sample_rate, start_position, size);
264                 double *bp = buffer[ch]->get_data(), *cp = ibfr[ch];
265                 for( int ch=0; ch<size; ++ch ) *cp++ = *bp++ * level;
266         }
267         bfr_pos += size;
268         int n = len - ofs;
269
270         for( int ch=0; ch<nch; ++ch ) {
271                 ring_buffer *rp = rbfr[ch];
272                 rp->iseek(rpos);  rp->oseek(rpos+ofs);
273                 double *bp = ibfr[ch];
274                 for( int i=n; --i>=0; )         // add front to end reflection
275                         rp->onxt() = atten * (rp->inxt() + *bp++);
276
277                 double *op = buffer[ch]->get_data(), *ip = ibfr[ch];
278                 rp->iseek(rpos);
279                 for( int i=size; --i>=0; )      // output reflection + input
280                         *op++ = rp->inxt() + *ip++;
281
282                 rp->iseek(rpos+n);
283                 for( int i=size-n; --i>=0; )    // add end to front reflection
284                         rp->onxt() = atten * (rp->inxt() + *bp++);
285         }
286         rpos += size;
287
288         return 0;
289 }
290
291
292
293 NEW_WINDOW_MACRO(Echo, EchoWindow)
294
295
296 int Echo::load_configuration()
297 {
298         KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
299         EchoConfig old_config;
300         old_config.copy_from(config);
301         read_data(prev_keyframe);
302         return !old_config.equivalent(config) ? 1 : 0;
303 }
304
305 void Echo::read_data(KeyFrame *keyframe)
306 {
307         int result;
308         FileXML input;
309         input.set_shared_input(keyframe->xbuf);
310
311         while(!(result = input.read_tag()) ) {
312                 if( !input.tag.title_is("ECHO")) continue;
313                 config.level = input.tag.get_property("LEVEL", config.level);
314                 config.atten = input.tag.get_property("ATTEN", config.atten);
315                 config.offset = input.tag.get_property("OFFSET", config.offset);
316                 if( !is_defaults() ) continue;
317         }
318 }
319
320 void Echo::save_data(KeyFrame *keyframe)
321 {
322         FileXML output;
323         output.set_shared_output(keyframe->xbuf);
324
325         output.tag.set_title("ECHO");
326         output.tag.set_property("LEVEL", config.level);
327         output.tag.set_property("ATTEN", config.atten);
328         output.tag.set_property("OFFSET", config.offset);
329         output.append_tag();
330         output.tag.set_title("/ECHO");
331         output.append_tag();
332         output.append_newline();
333         output.terminate_string();
334 }
335
336
337