[beast: 38/95] BSE: fully port Sequencer to C++ and std::thread



commit 53fc0a29b84d2a23c8a1be02c373565b617ac57b
Author: Tim Janik <timj gnu org>
Date:   Sat Mar 16 22:19:07 2013 +0100

    BSE: fully port Sequencer to C++ and std::thread

 bse/bsemain.cc                |    5 +-
 bse/bsemain.hh                |    3 -
 bse/bsemididevice-oss.cc      |    4 +-
 bse/bsepart.cc                |    2 +-
 bse/bsepcmdevice-null.cc      |    6 +-
 bse/bseproject.cc             |    4 +-
 bse/bsesequencer.cc           |  342 +++++++++++++++++++----------------------
 bse/bsesequencer.hh           |   67 ++++++---
 bse/bsesong.cc                |    4 +-
 bse/bsetrack.cc               |    2 +-
 drivers/bsemididevice-alsa.cc |    4 +-
 11 files changed, 219 insertions(+), 224 deletions(-)
---
diff --git a/bse/bsemain.cc b/bse/bsemain.cc
index 6e72337..fd24a75 100644
--- a/bse/bsemain.cc
+++ b/bse/bsemain.cc
@@ -35,7 +35,6 @@ const guint            bse_interface_age = BSE_INTERFACE_AGE;
 const guint             bse_binary_age = BSE_BINARY_AGE;
 const gchar            *bse_version = BSE_VERSION;
 GMainContext            *bse_main_context = NULL;
