Simplify strip silence dialogue threading, hopefully fixing #3560 in the process.
authorCarl Hetherington <carl@carlh.net>
Mon, 22 Nov 2010 17:15:32 +0000 (17:15 +0000)
committerCarl Hetherington <carl@carlh.net>
Mon, 22 Nov 2010 17:15:32 +0000 (17:15 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@8068 d708f5d6-7413-0410-9779-e7cbd77b26cf

gtk2_ardour/strip_silence_dialog.cc
gtk2_ardour/strip_silence_dialog.h

index d0f806e71a32b0b6fab13fa3d40d08e0185916cb..48b69901e71e7cd19e5ae99f529ed98f31391d06 100644 (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;
+               }
+       }
 
-void
-StripSilenceDialog::threshold_changed ()
-{
-       update_threshold_line ();
-       maybe_start_silence_detection ();
+       return 0;
 }
 
 void
-StripSilenceDialog::maybe_start_silence_detection ()
+StripSilenceDialog::restart_thread ()
 {
-        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);
-                }
-        }
+       /* Cancel any current run */
+       _interthread_info.cancel = true;
 
+       /* Block until the thread waits() */
+       _lock.lock ();
+       /* Reset the flag */
+       _interthread_info.cancel = false;
+       _lock.unlock ();
 
-        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;
+       /* And re-awake the thread */
+       _run_cond.signal ();
 }
 
 void
-StripSilenceDialog::stop_thread ()
+StripSilenceDialog::threshold_changed ()
 {
-        Glib::Mutex::Lock lm (run_lock);
-
-        itt.cancel = true;
-        thread_should_exit = true;
-        thread_run->signal (); 
-        thread_waiting->wait (run_lock);
-        itt.thread = 0;
+       update_threshold_line ();
+       restart_thread ();
 }
 
 void
index 3b800eb1155ffb03cb0ef6955b0230efd1fe0eeb..c9b31578b99fde1e8dc60a4c7620fce6de980531 100644 (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;
 };