handle multiple imports of the same file better (via better source naming); make...
[ardour2.git] / gtk2_ardour / strip_silence_dialog.cc
blobd0f806e71a32b0b6fab13fa3d40d08e0185916cb
1 /*
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.
20 #include <iostream>
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"
36 #include "waveview.h"
37 #include "simplerect.h"
38 #include "rgb_macros.h"
39 #include "i18n.h"
40 #include "logmeter.h"
42 using namespace ARDOUR;
43 using namespace std;
44 using namespace ArdourCanvas;
46 Glib::StaticMutex StripSilenceDialog::run_lock;
47 Glib::Cond* StripSilenceDialog::thread_waiting = 0;
48 Glib::Cond* StripSilenceDialog::thread_run = 0;
49 bool StripSilenceDialog::thread_should_exit = false;
50 InterThreadInfo StripSilenceDialog::itt;
51 StripSilenceDialog* StripSilenceDialog::current = 0;
53 /** Construct Strip silence dialog box */
54 StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOUR::AudioRegion> > const & regions)
55 : ArdourDialog (_("Strip Silence"))
56 , ProgressReporter ()
57 , _minimum_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
58 , _fade_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
59 , _wave_width (640)
60 , _wave_height (64)
61 , restart_queued (false)
62 , _peaks_ready_connection (0)
64 set_session (s);
66 if (thread_waiting == 0) {
67 thread_waiting = new Glib::Cond;
68 thread_run = new Glib::Cond;
71 Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
73 Gtk::Table* table = Gtk::manage (new Gtk::Table (3, 3));
74 table->set_spacings (6);
76 int n = 0;
78 table->attach (*Gtk::manage (new Gtk::Label (_("Threshold"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
79 table->attach (_threshold, 1, 2, n, n + 1, Gtk::FILL);
80 table->attach (*Gtk::manage (new Gtk::Label (_("dbFS"))), 2, 3, n, n + 1, Gtk::FILL);
81 ++n;
83 _threshold.set_digits (1);
84 _threshold.set_increments (1, 10);
85 _threshold.set_range (-120, 0);
86 _threshold.set_value (-60);
88 table->attach (*Gtk::manage (new Gtk::Label (_("Minimum length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
89 table->attach (_minimum_length, 1, 2, n, n + 1, Gtk::FILL);
90 ++n;
92 _minimum_length.set_session (s);
93 _minimum_length.set_mode (AudioClock::Frames);
94 _minimum_length.set (1000, true);
96 table->attach (*Gtk::manage (new Gtk::Label (_("Fade length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
97 table->attach (_fade_length, 1, 2, n, n + 1, Gtk::FILL);
98 ++n;
100 _fade_length.set_session (s);
101 _fade_length.set_mode (AudioClock::Frames);
102 _fade_length.set (64, true);
104 hbox->pack_start (*table);
106 table = Gtk::manage (new Gtk::Table (3, 2));
107 table->set_spacings (6);
109 n = 0;
111 table->attach (*Gtk::manage (new Gtk::Label (_("Silent segments:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
112 table->attach (_segment_count_label, 5, 6, n, n + 1, Gtk::FILL);
113 _segment_count_label.set_alignment (0, 0.5);
114 ++n;
116 table->attach (*Gtk::manage (new Gtk::Label (_("Shortest silence:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
117 table->attach (_shortest_silence_label, 5, 6, n, n + 1, Gtk::FILL);
118 _shortest_silence_label.set_alignment (0, 0.5);
119 ++n;
121 table->attach (*Gtk::manage (new Gtk::Label (_("Shortest audible:"), 1, 0.5)), 3, 4, n, n + 1, Gtk::FILL);
122 table->attach (_shortest_audible_label, 5, 6, n, n + 1, Gtk::FILL);
123 _shortest_audible_label.set_alignment (0, 0.5);
124 ++n;
126 hbox->pack_start (*table);
128 /* dummy label for padding */
129 hbox->pack_start (*Gtk::manage (new Gtk::Label ("")), true, true);
131 get_vbox()->pack_start (*hbox, false, false);
133 add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
134 add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_OK);
136 _canvas = new CanvasAA ();
137 _canvas->signal_size_allocate().connect (sigc::mem_fun (*this, &StripSilenceDialog::canvas_allocation));
138 _canvas->set_size_request (_wave_width, _wave_height * regions.size());
140 for (list<boost::shared_ptr<ARDOUR::AudioRegion> >::const_iterator i = regions.begin(); i != regions.end(); ++i) {
141 Wave* w = new Wave (_canvas->root(), *i);
142 _waves.push_back (w);
145 get_vbox()->pack_start (*_canvas, true, true);
147 get_vbox()->pack_start (_progress_bar, true, true);
149 show_all ();
151 _threshold.get_adjustment()->signal_value_changed().connect (sigc::mem_fun (*this, &StripSilenceDialog::threshold_changed));
152 _minimum_length.ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::maybe_start_silence_detection));
154 create_waves ();
155 update_silence_rects ();
156 update_threshold_line ();
158 maybe_start_silence_detection ();
162 StripSilenceDialog::~StripSilenceDialog ()
164 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
165 delete *i;
168 _waves.clear ();
170 delete _peaks_ready_connection;
171 delete _canvas;
174 void
175 StripSilenceDialog::create_waves ()
177 int n = 0;
179 delete _peaks_ready_connection;
180 _peaks_ready_connection = 0;
182 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
183 if ((*i)->region->audio_source(0)->peaks_ready (boost::bind (&StripSilenceDialog::peaks_ready, this), &_peaks_ready_connection, gui_context())) {
184 (*i)->view = new WaveView (*(_canvas->root()));
185 (*i)->view->property_data_src() = static_cast<gpointer>((*i)->region.get());
186 (*i)->view->property_cache() = WaveView::create_cache ();
187 (*i)->view->property_cache_updater() = true;
188 (*i)->view->property_channel() = 0;
189 (*i)->view->property_length_function() = (void *) region_length_from_c;
190 (*i)->view->property_sourcefile_length_function() = (void *) sourcefile_length_from_c;
191 (*i)->view->property_peak_function() = (void *) region_read_peaks_from_c;
192 (*i)->view->property_x() = 0;
193 (*i)->view->property_y() = n * _wave_height;
194 (*i)->view->property_height() = _wave_height;
195 (*i)->view->property_samples_per_unit() = (*i)->samples_per_unit;
196 (*i)->view->property_region_start() = (*i)->region->start();
197 (*i)->view->property_wave_color() = ARDOUR_UI::config()->canvasvar_WaveForm.get();
198 (*i)->view->property_fill_color() = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
199 (*i)->view->property_logscaled() = true;
200 (*i)->view->property_rectified() = true;
203 ++n;
207 void
208 StripSilenceDialog::peaks_ready ()
210 delete _peaks_ready_connection;
211 _peaks_ready_connection = 0;
212 create_waves ();
215 void
216 StripSilenceDialog::canvas_allocation (Gtk::Allocation& alloc)
218 int n = 0;
220 _canvas->set_scroll_region (0.0, 0.0, alloc.get_width(), alloc.get_height());
221 _wave_width = alloc.get_width ();
222 _wave_height = alloc.get_height ();
224 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i, ++n) {
225 (*i)->samples_per_unit = ((double) (*i)->region->length() / _wave_width);
227 if ((*i)->view) {
228 (*i)->view->property_y() = n * _wave_height;
229 (*i)->view->property_samples_per_unit() = (*i)->samples_per_unit;
230 (*i)->view->property_height() = _wave_height;
234 resize_silence_rects ();
235 update_threshold_line ();
238 void
239 StripSilenceDialog::resize_silence_rects ()
241 int n = 0;
243 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
245 list<pair<frameoffset_t, framecnt_t> >::const_iterator j;
246 list<SimpleRect*>::iterator r;
248 for (j = (*i)->silence.begin(), r = (*i)->silence_rects.begin();
249 j != (*i)->silence.end() && r != (*i)->silence_rects.end(); ++j, ++r) {
250 (*r)->property_x1() = j->first / (*i)->samples_per_unit;
251 (*r)->property_x2() = j->second / (*i)->samples_per_unit;
252 (*r)->property_y1() = n * _wave_height;
253 (*r)->property_y2() = (n + 1) * _wave_height;
254 (*r)->property_outline_pixels() = 0;
255 (*r)->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
258 ++n;
262 void
263 StripSilenceDialog::update_threshold_line ()
265 int n = 0;
267 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
268 (*i)->threshold_line->property_x1() = 0;
269 (*i)->threshold_line->property_x2() = _wave_width;
271 double const y = alt_log_meter (_threshold.get_value());
273 (*i)->threshold_line->property_y1() = (n + 1 - y) * _wave_height;
274 (*i)->threshold_line->property_y2() = (n + 1 - y) * _wave_height;
277 ++n;
280 void
281 StripSilenceDialog::update_silence_rects ()
283 int n = 0;
284 uint32_t max_segments = 0;
285 uint32_t sc;
287 for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
288 for (list<SimpleRect*>::iterator j = (*i)->silence_rects.begin(); j != (*i)->silence_rects.end(); ++j) {
289 delete *j;
292 (*i)->silence_rects.clear ();
293 sc = 0;
295 for (list<pair<frameoffset_t, framecnt_t> >::const_iterator j = (*i)->silence.begin(); j != (*i)->silence.end(); ++j) {
297 SimpleRect* r = new SimpleRect (*(_canvas->root()));
298 r->property_x1() = j->first / (*i)->samples_per_unit;
299 r->property_x2() = j->second / (*i)->samples_per_unit;
300 r->property_y1() = n * _wave_height;
301 r->property_y2() = (n + 1) * _wave_height;
302 r->property_outline_pixels() = 0;
303 r->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
304 (*i)->silence_rects.push_back (r);
305 sc++;
308 max_segments = max (max_segments, sc);
309 ++n;
312 if (min_audible > 0) {
313 float ms, ma;
314 char const * aunits;
315 char const * sunits;
317 ma = (float) min_audible/_session->frame_rate();
318 ms = (float) min_silence/_session->frame_rate();
320 if (min_audible < _session->frame_rate()) {
321 aunits = _("ms");
322 ma *= 1000.0;
323 } else {
324 aunits = _("s");
327 if (min_silence < _session->frame_rate()) {
328 sunits = _("ms");
329 ms *= 1000.0;
330 } else {
331 sunits = _("s");
334 _segment_count_label.set_text (string_compose ("%1", max_segments));
335 if (max_segments > 0) {
336 _shortest_silence_label.set_text (string_compose ("%1 %2", ms, sunits));
337 _shortest_audible_label.set_text (string_compose ("%1 %2", ma, aunits));
338 } else {
339 _shortest_silence_label.set_text ("");
340 _shortest_audible_label.set_text ("");
342 } else {
343 _segment_count_label.set_text (_("Full silence"));
344 _shortest_silence_label.set_text ("");
345 _shortest_audible_label.set_text ("");
349 bool
350 StripSilenceDialog::_detection_done (void* arg)
352 StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
353 return ssd->detection_done ();
356 bool
357 StripSilenceDialog::detection_done ()
359 get_window()->set_cursor (Gdk::Cursor (Gdk::LEFT_PTR));
360 update_silence_rects ();
361 return false;
364 void*
365 StripSilenceDialog::_detection_thread_work (void* arg)
367 StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
368 return ssd->detection_thread_work ();
371 void*
372 StripSilenceDialog::detection_thread_work ()
374 ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
376 while (1) {
378 run_lock.lock ();
379 thread_waiting->signal ();
380 thread_run->wait (run_lock);
382 if (thread_should_exit) {
383 thread_waiting->signal ();
384 run_lock.unlock ();
385 break;
388 if (current) {
389 StripSilenceDialog* ssd = current;
390 run_lock.unlock ();
392 for (list<Wave*>::iterator i = ssd->_waves.begin(); i != ssd->_waves.end(); ++i) {
393 (*i)->silence = (*i)->region->find_silence (dB_to_coefficient (ssd->threshold ()), ssd->minimum_length (), ssd->itt);
394 ssd->update_stats ((*i)->silence);
397 if (!ssd->itt.cancel) {
398 g_idle_add ((gboolean (*)(void*)) StripSilenceDialog::_detection_done, ssd);
400 } else {
401 run_lock.unlock ();
406 return 0;
409 void
410 StripSilenceDialog::threshold_changed ()
412 update_threshold_line ();
413 maybe_start_silence_detection ();
416 void
417 StripSilenceDialog::maybe_start_silence_detection ()
419 if (!restart_queued) {
420 restart_queued = true;
421 Glib::signal_idle().connect (sigc::mem_fun (*this, &StripSilenceDialog::start_silence_detection));
425 bool
426 StripSilenceDialog::start_silence_detection ()
428 Glib::Mutex::Lock lm (run_lock);
429 restart_queued = false;
431 if (!itt.thread) {
433 itt.done = false;
434 itt.cancel = false;
435 itt.progress = 0.0;
436 current = this;
438 pthread_create (&itt.thread, 0, StripSilenceDialog::_detection_thread_work, this);
439 /* wait for it to get started */
440 thread_waiting->wait (run_lock);
442 } else {
444 /* stop whatever the thread is doing */
446 itt.cancel = 1;
447 current = 0;
449 while (!itt.done) {
450 thread_run->signal ();
451 thread_waiting->wait (run_lock);
456 itt.cancel = false;
457 itt.done = false;
458 itt.progress = 0.0;
459 current = this;
461 /* and start it up (again) */
463 thread_run->signal ();
465 /* change cursor */
467 get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
469 /* don't call again until needed */
471 return false;
474 void
475 StripSilenceDialog::stop_thread ()
477 Glib::Mutex::Lock lm (run_lock);
479 itt.cancel = true;
480 thread_should_exit = true;
481 thread_run->signal ();
482 thread_waiting->wait (run_lock);
483 itt.thread = 0;
486 void
487 StripSilenceDialog::update_stats (const SilenceResult& res)
489 if (res.empty()) {
490 return;
493 max_silence = 0;
494 min_silence = max_framepos;
495 max_audible = 0;
496 min_audible = max_framepos;
498 SilenceResult::const_iterator cur;
500 cur = res.begin();
502 framepos_t start = 0;
503 framepos_t end;
504 bool in_silence;
506 if (cur->first == 0) {
507 /* initial segment, starting at zero, is silent */
508 end = cur->second;
509 in_silence = true;
510 } else {
511 /* initial segment, starting at zero, is audible */
512 end = cur->first;
513 in_silence = false;
516 while (cur != res.end()) {
518 framecnt_t interval_duration;
520 interval_duration = end - start;
522 if (in_silence) {
524 max_silence = max (max_silence, interval_duration);
525 min_silence = min (min_silence, interval_duration);
526 } else {
528 max_audible = max (max_audible, interval_duration);
529 min_audible = min (min_audible, interval_duration);
532 start = end;
533 ++cur;
534 end = cur->first;
535 in_silence = !in_silence;
539 nframes_t
540 StripSilenceDialog::minimum_length () const
542 return _minimum_length.current_duration (_waves.front()->region->position());
545 nframes_t
546 StripSilenceDialog::fade_length () const
548 return _minimum_length.current_duration (_waves.front()->region->position());
551 StripSilenceDialog::Wave::Wave (Group* g, boost::shared_ptr<AudioRegion> r)
552 : region (r), view (0), samples_per_unit (1)
554 threshold_line = new ArdourCanvas::SimpleLine (*g);
555 threshold_line->property_color_rgba() = RGBA_TO_UINT (0, 0, 0, 128);
558 StripSilenceDialog::Wave::~Wave ()
560 delete view;
561 delete threshold_line;
562 for (list<SimpleRect*>::iterator i = silence_rects.begin(); i != silence_rects.end(); ++i) {
563 delete *i;
567 void
568 StripSilenceDialog::update_progress_gui (float p)
570 _progress_bar.set_fraction (p);