expose more info from plugin-strip (for GUI display)
[ardour.git] / libs / ardour / session_events.cc
index 716ade0075abe64c463be427dae349230ee1fa8e..9d11cb0b9194859c9bebbb3fffc954f120339cb6 100644 (file)
 #include <cmath>
 #include <unistd.h>
 
-#include "ardour/timestamps.h"
-
 #include "pbd/error.h"
-#include <glibmm/thread.h>
+#include "pbd/enumwriter.h"
+#include "pbd/stacktrace.h"
+#include "pbd/pthread_utils.h"
 
-#include "ardour/ardour.h"
-#include "ardour/audio_diskstream.h"
-#include "ardour/butler.h"
-#include "ardour/session.h"
+#include "ardour/debug.h"
+#include "ardour/session_event.h"
 
 #include "i18n.h"
 
@@ -36,124 +34,192 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-MultiAllocSingleReleasePool Session::Event::pool ("event", sizeof (Session::Event), 512);
-
-static const char* event_names[] = {
-       "SetTransportSpeed",
-       "SetDiskstreamSpeed",
-       "Locate",
-       "LocateRoll",
-       "LocateRollLocate",
-       "SetLoop",
-       "PunchIn",
-       "PunchOut",
-       "RangeStop",
-       "RangeLocate",
-       "Overwrite",
-       "SetSlaveSource",
-       "Audition",
-       "InputConfigurationChange",
-       "SetAudioRange",
-       "SetMusicRange",
-       "SetPlayRange",
-       "StopOnce",
-       "AutoLoop"
-};
+PerThreadPool* SessionEvent::pool;
+
+void
+SessionEvent::init_event_pool ()
+{
+       pool = new PerThreadPool;
+}
+
+bool
+SessionEvent::has_per_thread_pool ()
+{
+       return pool->has_per_thread_pool ();
+}
 
 void
-Session::add_event (nframes_t frame, Event::Type type, nframes_t target_frame)
+SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
 {
-       Event* ev = new Event (type, Event::Add, frame, target_frame, 0);
+       /* this is a per-thread call that simply creates a thread-private ptr to
+          a CrossThreadPool for use by this thread whenever events are allocated/released
+          from SessionEvent::pool()
+       */
+       pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
+}
+
+SessionEvent::SessionEvent (Type t, Action a, framepos_t when, framepos_t where, double spd, bool yn, bool yn2, bool yn3)
+       : type (t)
+       , action (a)
+       , action_frame (when)
+       , target_frame (where)
+       , speed (spd)
+       , yes_or_no (yn)
+       , second_yes_or_no (yn2)
+       , third_yes_or_no (yn3)
+       , event_loop (0)
+{
+       DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
+}
+
+void *
+SessionEvent::operator new (size_t)
+{
+       CrossThreadPool* p = pool->per_thread_pool ();
+       SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
+       DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 Allocating SessionEvent from %2 ev @ %3 pool size %4 free %5 used %6\n", pthread_name(), p->name(), ev,
+                                                          p->total(), p->available(), p->used()));
+
+       ev->own_pool = p;
+       return ev;
+}
+
+void
+SessionEvent::operator delete (void *ptr, size_t /*size*/)
+{
+       Pool* p = pool->per_thread_pool (false);
+       SessionEvent* ev = static_cast<SessionEvent*> (ptr);
+
+       DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
+                            "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
+                            pthread_name(), ev, enum_2_string (ev->type), enum_2_string (ev->action), p->name(), ev->own_pool->name(), ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used()
+                            ));
+
+       if (p && p == ev->own_pool) {
+               p->release (ptr);
+       } else {
+               assert(ev->own_pool);
+               ev->own_pool->push (ev);
+               DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 was wrong thread for this pool, pushed event onto pending list, will be deleted on next alloc from %2 pool size %3 free %4 used %5 pending %6\n",
+                                                                  pthread_name(), ev->own_pool->name(),
+                                                                  ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
+                                                                  ev->own_pool->pending_size()));
+       }
+}
+
+void
+SessionEventManager::add_event (framepos_t frame, SessionEvent::Type type, framepos_t target_frame)
+{
+       SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, frame, target_frame, 0);
        queue_event (ev);
 }
 
 void
-Session::remove_event (nframes_t frame, Event::Type type)
+SessionEventManager::remove_event (framepos_t frame, SessionEvent::Type type)
 {
-       Event* ev = new Event (type, Event::Remove, frame, 0, 0);
+       SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, frame, 0, 0);
        queue_event (ev);
 }
 
 void
-Session::replace_event (Event::Type type, nframes_t frame, nframes_t target)
+SessionEventManager::replace_event (SessionEvent::Type type, framepos_t frame, framepos_t target)
 {
-       Event* ev = new Event (type, Event::Replace, frame, target, 0);
+       SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, frame, target, 0);
        queue_event (ev);
 }
 
 void
