Remove all use of nframes_t.
[ardour.git] / gtk2_ardour / strip_silence_dialog.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21
22 #include <gtkmm/table.h>
23 #include <gtkmm/label.h>
24 #include <gtkmm/stock.h>
25 #include "ardour/audioregion.h"
26 #include "ardour/audiosource.h"
27
28 #include "ardour/dB.h"
29 #include "ardour_ui.h"
30 #include "ardour/session.h"
31
32 #include "gui_thread.h"
33 #include "strip_silence_dialog.h"
34 #include "canvas_impl.h"
35 #include "simpleline.h"
36 #include "waveview.h"
37 #include "simplerect.h"
38 #include "rgb_macros.h"
39 #include "i18n.h"
40 #include "logmeter.h"
41
42 using namespace ARDOUR;
43 using namespace std;
44 using namespace ArdourCanvas;
45
46 /** Construct Strip silence dialog box */
47 StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOUR::AudioRegion> > const & regions)
48         : ArdourDialog (_("Strip Silence"))
49         , ProgressReporter ()
50         , _minimum_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
51         , _fade_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
52         , _wave_width (640)
53         , _wave_height (64)
54         , _peaks_ready_connection (0)
55         , _destroying (false)
56 {
57         set_session (s);
58
59         Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
60         
61         Gtk::Table* table = Gtk::manage (new Gtk::Table (3, 3));
62         table->set_spacings (6);
63
64         int n = 0;
65
66         table->attach (*Gtk::manage (new Gtk::Label (_("Threshold"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
67         table->attach (_threshold, 1, 2, n, n + 1, Gtk::FILL);
68         table->attach (*Gtk::manage (new Gtk::Label (_("dbFS"))), 2, 3, n, n + 1, Gtk::FILL);
69         ++n;
70         
71         _threshold.set_digits (1);
72         _threshold.set_increments (1, 10);
73         _threshold.set_range (-120, 0);
74         _threshold.set_value (-60);
75
76         table->attach (*Gtk::manage (new Gtk::Label (_("Minimum length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
77         table->attach (_minimum_length, 1, 2, n, n + 1, Gtk::FILL);
78         ++n;
79         
80         _minimum_length.set_session (s);
81         _minimum_length.set_mode (AudioClock::Frames);
82         _minimum_length.set (1000, true);
83
84         table->attach (*Gtk::manage (new Gtk::Label (_("Fade length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
85         table->attach (_fade_length, 1, 2, n, n + 1, Gtk::FILL);
86         ++n;
87
88         _fade_length.set_session (s);
89         _fade_length.set_mode (AudioClock::Frames);
90         _fade_length.set (64, true);
91
92         hbox->pack_start (*table);
93
94         table = Gtk::manage (new Gtk::Table (3, 2));
95         table->set_spacings (6);
96         
97         n = 0;
98         
99         table->attach (*Gtk::manage (new Gtk::Label (_("Silent segments:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
100         table->attach (_segment_count_label, 5, 6, n, n + 1, Gtk::FILL);
101         _segment_count_label.set_alignment (0, 0.5);
102         ++n;
103
104         table->attach (*Gtk::manage (new Gtk::Label (_("Shortest silence:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
105         table->attach (_shortest_silence_label, 5, 6, n, n + 1, Gtk::FILL);
106         _shortest_silence_label.set_alignment (0, 0.5);
107         ++n;
108
109         table->attach (*Gtk::manage (new Gtk::Label (_("Shortest audible:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
110         table->attach (_shortest_audible_label, 5, 6, n, n + 1, Gtk::FILL);
111         _shortest_audible_label.set_alignment (0, 0.5);
112         ++n;
113         
114         hbox->pack_start (*table);
115
116         /* dummy label for padding */
117         hbox->pack_start (*Gtk::manage (new Gtk::Label ("")), true, true);
118
119         get_vbox()->pack_start (*hbox, false, false);
120
121         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
122         add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_OK);
123
124         _canvas = new CanvasAA ();
125         _canvas->signal_size_allocate().connect (sigc::mem_fun (*this, &StripSilenceDialog::canvas_allocation));
126         _canvas->set_size_request (_wave_width, _wave_height * regions.size());
127
128         for (list<boost::shared_ptr<ARDOUR::AudioRegion> >::const_iterator i = regions.begin(); i != regions.end(); ++i) {
129                 Wave* w = new Wave (_canvas->root(), *i);
130                 _waves.push_back (w);
131         }
132
133         get_vbox()->pack_start (*_canvas, true, true);
134
135         get_vbox()->pack_start (_progress_bar, true, true);
136
137         show_all ();
138
139         _threshold.get_adjustment()->signal_value_changed().connect (sigc::mem_fun (*this, &StripSilenceDialog::threshold_changed));
140         _minimum_length.ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::restart_thread));
141
142         create_waves ();
143         update_silence_rects ();
144         update_threshold_line ();
145
146         /* Create a thread which runs while the dialogue is open to compute the silence regions */
147         Completed.connect (_completed_connection, MISSING_INVALIDATOR, ui_bind (&StripSilenceDialog::update, this), gui_context ());
148         _thread_should_finish = false;
149         pthread_create (&_thread, 0, StripSilenceDialog::_detection_thread_work, this);
150 }
151
152
153 StripSilenceDialog::~StripSilenceDialog ()
154 {
155         _destroying = true;
156         
157         /* Terminate our thread */
158         
159         _lock.lock ();
160         _interthread_info.cancel = true;
161         _thread_should_finish = true;
162         _lock.unlock ();
163
164         _run_cond.signal ();
165         pthread_join (_thread, 0);
166         
167         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
168                 delete *i;
169         }
170
171         _waves.clear ();
172         
173         delete _peaks_ready_connection;
174         delete _canvas;
175 }
176
177 void
178 StripSilenceDialog::create_waves ()
179 {
180         int n = 0;
181
182         delete _peaks_ready_connection;
183         _peaks_ready_connection = 0;
184
185         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
186                 if ((*i)->region->audio_source(0)->peaks_ready (boost::bind (&StripSilenceDialog::peaks_ready, this), &_peaks_ready_connection, gui_context())) {
187                         (*i)->view = new WaveView (*(_canvas->root()));
188                         (*i)->view->property_data_src() = static_cast<gpointer>((*i)->region.get());
189                         (*i)->view->property_cache() = WaveView::create_cache ();
190                         (*i)->view->property_cache_updater() = true;
191                         (*i)->view->property_channel() = 0;
192                         (*i)->view->property_length_function() = (void *) region_length_from_c;
193                         (*i)->view->property_sourcefile_length_function() = (void *) sourcefile_length_from_c;
194                         (*i)->view->property_peak_function() = (void *) region_read_peaks_from_c;
195                         (*i)->view->property_x() = 0;
196                         (*i)->view->property_y() = n * _wave_height;
197                         (*i)->view->property_height() = _wave_height;
198                         (*i)->view->property_samples_per_unit() = (*i)->samples_per_unit;
199                         (*i)->view->property_region_start() = (*i)->region->start();
200                         (*i)->view->property_wave_color() = ARDOUR_UI::config()->canvasvar_WaveForm.get();
201                         (*i)->view->property_fill_color() = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
202                         (*i)->view->property_logscaled() = true;
203                         (*i)->view->property_rectified() = true;
204                 }
205
206                 ++n;
207         }
208 }
209
210 void
211 StripSilenceDialog::peaks_ready ()
212 {
213         delete _peaks_ready_connection;
214         _peaks_ready_connection = 0;
215         create_waves ();
216 }
217
218 void
219 StripSilenceDialog::canvas_allocation (Gtk::Allocation& alloc)
220 {
221         int n = 0;
222
223         _canvas->set_scroll_region (0.0, 0.0, alloc.get_width(), alloc.get_height());
224         _wave_width = alloc.get_width ();
225         _wave_height = alloc.get_height ();
226
227         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i, ++n) {
228                 (*i)->samples_per_unit = ((double) (*i)->region->length() / _wave_width);
229
230                 if ((*i)->view) {
231                         (*i)->view->property_y() = n * _wave_height;
232                         (*i)->view->property_samples_per_unit() = (*i)->samples_per_unit;
233                         (*i)->view->property_height() = _wave_height;
234                 }
235         }
236
237         resize_silence_rects ();
238         update_threshold_line ();
239 }
240
241 void
242 StripSilenceDialog::resize_silence_rects ()
243 {
244         int n = 0;
245
246         /* Lock so that we don't contend with the detection thread for access to the silence regions */
247         Glib::Mutex::Lock lm (_lock);
248         
249         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
250
251                 list<pair<frameoffset_t, framecnt_t> >::const_iterator j;
252                 list<SimpleRect*>::iterator r;
253
254                 for (j = (*i)->silence.begin(), r = (*i)->silence_rects.begin(); 
255                      j != (*i)->silence.end() && r != (*i)->silence_rects.end(); ++j, ++r) {
256                         (*r)->property_x1() = j->first / (*i)->samples_per_unit;
257                         (*r)->property_x2() = j->second / (*i)->samples_per_unit;
258                         (*r)->property_y1() = n * _wave_height;
259                         (*r)->property_y2() = (n + 1) * _wave_height;
260                         (*r)->property_outline_pixels() = 0;
261                         (*r)->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
262                 }
263
264                 ++n;
265         }
266 }
267
268 void
269 StripSilenceDialog::update_threshold_line ()
270 {
271         int n = 0;
272
273         /* Don't need to lock here as we're not reading the _waves silence details */
274
275         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
276                 (*i)->threshold_line->property_x1() = 0;
277                 (*i)->threshold_line->property_x2() = _wave_width;
278                 
279                 double const y = alt_log_meter (_threshold.get_value());
280
281                 (*i)->threshold_line->property_y1() = (n + 1 - y) * _wave_height;
282                 (*i)->threshold_line->property_y2() = (n + 1 - y) * _wave_height;
283         }
284
285         ++n;
286 }
287
288 void
289 StripSilenceDialog::update ()
290 {
291         update_silence_rects ();
292         update_threshold_line ();
293         /* XXX: first one only?! */
294         update_stats (_waves.front()->silence);
295 }
296
297 void
298 StripSilenceDialog::update_silence_rects ()
299 {
300         int n = 0;
301         uint32_t max_segments = 0;
302         uint32_t sc;
303
304         /* Lock so that we don't contend with the detection thread for access to the silence regions */
305         Glib::Mutex::Lock lm (_lock);
306
307         for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
308                 for (list<SimpleRect*>::iterator j = (*i)->silence_rects.begin(); j != (*i)->silence_rects.end(); ++j) {
309                         delete *j;
310                 }
311
312                 (*i)->silence_rects.clear ();                
313                 sc = 0;
314
315                 for (list<pair<frameoffset_t, framecnt_t> >::const_iterator j = (*i)->silence.begin(); j != (*i)->silence.end(); ++j) {
316
317                         SimpleRect* r = new SimpleRect (*(_canvas->root()));
318                         r->property_x1() = j->first / (*i)->samples_per_unit;
319                         r->property_x2() = j->second / (*i)->samples_per_unit;
320                         r->property_y1() = n * _wave_height;
321                         r->property_y2() = (n + 1) * _wave_height;
322                         r->property_outline_pixels() = 0;
323                         r->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
324                         (*i)->silence_rects.push_back (r);
325                         sc++;
326                 }
327
328                 max_segments = max (max_segments, sc);
329                 ++n;
330         }
331
332         if (min_audible > 0) {
333                 float ms, ma;
334                 char const * aunits;
335                 char const * sunits;
336
337                 ma = (float) min_audible/_session->frame_rate();
338                 ms = (float) min_silence/_session->frame_rate();
339
340                 if (min_audible < _session->frame_rate()) {
341                         aunits = _("ms");
342                         ma *= 1000.0;
343                 } else {
344                         aunits = _("s");
345                 }
346
347                 if (min_silence < _session->frame_rate()) {
348                         sunits = _("ms");
349                         ms *= 1000.0;
350                 } else {
351                         sunits = _("s");
352                 }
353
354                 _segment_count_label.set_text (string_compose ("%1", max_segments));
355                 if (max_segments > 0) {
356                         _shortest_silence_label.set_text (string_compose ("%1 %2", ms, sunits));
357                         _shortest_audible_label.set_text (string_compose ("%1 %2", ma, aunits));
358                 } else {
359                         _shortest_silence_label.set_text ("");
360                         _shortest_audible_label.set_text ("");
361                 }
362         } else {
363                 _segment_count_label.set_text (_("Full silence"));
364                 _shortest_silence_label.set_text ("");
365                 _shortest_audible_label.set_text ("");
366         }
367 }
368
369 void *
370 StripSilenceDialog::_detection_thread_work (void* arg)
371 {
372         StripSilenceDialog* d = reinterpret_cast<StripSilenceDialog*> (arg);
373         return d->detection_thread_work ();
374 }
375
376 /** Body of our silence detection thread */
377 void *
378 StripSilenceDialog::detection_thread_work ()
379 {
380         ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
381
382         /* Hold this lock when we are doing work */
383         _lock.lock ();
384         
385         while (1) {
386                 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
387                         (*i)->silence = (*i)->region->find_silence (dB_to_coefficient (threshold ()), minimum_length (), _interthread_info);
388                         if (_interthread_info.cancel) {
389                                 break;
390                         }
391                 }
392
393                 if (!_interthread_info.cancel) {
394                         Completed (); /* EMIT SIGNAL */
395                 }
396
397                 /* Our work is done; sleep until there is more to do.
398                  * The lock is released while we are waiting.
399                  */
400                 _run_cond.wait (_lock);
401
402                 if (_thread_should_finish) {
403                         _lock.unlock ();
404                         return 0;
405                 }
406         }
407
408         return 0;
409 }
410
411 void
412 StripSilenceDialog::restart_thread ()
413 {
414         if (_destroying) {
415                 /* I don't know how this happens, but it seems to be possible for this
416                    method to be called after our destructor has finished executing.
417                    If this happens, bad things follow; _lock cannot be locked and
418                    Ardour hangs.  So if we are destroying, just bail early.
419                 */
420                 return;
421         }
422         
423         /* Cancel any current run */
424         _interthread_info.cancel = true;
425
426         /* Block until the thread waits() */
427         _lock.lock ();
428         /* Reset the flag */
429         _interthread_info.cancel = false;
430         _lock.unlock ();
431
432         /* And re-awake the thread */
433         _run_cond.signal ();
434 }
435
436 void
437 StripSilenceDialog::threshold_changed ()
438 {
439         update_threshold_line ();
440         restart_thread ();
441 }
442
443 void
444 StripSilenceDialog::update_stats (const SilenceResult& res)
445 {
446         if (res.empty()) {
447                 return;
448         }
449
450         max_silence = 0;
451         min_silence = max_framepos;
452         max_audible = 0;
453         min_audible = max_framepos;
454         
455         SilenceResult::const_iterator cur;
456
457         cur = res.begin();
458
459         framepos_t start = 0;
460         framepos_t end;
461         bool in_silence;
462
463         if (cur->first == 0) {
464                 /* initial segment, starting at zero, is silent */
465                 end = cur->second;
466                 in_silence = true;
467         } else {
468                 /* initial segment, starting at zero, is audible */
469                 end = cur->first;
470                 in_silence = false;
471         }
472
473         while (cur != res.end()) {
474
475                 framecnt_t interval_duration;
476
477                 interval_duration = end - start;
478
479                 if (in_silence) {
480
481                         max_silence = max (max_silence, interval_duration);
482                         min_silence = min (min_silence, interval_duration);
483                 } else {
484
485                         max_audible = max (max_audible, interval_duration);
486                         min_audible = min (min_audible, interval_duration);
487                 }
488
489                 start = end;
490                 ++cur;
491                 end = cur->first;
492                 in_silence = !in_silence;
493         }
494 }
495
496 framecnt_t
497 StripSilenceDialog::minimum_length () const
498 {
499         return _minimum_length.current_duration (_waves.front()->region->position());
500 }
501
502 framecnt_t
503 StripSilenceDialog::fade_length () const
504 {
505         return _minimum_length.current_duration (_waves.front()->region->position());
506 }
507                 
508 StripSilenceDialog::Wave::Wave (Group* g, boost::shared_ptr<AudioRegion> r)
509         : region (r), view (0), samples_per_unit (1)
510 {
511         threshold_line = new ArdourCanvas::SimpleLine (*g);
512         threshold_line->property_color_rgba() = RGBA_TO_UINT (0, 0, 0, 128);
513 }
514
515 StripSilenceDialog::Wave::~Wave ()
516 {
517         delete view;
518         delete threshold_line;
519         for (list<SimpleRect*>::iterator i = silence_rects.begin(); i != silence_rects.end(); ++i) {
520                 delete *i;
521         }
522 }
523
524 void
525 StripSilenceDialog::update_progress_gui (float p)
526 {
527         _progress_bar.set_fraction (p);
528 }