4fff8f2bb35fe685a3393dba7901639a34398db3
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / removegaps / removegaps.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2012 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 "bchash.h"
24 #include "clip.h"
25 #include "filexml.h"
26 #include "language.h"
27 #include "removegaps.h"
28 #include "samples.h"
29 #include "theme.h"
30 #include "transportque.inc"
31
32 #include <string.h>
33
34
35 // Maximum seconds to skip, if end of file
36 #define MAX_SKIPPED_DURATION 5.0
37 #define MIN_GAP_DURATION 0.01
38 #define MAX_GAP_DURATION 1.0
39 #define MIN_GAP_THRESHOLD INFINITYGAIN
40 #define MAX_GAP_THRESHOLD 0.0
41
42 REGISTER_PLUGIN(RemoveGaps);
43
44
45
46 RemoveGapsConfig::RemoveGapsConfig()
47 {
48         threshold = -20.0;
49         duration = 0.1;
50 }
51
52
53 int RemoveGapsConfig::equivalent(RemoveGapsConfig &src)
54 {
55         return EQUIV(threshold, src.threshold) &&
56                 EQUIV(duration, src.duration);
57 }
58
59 void RemoveGapsConfig::copy_from(RemoveGapsConfig &src)
60 {
61         this->threshold = src.threshold;
62         this->duration = src.duration;
63 }
64
65 void RemoveGapsConfig::interpolate(RemoveGapsConfig &prev,
66         RemoveGapsConfig &next,
67         int64_t prev_frame,
68         int64_t next_frame,
69         int64_t current_frame)
70 {
71         this->threshold = prev.threshold;
72         this->duration = prev.duration;
73         boundaries();
74 }
75
76 void RemoveGapsConfig::boundaries()
77 {
78         CLAMP(threshold, MIN_GAP_THRESHOLD, MAX_GAP_THRESHOLD);
79         CLAMP(duration, MIN_GAP_DURATION, MAX_GAP_DURATION);
80 }
81
82
83
84
85 RemoveGapsWindow::RemoveGapsWindow(RemoveGaps *plugin)
86  : PluginClientWindow(plugin,
87         xS(320),
88         yS(160),
89         xS(320),
90         yS(160),
91         0)
92 {
93         this->plugin = plugin;
94 }
95
96 RemoveGapsWindow::~RemoveGapsWindow()
97 {
98 }
99
100 void RemoveGapsWindow::create_objects()
101 {
102         int x = xS(10), y = yS(10);
103         BC_Title *title;
104
105         add_subwindow(title = new BC_Title(x, y, _("Threshold of gap (DB):")));
106
107         add_subwindow(threshold = new RemoveGapsThreshold(this,
108                 plugin,
109                 x + title->get_w() + plugin->get_theme()->widget_border,
110                 y));
111         y += threshold->get_h() + plugin->get_theme()->widget_border;
112         add_subwindow(title = new BC_Title(x, y, _("Max duration of gap (Seconds):")));
113         add_subwindow(duration = new RemoveGapsDuration(this,
114                 plugin,
115                 x + title->get_w() + plugin->get_theme()->widget_border,
116                 y));
117         show_window(1);
118 }
119
120
121
122
123
124
125 RemoveGapsThreshold::RemoveGapsThreshold(RemoveGapsWindow *window,
126         RemoveGaps *plugin,
127         int x,
128         int y)
129  : BC_FPot(x,
130         y,
131         plugin->config.threshold,
132         MIN_GAP_THRESHOLD,
133         MAX_GAP_THRESHOLD)
134 {
135         this->plugin = plugin;
136         set_precision(0.1);
137 }
138
139 int RemoveGapsThreshold::handle_event()
140 {
141         plugin->config.threshold = get_value();
142         plugin->send_configure_change();
143         return 1;
144 }
145
146
147
148
149
150
151
152
153 RemoveGapsDuration::RemoveGapsDuration(RemoveGapsWindow *window,
154         RemoveGaps *plugin,
155         int x,
156         int y)
157  : BC_FPot(x,
158         y,
159         plugin->config.duration,
160         MIN_GAP_DURATION,
161         MAX_GAP_DURATION)
162 {
163         this->plugin = plugin;
164         set_precision(0.01);
165 }
166
167 int RemoveGapsDuration::handle_event()
168 {
169         plugin->config.duration = get_value();
170         plugin->send_configure_change();
171         return 1;
172 }
173
174
175
176
177
178
179
180 RemoveGaps::RemoveGaps(PluginServer *server)
181  : PluginAClient(server)
182 {
183         need_reconfigure = 1;
184         source_start = 0;
185         dest_start = 0;
186         gap_length = 0;
187         temp_position = 0;
188         temp = 0;
189 }
190
191
192 RemoveGaps::~RemoveGaps()
193 {
194         delete temp;
195 }
196
197 const char* RemoveGaps::plugin_title() { return N_("Remove Gaps"); }
198 int RemoveGaps::is_realtime() { return 1; }
199
200 NEW_WINDOW_MACRO(RemoveGaps, RemoveGapsWindow)
201
202 LOAD_CONFIGURATION_MACRO(RemoveGaps, RemoveGapsConfig)
203
204
205 int RemoveGaps::process_buffer(int64_t size,
206         Samples *buffer,
207         int64_t start_position,
208         int sample_rate)
209 {
210         need_reconfigure |= load_configuration();
211
212
213         if(need_reconfigure || start_position != dest_start)
214         {
215                 source_start = start_position;
216                 temp_position = 0;
217                 need_reconfigure = 0;
218         }
219
220         dest_start = start_position;
221         double *buffer_samples = buffer->get_data();
222         double *temp_samples = !temp ? 0 : temp->get_data();
223
224         double threshold = DB::fromdb(config.threshold);
225         int64_t duration = (int64_t)(config.duration * sample_rate);
226         for(int i = 0; i < size; )
227         {
228                 if(!temp || temp_position >= temp->get_allocated())
229                 {
230 // Expand temp buffer if smaller than buffer read
231                         if(!temp)
232                         {
233                                 temp = new Samples(get_buffer_size());
234                                 temp_samples = temp->get_data();
235                         }
236                         temp_position = 0;
237
238 // Fill new temp buffer
239                         read_samples(temp,
240                                 0,
241                                 sample_rate,
242                                 source_start,
243                                 get_buffer_size());
244                         if(get_direction() == PLAY_FORWARD)
245                                 source_start += size;
246                         else
247                                 source_start -= size;
248                 }
249
250                 double sample = temp_samples[temp_position];
251                 if(fabs(sample) < threshold)
252                 {
253                         gap_length++;
254                 }
255                 else
256                 {
257                         gap_length = 0;
258                 }
259
260 // Only copy sample if gap below duration or no more samples to read
261                 if(gap_length < duration || gap_length > MAX_SKIPPED_DURATION * sample_rate)
262                 {
263                         buffer_samples[i++] = sample;
264                 }
265
266                 temp_position++;
267         }
268
269
270         if(get_direction() == PLAY_FORWARD)
271                 dest_start += size;
272         else
273                 dest_start -= size;
274
275         return 0;
276 }
277
278 void RemoveGaps::render_stop()
279 {
280         need_reconfigure = 1;
281 }
282
283
284
285
286 void RemoveGaps::save_data(KeyFrame *keyframe)
287 {
288         FileXML output;
289
290 // cause data to be stored directly in text
291         output.set_shared_output(keyframe->xbuf);
292         output.tag.set_title("REMOVEGAPS");
293         output.tag.set_property("DURATION", config.duration);
294         output.tag.set_property("THRESHOLD", config.threshold);
295         output.append_tag();
296         output.tag.set_title("/REMOVEGAPS");
297         output.append_tag();
298         output.append_newline();
299         output.terminate_string();
300 }
301
302 void RemoveGaps::read_data(KeyFrame *keyframe)
303 {
304         FileXML input;
305
306         input.set_shared_input(keyframe->xbuf);
307
308         while(!input.read_tag())
309         {
310                 if(input.tag.title_is("REMOVEGAPS"))
311                 {
312                         config.duration = input.tag.get_property("DURATION", config.duration);
313                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
314                 }
315         }
316
317 }
318
319 void RemoveGaps::update_gui()
320 {
321         if(thread)
322         {
323                 if(load_configuration())
324                 {
325                         thread->window->lock_window("RemoveGaps::update_gui");
326                         ((RemoveGapsWindow*)thread->window)->duration->update((float)config.duration);
327                         ((RemoveGapsWindow*)thread->window)->threshold->update((float)config.threshold);
328                         thread->window->unlock_window();
329                 }
330         }
331 }
332
333
334
335
336