-BirnetMutex             bse_main_sequencer_mutex = { 0, };
 static volatile gboolean bse_initialization_stage = 0;
 static gboolean          textdomain_setup = FALSE;
 static BseMainArgs       default_main_args = {
@@ -180,7 +179,6 @@ static void
 bse_init_core (void)
 {
   /* global threading things */
-  sfi_mutex_init (&bse_main_sequencer_mutex);
   bse_main_context = g_main_context_new ();
   sfi_thread_set_wakeup ((BirnetThreadWakeup) g_main_context_wakeup,
                         bse_main_context, NULL);
@@ -334,7 +332,8 @@ bse_main_loop (Rapicorn::AsyncBlockingQueue<int> *init_queue)
   Bse::TaskRegistry::add ("BSE Core", Rapicorn::ThisThread::process_pid(), 
Rapicorn::ThisThread::thread_pid());
   bse_init_core ();
   // start other threads
-  bse_sequencer_init_thread ();
+  struct Internal : Bse::Sequencer { using Bse::Sequencer::_init_threaded; };
+  Internal::_init_threaded();
   // complete initialization
   bse_initialization_stage++;   // = 2
   init_queue->push ('B');       // signal completion to caller
diff --git a/bse/bsemain.hh b/bse/bsemain.hh
index 2a20be0..8b6c993 100644
--- a/bse/bsemain.hh
+++ b/bse/bsemain.hh
@@ -32,8 +32,6 @@ void            bse_message_to_default_handler   (const BseMessage *msg);
 /* --- global macros --- */
 #define        BSE_THREADS_ENTER()                     // bse_main_global_lock ()
 #define        BSE_THREADS_LEAVE()                     // bse_main_global_unlock ()
-#define        BSE_SEQUENCER_LOCK()                    sfi_mutex_lock (&bse_main_sequencer_mutex)
-#define        BSE_SEQUENCER_UNLOCK()                  sfi_mutex_unlock (&bse_main_sequencer_mutex)
 #define        BSE_DBG_EXT                             (bse_main_args->debug_extensions != FALSE)
 #define        BSE_CONFIG(field)                       (bse_main_args->field)
 /* --- argc/argv overide settings --- */
@@ -79,7 +77,6 @@ void    _bse_init_c_wrappers    ();
 extern BseMainArgs     *bse_main_args;
 extern BseTraceArgs     bse_trace_args;
 extern GMainContext    *bse_main_context;
-extern BirnetMutex     bse_main_sequencer_mutex;
 
 G_END_DECLS
 
diff --git a/bse/bsemididevice-oss.cc b/bse/bsemididevice-oss.cc
index c178d9c..7bc9dae 100644
--- a/bse/bsemididevice-oss.cc
+++ b/bse/bsemididevice-oss.cc
@@ -152,7 +152,7 @@ bse_midi_device_oss_open (BseDevice     *device,
       GPollFD pfd = { 0, };
       pfd.fd = oss->fd;
       pfd.events = G_IO_IN;
-      bse_sequencer_add_io_watch (1, &pfd, oss_midi_io_handler, oss);
+      Bse::Sequencer::instance().add_io_watch (1, &pfd, oss_midi_io_handler, oss);
     }
   else
     {
@@ -171,7 +171,7 @@ bse_midi_device_oss_close (BseDevice *device)
   BSE_MIDI_DEVICE (device)->handle = NULL;
   g_assert (handle->running_thread == FALSE);
   /* midi_handle_abort_wait (handle); */
-  bse_sequencer_remove_io_watch (oss_midi_io_handler, oss);
+  Bse::Sequencer::instance().remove_io_watch (oss_midi_io_handler, oss);
   (void) close (oss->fd);
   g_free (oss);
 }
diff --git a/bse/bsepart.cc b/bse/bsepart.cc
index d6951be..e01d2b9 100644
--- a/bse/bsepart.cc
+++ b/bse/bsepart.cc
@@ -1,6 +1,6 @@
 // Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 #include "bsepart.hh"
-#include "bsemain.hh"
+#include "bsesequencer.hh"
 #include "bsestorage.hh"
 #include "bsesong.hh"
 #include "bsetrack.hh"
diff --git a/bse/bsepcmdevice-null.cc b/bse/bsepcmdevice-null.cc
index 088b41a..b8f13cb 100644
--- a/bse/bsepcmdevice-null.cc
+++ b/bse/bsepcmdevice-null.cc
@@ -66,16 +66,18 @@ bse_pcm_device_null_close (BseDevice *device)
   BSE_PCM_DEVICE (device)->handle = NULL;
   g_free (null);
 }
+
 static gboolean
 null_device_check_io (BsePcmHandle *handle,
                       glong        *timeoutp)
 {
   /* keep the sequencer busy or we will constantly timeout */
-  bse_sequencer_wakeup();
+  Bse::Sequencer::instance().wakeup();
   *timeoutp = 1;
   /* ensure sequencer fairness */
-  return !bse_sequencer_thread_lagging (2);
+  return !Bse::Sequencer::instance().thread_lagging (2);
 }
+
 static guint
 null_device_latency (BsePcmHandle *handle)
 {
diff --git a/bse/bseproject.cc b/bse/bseproject.cc
index ff9a314..7316736 100644
--- a/bse/bseproject.cc
+++ b/bse/bseproject.cc
@@ -749,7 +749,7 @@ bse_project_start_playback (BseProject *self)
     bse_project_state_changed (self, BSE_PROJECT_PLAYING);
   /* then, start the sequencer */
   while (songs)
-    bse_sequencer_start_song ((BseSong*) sfi_ring_pop_head (&songs), 0);
+    Bse::Sequencer::instance().start_song ((BseSong*) sfi_ring_pop_head (&songs), 0);
 }
 void
 bse_project_stop_playback (BseProject *self)
@@ -765,7 +765,7 @@ bse_project_stop_playback (BseProject *self)
     {
       BseSuper *super = BSE_SUPER (slist->data);
       if (BSE_IS_SONG (super))
-        bse_sequencer_remove_song (BSE_SONG (super));
+        Bse::Sequencer::instance().remove_song (BSE_SONG (super));
       if (super->context_handle != ~uint (0) && BSE_SUPER_NEEDS_CONTEXT (super))
        {
          BseSource *source = BSE_SOURCE (super);
diff --git a/bse/bsesequencer.cc b/bse/bsesequencer.cc
index 9719aeb..d8bc498 100644
--- a/bse/bsesequencer.cc
+++ b/bse/bsesequencer.cc
@@ -15,61 +15,31 @@
 #include <errno.h>
 #include <string.h>
 #include <vector>
-using namespace std;    // FIXME
-
-/* due to a linker/compiler bug on SuSE 9.2, we need to
- * define extern "C" symbols outside of any C++ namespace,
- * in order for C code to link against it.
- */
-extern "C" { BirnetThread *bse_sequencer_thread = NULL; }
 
 #define CHECKTRACE()    UNLIKELY (bse_trace_args.sequencer)
 #define SEQTRACE(...)   do { if (CHECKTRACE()) sfi_debug_channel_printf (bse_trace_args.sequencer, NULL, 
__VA_ARGS__); } while (0)
 #define        BSE_SEQUENCER_FUTURE_BLOCKS    (7)
 
-/* --- prototypes --- */
-static void    bse_sequencer_thread_main       (gpointer        data);
-static void    bse_sequencer_process_song_SL   (BseSong        *song,
-                                                guint           n_ticks);
-/* --- variables --- */
-static BseSequencer    *global_sequencer = NULL;
-static BirnetCond          current_watch_cond = { 0, };
-static gint             sequencer_wake_up_pipe[2] = { -1, -1 };
+namespace Bse {
 
-/* --- functions --- */
-extern "C" void
-bse_sequencer_init_thread (void)
-{
-  g_assert (bse_sequencer_thread == NULL);
-  sfi_cond_init (&current_watch_cond);
-  if (pipe (sequencer_wake_up_pipe) < 0)
-    g_error ("failed to create sequencer wake-up pipe: %s", strerror (errno));
-  glong flags = fcntl (sequencer_wake_up_pipe[0], F_GETFL, 0);
-  fcntl (sequencer_wake_up_pipe[0], F_SETFL, O_NONBLOCK | flags);
-  flags = fcntl (sequencer_wake_up_pipe[1], F_GETFL, 0);
-  fcntl (sequencer_wake_up_pipe[1], F_SETFL, O_NONBLOCK | flags);
-  /* initialize BseSequencer */
-  static BseSequencer sseq = { 0, };
-  sseq.stamp = Bse::TickStamp::current();
-  g_assert (sseq.stamp > 0);
-  global_sequencer = &sseq;
-  bse_sequencer_thread = sfi_thread_run ("Sequencer", bse_sequencer_thread_main, NULL);
-  if (!bse_sequencer_thread)
-    g_error ("failed to create sequencer thread");
-}
+using Rapicorn::ThreadInfo; // FIXME
+
+Sequencer              *Sequencer::singleton_ = NULL;
+Mutex                   Sequencer::sequencer_mutex_;
+static int              sequencer_wake_up_pipe[2] = { -1, -1 };
+static ThreadInfo      *sequencer_thread_self = NULL;
 
 void
-bse_sequencer_wakeup ()
+Sequencer::wakeup ()
 {
-  guint8 wake_up_message = 'W';
-  gint err;
+  uint8 wake_up_message = 'W';
+  int err;
   do
     err = write (sequencer_wake_up_pipe[1], &wake_up_message, 1);
   while (err < 0 && errno == EINTR);
 }
 
-namespace { // Anon
-class PollPool {
+class Sequencer::PollPool {
 public:
   struct IOWatch {
     BseIOWatch  watch_func;
@@ -170,32 +140,29 @@ public:
   BIRNET_STATIC_ASSERT (offsetof (GPollFD, revents) == offsetof (struct pollfd, revents));
   BIRNET_STATIC_ASSERT (sizeof (((GPollFD*) 0)->revents) == sizeof (((struct pollfd*) 0)->revents));
 };
-} // Anon
-static PollPool sequencer_poll_pool;
-extern "C" void
-bse_sequencer_add_io_watch (guint           n_pfds,
-                            const GPollFD  *pfds,
-                            BseIOWatch      watch_func,
-                            gpointer        data)
+
+void
+Sequencer::add_io_watch (uint n_pfds, const GPollFD *pfds, BseIOWatch watch_func, void *watch_data)
 {
   g_return_if_fail (watch_func != NULL);
-  BSE_SEQUENCER_LOCK ();
-  sequencer_poll_pool.add_watch (n_pfds, pfds, watch_func, data);
-  BSE_SEQUENCER_UNLOCK ();
+  BSE_SEQUENCER_LOCK();
+  poll_pool_->add_watch (n_pfds, pfds, watch_func, watch_data);
+  BSE_SEQUENCER_UNLOCK();
 }
-static BseIOWatch current_watch_func = NULL;
+
+static BseIOWatch current_watch_func = NULL;    // global guards allow removal of watch function while it's 
in use
 static gpointer   current_watch_data = NULL;
 static bool       current_watch_needs_remove1 = false;
 static bool       current_watch_needs_remove2 = false;
-extern "C" void
-bse_sequencer_remove_io_watch (BseIOWatch      watch_func,
-                               gpointer        watch_data)
+
+void
+Sequencer::remove_io_watch (BseIOWatch watch_func, void *watch_data)
 {
   g_return_if_fail (watch_func != NULL);
   /* removal requirements:
    * - any thread should be able to remove an io watch (once)
    * - a watch_func() should be able to remove its own io watch
-   * - a watch_func() may not get called after bse_sequencer_remove_io_watch()
+   * - a watch_func() may not get called after remove_io_watch()
    *   finished in any thread
    * - concurrent removal of an io watch from within its watch_func() (executed
    *   within the sequencer thread) and from an external thread at the same
@@ -204,10 +171,10 @@ bse_sequencer_remove_io_watch (BseIOWatch      watch_func,
    *   least conceptually) or has never been installed
    */
   bool removal_success;
-  BSE_SEQUENCER_LOCK ();
+  BSE_SEQUENCER_LOCK();
   if (current_watch_func == watch_func && current_watch_data == watch_data)
     {  /* watch_func() to be removed is currently in call */
-      if (bse_sequencer_thread == sfi_thread_self())
+      if (&ThreadInfo::self() == sequencer_thread_self)
         {
           /* allow removal calls from within a watch_func() */
           removal_success = !current_watch_needs_remove1;       /* catch multiple calls */
@@ -220,37 +187,39 @@ bse_sequencer_remove_io_watch (BseIOWatch      watch_func,
           current_watch_needs_remove2 = true;
           /* wait until watch_func() call has finished */
           while (current_watch_func == watch_func && current_watch_data == watch_data)
-            sfi_cond_wait (&current_watch_cond, &bse_main_sequencer_mutex);
+            watch_cond_.wait (sequencer_mutex_);
         }
     }
   else /* can remove (watch_func(watch_data) not in call) */
     {
-      removal_success = sequencer_poll_pool.remove_watch (watch_func, watch_data);
+      removal_success = poll_pool_->remove_watch (watch_func, watch_data);
       /* wake up sequencer thread, so it stops polling on fds it doesn't own anymore */
-      bse_sequencer_wakeup();
+      wakeup();
     }
-  BSE_SEQUENCER_UNLOCK ();
+  BSE_SEQUENCER_UNLOCK();
   if (!removal_success)
     g_warning ("%s: failed to remove %p(%p)", G_STRFUNC, watch_func, watch_data);
 }
-static bool
-bse_sequencer_poll_Lm (gint timeout_ms)
+
+bool
+Sequencer::pool_poll_Lm (gint timeout_ms)
 {
-  guint n_pfds = sequencer_poll_pool.get_n_pfds() + 1;  /* one for the wake-up pipe */
+  guint n_pfds = poll_pool_->get_n_pfds() + 1;  /* one for the wake-up pipe */
   GPollFD *pfds = g_newa (GPollFD, n_pfds);
   pfds[0].fd = sequencer_wake_up_pipe[0];
   pfds[0].events = G_IO_IN;
   pfds[0].revents = 0;
-  sequencer_poll_pool.fill_pfds (n_pfds - 1, pfds + 1); /* rest used for io watch array */
-  BSE_SEQUENCER_UNLOCK ();
-  gint result = poll ((struct pollfd*) pfds, n_pfds, timeout_ms);
+  poll_pool_->fill_pfds (n_pfds - 1, pfds + 1); /* rest used for io watch array */
+  BSE_SEQUENCER_UNLOCK();
+  int result = poll ((struct pollfd*) pfds, n_pfds, timeout_ms);
   if (result < 0 && errno != EINTR)
     g_printerr ("%s: poll() error: %s\n", G_STRFUNC, g_strerror (errno));
-  BSE_SEQUENCER_LOCK ();
+  BSE_SEQUENCER_LOCK();
   if (result > 0 && pfds[0].revents)
     {
       guint8 buffer[256];
-      read (sequencer_wake_up_pipe[0], buffer, 256);    /* eat wake up message */
+      const size_t unused = read (sequencer_wake_up_pipe[0], buffer, 256);      // eat wake up message
+      (void) unused;
       result -= 1;
     }
   if (result > 0)
@@ -258,39 +227,38 @@ bse_sequencer_poll_Lm (gint timeout_ms)
       /* dispatch io watches */
       guint watch_n_pfds;
       GPollFD *watch_pfds;
-      while (sequencer_poll_pool.fetch_notify_watch (current_watch_func, current_watch_data, watch_n_pfds, 
watch_pfds))
+      while (poll_pool_->fetch_notify_watch (current_watch_func, current_watch_data, watch_n_pfds, 
watch_pfds))
         {
           g_assert (!current_watch_needs_remove1 && !current_watch_needs_remove2);
-          BSE_SEQUENCER_UNLOCK ();
+          BSE_SEQUENCER_UNLOCK();
           bool current_watch_stays_alive = current_watch_func (current_watch_data, watch_n_pfds, watch_pfds);
-          BSE_SEQUENCER_LOCK ();
-          if (current_watch_needs_remove1 ||            /* removal queued from within io handler */
-              current_watch_needs_remove2 ||            /* removal queued from other thread */
-              !current_watch_stays_alive)               /* removal requested by io handler return value */
-            sequencer_poll_pool.remove_watch (current_watch_func, current_watch_data);
+          BSE_SEQUENCER_LOCK();
+          if (current_watch_needs_remove1 ||            // removal queued from within io handler
+              current_watch_needs_remove2 ||            // removal queued from other thread
+              !current_watch_stays_alive)               // removal requested by io handler return value
+            poll_pool_->remove_watch (current_watch_func, current_watch_data);
           current_watch_needs_remove1 = false;
           current_watch_needs_remove2 = false;
           current_watch_func = NULL;
           current_watch_data = NULL;
-          sfi_cond_broadcast (&current_watch_cond);     /* wake up threads in 
bse_sequencer_remove_io_watch() */
+          watch_cond_.broadcast();      // wake up threads in remove_io_watch()
         }
     }
-  return !sfi_thread_aborted();
+  return true;
 }
-extern "C" void
-bse_sequencer_start_song (BseSong        *song,
-                          guint64         start_stamp)
+
+void
+Sequencer::start_song (BseSong *song, uint64 start_stamp)
 {
-  g_assert (bse_sequencer_thread != NULL);
   g_return_if_fail (BSE_IS_SONG (song));
   g_return_if_fail (BSE_SOURCE_PREPARED (song));
   g_return_if_fail (song->sequencer_start_request_SL == 0);
-  g_assert (song->sequencer_owns_refcount_SL == FALSE);
+  g_assert (song->sequencer_owns_refcount_SL == false);
   start_stamp = MAX (start_stamp, 1);
   g_object_ref (song);
-  BSE_SEQUENCER_LOCK ();
-  song->sequencer_owns_refcount_SL = TRUE;
-  song->sequencer_start_request_SL = start_stamp <= 1 ? global_sequencer->stamp : start_stamp;
+  BSE_SEQUENCER_LOCK();
+  song->sequencer_owns_refcount_SL = true;
+  song->sequencer_start_request_SL = start_stamp <= 1 ? stamp_ : start_stamp;
   song->sequencer_start_SL = 0;
   song->sequencer_done_SL = 0;
   song->delta_stamp_SL = 0;
@@ -301,83 +269,89 @@ bse_sequencer_start_song (BseSong        *song,
       BseTrack *track = (BseTrack*) ring->data;
       track->track_done_SL = FALSE;
     }
-  global_sequencer->songs = sfi_ring_append (global_sequencer->songs, song);
-  BSE_SEQUENCER_UNLOCK ();
-  bse_sequencer_wakeup();
+  songs_ = sfi_ring_append (songs_, song);
+  BSE_SEQUENCER_UNLOCK();
+  wakeup();
 }
-extern "C" void
-bse_sequencer_remove_song (BseSong *song)
+
+void
+Sequencer::remove_song (BseSong *song)
 {
   g_return_if_fail (BSE_IS_SONG (song));
   g_return_if_fail (BSE_SOURCE_PREPARED (song));
   if (song->sequencer_start_request_SL == 0)
     {
-      g_assert (song->sequencer_owns_refcount_SL == FALSE);
-      return;   /* uncontained */
+      g_assert (song->sequencer_owns_refcount_SL == false);
+      return;   // uncontained
     }
-  BSE_SEQUENCER_LOCK ();
-  SfiRing *ring = sfi_ring_find (global_sequencer->songs, song);
-  global_sequencer->songs = sfi_ring_remove_node (global_sequencer->songs, ring);
+  BSE_SEQUENCER_LOCK();
+  SfiRing *ring = sfi_ring_find (songs_, song);
+  songs_ = sfi_ring_remove_node (songs_, ring);
   song->sequencer_start_request_SL = 0;
   if (!song->sequencer_done_SL)
-    song->sequencer_done_SL = global_sequencer->stamp;
+    song->sequencer_done_SL = stamp_;
   if (!song->sequencer_start_SL)
     song->sequencer_start_SL = song->sequencer_done_SL;
-  gboolean need_unref = song->sequencer_owns_refcount_SL;
-  song->sequencer_owns_refcount_SL = FALSE;
-  BSE_SEQUENCER_UNLOCK ();
+  bool need_unref = song->sequencer_owns_refcount_SL;
+  song->sequencer_owns_refcount_SL = false;
+  BSE_SEQUENCER_UNLOCK();
   if (!ring)
     g_warning ("%s: failed to find %s in sequencer", G_STRLOC, bse_object_debug_name (song));
   if (need_unref)
     g_object_unref (song);
 }
-static gboolean
-bse_sequencer_remove_song_async (gpointer data) /* UserThread */
+
+static int
+sequencer_remove_song_async (gpointer data) /* UserThread */
 {
   BseSong *song = BSE_SONG (data);
-  if (BSE_SOURCE_PREPARED (song) &&     /* project might be deactivated already */
-      song->sequencer_done_SL)          /* song might have been removed and re-added */
+  if (BSE_SOURCE_PREPARED (song) &&     // project might be deactivated already
+      song->sequencer_done_SL)          // song might have been removed and re-added
     {
-      bse_sequencer_remove_song (song);
+      Sequencer::instance().remove_song (song);
       BseProject *project = bse_item_get_project (BSE_ITEM (song));
       bse_project_check_auto_stop (project);
     }
-  g_object_unref (song);                        /* sequencer_owns_refcount_SL = FALSE from 
bse_sequencer_queue_remove_song_SL() */
-  return FALSE;
+  g_object_unref (song);                // sequencer_owns_refcount_SL = false; from 
sequencer_queue_remove_song_SL()
+  return false;
 }
+
 static void
-bse_sequencer_queue_remove_song_SL (BseSong *song)
+sequencer_queue_remove_song_SL (BseSong *song)
 {
-  g_return_if_fail (song->sequencer_owns_refcount_SL == TRUE);
-  song->sequencer_owns_refcount_SL = FALSE;     /* g_object_unref() in bse_sequencer_remove_song_async() */
-  /* queue a job into the BSE core for immediate execution */
-  bse_idle_now (bse_sequencer_remove_song_async, song);
+  g_return_if_fail (song->sequencer_owns_refcount_SL == true);
+  song->sequencer_owns_refcount_SL = false;     // g_object_unref() in sequencer_remove_song_async()
+  // queue a job into the BSE core for immediate execution
+  bse_idle_now (sequencer_remove_song_async, song);
 }
-extern "C" gboolean
-bse_sequencer_thread_lagging (guint n_blocks)
+
+bool
+Sequencer::thread_lagging (uint n_blocks)
 {
-  /* return whether the sequencer lags for n_blocks future stamps */
-  const guint64 cur_stamp = Bse::TickStamp::current();
-  guint64 next_stamp = cur_stamp + n_blocks * bse_engine_block_size();
-  BSE_SEQUENCER_LOCK ();
-  gboolean lagging = global_sequencer->stamp < next_stamp;
-  BSE_SEQUENCER_UNLOCK ();
+  // return whether the sequencer lags for n_blocks future stamps
+  const uint64 cur_stamp = Bse::TickStamp::current();
+  uint64 next_stamp = cur_stamp + n_blocks * bse_engine_block_size();
+  BSE_SEQUENCER_LOCK();
+  bool lagging = stamp_ < next_stamp;
+  BSE_SEQUENCER_UNLOCK();
   return lagging;
 }
-static void
-bse_sequencer_thread_main (gpointer data)
+
+void
+Sequencer::sequencer_thread ()
 {
   Bse::TaskRegistry::add ("Sequencer", Rapicorn::ThisThread::process_pid(), 
Rapicorn::ThisThread::thread_pid());
+  sequencer_thread_self = &ThreadInfo::self();
   SEQTRACE ("SEQ:thrdstrt: now=%llu", Bse::TickStamp::current());
-  Bse::TickStampWakeupP wakeup = Bse::TickStamp::create_wakeup (bse_sequencer_wakeup);
+  Bse::TickStampWakeupP wakeup = Bse::TickStamp::create_wakeup ([&]() { this->wakeup(); });
   bse_message_setup_thread_handler();
-  BSE_SEQUENCER_LOCK ();
+  BSE_SEQUENCER_LOCK();
   do
     {
       const guint64 cur_stamp = Bse::TickStamp::current();
       guint64 next_stamp = cur_stamp + BSE_SEQUENCER_FUTURE_BLOCKS * bse_engine_block_size();
       SfiRing *ring;
-      for (ring = global_sequencer->songs; ring; ring = sfi_ring_walk (ring, global_sequencer->songs))
+      for (ring = songs_; ring; ring = sfi_ring_walk (ring, songs_))
        {
           BseSong *song = BSE_SONG (ring->data);
           bool forced_ticks = 0;
@@ -403,7 +377,7 @@ bse_sequencer_thread_main (gpointer data)
                     }
                  if (n_ticks < 1)
                    break;
-                 bse_sequencer_process_song_SL (song, n_ticks);
+                 process_song_SL (song, n_ticks);
                  stamp_diff = (next_stamp - song->sequencer_start_SL) - song->delta_stamp_SL;
                }
               if (old_song_pos <= cur_stamp && !song_starting) /* detect underrun after song start */
@@ -411,32 +385,24 @@ bse_sequencer_thread_main (gpointer data)
                   gchar *dh = bse_object_strdup_debug_handle (song);    /* thread safe */
                   /* if (!song->sequencer_underrun_detected_SL) */
                   g_printerr ("BseSequencer: underrun by %lld blocks for song: %s\n",
-                              (cur_stamp - old_song_pos) / bse_engine_block_size() + 1,
+                              uint64 ((cur_stamp - old_song_pos) / bse_engine_block_size() + 1),
                               dh);
                   song->sequencer_underrun_detected_SL = TRUE;
                   g_free (dh);
                 }
            }
        }
-      global_sequencer->stamp = next_stamp;
+      stamp_ = next_stamp;
       wakeup->awake_after (cur_stamp + bse_engine_block_size ());
     }
-  while (bse_sequencer_poll_Lm (-1));
-  BSE_SEQUENCER_UNLOCK ();
+  while (pool_poll_Lm (-1));
+  BSE_SEQUENCER_UNLOCK();
   SEQTRACE ("SEQ:thrdstop: now=%llu", Bse::TickStamp::current());
   Bse::TaskRegistry::remove (Rapicorn::ThisThread::thread_pid());
 }
