Simplify strip silence dialogue threading, hopefully fixing #3560 in the process.

git-svn-id: svn://localhost/ardour2/branches/3.0@8068 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington
2010-11-22 17:15:32 +00:00
parent d000c7e79d
commit bf411d3730
2 changed files with 102 additions and 162 deletions

View File

@@ -43,13 +43,6 @@ using namespace ARDOUR;
using namespace std;
using namespace ArdourCanvas;
Glib::StaticMutex StripSilenceDialog::run_lock;
Glib::Cond* StripSilenceDialog::thread_waiting = 0;
Glib::Cond* StripSilenceDialog::thread_run = 0;
bool StripSilenceDialog::thread_should_exit = false;
InterThreadInfo StripSilenceDialog::itt;
StripSilenceDialog* StripSilenceDialog::current = 0;
/** Construct Strip silence dialog box */
StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOUR::AudioRegion> > const & regions)
: ArdourDialog (_("Strip Silence"))
@@ -58,16 +51,10 @@ StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOU
, _fade_length (X_("silence duration"), true, "SilenceDurationClock", true, false, true, false)
, _wave_width (640)
, _wave_height (64)
, restart_queued (false)
, _peaks_ready_connection (0)
{
set_session (s);
if (thread_waiting == 0) {
thread_waiting = new Glib::Cond;
thread_run = new Glib::Cond;
}
Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox);
Gtk::Table* table = Gtk::manage (new Gtk::Table (3, 3));
@@ -149,18 +136,31 @@ StripSilenceDialog::StripSilenceDialog (Session* s, list<boost::shared_ptr<ARDOU
show_all ();
_threshold.get_adjustment()->signal_value_changed().connect (sigc::mem_fun (*this, &StripSilenceDialog::threshold_changed));
_minimum_length.ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::maybe_start_silence_detection));
_minimum_length.ValueChanged.connect (sigc::mem_fun (*this, &StripSilenceDialog::restart_thread));
create_waves ();
update_silence_rects ();
update_threshold_line ();
maybe_start_silence_detection ();
/* Create a thread which runs while the dialogue is open to compute the silence regions */
Completed.connect (_completed_connection, MISSING_INVALIDATOR, ui_bind (&StripSilenceDialog::update, this), gui_context ());
_thread_should_finish = false;
pthread_create (&_thread, 0, StripSilenceDialog::_detection_thread_work, this);
}
StripSilenceDialog::~StripSilenceDialog ()
{
/* Terminate our thread */
_lock.lock ();
_interthread_info.cancel = true;
_thread_should_finish = true;
_lock.unlock ();
_run_cond.signal ();
pthread_join (_thread, 0);
for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
delete *i;
}
@@ -240,6 +240,9 @@ StripSilenceDialog::resize_silence_rects ()
{
int n = 0;
/* Lock so that we don't contend with the detection thread for access to the silence regions */
Glib::Mutex::Lock lm (_lock);
for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
list<pair<frameoffset_t, framecnt_t> >::const_iterator j;
@@ -263,7 +266,9 @@ void
StripSilenceDialog::update_threshold_line ()
{
int n = 0;
/* Don't need to lock here as we're not reading the _waves silence details */
for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
(*i)->threshold_line->property_x1() = 0;
(*i)->threshold_line->property_x2() = _wave_width;
@@ -277,6 +282,15 @@ StripSilenceDialog::update_threshold_line ()
++n;
}
void
StripSilenceDialog::update ()
{
update_silence_rects ();
update_threshold_line ();
/* XXX: first one only?! */
update_stats (_waves.front()->silence);
}
void
StripSilenceDialog::update_silence_rects ()
{
@@ -284,6 +298,9 @@ StripSilenceDialog::update_silence_rects ()
uint32_t max_segments = 0;
uint32_t sc;
/* Lock so that we don't contend with the detection thread for access to the silence regions */
Glib::Mutex::Lock lm (_lock);
for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
for (list<SimpleRect*>::iterator j = (*i)->silence_rects.begin(); j != (*i)->silence_rects.end(); ++j) {
delete *j;
@@ -346,141 +363,69 @@ StripSilenceDialog::update_silence_rects ()
}
}
bool
StripSilenceDialog::_detection_done (void* arg)
{
StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
return ssd->detection_done ();
}
bool
StripSilenceDialog::detection_done ()
{
get_window()->set_cursor (Gdk::Cursor (Gdk::LEFT_PTR));
update_silence_rects ();
return false;
}
void*
void *
StripSilenceDialog::_detection_thread_work (void* arg)
{
StripSilenceDialog* ssd = (StripSilenceDialog*) arg;
return ssd->detection_thread_work ();
StripSilenceDialog* d = reinterpret_cast<StripSilenceDialog*> (arg);
return d->detection_thread_work ();
}
void*
/** Body of our silence detection thread */
void *
StripSilenceDialog::detection_thread_work ()
{
ARDOUR_UI::instance()->register_thread ("gui", pthread_self(), "silence", 32);
while (1) {
run_lock.lock ();
thread_waiting->signal ();
thread_run->wait (run_lock);
/* Hold this lock when we are doing work */
_lock.lock ();
while (1) {
for (list<Wave*>::iterator i = _waves.begin(); i != _waves.end(); ++i) {
(*i)->silence = (*i)->region->find_silence (dB_to_coefficient (threshold ()), minimum_length (), _interthread_info);
if (_interthread_info.cancel) {
break;
}
}
if (thread_should_exit) {
thread_waiting->signal ();
run_lock.unlock ();
break;
}
if (!_interthread_info.cancel) {
Completed (); /* EMIT SIGNAL */
}
if (current) {
StripSilenceDialog* ssd = current;
run_lock.unlock ();
for (list<Wave*>::iterator i = ssd->_waves.begin(); i != ssd->_waves.end(); ++i) {
(*i)->silence = (*i)->region->find_silence (dB_to_coefficient (ssd->threshold ()), ssd->minimum_length (), ssd->itt);
ssd->update_stats ((*i)->silence);
}
if (!ssd->itt.cancel) {
g_idle_add ((gboolean (*)(void*)) StripSilenceDialog::_detection_done, ssd);
}
} else {
run_lock.unlock ();
}
/* Our work is done; sleep until there is more to do.
* The lock is released while we are waiting.
*/
_run_cond.wait (_lock);
}
return 0;
if (_thread_should_finish) {
_lock.unlock ();
return 0;
}
}
return 0;
}
void
StripSilenceDialog::restart_thread ()
{
/* Cancel any current run */
_interthread_info.cancel = true;
/* Block until the thread waits() */
_lock.lock ();
/* Reset the flag */
_interthread_info.cancel = false;
_lock.unlock ();
/* And re-awake the thread */
_run_cond.signal ();
}
void
StripSilenceDialog::threshold_changed ()
{
update_threshold_line ();
maybe_start_silence_detection ();
}
void
StripSilenceDialog::maybe_start_silence_detection ()
{
if (!restart_queued) {
restart_queued = true;
Glib::signal_idle().connect (sigc::mem_fun (*this, &StripSilenceDialog::start_silence_detection));
}
}
bool
StripSilenceDialog::start_silence_detection ()
{
Glib::Mutex::Lock lm (run_lock);
restart_queued = false;
if (!itt.thread) {
itt.done = false;
itt.cancel = false;
itt.progress = 0.0;
current = this;
pthread_create (&itt.thread, 0, StripSilenceDialog::_detection_thread_work, this);
/* wait for it to get started */
thread_waiting->wait (run_lock);
} else {
/* stop whatever the thread is doing */
itt.cancel = 1;
current = 0;
while (!itt.done) {
thread_run->signal ();
thread_waiting->wait (run_lock);
}
}
itt.cancel = false;
itt.done = false;
itt.progress = 0.0;
current = this;
/* and start it up (again) */
thread_run->signal ();
/* change cursor */
get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH));
/* don't call again until needed */
return false;
}
void
StripSilenceDialog::stop_thread ()
{
Glib::Mutex::Lock lm (run_lock);
itt.cancel = true;
thread_should_exit = true;
thread_run->signal ();
thread_waiting->wait (run_lock);
itt.thread = 0;
restart_thread ();
}
void

