X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fbutler.cc;h=62f3a525ea82582c8dc866a627d0ef715a0de2e3;hb=f269e39115934c8e48dbc66e495d8cfdba1e70f0;hp=a89e8295d282944723d733d6dc236973ab59c1ad;hpb=8e3ec4b9abb10b9e3b6289891c88a2156a26c383;p=ardour.git diff --git a/libs/ardour/butler.cc b/libs/ardour/butler.cc index a89e8295d2..62f3a525ea 100644 --- a/libs/ardour/butler.cc +++ b/libs/ardour/butler.cc @@ -54,6 +54,7 @@ Butler::Butler(Session& s) 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)); } @@ -62,30 +63,57 @@ Butler::~Butler() terminate_thread (); } +void +Butler::map_parameters () +{ + /* use any current ones that we care about */ + boost::function 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 (); - } else if (p == "capture-buffer-seconds") { - audio_dstream_capture_buffer_size = (uint32_t) floor (Config->get_audio_capture_buffer_seconds() * _session.frame_rate()); - _session.adjust_capture_buffering (); - } + if (p == "playback-buffer-seconds") { + _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 { + std::cerr << "Skip explicit buffer seconds, preset in use\n"; + } + } 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 { + std::cerr << "Skip explicit buffer seconds, preset in use\n"; + } + } else if (p == "buffering-preset") { + Diskstream::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())); + } } int Butler::start_thread() { - const float rate = (float)_session.frame_rate(); + // set up capture and playback buffering + Diskstream::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); @@ -101,6 +129,11 @@ Butler::start_thread() //pthread_detach (thread); have_thread = true; + + // we are ready to request buffer adjustments + _session.adjust_capture_buffering (); + _session.adjust_playback_buffering (); + return 0; } @@ -165,7 +198,7 @@ Butler::thread_work () } } - + restart: DEBUG_TRACE (DEBUG::Butler, "at restart for disk work\n"); disk_work_outstanding = false; @@ -182,6 +215,7 @@ Butler::thread_work () boost::shared_ptr tr = boost::dynamic_pointer_cast (_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); } @@ -210,7 +244,7 @@ Butler::thread_work () case 0: 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; @@ -234,44 +268,7 @@ Butler::thread_work () 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 tr = boost::dynamic_pointer_cast (*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 = flush_tracks_to_disk_normal (rl, err); if (err && _session.actively_recording()) { /* stop the transport and try to catch as much possible @@ -281,12 +278,6 @@ Butler::thread_work () _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; @@ -296,7 +287,6 @@ Butler::thread_work () _session.refresh_disk_space (); } - { Glib::Threads::Mutex::Lock lm (request_lock); @@ -317,6 +307,100 @@ Butler::thread_work () return (0); } +bool +Butler::flush_tracks_to_disk_normal (boost::shared_ptr 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 tr = boost::dynamic_pointer_cast (*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 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 tr = boost::dynamic_pointer_cast (*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 () {