-static void
-bse_sequencer_process_track_SL (BseTrack        *track,
-                                gdouble          start_stamp,
-                                guint            start_tick,
-                                guint            n_ticks,
-                                gdouble          stamps_per_tick,
-                                BseMidiReceiver *midi_receiver);
-static gboolean
-bse_sequencer_process_song_unlooped_SL (BseSong *song,
-                                        guint    n_ticks,
-                                        gboolean force_active_tracks)
+
+bool
+Sequencer::process_song_unlooped_SL (BseSong *song, uint n_ticks, bool force_active_tracks)
 {
   BseMidiReceiver *midi_receiver = song->midi_receiver_SL;
   gdouble current_stamp = song->sequencer_start_SL + song->delta_stamp_SL;
@@ -452,10 +418,7 @@ bse_sequencer_process_song_unlooped_SL (BseSong *song,
       if (!track->track_done_SL || force_active_tracks)
        {
          track->track_done_SL = FALSE;
-         bse_sequencer_process_track_SL (track, current_stamp,
-                                          song->tick_SL, tick_bound,
-                                          stamps_per_tick,
-                                          midi_receiver);
+         process_track_SL (track, current_stamp, song->tick_SL, tick_bound, stamps_per_tick, midi_receiver);
        }
       if (track->track_done_SL)
        n_done_tracks++;
@@ -465,9 +428,9 @@ bse_sequencer_process_song_unlooped_SL (BseSong *song,
   song->delta_stamp_SL += n_ticks * stamps_per_tick;
   return n_done_tracks != n_tracks;
 }
-static void
-bse_sequencer_process_song_SL (BseSong *song,
-                               guint    n_ticks)
+
+void
+Sequencer::process_song_SL (BseSong *song, uint n_ticks)
 {
   gboolean tracks_active = TRUE;
   if (song->loop_enabled_SL && (gint64) song->tick_SL <= song->loop_right_SL)
@@ -476,7 +439,7 @@ bse_sequencer_process_song_SL (BseSong *song,
        guint tdiff = song->loop_right_SL - song->tick_SL;
        tdiff = MIN (tdiff, n_ticks);
        if (tdiff)
-         bse_sequencer_process_song_unlooped_SL (song, tdiff, TRUE);
+         process_song_unlooped_SL (song, tdiff, true);
        n_ticks -= tdiff;
        if ((gint64) song->tick_SL >= song->loop_right_SL)
          {
@@ -485,30 +448,20 @@ bse_sequencer_process_song_SL (BseSong *song,
       }
     while (n_ticks);
   else
-    tracks_active = bse_sequencer_process_song_unlooped_SL (song, n_ticks, FALSE);
+    tracks_active = process_song_unlooped_SL (song, n_ticks, false);
   if (!song->sequencer_done_SL && !tracks_active)
     {
-      song->sequencer_done_SL = global_sequencer->stamp;
-      bse_sequencer_queue_remove_song_SL (song);
+      song->sequencer_done_SL = stamp_;
+      sequencer_queue_remove_song_SL (song);
     }
 }
-static void
-bse_sequencer_process_part_SL (BsePart         *part,
-                               gdouble          start_stamp,
-                               guint           start_tick,
-                               guint            bound, /* start_tick + n_ticks */
-                               gdouble          stamps_per_tick,
-                               BseMidiReceiver *midi_receiver,
-                               guint            midi_channel);
-static void
-bse_sequencer_process_track_SL (BseTrack        *track,
-                                gdouble          start_stamp,
-                                guint           start_tick,
-                                guint            bound, /* start_tick + n_ticks */
-                                gdouble          stamps_per_tick,
-                                BseMidiReceiver *midi_receiver)
+
+void
+Sequencer::process_track_SL (BseTrack *track, double start_stamp, uint start_tick,
+                             uint bound, /* start_tick + n_ticks */
+                             double stamps_per_tick, BseMidiReceiver *midi_receiver)
 {
-  guint start, next;
+  uint start, next;
   BsePart *part = bse_track_get_part_SL (track, start_tick, &start, &next);
   /* advance to first part */
   if (!part && next)
@@ -535,23 +488,18 @@ bse_sequencer_process_track_SL (BseTrack        *track,
       part_bound = next ? MIN (bound, next) : bound;
       part_bound -= start;
       if (!track->muted_SL)
-       bse_sequencer_process_part_SL (part, part_stamp,
-                                       part_start, part_bound, stamps_per_tick,
-                                       midi_receiver, track->midi_channel_SL);
+       process_part_SL (part, part_stamp, part_start, part_bound, stamps_per_tick, midi_receiver, 
track->midi_channel_SL);
       part = next ? bse_track_get_part_SL (track, next, &start, &next) : NULL;
     }
 }
-static void
-bse_sequencer_process_part_SL (BsePart         *part,
-                               gdouble          start_stamp,
-                               guint           start_tick,
-                               guint            tick_bound, /* start_tick + n_ticks */
-                               gdouble          stamps_per_tick,
-                               BseMidiReceiver *midi_receiver,
-                               guint            midi_channel)
+
+void
+Sequencer::process_part_SL (BsePart *part, double start_stamp, uint start_tick,
+                            uint tick_bound, /* start_tick + n_ticks */
+                            double stamps_per_tick, BseMidiReceiver *midi_receiver, uint midi_channel)
 {
   BsePartTickNode *node, *last;
-  guint channel;
+  uint channel;
   for (channel = 0; channel < part->n_channels; channel++)
     {
       BsePartEventNote *note = bse_part_note_channel_lookup_ge (&part->channels[channel], start_tick);
@@ -571,9 +519,9 @@ bse_sequencer_process_part_SL (BsePart         *part,
           if (CHECKTRACE())
             {
               SEQTRACE ("SEQ:note-on:  tick=%llu midinote=%-3d velocity=%02x freq=% 10f now=%llu",
-                        eon->delta_time,  note->note, bse_ftoi (note->velocity * 128), freq, 
Bse::TickStamp::current());
+                        uint64 (eon->delta_time),  note->note, bse_ftoi (note->velocity * 128), freq, 
Bse::TickStamp::current());
               SEQTRACE ("SEQ:note-off: tick=%llu midinote=%-3d velocity=%02x freq=% 10f now=%llu",
-                        eoff->delta_time, note->note, bse_ftoi (note->velocity * 128), freq, 
Bse::TickStamp::current());
+                        uint64 (eoff->delta_time), note->note, bse_ftoi (note->velocity * 128), freq, 
Bse::TickStamp::current());
             }
           note++;
         }
@@ -590,8 +538,34 @@ bse_sequencer_process_part_SL (BsePart         *part,
                                                        BseMidiSignalType (cev->ctype), cev->value);
           bse_midi_receiver_push_event (midi_receiver, event);
           SEQTRACE ("SEQ:control:  tick=%llu midisignal=%-3d value=%f now=%llu",
-                    event->delta_time, cev->ctype, cev->value, Bse::TickStamp::current());
+                    uint64 (event->delta_time), cev->ctype, cev->value, Bse::TickStamp::current());
         }
       node++;
     }
 }