-Session::clear_events (Event::Type type)
+SessionEventManager::clear_events (SessionEvent::Type type)
 {
-       Event* ev = new Event (type, Event::Clear, 0, 0, 0);
+       SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
        queue_event (ev);
 }
 
+void
+SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
+{
+       SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
+       ev->rt_slot = after;
+
+       /* in the calling thread, after the clear is complete, arrange to flush things from the event
+          pool pending list (i.e. to make sure they are really back in the free list and available
+          for future events).
+       */
+
+       ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
+       if (ev->event_loop) {
+               ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
+       }
+
+       queue_event (ev);
+}
 
 void
-Session::dump_events () const
+SessionEventManager::dump_events () const
 {
        cerr << "EVENT DUMP" << endl;
        for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
-               cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl;
+
+               cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_frame << endl;
        }
        cerr << "Next event: ";
 
-        if ((Events::const_iterator) next_event == events.end()) {
+       if ((Events::const_iterator) next_event == events.end()) {
                cerr << "none" << endl;
        } else {
                cerr << "at " << (*next_event)->action_frame << ' '
-                    << (*next_event)->type << " target = "
+                    << enum_2_string ((*next_event)->type) << " target = "
                     << (*next_event)->target_frame << endl;
        }
        cerr << "Immediate events pending:\n";
        for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
-               cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl;
+               cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_frame << endl;
        }
        cerr << "END EVENT_DUMP" << endl;
 }
 
 void
-Session::queue_event (Event* ev)
-{
-       if (_state_of_the_state & Loading) {
-               merge_event (ev);
-       } else {
-               pending_events.write (&ev, 1);
-       }
-}
-
-void
-Session::merge_event (Event* ev)
+SessionEventManager::merge_event (SessionEvent* ev)
 {
        switch (ev->action) {
-       case Event::Remove:
+       case SessionEvent::Remove:
                _remove_event (ev);
                delete ev;
                return;
 
-       case Event::Replace:
+       case SessionEvent::Replace:
                _replace_event (ev);
                return;
 
-       case Event::Clear:
+       case SessionEvent::Clear:
                _clear_event_type (ev->type);
-               delete ev;
+               /* run any additional realtime callback, if any */
+               if (ev->rt_slot) {
+                       ev->rt_slot ();
+               }
+               if (ev->event_loop) {
+                       /* run non-realtime callback (in some other thread) */
+                       ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
+               } else {
+                       delete ev;
+               }
                return;
 
-       case Event::Add:
+       case SessionEvent::Add:
                break;
        }
 
        /* try to handle immediate events right here */
 
-       if (ev->action_frame == 0) {
+       if (ev->action_frame == SessionEvent::Immediate) {
                process_event (ev);
                return;
        }
 
        switch (ev->type) {
-       case Event::AutoLoop:
-       case Event::StopOnce:
+       case SessionEvent::AutoLoop:
+       case SessionEvent::AutoLoopDeclick:
+       case SessionEvent::StopOnce:
                _clear_event_type (ev->type);
                break;
 
@@ -161,21 +227,21 @@ Session::merge_event (Event* ev)
                for (Events::iterator i = events.begin(); i != events.end(); ++i) {
                        if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
                          error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."),
-                                                event_names[ev->type], ev->action_frame) << endmsg;
+                                                 enum_2_string (ev->type), ev->action_frame) << endmsg;
                                return;
                        }
                }
        }
 
        events.insert (events.begin(), ev);
-       events.sort (Event::compare);
+       events.sort (SessionEvent::compare);
        next_event = events.begin();
        set_next_event ();
 }
 
 /** @return true when @a ev is deleted. */
 bool
-Session::_replace_event (Event* ev)
+SessionEventManager::_replace_event (SessionEvent* ev)
 {
        bool ret = false;
        Events::iterator i;
@@ -198,7 +264,7 @@ Session::_replace_event (Event* ev)
                events.insert (events.begin(), ev);
        }
 
-       events.sort (Event::compare);
+       events.sort (SessionEvent::compare);
        next_event = events.end();
        set_next_event ();
 
@@ -207,7 +273,7 @@ Session::_replace_event (Event* ev)
 
 /** @return true when @a ev is deleted. */
 bool
-Session::_remove_event (Session::Event* ev)
+SessionEventManager::_remove_event (SessionEvent* ev)
 {
        bool ret = false;
        Events::iterator i;
@@ -222,7 +288,7 @@ Session::_remove_event (Session::Event* ev)
                        if (i == next_event) {
                                ++next_event;
                        }
-                       events.erase (i);
+                       i = events.erase (i);
                        break;
                }
        }
@@ -235,7 +301,7 @@ Session::_remove_event (Session::Event* ev)
 }
 
 void
-Session::_clear_event_type (Event::Type type)
+SessionEventManager::_clear_event_type (SessionEvent::Type type)
 {
        Events::iterator i, tmp;
 
@@ -271,184 +337,3 @@ Session::_clear_event_type (Event::Type type)
        set_next_event ();
 }
 
