ardour
strip_silence_dialog.cc
Go to the documentation of this file.
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 
26 #include "ardour/audioregion.h"
27 #include "ardour/dB.h"
28 #include "ardour_ui.h"
29 
30 #include "audio_clock.h"
31 #include "gui_thread.h"
32 #include "strip_silence_dialog.h"
33 #include "region_view.h"
34 #include "rgb_macros.h"
35 #include "i18n.h"
36 #include "logmeter.h"
37 
38 using namespace ARDOUR;
39 using namespace std;
40 using namespace ArdourCanvas;
41 
43 StripSilenceDialog::StripSilenceDialog (Session* s, list<RegionView*> const & v)
44  : ArdourDialog (_("Strip Silence"))
45  , ProgressReporter ()
46  , _minimum_length (new AudioClock (X_("silence duration"), true, "", true, false, true, false))
47  , _fade_length (new AudioClock (X_("silence duration"), true, "", true, false, true, false))
48  , _peaks_ready_connection (0)
49  , _destroying (false)
50 {
51  set_session (s);
52 
53  for (list<RegionView*>::const_iterator r = v.begin(); r != v.end(); ++r) {
54  views.push_back (ViewInterval (*r));
55  }
56 
57  Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
58 
59  Gtk::Table* table = Gtk::manage (new Gtk::Table (3, 3));
60  table->set_spacings (6);
61 
62  int n = 0;
63 
64  table->attach (*Gtk::manage (new Gtk::Label (_("Threshold"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
65  table->attach (_threshold, 1, 2, n, n + 1, Gtk::FILL);
66  table->attach (*Gtk::manage (new Gtk::Label (_("dbFS"))), 2, 3, n, n + 1, Gtk::FILL);
67  ++n;
68 
69  _threshold.set_digits (1);
70  _threshold.set_increments (1, 10);
71  _threshold.set_range (-120, 0);
72  _threshold.set_value (-60);
73  _threshold.set_activates_default ();
74 
75  table->attach (*Gtk::manage (new Gtk::Label (_("Minimum length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
76  table->attach (*_minimum_length, 1, 2, n, n + 1, Gtk::FILL);
77  ++n;
78 
81  _minimum_length->set (1000, true);
82 
83  table->attach (*Gtk::manage (new Gtk::Label (_("Fade length"), 1, 0.5)), 0, 1, n, n + 1, Gtk::FILL);
84  table->attach (*_fade_length, 1, 2, n, n + 1, Gtk::FILL);
85  ++n;
86 
89  _fade_length->set (64, true);
90 
91  hbox->pack_start (*table);
92 
93  get_vbox()->pack_start (*hbox, false, false);
94 
95  add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
96  add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_OK);
97  set_default_response (Gtk::RESPONSE_OK);
98 
99  get_vbox()->pack_start (_progress_bar, true, true, 12);
100 
101  show_all ();
102 
103  _threshold.get_adjustment()->signal_value_changed().connect (sigc::mem_fun (*this, &StripSilenceDialog::threshold_changed));
104  _minimum_length->ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::restart_thread));
105 
108 
109  /* Create a thread which runs while the dialogue is open to compute the silence regions */
111  _thread_should_finish = false;
113 }
114 
115 
117 {
118  _destroying = true;
119 
120  /* Terminate our thread */
121 
122  _lock.lock ();
123  _interthread_info.cancel = true;
124  _thread_should_finish = true;
125  _lock.unlock ();
126 
127  _run_cond.signal ();
128  pthread_join (_thread, 0);
129 
130  delete _minimum_length;
131  delete _fade_length;
132 
134 }
135 
136 void
138 {
139  for (list<ViewInterval>::iterator v = views.begin(); v != views.end(); ++v) {
140  pair<boost::shared_ptr<Region>,AudioIntervalResult> newpair (v->view->region(), v->intervals);
141  m.insert (newpair);
142  }
143 }
144 
145 void
147 {
148  for (list<ViewInterval>::iterator v = views.begin(); v != views.end(); ++v) {
149  v->view->drop_silent_frames ();
150  }
151 }
152 
153 void
155 {
156 #if 0
157  int n = 0;
158 
159  /* Don't need to lock here as we're not reading the _waves silence details */
160 
161  for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
162  (*i)->threshold_line->property_x1() = 0;
163  (*i)->threshold_line->property_x2() = _wave_width;
164 
165  double const y = alt_log_meter (_threshold.get_value());
166 
167  (*i)->threshold_line->property_y1() = (n + 1 - y) * _wave_height;
168  (*i)->threshold_line->property_y2() = (n + 1 - y) * _wave_height;
169  }
170 
171  ++n;
172 #endif
173 }
174 
175 void
177 {
180 }
181 
182 void
184 {
185  /* Lock so that we don't contend with the detection thread for access to the silence regions */
187  double const y = _threshold.get_value();
188 
189  for (list<ViewInterval>::iterator v = views.begin(); v != views.end(); ++v) {
190  v->view->set_silent_frames (v->intervals, y);
191  }
192 }
193 
194 void *
196 {
197  StripSilenceDialog* d = reinterpret_cast<StripSilenceDialog*> (arg);
198  return d->detection_thread_work ();
199 }
200 
202 void *
204 {
205  ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
206 
207  /* Hold this lock when we are doing work */
208  _lock.lock ();
209 
210  while (1) {
211  for (list<ViewInterval>::iterator i = views.begin(); i != views.end(); ++i) {
213 
214  if (ar) {
216  }
217 
219  break;
220  }
221  }
222 
223  if (!_interthread_info.cancel) {
224  Completed (); /* EMIT SIGNAL */
225  }
226 
227  /* Our work is done; sleep until there is more to do.
228  * The lock is released while we are waiting.
229  */
230  _run_cond.wait (_lock);
231 
232  if (_thread_should_finish) {
233  _lock.unlock ();
234  return 0;
235  }
236  }
237 
238  return 0;
239 }
240 
241 void
243 {
244  if (_destroying) {
245  /* I don't know how this happens, but it seems to be possible for this
246  method to be called after our destructor has finished executing.
247  If this happens, bad things follow; _lock cannot be locked and
248  Ardour hangs. So if we are destroying, just bail early.
249  */
250  return;
251  }
252 
253  /* Cancel any current run */
254  _interthread_info.cancel = true;
255 
256  /* Block until the thread waits() */
257  _lock.lock ();
258  /* Reset the flag */
259  _interthread_info.cancel = false;
260  _lock.unlock ();
261 
262  /* And re-awake the thread */
263  _run_cond.signal ();
264 }
265 
266 void
268 {
270  restart_thread ();
271 }
272 
275 {
276  return _minimum_length->current_duration (views.front().view->region()->position());
277 }
278 
281 {
282  return _fade_length->current_duration (views.front().view->region()->position());
283 }
284 
285 void
287 {
288  _progress_bar.set_fraction (p);
289 }
PBD::Signal0< void > Completed
emitted when a silence detection has completed
void set(framepos_t, bool force=false, ARDOUR::framecnt_t offset=0)
Definition: audio_clock.cc:956
void set_session(ARDOUR::Session *s)
ARDOUR::InterThreadInfo _interthread_info
double threshold() const
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
std::list< std::pair< frameoffset_t, frameoffset_t > > AudioIntervalResult
Definition: types.h:83
Dialog box to set options for the `strip silence' filter.
StripSilenceDialog(ARDOUR::Session *, std::list< RegionView * > const &)
ARDOUR::framecnt_t fade_length() const
Definition: Beats.hpp:239
void register_thread(std::string, pthread_t, std::string, uint32_t num_requests)
Definition: abstract_ui.cc:78
std::map< boost::shared_ptr< ARDOUR::Region >, AudioIntervalResult > AudioIntervalMap
Definition: types.h:85
std::list< ViewInterval > views
#define _(Text)
Definition: i18n.h:11
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
PBD::ScopedConnection * _peaks_ready_connection
Definition: amp.h:29
Glib::Threads::Cond _run_cond
condition to wake the thread
#define gui_context()
Definition: gui_thread.h:36
static float alt_log_meter(float power)
Definition: logmeter.h:31
void set_mode(Mode)
sigc::signal< void > ValueChanged
Definition: audio_clock.h:92
PBD::ScopedConnection _completed_connection
AudioClock * _minimum_length
pthread_t _thread
thread to compute silence in the background
bool _thread_should_finish
true if the thread should terminate
Gtk::ProgressBar _progress_bar
Glib::Threads::Mutex _lock
lock held while the thread is doing work
static float dB_to_coefficient(float dB)
Definition: dB.h:30
framepos_t current_duration(framepos_t position=0) const
virtual void set_session(ARDOUR::Session *)
ARDOUR::framecnt_t minimum_length() const
void silences(ARDOUR::AudioIntervalMap &)
AudioIntervalResult find_silence(Sample, framecnt_t, InterThreadInfo &) const
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
Gtk::SpinButton _threshold
int pthread_create(pthread_t *__restrict thread, __const pthread_attr_t *__restrict attr, void *(*start_routine)(void *), void *__restrict arg)
Definition: gprofhelper.c:83
static void * _detection_thread_work(void *)