+
+Sequencer::Sequencer() :
+  stamp_ (0), songs_ (NULL)
+{
+  stamp_ = Bse::TickStamp::current();
+  assert (stamp_ > 0);
+
+  if (pipe (sequencer_wake_up_pipe) < 0)
+    g_error ("failed to create sequencer wake-up pipe: %s", strerror (errno));
+  long flags = fcntl (sequencer_wake_up_pipe[0], F_GETFL, 0);
+  fcntl (sequencer_wake_up_pipe[0], F_SETFL, O_NONBLOCK | flags);
+  flags = fcntl (sequencer_wake_up_pipe[1], F_GETFL, 0);
+  fcntl (sequencer_wake_up_pipe[1], F_SETFL, O_NONBLOCK | flags);
+
+  poll_pool_ = new PollPool;
+}
+
+void
+Sequencer::_init_threaded ()
+{
+  assert (singleton_ == NULL);
+  singleton_ = new Sequencer();
+  singleton_->thread_ = std::thread (&Sequencer::sequencer_thread, singleton_); // FIXME: join on exit
+}
+
+} // Bse
diff --git a/bse/bsesequencer.hh b/bse/bsesequencer.hh
index b2b7ef7..52d8bd2 100644
--- a/bse/bsesequencer.hh
+++ b/bse/bsesequencer.hh
@@ -1,30 +1,53 @@
 // Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
