Allow more than one peaks_ready callback to be pending for an AudioRegionView
[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 "waveview.h"
36 #include "simplerect.h"
37 #include "rgb_macros.h"
38 #include "i18n.h"
39
40 using namespace ARDOUR;
41 using namespace std;
42 using namespace ArdourCanvas;
43
44 Glib::StaticMutex StripSilenceDialog::run_lock;
45 Glib::Cond*       StripSilenceDialog::thread_waiting = 0;
46 Glib::Cond*       StripSilenceDialog::thread_run = 0;
47 bool              StripSilenceDialog::thread_should_exit = false;
48 InterThreadInfo   StripSilenceDialog::itt;
49 StripSilenceDialog* StripSilenceDialog::current = 0;
50
51 /** Construct Strip silence dialog box */
52 StripSilenceDialog::StripSilenceDialog (Session* s, std::list<boost::shared_ptr<ARDOUR::AudioRegion> > const & regions)
53         : ArdourDialog (_("Strip Silence"))
54         , _minimum_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
55         , _fade_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
56         , _wave_width (640)
57         , _wave_height (64)
58         , restart_queued (false)
59         , _peaks_ready_connection (0)
60 {
61         set_session (s);
62
63         if (thread_waiting == 0) {
64                 thread_waiting = new Glib::Cond;
65                 thread_run = new Glib::Cond;
66         }
67         
68         for (std::list<boost::shared_ptr<ARDOUR::AudioRegion> >::const_iterator i = regions.begin(); i != regions.end(); ++i) {
69
70                 Wave w;
71                 w.region = *i;
72                 _waves.push_back (w);
73
74         }
75
76         Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
77         hbox->set_spacing (16);
78
79         Gtk::Table* table = Gtk::manage (new Gtk::Table (4, 3));
80         table->set_spacings (4);
81
82         Gtk::Label* l = Gtk::manage (new Gtk::Label (_("Threshold:")));
83         l->set_alignment (1, 0.5);
84         
85         hbox->pack_start (*l, false, false);
86         hbox->pack_start (_threshold, true, true);
87
88         _threshold.set_digits (1);
89         _threshold.set_increments (1, 10);
90         _threshold.set_range (-120, 0);
91         _threshold.set_value (-60);
92
93         l = Gtk::manage (new Gtk::Label (_("dBFS")));
94         l->set_alignment (0, 0.5);
95
96         hbox->pack_start (*l, false, false);
97         
98         l = Gtk::manage (new Gtk::Label (_("Minimum length:")));
99         l->set_alignment (1, 0.5);
100
101         hbox->pack_start (*l, false, false);
102         hbox->pack_start (_minimum_length, true, true);
103
104         _minimum_length.set_session (s);
105         _minimum_length.set_mode (AudioClock::Frames);
106         _minimum_length.set (1000, true);
107
108         l = Gtk::manage (new Gtk::Label (_("Fade length:")));
109         l->set_alignment (1, 0.5);
110
111         hbox->pack_start (*l, false, false);
112         hbox->pack_start (_fade_length, true, true);
113
114         _fade_length.set_session (s);
115         _fade_length.set_mode (AudioClock::Frames);
116         _fade_length.set (64, true);
117
118         _segment_count_label.set_text (_("Silent segments: none"));
119
120         get_vbox()->pack_start (*hbox, false, false);
121
122
123         add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
124         add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_OK);
125
126         _canvas = new CanvasAA ();
127         _canvas->signal_size_allocate().connect (sigc::mem_fun (*this, &StripSilenceDialog::canvas_allocation));
128         _canvas->set_size_request (_wave_width, _wave_height * _waves.size ());
129
130         get_vbox()->pack_start (*_canvas, true, true);
131         get_vbox()->pack_start (_segment_count_label, false, false);
132
133         show_all ();
134
135         _threshold.get_adjustment()->signal_value_changed().connect (sigc::mem_fun (*this, &StripSilenceDialog::maybe_start_silence_detection));
136         _minimum_length.ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::maybe_start_silence_detection));
137
138         create_waves ();
139         update_silence_rects ();
140 }
141
142
143 StripSilenceDialog::~StripSilenceDialog ()
144 {
145         for (std::list<Wave>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
146                 delete i->view;
147                 for (std::list<SimpleRect*>::iterator j = i->silence_rects.begin(); j != i->silence_rects.end(); ++j) {
148                         delete *j;
149                 }
150         }
151
152         delete _peaks_ready_connection;
153
154         delete _canvas;
155 }
156
157 void
158 StripSilenceDialog::create_waves ()
159 {
160         int n = 0;
161
162         delete _peaks_ready_connection;
163         _peaks_ready_connection = 0;
164
165         for (std::list<Wave>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
166                 if (i->region->audio_source(0)->peaks_ready (boost::bind (&StripSilenceDialog::peaks_ready, this), &_peaks_ready_connection, gui_context())) {
167                         i->view = new WaveView (*(_canvas->root()));
168                         i->view->property_data_src() = static_cast<gpointer>(i->region.get());
169                         i->view->property_cache() = WaveView::create_cache ();
170                         i->view->property_cache_updater() = true;
171                         i->view->property_channel() = 0;
172                         i->view->property_length_function() = (void *) region_length_from_c;
173                         i->view->property_sourcefile_length_function() = (void *) sourcefile_length_from_c;
174                         i->view->property_peak_function() = (void *) region_read_peaks_from_c;
175                         i->view->property_x() = 0;
176                         i->view->property_y() = n * _wave_height;
177                         i->view->property_height() = _wave_height;
178                         i->view->property_samples_per_unit() = i->samples_per_unit;
179                         i->view->property_region_start() = i->region->start();
180                         i->view->property_wave_color() = ARDOUR_UI::config()->canvasvar_WaveForm.get();
181                         i->view->property_fill_color() = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
182                 }
183
184                 ++n;
185         }
186 }
187
188 void
189 StripSilenceDialog::peaks_ready ()
190 {
191         delete _peaks_ready_connection;
192         _peaks_ready_connection = 0;
193         create_waves ();
194 }
195
196 void
197 StripSilenceDialog::canvas_allocation (Gtk::Allocation& alloc)
198 {
199         int n = 0;
200
201         _canvas->set_scroll_region (0.0, 0.0, alloc.get_width(), alloc.get_height());
202         _wave_width = alloc.get_width ();
203         _wave_height = alloc.get_height ();
204
205         for (std::list<Wave>::iterator i = _waves.begin(); i != _waves.end(); ++i, ++n) {
206                 i->samples_per_unit = ((double) i->region->length() / _wave_width);
207
208                 if (i->view) {
209                         i->view->property_y() = n * _wave_height;
210                         i->view->property_samples_per_unit() = i->samples_per_unit;
211                         i->view->property_height() = _wave_height;
212                 }
213         }
214
215         redraw_silence_rects ();
216 }
217
218 void
219 StripSilenceDialog::redraw_silence_rects ()
220 {
221         int n = 0;
222
223         for (std::list<Wave>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
224
225                 std::list<std::pair<frameoffset_t, framecnt_t> >::const_iterator j;
226                 std::list<SimpleRect*>::iterator r;
227
228                 for (j = i->silence.begin(), r = i->silence_rects.begin(); 
229                      j != i->silence.end() && r != i->silence_rects.end(); ++j, ++r) {
230                         (*r)->property_x1() = j->first / i->samples_per_unit;
231                         (*r)->property_x2() = j->second / i->samples_per_unit;
232                         (*r)->property_y1() = n * _wave_height;
233                         (*r)->property_y2() = (n + 1) * _wave_height;
234                         (*r)->property_outline_pixels() = 0;
235                         (*r)->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
236                 }
237
238                 ++n;
239         }
240 }
241
242 void
243 StripSilenceDialog::update_silence_rects ()
244 {
245         int n = 0;
246         uint32_t max_segments = 0;
247         uint32_t sc;
248
249         for (std::list<Wave>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
250                 for (std::list<SimpleRect*>::iterator j = i->silence_rects.begin(); j != i->silence_rects.end(); ++j) {
251                         delete *j;
252                 }
253
254                 i->silence_rects.clear ();                
255                 sc = 0;
256
257                 for (std::list<std::pair<frameoffset_t, framecnt_t> >::const_iterator j = i->silence.begin(); j != i->silence.end(); ++j) {
258
259                         SimpleRect* r = new SimpleRect (*(_canvas->root()));
260                         r->property_x1() = j->first / i->samples_per_unit;
261                         r->property_x2() = j->second / i->samples_per_unit;
262                         r->property_y1() = n * _wave_height;
263                         r->property_y2() = (n + 1) * _wave_height;
264                         r->property_outline_pixels() = 0;
265                         r->property_fill_color_rgba() = RGBA_TO_UINT (128, 128, 128, 128);
266                         i->silence_rects.push_back (r);
267                         sc++;
268                 }
269
270                 max_segments = max (max_segments, sc);
271                 ++n;
272         }
273
274         if (min_audible > 0) {
275                 float ms, ma;
276                 char* aunits;
277                 char* sunits;
278
279                 ma = (float) min_audible/_session->frame_rate();
280                 ms = (float) min_silence/_session->frame_rate();
281
282                 if (min_audible > _session->frame_rate()) {
283                         aunits = _("secs");
284                         ma /= 1000.0;
285                 } else {
286                         aunits = _("msecs");
287                 }
288
289                 if (min_silence > _session->frame_rate()) {
290                         sunits = _("secs");
291                         ms /= 1000.0;
292                 } else {
293                         sunits = _("msecs");
294                 }
295
296                 _segment_count_label.set_text (string_compose (_("Silent segments: %1\nShortest silence %2 %3 Shortest audible %4 %5"), 
297                                                                max_segments, ms, sunits, ma, aunits));
298         } else {
299                 _segment_count_label.set_text (_("Full silence"));
300         }
301 }
302
303 bool
304 StripSilenceDialog::_detection_done (void* arg)
305 {
306         StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
307         return ssd->detection_done ();
308 }
309
310 bool
311 StripSilenceDialog::detection_done ()
312 {
313         get_window()->set_cursor (Gdk::Cursor (Gdk::LEFT_PTR));
314         update_silence_rects ();
315         return false;
316 }
317
318 void*
319 StripSilenceDialog::_detection_thread_work (void* arg)
320 {
321         StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
322         return ssd->detection_thread_work ();
323 }
324
325 void*
326 StripSilenceDialog::detection_thread_work ()
327 {
328         ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
329         
330         while (1) {
331
332                 run_lock.lock ();
333                 thread_waiting->signal ();
334                 thread_run->wait (run_lock);
335
336                 if (thread_should_exit) {
337                         thread_waiting->signal ();
338                         run_lock.unlock ();
339                         break;
340                 }
341
342                 if (current) {
343                         StripSilenceDialog* ssd = current;
344                         run_lock.unlock ();
345                         
346                         for (std::list<Wave>::iterator i = ssd->_waves.begin(); i != ssd->_waves.end(); ++i) {
347                                 i->silence = i->region->find_silence (dB_to_coefficient (ssd->threshold ()), ssd->minimum_length (), ssd->itt);
348                                 ssd->update_stats (i->silence);
349                         }
350                         
351                         if (!ssd->itt.cancel) {
352                                 g_idle_add ((gboolean (*)(void*)) StripSilenceDialog::_detection_done, ssd);
353                         }
354                 } else {
355                         run_lock.unlock ();
356                 }
357
358         }
359         
360         return 0;
361 }
362
363 void
364 StripSilenceDialog::maybe_start_silence_detection ()
365 {
366         if (!restart_queued) {
367                 restart_queued = true;
368                 Glib::signal_idle().connect (sigc::mem_fun (*this, &StripSilenceDialog::start_silence_detection));
369         }
370 }
371
372 bool
373 StripSilenceDialog::start_silence_detection ()
374 {
375         Glib::Mutex::Lock lm (run_lock);
376         restart_queued = false;
377
378         if (!itt.thread) {
379
380                 itt.done = false;
381                 itt.cancel = false;
382                 itt.progress = 0.0;
383                 current = this;
384
385                 pthread_create (&itt.thread, 0, StripSilenceDialog::_detection_thread_work, this);
386                 /* wait for it to get started */
387                 thread_waiting->wait (run_lock);
388
389         } else {
390                 
391                 /* stop whatever the thread is doing */
392
393                 itt.cancel = 1;
394                 current = 0;
395
396                 while (!itt.done) {
397                         thread_run->signal ();
398                         thread_waiting->wait (run_lock);
399                 }
400         }
401
402
403         itt.cancel = false;
404         itt.done = false;
405         itt.progress = 0.0;
406         current = this;
407         
408         /* and start it up (again) */
409         
410         thread_run->signal ();
411
412         /* change cursor */
413
414         get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
415
416         /* don't call again until needed */
417         
418         return false;
419 }
420
421 void
422 StripSilenceDialog::stop_thread ()
423 {
424         Glib::Mutex::Lock lm (run_lock);
425
426         itt.cancel = true;
427         thread_should_exit = true;
428         thread_run->signal (); 
429         thread_waiting->wait (run_lock);
430         itt.thread = 0;
431 }
432
433 void
434 StripSilenceDialog::update_stats (const SilenceResult& res)
435 {
436         if (res.empty()) {
437                 return;
438         }
439
440         max_silence = 0;
441         min_silence = max_frames;
442         max_audible = 0;
443         min_audible = max_frames;
444         
445         SilenceResult::const_iterator cur;
446
447         cur = res.begin();
448
449         framepos_t start = 0;
450         framepos_t end;
451         bool in_silence;
452
453         if (cur->first == 0) {
454                 /* initial segment, starting at zero, is silent */
455                 end = cur->second;
456                 in_silence = true;
457         } else {
458                 /* initial segment, starting at zero, is audible */
459                 end = cur->first;
460                 in_silence = false;
461         }
462
463         while (cur != res.end()) {
464
465                 framecnt_t interval_duration;
466
467                 interval_duration = end - start;
468
469                 if (in_silence) {
470
471                         max_silence = max (max_silence, interval_duration);
472                         min_silence = min (min_silence, interval_duration);
473                 } else {
474
475                         max_audible = max (max_audible, interval_duration);
476                         min_audible = min (min_audible, interval_duration);
477                 }
478
479                 start = end;
480                 ++cur;
481                 end = cur->first;
482                 in_silence = !in_silence;
483         }
484 }
485
486 nframes_t
487 StripSilenceDialog::minimum_length () const
488 {
489         return _minimum_length.current_duration (_waves.front().region->position());
490 }
491
492 nframes_t
493 StripSilenceDialog::fade_length () const
494 {
495         return _minimum_length.current_duration (_waves.front().region->position());
496 }