2 Copyright (C) 2009 Paul Davis
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.
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.
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.
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"
28 #include "ardour/dB.h"
29 #include "ardour_ui.h"
30 #include "ardour/session.h"
32 #include "gui_thread.h"
33 #include "strip_silence_dialog.h"
34 #include "canvas_impl.h"
35 #include "simpleline.h"
37 #include "simplerect.h"
38 #include "rgb_macros.h"
42 using namespace ARDOUR;
44 using namespace ArdourCanvas;
46 /** Construct Strip silence dialog box */
47 StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOUR::AudioRegion> > const & regions)
48 : ArdourDialog (_("Strip Silence"))
50 , _minimum_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
51 , _fade_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
54 , _peaks_ready_connection (0)
59 Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
61 Gtk::Table* table = Gtk::manage (new Gtk::Table (3, 3));
62 table->set_spacings (6);
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);
71 _threshold.set_digits (1);
72 _threshold.set_increments (1, 10);
73 _threshold.set_range (-120, 0);
74 _threshold.set_value (-60);
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);
80 _minimum_length.set_session (s);
81 _minimum_length.set_mode (AudioClock::Frames);
82 _minimum_length.set (1000, true);
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);
88 _fade_length.set_session (s);
89 _fade_length.set_mode (AudioClock::Frames);
90 _fade_length.set (64, true);
92 hbox->pack_start (*table);
94 table = Gtk::manage (new Gtk::Table (3, 2));
95 table->set_spacings (6);
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);
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);
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);
114 hbox->pack_start (*table);
116 /* dummy label for padding */
117 hbox->pack_start (*Gtk::manage (new Gtk::Label ("")), true, true);
119 get_vbox()->pack_start (*hbox, false, false);
121 add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
122 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_OK);
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());
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);
133 get_vbox()->pack_start (*_canvas, true, true);
135 get_vbox()->pack_start (_progress_bar, true, true);
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));
143 update_silence_rects ();
144 update_threshold_line ();
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);
153 StripSilenceDialog::~StripSilenceDialog ()
157 /* Terminate our thread */
160 _interthread_info.cancel = true;
161 _thread_should_finish = true;
165 pthread_join (_thread, 0);
167 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
173 delete _peaks_ready_connection;
178 StripSilenceDialog::create_waves ()
182 delete _peaks_ready_connection;
183 _peaks_ready_connection = 0;
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;
211 StripSilenceDialog::peaks_ready ()
213 delete _peaks_ready_connection;
214 _peaks_ready_connection = 0;
219 StripSilenceDialog::canvas_allocation (Gtk::Allocation& alloc)
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 ();
227 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i, ++n) {
228 (*i)->samples_per_unit = ((double) (*i)->region->length() / _wave_width);
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;
237 resize_silence_rects ();
238 update_threshold_line ();
242 StripSilenceDialog::resize_silence_rects ()
246 /* Lock so that we don't contend with the detection thread for access to the silence regions */
247 Glib::Mutex::Lock lm (_lock);
249 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
251 list<pair<frameoffset_t, framecnt_t> >::const_iterator j;
252 list<SimpleRect*>::iterator r;
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);
269 StripSilenceDialog::update_threshold_line ()
273 /* Don't need to lock here as we're not reading the _waves silence details */
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;
279 double const y = alt_log_meter (_threshold.get_value());
281 (*i)->threshold_line->property_y1() = (n + 1 - y) * _wave_height;
282 (*i)->threshold_line->property_y2() = (n + 1 - y) * _wave_height;
289 StripSilenceDialog::update ()
291 update_silence_rects ();
292 update_threshold_line ();
293 /* XXX: first one only?! */
294 update_stats (_waves.front()->silence);
298 StripSilenceDialog::update_silence_rects ()
301 uint32_t max_segments = 0;
304 /* Lock so that we don't contend with the detection thread for access to the silence regions */
305 Glib::Mutex::Lock lm (_lock);
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) {
312 (*i)->silence_rects.clear ();
315 for (list<pair<frameoffset_t, framecnt_t> >::const_iterator j = (*i)->silence.begin(); j != (*i)->silence.end(); ++j) {
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);
328 max_segments = max (max_segments, sc);
332 if (min_audible > 0) {
337 ma = (float) min_audible/_session->frame_rate();
338 ms = (float) min_silence/_session->frame_rate();
340 if (min_audible < _session->frame_rate()) {
347 if (min_silence < _session->frame_rate()) {
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));
359 _shortest_silence_label.set_text ("");
360 _shortest_audible_label.set_text ("");
363 _segment_count_label.set_text (_("Full silence"));
364 _shortest_silence_label.set_text ("");
365 _shortest_audible_label.set_text ("");
370 StripSilenceDialog::_detection_thread_work (void* arg)
372 StripSilenceDialog* d = reinterpret_cast<StripSilenceDialog*> (arg);
373 return d->detection_thread_work ();
376 /** Body of our silence detection thread */
378 StripSilenceDialog::detection_thread_work ()
380 ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
382 /* Hold this lock when we are doing work */
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) {
393 if (!_interthread_info.cancel) {
394 Completed (); /* EMIT SIGNAL */
397 /* Our work is done; sleep until there is more to do.
398 * The lock is released while we are waiting.
400 _run_cond.wait (_lock);
402 if (_thread_should_finish) {
412 StripSilenceDialog::restart_thread ()
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.
423 /* Cancel any current run */
424 _interthread_info.cancel = true;
426 /* Block until the thread waits() */
429 _interthread_info.cancel = false;
432 /* And re-awake the thread */
437 StripSilenceDialog::threshold_changed ()
439 update_threshold_line ();
444 StripSilenceDialog::update_stats (const SilenceResult& res)
451 min_silence = max_framepos;
453 min_audible = max_framepos;
455 SilenceResult::const_iterator cur;
459 framepos_t start = 0;
463 if (cur->first == 0) {
464 /* initial segment, starting at zero, is silent */
468 /* initial segment, starting at zero, is audible */
473 while (cur != res.end()) {
475 framecnt_t interval_duration;
477 interval_duration = end - start;
481 max_silence = max (max_silence, interval_duration);
482 min_silence = min (min_silence, interval_duration);
485 max_audible = max (max_audible, interval_duration);
486 min_audible = min (min_audible, interval_duration);
492 in_silence = !in_silence;
497 StripSilenceDialog::minimum_length () const
499 return _minimum_length.current_duration (_waves.front()->region->position());
503 StripSilenceDialog::fade_length () const
505 return _minimum_length.current_duration (_waves.front()->region->position());
508 StripSilenceDialog::Wave::Wave (Group* g, boost::shared_ptr<AudioRegion> r)
509 : region (r), view (0), samples_per_unit (1)
511 threshold_line = new ArdourCanvas::SimpleLine (*g);
512 threshold_line->property_color_rgba() = RGBA_TO_UINT (0, 0, 0, 128);
515 StripSilenceDialog::Wave::~Wave ()
518 delete threshold_line;
519 for (list<SimpleRect*>::iterator i = silence_rects.begin(); i != silence_rects.end(); ++i) {
525 StripSilenceDialog::update_progress_gui (float p)
527 _progress_bar.set_fraction (p);