View File

@@ -43,17 +43,21 @@ public:
nframes_t minimum_length () const;
nframes_t fade_length () const;
static void stop_thread ();
private:
typedef std::list<std::pair<ARDOUR::frameoffset_t,ARDOUR::framecnt_t> > SilenceResult;
void create_waves ();
void peaks_ready ();
void canvas_allocation (Gtk::Allocation &);
void update_silence_rects ();
void resize_silence_rects ();
void update ();
void update_threshold_line ();
void update_stats (SilenceResult const &);
void threshold_changed ();
void update_progress_gui (float);
void restart_thread ();
Gtk::SpinButton _threshold;
AudioClock _minimum_length;
@@ -62,32 +66,23 @@ private:
Gtk::Label _shortest_silence_label;
Gtk::Label _shortest_audible_label;
Gtk::ProgressBar _progress_bar;
typedef std::list<std::pair<ARDOUR::frameoffset_t,ARDOUR::framecnt_t> > SilenceResult;
struct Wave {
boost::shared_ptr<ARDOUR::AudioRegion> region;
ArdourCanvas::WaveView* view;
std::list<ArdourCanvas::SimpleRect*> silence_rects;
ArdourCanvas::SimpleLine* threshold_line;
double samples_per_unit;
SilenceResult silence;
Wave (ArdourCanvas::Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
~Wave ();
boost::shared_ptr<ARDOUR::AudioRegion> region;
ArdourCanvas::WaveView* view;
std::list<ArdourCanvas::SimpleRect*> silence_rects;
ArdourCanvas::SimpleLine* threshold_line;
double samples_per_unit;
SilenceResult silence;
Wave (ArdourCanvas::Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
~Wave ();
};
ArdourCanvas::Canvas* _canvas;
std::list<Wave*> _waves;
int _wave_width;
int _wave_height;
bool restart_queued;
static ARDOUR::InterThreadInfo itt;
static bool thread_should_exit;
static Glib::Cond *thread_run;
static Glib::Cond *thread_waiting;
static Glib::StaticMutex run_lock;
static StripSilenceDialog* current;
ARDOUR::framecnt_t max_audible;
ARDOUR::framecnt_t min_audible;
@@ -95,14 +90,14 @@ private:
ARDOUR::framecnt_t min_silence;
PBD::ScopedConnection* _peaks_ready_connection;
static bool _detection_done (void*);
static void* _detection_thread_work (void*);
bool detection_done ();
void* detection_thread_work ();
bool start_silence_detection ();
void maybe_start_silence_detection ();
void update_stats (const SilenceResult&);
pthread_t _thread; ///< thread to compute silence in the background
static void * _detection_thread_work (void *);
void * detection_thread_work ();
Glib::Mutex _lock; ///< lock held while the thread is doing work
Glib::Cond _run_cond; ///< condition to wake the thread
bool _thread_should_finish; ///< true if the thread should terminate
PBD::Signal0<void> Completed; ///< emitted when a silence detection has completed
PBD::ScopedConnection _completed_connection;
ARDOUR::InterThreadInfo _interthread_info;
};