summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/butler.h33
-rw-r--r--libs/ardour/ardour/diskstream.h1
-rw-r--r--libs/ardour/ardour/midi_diskstream.h1
-rw-r--r--libs/ardour/ardour/session.h37
-rw-r--r--libs/ardour/audio_diskstream.cc30
-rw-r--r--libs/ardour/butler.cc365
-rw-r--r--libs/ardour/midi_diskstream.cc3
-rw-r--r--libs/ardour/session.cc8
-rw-r--r--libs/ardour/session_butler.cc343
-rw-r--r--libs/ardour/session_events.cc5
-rw-r--r--libs/ardour/session_export.cc8
-rw-r--r--libs/ardour/session_process.cc25
-rw-r--r--libs/ardour/session_state.cc5
-rw-r--r--libs/ardour/session_transport.cc32
14 files changed, 466 insertions, 430 deletions
diff --git a/libs/ardour/ardour/butler.h b/libs/ardour/ardour/butler.h
index 524651ba28..b88b820736 100644
--- a/libs/ardour/ardour/butler.h
+++ b/libs/ardour/ardour/butler.h
@@ -25,17 +25,48 @@
namespace ARDOUR {
+class Session;
+
class Butler {
public:
- Butler();
+ Butler(Session* session);
~Butler();
+ int start_thread();
+ void terminate_thread();
+ void schedule_transport_work();
+ void summon();
+ void stop();
+ void wait_until_finished();
+ bool transport_work_requested() const;
+
+ float read_data_rate() const; ///< in usec
+ float write_data_rate() const;
+
+ uint32_t audio_diskstream_buffer_size() const { return audio_dstream_buffer_size; }
+ uint32_t midi_diskstream_buffer_size() const { return midi_dstream_buffer_size; }
+
+ static void* _thread_work(void *arg);
+ void* thread_work();
+
+ struct Request {
+ enum Type {
+ Wake,
+ Run,
+ Pause,
+ Quit
+ };
+ };
+
+ Session* session;
pthread_t thread;
Glib::Mutex request_lock;
Glib::Cond paused;
bool should_run;
mutable gint should_do_transport_work;
int request_pipe[2];
+ uint32_t audio_dstream_buffer_size;
+ uint32_t midi_dstream_buffer_size;
};
} // namespace ARDOUR
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
index e135d1319b..8ae9dff6e3 100644
--- a/libs/ardour/ardour/diskstream.h
+++ b/libs/ardour/ardour/diskstream.h
@@ -163,6 +163,7 @@ class Diskstream : public SessionObject, public boost::noncopyable
protected:
friend class Session;
+ friend class Butler;
/* the Session is the only point of access for these because they require
* that the Session is "inactive" while they are called.
diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h
index 4dbabcc802..930d37330a 100644
--- a/libs/ardour/ardour/midi_diskstream.h
+++ b/libs/ardour/ardour/midi_diskstream.h
@@ -112,6 +112,7 @@ class MidiDiskstream : public Diskstream
protected:
friend class Session;
+ friend class Butler;
/* the Session is the only point of access for these
because they require that the Session is "inactive"
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index aa15f07e8d..baf7343496 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -298,14 +298,18 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
bool have_captured() const { return _have_captured; }
void refill_all_diskstream_buffers ();
- uint32_t audio_diskstream_buffer_size() const { return audio_dstream_buffer_size; }
- uint32_t midi_diskstream_buffer_size() const { return midi_dstream_buffer_size; }
+ Butler* butler() { return _butler; }
+ void butler_transport_work ();
uint32_t get_next_diskstream_id() const { return n_diskstreams(); }
uint32_t n_diskstreams() const;
+ void refresh_disk_space ();
+
typedef std::list<boost::shared_ptr<Diskstream> > DiskstreamList;
+ SerializedRCUManager<DiskstreamList>& diskstream_list() { return diskstreams; }
+
int load_routes (const XMLNode&, int);
boost::shared_ptr<RouteList> get_routes() const {
return routes.reader ();
@@ -924,9 +928,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
void reset_playback_load_min ();
void reset_capture_load_min ();
- float read_data_rate () const; // in usec
- float write_data_rate () const;
-
/* ranges */
void set_audio_range (std::list<AudioRange>&);
@@ -975,8 +976,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
protected:
friend class Diskstream;
- void stop_butler ();
- void wait_till_butler_finished();
protected:
friend class Route;
@@ -1188,18 +1187,7 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
bool pending_abort;
bool pending_auto_loop;
- Butler* butler;
-
- bool transport_work_requested() const;
-
- struct ButlerRequest {
- enum Type {
- Wake,
- Run,
- Pause,
- Quit
- };
- };
+ Butler* _butler;
enum PostTransportWork {
PostTransportStop = 0x1,
@@ -1232,13 +1220,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
PostTransportWork post_transport_work;
- void summon_butler ();
- void schedule_butler_transport_work ();
- int start_butler_thread ();
- void terminate_butler_thread ();
- static void *_butler_thread_work (void *arg);
- void* butler_thread_work ();
-
uint32_t cumulative_rf_motion;
uint32_t rf_scale;
@@ -1421,7 +1402,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
void non_realtime_locate ();
void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
void non_realtime_overwrite (int entry_request_count, bool& finished);
- void butler_transport_work ();
void post_transport ();
void engine_halted ();
void xrun_recovery ();
@@ -1439,8 +1419,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
SerializedRCUManager<DiskstreamList> diskstreams;
- uint32_t audio_dstream_buffer_size;
- uint32_t midi_dstream_buffer_size;
int load_diskstreams (const XMLNode&);
/* routes stuff */
@@ -1581,7 +1559,6 @@ class Session : public PBD::StatefulDestructible, public boost::noncopyable
Glib::Mutex space_lock;
std::string get_best_session_directory_for_new_source ();
- void refresh_disk_space ();
mutable gint _playback_load;
mutable gint _capture_load;
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index aa002130f8..661ddf139e 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -37,24 +37,25 @@
#include "pbd/enumwriter.h"
#include "pbd/stacktrace.h"
-#include "ardour/ardour.h"
-#include "ardour/audioengine.h"
#include "ardour/analyser.h"
+#include "ardour/ardour.h"
+#include "ardour/audio_buffer.h"
#include "ardour/audio_diskstream.h"
-#include "ardour/utils.h"
-#include "ardour/configuration.h"
+#include "ardour/audio_port.h"
+#include "ardour/audioengine.h"
#include "ardour/audiofilesource.h"
-#include "ardour/send.h"
-#include "ardour/region_factory.h"
#include "ardour/audioplaylist.h"
-#include "ardour/playlist_factory.h"
-#include "ardour/cycle_timer.h"
#include "ardour/audioregion.h"
-#include "ardour/audio_port.h"
-#include "ardour/source_factory.h"
-#include "ardour/audio_buffer.h"
-#include "ardour/session.h"
+#include "ardour/butler.h"
+#include "ardour/configuration.h"
+#include "ardour/cycle_timer.h"
#include "ardour/io.h"
+#include "ardour/playlist_factory.h"
+#include "ardour/region_factory.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
+#include "ardour/source_factory.h"
+#include "ardour/utils.h"
#include "i18n.h"
#include <locale.h>
@@ -2065,10 +2066,9 @@ AudioDiskstream::set_align_style_from_io ()
int
AudioDiskstream::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
{
-
while (how_many--) {
- c->push_back (new ChannelInfo(_session.audio_diskstream_buffer_size(), speed_buffer_size, wrap_buffer_size));
- interpolation.add_channel_to (_session.audio_diskstream_buffer_size(), speed_buffer_size);
+ c->push_back (new ChannelInfo(_session.butler()->audio_diskstream_buffer_size(), speed_buffer_size, wrap_buffer_size));
+ interpolation.add_channel_to (_session.butler()->audio_diskstream_buffer_size(), speed_buffer_size);
}
_n_channels.set(DataType::AUDIO, c->size());
diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc
index 33811ca387..3d43c11e38 100644
--- a/libs/ardour/butler.cc
+++ b/libs/ardour/butler.cc
@@ -17,12 +17,32 @@
*/
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include "pbd/error.h"
+#include "pbd/pthread_utils.h"
#include "ardour/butler.h"
+#include "ardour/crossfade.h"
+#include "ardour/io.h"
+#include "ardour/midi_diskstream.h"
+#include "ardour/session.h"
+
+#include "i18n.h"
+
+using namespace PBD;
+
+static float _read_data_rate;
+static float _write_data_rate;
namespace ARDOUR {
-Butler::Butler()
- : thread(0)
+Butler::Butler(Session* s)
+ : session(s)
+ , thread(0)
+ , audio_dstream_buffer_size(0)
+ , midi_dstream_buffer_size(0)
{
g_atomic_int_set(&should_do_transport_work, 0);
}
@@ -31,4 +51,345 @@ Butler::~Butler()
{
}
+int
+Butler::start_thread()
+{
+ const float rate = (float)session->frame_rate();
+
+ /* size is in Samples, not bytes */
+ audio_dstream_buffer_size = (uint32_t) floor (Config->get_audio_track_buffer_seconds() * rate);
+
+ /* size is in bytes
+ * XXX: Jack needs to tell us the MIDI buffer size
+ * (i.e. how many MIDI bytes we might see in a cycle)
+ */
+ midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * rate);
+
+ MidiDiskstream::set_readahead_frames ((nframes_t)(Config->get_midi_readahead() * rate));
+
+ Crossfade::set_buffer_size (audio_dstream_buffer_size);
+
+ should_run = false;
+
+ if (pipe (request_pipe)) {
+ error << string_compose(_("Cannot create transport request signal pipe (%1)"),
+ strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (request_pipe[0], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"),
+ strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (request_pipe[1], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"),
+ strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (pthread_create_and_store ("disk butler", &thread, 0, _thread_work, this)) {
+ error << _("Session: could not create butler thread") << endmsg;
+ return -1;
+ }
+
+ //pthread_detach (thread);
+
+ return 0;
+}
+
+void
+Butler::terminate_thread ()
+{
+ if (thread) {
+ void* status;
+ const char c = Request::Quit;
+ ::write (request_pipe[1], &c, 1);
+ pthread_join (thread, &status);
+ }
+}
+
+void *
+Butler::_thread_work (void* arg)
+{
+ PBD::notify_gui_about_thread_creation (pthread_self(), X_("Butler"));
+ return ((Butler *) arg)->thread_work ();
+}
+
+void *
+Butler::thread_work ()
+{
+ uint32_t err = 0;
+ int32_t bytes;
+ bool compute_io;
+ microseconds_t begin, end;
+
+ struct pollfd pfd[1];
+ bool disk_work_outstanding = false;
+ Session::DiskstreamList::iterator i;
+
+ while (true) {
+ pfd[0].fd = request_pipe[0];
+ pfd[0].events = POLLIN|POLLERR|POLLHUP;
+
+ if (poll (pfd, 1, (disk_work_outstanding ? 0 : -1)) < 0) {
+
+ if (errno == EINTR) {
+ continue;
+ }
+
+ error << string_compose (_("poll on butler request pipe failed (%1)"),
+ strerror (errno))
+ << endmsg;
+ break;
+ }
+
+ if (pfd[0].revents & ~POLLIN) {
+ error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg;
+ break;
+ }
+
+ if (pfd[0].revents & POLLIN) {
+
+ char req;
+
+ /* empty the pipe of all current requests */
+
+ while (1) {
+ size_t nread = ::read (request_pipe[0], &req, sizeof (req));
+ if (nread == 1) {
+
+ switch ((Request::Type) req) {
+
+ case Request::Wake:
+ break;
+
+ case Request::Run:
+ should_run = true;
+ break;
+
+ case Request::Pause:
+ should_run = false;
+ break;
+
+ case Request::Quit:
+ pthread_exit_pbd (0);
+ /*NOTREACHED*/
+ break;
+
+ default:
+ break;
+ }
+
+ } else if (nread == 0) {
+ break;
+ } else if (errno == EAGAIN) {
+ break;
+ } else {
+ fatal << _("Error reading from butler request pipe") << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+ }
+
+ if (transport_work_requested()) {
+ session->butler_transport_work ();
+ }
+
+ disk_work_outstanding = false;
+ bytes = 0;
+ compute_io = true;
+
+ begin = get_microseconds();
+
+ boost::shared_ptr<Session::DiskstreamList> dsl = session->diskstream_list().reader ();
+
+// for (i = dsl->begin(); i != dsl->end(); ++i) {
+// cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
+// }
+
+ for (i = dsl->begin(); !transport_work_requested() && should_run && i != dsl->end(); ++i) {
+
+ boost::shared_ptr<Diskstream> ds = *i;
+
+ /* don't read inactive tracks */
+
+ boost::shared_ptr<IO> io = ds->io();
+
+ if (io && !io->active()) {
+ continue;
+ }
+
+ switch (ds->do_refill ()) {
+ case 0:
+ bytes += ds->read_data_count();
+ break;
+ case 1:
+ bytes += ds->read_data_count();
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ compute_io = false;
+ error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
+ break;
+ }
+
+ }
+
+ if (i != dsl->end()) {
+ /* we didn't get to all the streams */
+ disk_work_outstanding = true;
+ }
+
+ if (!err && transport_work_requested()) {
+ continue;
+ }
+
+ if (compute_io) {
+ end = get_microseconds();
+ if (end - begin > 0) {
+ _read_data_rate = (float) bytes / (float) (end - begin);
+ } else {
+ _read_data_rate = 0; // infinity better
+ }
+ }
+
+ bytes = 0;
+ compute_io = true;
+ begin = get_microseconds();
+
+ for (i = dsl->begin(); !transport_work_requested() && should_run && i != dsl->end(); ++i) {
+ // cerr << "write behind for " << (*i)->name () << endl;
+
+ /* note that we still try to flush diskstreams attached to inactive routes
+ */
+
+ switch ((*i)->do_flush (ButlerContext)) {
+ case 0:
+ bytes += (*i)->write_data_count();
+ break;
+ case 1:
+ bytes += (*i)->write_data_count();
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ err++;
+ compute_io = false;
+ error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
+ /* don't break - try to flush all streams in case they
+ are split across disks.
+ */
+ }
+ }
+
+ if (err && session->actively_recording()) {
+ /* stop the transport and try to catch as much possible
+ captured state as we can.
+ */
+ session->request_stop ();
+ }
+
+ if (i != dsl->end()) {
+ /* we didn't get to all the streams */
+ disk_work_outstanding = true;
+ }
+
+ if (!err && transport_work_requested()) {
+ continue;
+ }
+
+ if (compute_io) {
+ // there are no apparent users for this calculation?
+ end = get_microseconds();
+ if(end-begin > 0) {
+ _write_data_rate = (float) bytes / (float) (end - begin);
+ } else {
+ _write_data_rate = 0; // Well, infinity would be better
+ }
+ }
+
+ if (!disk_work_outstanding) {
+ session->refresh_disk_space ();
+ }
+
+
+ {
+ Glib::Mutex::Lock lm (request_lock);
+
+ if (should_run && (disk_work_outstanding || transport_work_requested())) {
+// for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+// cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
+// }
+
+ continue;
+ }
+
+ paused.signal();
+ }
+ }
+
+ pthread_exit_pbd (0);
+ /*NOTREACHED*/
+ return (0);
+}
+
+void
+Butler::schedule_transport_work ()
+{
+ g_atomic_int_inc (&should_do_transport_work);
+ summon ();
+}
+
+void
+Butler::summon ()
+{
+ char c = Request::Run;
+ ::write (request_pipe[1], &c, 1);
+}
+
+void
+Butler::stop ()
+{
+ Glib::Mutex::Lock lm (request_lock);
+ char c = Request::Pause;
+ ::write (request_pipe[1], &c, 1);
+ paused.wait(request_lock);
+}
+
+void
+Butler::wait_until_finished ()
+{
+ Glib::Mutex::Lock lm (request_lock);
+ char c = Request::Wake;
+ ::write (request_pipe[1], &c, 1);
+ paused.wait(request_lock);
+}
+
+bool
+Butler::transport_work_requested () const
+{
+ return g_atomic_int_get(&should_do_transport_work);
+}
+
+float
+Butler::read_data_rate () const
+{
+ /* disk i/o in excess of 10000MB/sec indicate the buffer cache
+ in action. ignore it.
+ */
+ return _read_data_rate > 10485.7600000f ? 0.0f : _read_data_rate;
+}
+
+float
+Butler::write_data_rate () const
+{
+ /* disk i/o in excess of 10000MB/sec indicate the buffer cache
+ in action. ignore it.
+ */
+ return _write_data_rate > 10485.7600000f ? 0.0f : _write_data_rate;
+}
+
} // namespace ARDOUR
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
index 262a29d1df..1840b950fd 100644
--- a/libs/ardour/midi_diskstream.cc
+++ b/libs/ardour/midi_diskstream.cc
@@ -38,6 +38,7 @@
#include "ardour/ardour.h"
#include "ardour/audioengine.h"
+#include "ardour/butler.h"
#include "ardour/configuration.h"
#include "ardour/cycle_timer.h"
#include "ardour/io.h"
@@ -123,7 +124,7 @@ MidiDiskstream::init (Diskstream::Flag f)
set_block_size (_session.get_block_size());
allocate_temporary_buffers ();
- const size_t size = _session.midi_diskstream_buffer_size();
+ const size_t size = _session.butler()->midi_diskstream_buffer_size();
_playback_buf = new MidiRingBuffer<nframes_t>(size);
_capture_buf = new MidiRingBuffer<nframes_t>(size);
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index a42d764c9e..aef643efea 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -130,7 +130,7 @@ Session::Session (AudioEngine &eng,
_session_dir (new SessionDirectory(fullpath)),
pending_events (2048),
state_tree (0),
- butler (new Butler ()),
+ _butler (new Butler (this)),
post_transport_work((PostTransportWork)0),
_send_smpte_update (false),
midi_thread (pthread_t (0)),
@@ -216,7 +216,7 @@ Session::Session (AudioEngine &eng,
_session_dir ( new SessionDirectory(fullpath)),
pending_events (2048),
state_tree (0),
- butler (new Butler ()),
+ _butler (new Butler (this)),
post_transport_work((PostTransportWork)0),
_send_smpte_update (false),
midi_thread (pthread_t (0)),
@@ -356,7 +356,7 @@ Session::destroy ()
Stateful::loading_state_version = 0;
- terminate_butler_thread ();
+ _butler->terminate_thread ();
//terminate_midi_thread ();
if (click_data != default_click) {
@@ -3499,7 +3499,7 @@ Session::set_audition (boost::shared_ptr<Region> r)
{
pending_audition_region = r;
post_transport_work = PostTransportWork (post_transport_work | PostTransportAudition);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
}
void
diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc
index 2a217e5630..41b4851ce5 100644
--- a/libs/ardour/session_butler.cc
+++ b/libs/ardour/session_butler.cc
@@ -47,9 +47,6 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
-static float _read_data_rate;
-static float _write_data_rate;
-
/* XXX put this in the right place */
static inline uint32_t next_power_of_two (uint32_t n)
@@ -68,326 +65,13 @@ static inline uint32_t next_power_of_two (uint32_t n)
BUTLER THREAD
---------------------------------------------------------------------------*/
-int
-Session::start_butler_thread ()
-{
- /* size is in Samples, not bytes */
- audio_dstream_buffer_size = (uint32_t) floor (Config->get_audio_track_buffer_seconds() * (float) frame_rate());
-
- /* size is in bytes
- * XXX: Jack needs to tell us the MIDI buffer size
- * (i.e. how many MIDI bytes we might see in a cycle)
- */
- midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * (float)frame_rate());
- MidiDiskstream::set_readahead_frames ((nframes_t) (Config->get_midi_readahead() * (float) frame_rate()));
-
- Crossfade::set_buffer_size (audio_dstream_buffer_size);
-
- butler->should_run = false;
-
- if (pipe (butler->request_pipe)) {
- error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
- return -1;
- }
-
- if (fcntl (butler->request_pipe[0], F_SETFL, O_NONBLOCK)) {
- error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
- return -1;
- }
-
- if (fcntl (butler->request_pipe[1], F_SETFL, O_NONBLOCK)) {
- error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
- return -1;
- }
-
- if (pthread_create_and_store ("disk butler", &butler->thread, 0, _butler_thread_work, this)) {
- error << _("Session: could not create butler thread") << endmsg;
- return -1;
- }
-
- // pthread_detach (butler->thread);
-
- return 0;
-}
-
-void
-Session::terminate_butler_thread ()
-{
- if (butler->thread) {
- void* status;
- char c = ButlerRequest::Quit;
- ::write (butler->request_pipe[1], &c, 1);
- pthread_join (butler->thread, &status);
- }
-}
-
-void
-Session::schedule_butler_transport_work ()
-{
- g_atomic_int_inc (&butler->should_do_transport_work);
- summon_butler ();
-}
-
void
Session::schedule_curve_reallocation ()
{
post_transport_work = PostTransportWork (post_transport_work | PostTransportCurveRealloc);
- schedule_butler_transport_work ();
-}
-
-void
-Session::summon_butler ()
-{
- char c = ButlerRequest::Run;
- ::write (butler->request_pipe[1], &c, 1);
- // PBD::stacktrace (cerr);
-}
-
-void
-Session::stop_butler ()
-{
- Glib::Mutex::Lock lm (butler->request_lock);
- char c = ButlerRequest::Pause;
- ::write (butler->request_pipe[1], &c, 1);
- butler->paused.wait(butler->request_lock);
-}
-
-void
-Session::wait_till_butler_finished ()
-{
- Glib::Mutex::Lock lm (butler->request_lock);
- char c = ButlerRequest::Wake;
- ::write (butler->request_pipe[1], &c, 1);
- butler->paused.wait(butler->request_lock);
-}
-
-void *
-Session::_butler_thread_work (void* arg)
-{
- PBD::notify_gui_about_thread_creation (pthread_self(), X_("Butler"));
- return ((Session *) arg)->butler_thread_work ();
- return 0;
-}
-
-void *
-Session::butler_thread_work ()
-{
- uint32_t err = 0;
- int32_t bytes;
- bool compute_io;
- microseconds_t begin, end;
-
- struct pollfd pfd[1];
- bool disk_work_outstanding = false;
- DiskstreamList::iterator i;
-
- while (true) {
- pfd[0].fd = butler->request_pipe[0];
- pfd[0].events = POLLIN|POLLERR|POLLHUP;
-
- if (poll (pfd, 1, (disk_work_outstanding ? 0 : -1)) < 0) {
-
- if (errno == EINTR) {
- continue;
- }
-
- error << string_compose (_("poll on butler request pipe failed (%1)"),
- strerror (errno))
- << endmsg;
- break;
- }
-
- if (pfd[0].revents & ~POLLIN) {
- error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg;
- break;
- }
-
- if (pfd[0].revents & POLLIN) {
-
- char req;
-
- /* empty the pipe of all current requests */
-
- while (1) {
- size_t nread = ::read (butler->request_pipe[0], &req, sizeof (req));
- if (nread == 1) {
-
- switch ((ButlerRequest::Type) req) {
-
- case ButlerRequest::Wake:
- break;
-
- case ButlerRequest::Run:
- butler->should_run = true;
- break;
-
- case ButlerRequest::Pause:
- butler->should_run = false;
- break;
-
- case ButlerRequest::Quit:
- pthread_exit_pbd (0);
- /*NOTREACHED*/
- break;
-
- default:
- break;
- }
-
- } else if (nread == 0) {
- break;
- } else if (errno == EAGAIN) {
- break;
- } else {
- fatal << _("Error reading from butler request pipe") << endmsg;
- /*NOTREACHED*/
- }
- }
- }
-
- if (transport_work_requested()) {
- butler_transport_work ();
- }
-
- disk_work_outstanding = false;
- bytes = 0;
- compute_io = true;
-
- begin = get_microseconds();
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
-
-// for (i = dsl->begin(); i != dsl->end(); ++i) {
-// cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
-// }
-
- for (i = dsl->begin(); !transport_work_requested() && butler->should_run && i != dsl->end(); ++i) {
-
- boost::shared_ptr<Diskstream> ds = *i;
-
- /* don't read inactive tracks */
-
- boost::shared_ptr<IO> io = ds->io();
-
- if (io && !io->active()) {
- continue;
- }
-
- switch (ds->do_refill ()) {
- case 0:
- bytes += ds->read_data_count();
- break;
- case 1:
- bytes += ds->read_data_count();
- disk_work_outstanding = true;
- break;
-
- default:
- compute_io = false;
- error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
- break;
- }
-
- }
-
- if (i != dsl->end()) {
- /* we didn't get to all the streams */
- disk_work_outstanding = true;
- }
-
- if (!err && transport_work_requested()) {
- continue;
- }
-
- if (compute_io) {
- end = get_microseconds();
- if(end-begin > 0) {
- _read_data_rate = (float) bytes / (float) (end - begin);
- } else { _read_data_rate = 0; // infinity better
- }
- }
-
- bytes = 0;
- compute_io = true;
- begin = get_microseconds();
-
- for (i = dsl->begin(); !transport_work_requested() && butler->should_run && i != dsl->end(); ++i) {
- // cerr << "write behind for " << (*i)->name () << endl;
-
- /* note that we still try to flush diskstreams attached to inactive routes
- */
-
- switch ((*i)->do_flush (ButlerContext)) {
- case 0:
- bytes += (*i)->write_data_count();
- break;
- case 1:
- bytes += (*i)->write_data_count();
- disk_work_outstanding = true;
- break;
-
- default:
- err++;
- compute_io = false;
- error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
- /* don't break - try to flush all streams in case they
- are split across disks.
- */
- }
- }
-
- if (err && actively_recording()) {
- /* stop the transport and try to catch as much possible
- captured state as we can.
- */
- request_stop ();
- }
-
- if (i != dsl->end()) {
- /* we didn't get to all the streams */
- disk_work_outstanding = true;
- }
-
- if (!err && transport_work_requested()) {
- continue;
- }
-
- if (compute_io) {
- // there are no apparent users for this calculation?
- end = get_microseconds();
- if(end-begin > 0) {
- _write_data_rate = (float) bytes / (float) (end - begin);
- } else {
- _write_data_rate = 0; // Well, infinity would be better
- }
- }
-
- if (!disk_work_outstanding) {
- refresh_disk_space ();
- }
-
-
- {
- Glib::Mutex::Lock lm (butler->request_lock);
-
- if (butler->should_run && (disk_work_outstanding || transport_work_requested())) {
-// for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
-// cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
-// }
-
- continue;
- }
-
- butler->paused.signal();
- }
- }
-
- pthread_exit_pbd (0);
- /*NOTREACHED*/
- return (0);
+ _butler->schedule_transport_work ();
}
-
void
Session::request_overwrite_buffer (Diskstream* stream)
{
@@ -417,25 +101,7 @@ Session::overwrite_some_buffers (Diskstream* ds)
}
post_transport_work = PostTransportWork (post_transport_work | PostTransportOverWrite);
- schedule_butler_transport_work ();
-}
-
-float
-Session::read_data_rate () const
-{
- /* disk i/o in excess of 10000MB/sec indicate the buffer cache
- in action. ignore it.
- */
- return _read_data_rate > 10485.7600000f ? 0.0f : _read_data_rate;
-}
-
-float
-Session::write_data_rate () const
-{
- /* disk i/o in excess of 10000MB/sec indicate the buffer cache
- in action. ignore it.
- */
- return _write_data_rate > 10485.7600000f ? 0.0f : _write_data_rate;
+ _butler->schedule_transport_work ();
}
uint32_t
@@ -474,8 +140,3 @@ Session::reset_playback_load_min ()
g_atomic_int_set (&_playback_load_min, 100);
}
-bool
-Session::transport_work_requested () const
-{
- return g_atomic_int_get(&butler->should_do_transport_work);
-}
diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc
index cf6e9f9c51..716ade0075 100644
--- a/libs/ardour/session_events.cc
+++ b/libs/ardour/session_events.cc
@@ -26,8 +26,9 @@
#include <glibmm/thread.h>
#include "ardour/ardour.h"
-#include "ardour/session.h"
#include "ardour/audio_diskstream.h"
+#include "ardour/butler.h"
+#include "ardour/session.h"
#include "i18n.h"
@@ -425,7 +426,7 @@ Session::process_event (Event* ev)
case Event::InputConfigurationChange:
post_transport_work = PostTransportWork (post_transport_work | PostTransportInputChange);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
break;
case Event::SetAudioRange:
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 764898875b..3eda11bab7 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -65,7 +65,7 @@ Session::pre_export ()
{
get_export_status (); // Init export_status
- wait_till_butler_finished ();
+ _butler->wait_until_finished ();
/* take everyone out of awrite to avoid disasters */
@@ -138,7 +138,7 @@ Session::start_audio_export (nframes_t position, bool realtime)
set_transport_speed (1.0, false);
butler_transport_work ();
- g_atomic_int_set (&butler->should_do_transport_work, 0);
+ g_atomic_int_set (&_butler->should_do_transport_work, 0);
post_transport ();
/* we are ready to go ... */
@@ -173,7 +173,7 @@ Session::process_export (nframes_t nframes)
we're running faster than realtime c/o JACK.
*/
- wait_till_butler_finished ();
+ _butler->wait_until_finished ();
}
/* do the usual stuff */
@@ -211,7 +211,7 @@ Session::stop_audio_export ()
*/
realtime_stop (true);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
if (!export_status->aborted()) {
ExportReadFinished ();
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
index 41f3a52d7d..8b4d88b704 100644
--- a/libs/ardour/session_process.cc
+++ b/libs/ardour/session_process.cc
@@ -27,14 +27,15 @@
#include <glibmm/thread.h>
#include "ardour/ardour.h"
-#include "ardour/session.h"
-#include "ardour/timestamps.h"
#include "ardour/audio_diskstream.h"
#include "ardour/audioengine.h"
-#include "ardour/slave.h"
#include "ardour/auditioner.h"
-#include "ardour/cycles.h"
+#include "ardour/butler.h"
#include "ardour/cycle_timer.h"
+#include "ardour/cycles.h"
+#include "ardour/session.h"
+#include "ardour/slave.h"
+#include "ardour/timestamps.h"
#include "midi++/manager.h"
@@ -64,7 +65,7 @@ Session::process (nframes_t nframes)
}
if (non_realtime_work_pending()) {
- if (!transport_work_requested ()) {
+ if (!_butler->transport_work_requested ()) {
post_transport ();
}
}
@@ -449,8 +450,9 @@ Session::process_with_events (nframes_t nframes)
} /* implicit release of route lock */
- if (session_needs_butler)
- summon_butler ();
+ if (session_needs_butler) {
+ _butler->summon ();
+ }
}
void
@@ -766,7 +768,7 @@ Session::follow_slave_silently (nframes_t nframes, float slave_speed)
commit_diskstreams (nframes, need_butler);
if (need_butler) {
- summon_butler ();
+ _butler->summon ();
}
int32_t frames_moved = (int32_t) floor (_transport_speed * nframes);
@@ -867,8 +869,9 @@ Session::process_without_events (nframes_t nframes)
maybe_stop (stop_limit);
check_declick_out ();
- if (session_needs_butler)
- summon_butler ();
+ if (session_needs_butler) {
+ _butler->summon ();
+ }
}
/** Process callback used when the auditioner is active.
@@ -889,7 +892,7 @@ Session::process_audition (nframes_t nframes)
/* run the auditioner, and if it says we need butler service, ask for it */
if (auditioner->play_audition (nframes) > 0) {
- summon_butler ();
+ _butler->summon ();
}
/* handle pending events */
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 4fc35fc964..4a5d974563 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -71,6 +71,7 @@
#include "ardour/audioregion.h"
#include "ardour/auditioner.h"
#include "ardour/buffer.h"
+#include "ardour/butler.h"
#include "ardour/configuration.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/crossfade.h"
@@ -184,8 +185,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
pending_locate_frame = 0;
pending_locate_roll = false;
pending_locate_flush = false;
- audio_dstream_buffer_size = 0;
- midi_dstream_buffer_size = 0;
state_was_pending = false;
set_next_event ();
outbound_mtc_smpte_frame = 0;
@@ -297,7 +296,7 @@ Session::second_stage_init (bool new_session)
remove_empty_sounds ();
}
- if (start_butler_thread()) {
+ if (_butler->start_thread()) {
return -1;
}
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
index 755202718b..a4e4a81c95 100644
--- a/libs/ardour/session_transport.cc
+++ b/libs/ardour/session_transport.cc
@@ -194,7 +194,7 @@ Session::butler_transport_work ()
boost::shared_ptr<RouteList> r = routes.reader ();
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- int on_entry = g_atomic_int_get (&butler->should_do_transport_work);
+ int on_entry = g_atomic_int_get (&_butler->should_do_transport_work);
finished = true;
if (post_transport_work & PostTransportCurveRealloc) {
@@ -227,9 +227,9 @@ Session::butler_transport_work ()
if (!(*i)->hidden()) {
(*i)->non_realtime_locate (_transport_frame);
}
- if (on_entry != g_atomic_int_get (&butler->should_do_transport_work)) {
+ if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
/* new request, stop seeking, and start again */
- g_atomic_int_dec_and_test (&butler->should_do_transport_work);
+ g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
goto restart;
}
}
@@ -243,7 +243,7 @@ Session::butler_transport_work ()
if (post_transport_work & PostTransportStop) {
non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
if (!finished) {
- g_atomic_int_dec_and_test (&butler->should_do_transport_work);
+ g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
goto restart;
}
}
@@ -251,7 +251,7 @@ Session::butler_transport_work ()
if (post_transport_work & PostTransportOverWrite) {
non_realtime_overwrite (on_entry, finished);
if (!finished) {
- g_atomic_int_dec_and_test (&butler->should_do_transport_work);
+ g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
goto restart;
}
}
@@ -260,7 +260,7 @@ Session::butler_transport_work ()
non_realtime_set_audition ();
}
- g_atomic_int_dec_and_test (&butler->should_do_transport_work);
+ g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
}
void
@@ -282,7 +282,7 @@ Session::non_realtime_overwrite (int on_entry, bool& finished)
if ((*i)->pending_overwrite) {
(*i)->overwrite_existing_buffers ();
}
- if (on_entry != g_atomic_int_get (&butler->should_do_transport_work)) {
+ if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
return;
}
@@ -432,7 +432,7 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
if (!(*i)->hidden()) {
(*i)->non_realtime_locate (_transport_frame);
}
- if (on_entry != g_atomic_int_get (&butler->should_do_transport_work)) {
+ if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
/* we will be back */
return;
@@ -712,7 +712,7 @@ Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool w
post_transport_work = PostTransportWork (post_transport_work | PostTransportRoll);
}
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
} else {
@@ -906,7 +906,7 @@ Session::set_transport_speed (double speed, bool abort)
}
if (post_transport_work & (PostTransportSpeed|PostTransportReverse)) {
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
}
}
}
@@ -951,7 +951,7 @@ Session::stop_transport (bool abort)
}
realtime_stop (abort);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
}
void
@@ -1133,7 +1133,7 @@ Session::set_slave_source (SlaveSource src)
if (non_rt_required) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
}
set_dirty();
@@ -1143,7 +1143,7 @@ void
Session::reverse_diskstream_buffers ()
{
post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
}
void
@@ -1151,7 +1151,7 @@ Session::set_diskstream_speed (Diskstream* stream, double speed)
{
if (stream->realtime_set_speed (speed, false)) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
- schedule_butler_transport_work ();
+ _butler->schedule_transport_work ();
set_dirty ();
}
}
@@ -1278,9 +1278,9 @@ Session::engine_halted ()
the picture.
*/
- g_atomic_int_set (&butler->should_do_transport_work, 0);
+ g_atomic_int_set (&_butler->should_do_transport_work, 0);
post_transport_work = PostTransportWork (0);
- stop_butler ();
+ _butler->stop ();
realtime_stop (false);
non_realtime_stop (false, 0, ignored);