-void
-Session::set_next_event ()
-{
-       if (events.empty()) {
-               next_event = events.end();
-               return;
-       }
-
-       if (next_event == events.end()) {
-               next_event = events.begin();
-       }
-
-       if ((*next_event)->action_frame > _transport_frame) {
-               next_event = events.begin();
-       }
-
-       for (; next_event != events.end(); ++next_event) {
-               if ((*next_event)->action_frame >= _transport_frame) {
-                       break;
-               }
-       }
-}
-
-void
-Session::process_event (Event* ev)
-{
-       bool remove = true;
-       bool del = true;
-
-       /* if we're in the middle of a state change (i.e. waiting
-          for the butler thread to complete the non-realtime
-          part of the change), we'll just have to queue this
-          event for a time when the change is complete.
-       */
-
-       if (non_realtime_work_pending()) {
-
-               /* except locates, which we have the capability to handle */
-
-               if (ev->type != Event::Locate) {
-                       immediate_events.insert (immediate_events.end(), ev);
-                       _remove_event (ev);
-                       return;
-               }
-       }
-
-       //printf("Processing event: %s\n", event_names[ev->type]);
-
-       switch (ev->type) {
-       case Event::SetLoop:
-               set_play_loop (ev->yes_or_no);
-               break;
-
-       case Event::AutoLoop:
-               if (play_loop) {
-                       start_locate (ev->target_frame, true, false, Config->get_seamless_loop());
-               }
-               remove = false;
-               del = false;
-               break;
-
-       case Event::Locate:
-               if (ev->yes_or_no) {
-                       // cerr << "forced locate to " << ev->target_frame << endl;
-                       locate (ev->target_frame, false, true, false);
-               } else {
-                       // cerr << "soft locate to " << ev->target_frame << endl;
-                       start_locate (ev->target_frame, false, true, false);
-               }
-               _send_smpte_update = true;
-               break;
-
-       case Event::LocateRoll:
-               if (ev->yes_or_no) {
-                       // cerr << "forced locate to+roll " << ev->target_frame << endl;
-                       locate (ev->target_frame, true, true, false);
-               } else {
-                       // cerr << "soft locate to+roll " << ev->target_frame << endl;
-                       start_locate (ev->target_frame, true, true, false);
-               }
-               _send_smpte_update = true;
-               break;
-
-       case Event::LocateRollLocate:
-               // locate is handled by ::request_roll_at_and_return()
-               _requested_return_frame = ev->target_frame;
-               request_locate (ev->target2_frame, true);
-               break;
-
-
-       case Event::SetTransportSpeed:
-               set_transport_speed (ev->speed, ev->yes_or_no);
-               break;
-
-       case Event::PunchIn:
-               // cerr << "PunchIN at " << transport_frame() << endl;
-               if (config.get_punch_in() && record_status() == Enabled) {
-                       enable_record ();
-               }
-               remove = false;
-               del = false;
-               break;
-
-       case Event::PunchOut:
-               // cerr << "PunchOUT at " << transport_frame() << endl;
-               if (config.get_punch_out()) {
-                       step_back_from_record ();
-               }
-               remove = false;
-               del = false;
-               break;
-
-       case Event::StopOnce:
-               if (!non_realtime_work_pending()) {
-                       stop_transport (ev->yes_or_no);
-                       _clear_event_type (Event::StopOnce);
-               }
-               remove = false;
-               del = false;
-               break;
-
-       case Event::RangeStop:
-               if (!non_realtime_work_pending()) {
-                       stop_transport (ev->yes_or_no);
-               }
-               remove = false;
-               del = false;
-               break;
-
-       case Event::RangeLocate:
-               start_locate (ev->target_frame, true, true, false);
-               remove = false;
-               del = false;
-               break;
-
-       case Event::Overwrite:
-               overwrite_some_buffers (static_cast<Diskstream*>(ev->ptr));
-               break;
-
-       case Event::SetDiskstreamSpeed:
-               set_diskstream_speed (static_cast<Diskstream*> (ev->ptr), ev->speed);
-               break;
-
-       case Event::SetSlaveSource:
-               set_slave_source (ev->slave);
-               break;
-
-       case Event::Audition:
-               set_audition (ev->region);
-               // drop reference to region
-               ev->region.reset ();
-               break;
-
-       case Event::InputConfigurationChange:
-               post_transport_work = PostTransportWork (post_transport_work | PostTransportInputChange);
-               _butler->schedule_transport_work ();
-               break;
-
-       case Event::SetAudioRange:
-               current_audio_range = ev->audio_range;
-               setup_auto_play ();
-               break;
-
-       case Event::SetPlayRange:
-               set_play_range (ev->yes_or_no);
-               break;
-
-       default:
-         fatal << string_compose(_("Programming error: illegal event type in process_event (%1)"), ev->type) << endmsg;
-               /*NOTREACHED*/
-               break;
-       };
-
-       if (remove) {
-               del = del && !_remove_event (ev);
-       }
-
-       if (del) {
-               delete ev;
-       }
-}