-#ifndef __BSE_SSEQUENCER_H__
-#define __BSE_SSEQUENCER_H__
+#ifndef __BSE_SSEQUENCER_HH__
+#define __BSE_SSEQUENCER_HH__
 #include <bse/bsesong.hh>
 
-G_BEGIN_DECLS
+namespace Bse {
 
-typedef struct {
-  guint64               stamp; /* sequencer time (ahead of real time) */
-  SfiRing              *songs;
-} BseSequencer;
+using Rapicorn::Mutex;    // FIXME
+using Rapicorn::Cond;    // FIXME
 
-extern BirnetThread*    bse_sequencer_thread; // FIXME
+/** Note and MIDI sequencer.
+ * The sequencer processes notes from parts and MIDI input and generates events for the synthesis engine.
+ */
+class Sequencer {
+  static Sequencer *singleton_;
+  static Mutex      sequencer_mutex_;
+  struct PollPool;
+  uint64     stamp_;            // sequencer time (ahead of real time)
+  SfiRing   *songs_;
+  Cond       watch_cond_;
+  PollPool  *poll_pool_;
+  std::thread thread_;
+private:
+  void          sequencer_thread ();
+  bool          pool_poll_Lm     (int timeout_ms);
+  void          process_part_SL  (BsePart *part, double start_stamp, uint start_tick,
+                                  uint tick_bound, /* start_tick + n_ticks */
+                                  double stamps_per_tick, BseMidiReceiver *midi_receiver, uint midi_channel);
+  void          process_track_SL (BseTrack *track, double start_stamp, uint start_tick,
+                                  uint bound, /* start_tick + n_ticks */
+                                  double stamps_per_tick, BseMidiReceiver *midi_receiver);
+  void          process_song_SL  (BseSong *song, uint n_ticks);
+  bool          process_song_unlooped_SL (BseSong *song, uint n_ticks, bool force_active_tracks);
+  explicit      Sequencer       ();
+protected:
+  static void   _init_threaded  ();
+public:
+  void          wakeup          ();
+  void          add_io_watch    (uint n_pfds, const GPollFD *pfds, BseIOWatch watch_func, void *watch_data);
+  void          remove_io_watch (BseIOWatch watch_func, void *watch_data);
+  void          start_song     (BseSong *song, uint64 start_stamp);
+  void          remove_song    (BseSong *song);
+  bool          thread_lagging  (uint n_blocks);
+  static Mutex& sequencer_mutex ()      { return sequencer_mutex_; }
+  static Sequencer& instance    ()      { return *singleton_; }
+};
 
-void                   bse_sequencer_wakeup            ();
-void                   bse_sequencer_init_thread       (void);
-void                    bse_sequencer_add_io_watch      (guint           n_pfds,
-                                                         const GPollFD  *pfds,
-                                                         BseIOWatch      watch_func,
-                                                         gpointer        data);
-void                    bse_sequencer_remove_io_watch   (BseIOWatch      watch_func,
-                                                         gpointer        data);
-void                   bse_sequencer_start_song        (BseSong        *song,
-                                                         guint64         start_stamp);
-void                    bse_sequencer_remove_song      (BseSong        *song);
-gboolean                bse_sequencer_thread_lagging    (guint           n_blocks);
+#define BSE_SEQUENCER_LOCK()    (Bse::Sequencer::sequencer_mutex().lock())
+#define BSE_SEQUENCER_UNLOCK()  (Bse::Sequencer::sequencer_mutex().unlock())
 
-G_END_DECLS
+} // Bse
 
