#include "pbd/error.h"
#include "pbd/pthread_utils.h"
-#include "ardour/debug.h"
+
#include "ardour/butler.h"
+#include "ardour/debug.h"
+#include "ardour/disk_io.h"
+#include "ardour/disk_reader.h"
#include "ardour/io.h"
-#include "ardour/midi_diskstream.h"
#include "ardour/session.h"
#include "ardour/track.h"
#include "ardour/auditioner.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace PBD;
g_atomic_int_set(&should_do_transport_work, 0);
SessionEvent::pool->set_trash (&pool_trash);
+ /* catch future changes to parameters */
Config->ParameterChanged.connect_same_thread (*this, boost::bind (&Butler::config_changed, this, _1));
}
terminate_thread ();
}
+void
+Butler::map_parameters ()
+{
+ /* use any current ones that we care about */
+ boost::function<void (std::string)> ff (boost::bind (&Butler::config_changed, this, _1));
+ Config->map_parameters (ff);
+}
+
void
Butler::config_changed (std::string p)
{
if (p == "playback-buffer-seconds") {
- /* size is in Samples, not bytes */
- audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
_session.adjust_playback_buffering ();
+ if (Config->get_buffering_preset() == Custom) {
+ /* size is in Samples, not bytes */
+ audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
+ _session.adjust_playback_buffering ();
+ } else {
+#ifndef NDEBUG
+ std::cerr << "Skip explicit buffer seconds, preset in use\n";
+#endif
+ }
} else if (p == "capture-buffer-seconds") {
+ if (Config->get_buffering_preset() == Custom) {
+ audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
+ _session.adjust_capture_buffering ();
+ } else {
+#ifndef NDEBUG
+ std::cerr << "Skip explicit buffer seconds, preset in use\n";
+#endif
+ }
+ } else if (p == "buffering-preset") {
+ DiskIOProcessor::set_buffering_parameters (Config->get_buffering_preset());
audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate());
+ audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * _session.frame_rate());
_session.adjust_capture_buffering ();
+ _session.adjust_playback_buffering ();
} else if (p == "midi-readahead") {
- MidiDiskstream::set_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * _session.frame_rate()));
+ DiskReader::set_midi_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * _session.frame_rate()));
}
}
int
Butler::start_thread()
{
- const float rate = (float)_session.frame_rate();
+ // set up capture and playback buffering
+ DiskIOProcessor::set_buffering_parameters (Config->get_buffering_preset());
/* size is in Samples, not bytes */
+ const float rate = (float)_session.frame_rate();
audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * rate);
audio_dstream_playback_buffer_size = (uint32_t) floor (Config->get_audio_playback_buffer_seconds() * rate);
/* size is in bytes
- * XXX: Jack needs to tell us the MIDI buffer size
+ * XXX: AudioEngine 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 ((framecnt_t) (Config->get_midi_readahead() * rate));
+ DiskReader::set_midi_readahead_frames ((framecnt_t) (Config->get_midi_readahead() * rate));
should_run = false;
//pthread_detach (thread);
have_thread = true;
+
+ // we are ready to request buffer adjustments
+ _session.adjust_capture_buffering ();
+ _session.adjust_playback_buffering ();
+
return 0;
}
}
}
-
+
restart:
DEBUG_TRACE (DEBUG::Butler, "at restart for disk work\n");
disk_work_outstanding = false;
if (transport_work_requested()) {
DEBUG_TRACE (DEBUG::Butler, string_compose ("do transport work @ %1\n", g_get_monotonic_time()));
_session.butler_transport_work ();
- DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttransport work complete @ %1\n", g_get_monotonic_time()));
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttransport work complete @ %1, twr = %2\n", g_get_monotonic_time(), transport_work_requested()));
}
frameoffset_t audition_seek;
- if (should_run && _session.is_auditioning()
- && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
+ if (should_run && _session.is_auditioning() && (audition_seek = _session.the_auditioner()->seek_frame()) >= 0) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (_session.the_auditioner());
DEBUG_TRACE (DEBUG::Butler, "seek the auditioner\n");
tr->seek(audition_seek);
+ tr->do_refill ();
_session.the_auditioner()->seek_response(audition_seek);
}
RouteList rl_with_auditioner = *rl;
rl_with_auditioner.push_back (_session.the_auditioner());
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("butler starts refill loop, twr = %1\n", transport_work_requested()));
+
for (i = rl_with_auditioner.begin(); !transport_work_requested() && should_run && i != rl_with_auditioner.end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (io && !io->active()) {
/* don't read inactive tracks */
- DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
+ // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
continue;
}
- DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
+ // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler refills %1, playback load = %2\n", tr->name(), tr->playback_buffer_load()));
switch (tr->do_refill ()) {
case 0:
- DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
+ //DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
break;
-
+
case 1:
DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill unfinished %1\n", tr->name()));
disk_work_outstanding = true;
goto restart;
}
- for (i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
- // cerr << "write behind for " << (*i)->name () << endl;
-
- boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-
- if (!tr) {
- continue;
- }
-
- /* note that we still try to flush diskstreams attached to inactive routes
- */
-
- gint64 before, after;
- int ret;
-
- DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
- before = g_get_monotonic_time ();
- ret = tr->do_flush (ButlerContext);
- after = g_get_monotonic_time ();
- switch (ret) {
- case 0:
- DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1, %2 usecs\n", tr->name(), after - before));
- break;
-
- case 1:
- DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1, %2 usecs\n", tr->name(), after - before));
- disk_work_outstanding = true;
- break;
-
- default:
- err++;
- error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
- std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
- /* don't break - try to flush all streams in case they
- are split across disks.
- */
- }
- }
+ disk_work_outstanding = disk_work_outstanding || flush_tracks_to_disk_normal (rl, err);
if (err && _session.actively_recording()) {
/* stop the transport and try to catch as much possible
_session.request_stop ();
}
- if (i != rl->begin() && i != rl->end()) {
- /* we didn't get to all the streams */
- DEBUG_TRACE (DEBUG::Butler, "not all tracks processed, will need to go back for more\n");
- disk_work_outstanding = true;
- }
-
if (!err && transport_work_requested()) {
DEBUG_TRACE (DEBUG::Butler, "transport work requested during flush, back to restart\n");
goto restart;
_session.refresh_disk_space ();
}
-
{
Glib::Threads::Mutex::Lock lm (request_lock);
return (0);
}
+bool
+Butler::flush_tracks_to_disk_normal (boost::shared_ptr<RouteList> rl, uint32_t& errors)
+{
+ bool disk_work_outstanding = false;
+
+ for (RouteList::iterator i = rl->begin(); !transport_work_requested() && should_run && i != rl->end(); ++i) {
+
+ // cerr << "write behind for " << (*i)->name () << endl;
+
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+
+ if (!tr) {
+ continue;
+ }
+
+ /* note that we still try to flush diskstreams attached to inactive routes
+ */
+
+ int ret;
+
+ // DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
+ ret = tr->do_flush (ButlerContext, false);
+ switch (ret) {
+ case 0:
+ //DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
+ break;
+
+ case 1:
+ //DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ errors++;
+ error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
+ std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
+ /* don't break - try to flush all streams in case they
+ are split across disks.
+ */
+ }
+ }
+
+ return disk_work_outstanding;
+}
+
+bool
+Butler::flush_tracks_to_disk_after_locate (boost::shared_ptr<RouteList> rl, uint32_t& errors)
+{
+ bool disk_work_outstanding = false;
+
+ /* almost the same as the "normal" version except that we do not test
+ * for transport_work_requested() and we force flushes.
+ */
+
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+
+ // cerr << "write behind for " << (*i)->name () << endl;
+
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+
+ if (!tr) {
+ continue;
+ }
+
+ /* note that we still try to flush diskstreams attached to inactive routes
+ */
+
+ int ret;
+
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
+ ret = tr->do_flush (ButlerContext, true);
+ switch (ret) {
+ case 0:
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
+ break;
+
+ case 1:
+ DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ errors++;
+ error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
+ std::cerr << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << std::endl;
+ /* don't break - try to flush all streams in case they
+ are split across disks.
+ */
+ }
+ }
+
+ return disk_work_outstanding;
+}
+
void
Butler::schedule_transport_work ()
{
+ DEBUG_TRACE (DEBUG::Butler, "requesting more transport work\n");
g_atomic_int_inc (&should_do_transport_work);
summon ();
}