-#endif /* __BSE_SSEQUENCER_H__ */
+#endif // __BSE_SSEQUENCER_HH__
diff --git a/bse/bsesong.cc b/bse/bsesong.cc
index d10f9ae..d4e07cd 100644
--- a/bse/bsesong.cc
+++ b/bse/bsesong.cc
@@ -463,8 +463,8 @@ static void
 bse_song_reset (BseSource *source)
 {
   BseSong *self = BSE_SONG (source);
-  bse_sequencer_remove_song (self),
-  /* chain parent class' handler */
+  Bse::Sequencer::instance().remove_song (self);
+  // chain parent class' handler
   BSE_SOURCE_CLASS (parent_class)->reset (source);
   g_assert (self->sequencer_start_request_SL == 0);
   /* outside of sequencer reach, so no locks needed */
diff --git a/bse/bsetrack.cc b/bse/bsetrack.cc
index 9238023..5b1a1cc 100644
--- a/bse/bsetrack.cc
+++ b/bse/bsetrack.cc
@@ -6,7 +6,7 @@
 #include "bsewave.hh"
 #include "bsepart.hh"
 #include "bsebus.hh"
-#include "bsemain.hh"
+#include "bsesequencer.hh"
 #include "gslcommon.hh"
 #include "bsesubsynth.hh"
 #include "bseproject.hh"
diff --git a/drivers/bsemididevice-alsa.cc b/drivers/bsemididevice-alsa.cc
index 55febb9..eeafa39 100644
--- a/drivers/bsemididevice-alsa.cc
+++ b/drivers/bsemididevice-alsa.cc
@@ -242,7 +242,7 @@ bse_midi_device_alsa_open (BseDevice     *device,
               if (wn && snd_rawmidi_poll_descriptors (alsa->write_handle, pfds + alsa->total_pfds, wn) >= 0)
                 alsa->total_pfds += wn;
               if (alsa->total_pfds)
-                bse_sequencer_add_io_watch (alsa->total_pfds, (GPollFD*) pfds, alsa_midi_io_handler, alsa);
+                Bse::Sequencer::instance().add_io_watch (alsa->total_pfds, (GPollFD*) pfds, 
alsa_midi_io_handler, alsa);
             }
         }
     }
@@ -264,7 +264,7 @@ bse_midi_device_alsa_close (BseDevice *device)
   AlsaMidiHandle *alsa = (AlsaMidiHandle*) BSE_MIDI_DEVICE (device)->handle;
   BSE_MIDI_DEVICE (device)->handle = NULL;
   if (alsa->total_pfds)
-    bse_sequencer_remove_io_watch (alsa_midi_io_handler, alsa);
+    Bse::Sequencer::instance().remove_io_watch (alsa_midi_io_handler, alsa);
   if (alsa->read_handle)
     snd_rawmidi_close (alsa->read_handle);
   if (alsa->write_handle)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]