#include <cerrno>
#include <unistd.h>
-#include <sigc++/bind.h>
-#include <sigc++/retype.h>
#include "pbd/undo.h"
#include "pbd/error.h"
-#include <glibmm/thread.h>
+#include "pbd/enumwriter.h"
#include "pbd/pthread_utils.h"
#include "pbd/memento_command.h"
-#include "pbd/stacktrace.h"
#include "midi++/mmc.h"
#include "midi++/port.h"
#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
#include "ardour/butler.h"
+#include "ardour/debug.h"
#include "ardour/location.h"
#include "ardour/session.h"
#include "ardour/slave.h"
using namespace std;
using namespace ARDOUR;
-using namespace sigc;
using namespace PBD;
+void
+Session::add_post_transport_work (PostTransportWork ptw)
+{
+ PostTransportWork oldval;
+ PostTransportWork newval;
+ int tries = 0;
+
+ while (tries < 8) {
+ oldval = (PostTransportWork) g_atomic_int_get (&_post_transport_work);
+ newval = PostTransportWork (oldval | ptw);
+ if (g_atomic_int_compare_and_exchange (&_post_transport_work, oldval, newval)) {
+ /* success */
+ return;
+ }
+ }
+
+ error << "Could not set post transport work! Crazy thread madness, call the programmers" << endmsg;
+}
+
void
Session::request_input_change_handling ()
{
if (!(_state_of_the_state & (InitialConnecting|Deletion))) {
- Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0);
+ SessionEvent* ev = new SessionEvent (SessionEvent::InputConfigurationChange, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
queue_event (ev);
}
}
void
-Session::request_slave_source (SlaveSource src)
+Session::request_sync_source (Slave* new_slave)
{
- Event* ev = new Event (Event::SetSlaveSource, Event::Add, Event::Immediate, 0, 0.0);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetSyncSource, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
+ bool seamless;
+
+ seamless = Config->get_seamless_loop ();
- if (src == JACK) {
- /* could set_seamless_loop() be disposed of entirely?*/
+ if (dynamic_cast<JACK_Slave*>(new_slave)) {
+ /* JACK cannot support seamless looping at present */
Config->set_seamless_loop (false);
+ } else {
+ /* reset to whatever the value was before we last switched slaves */
+ Config->set_seamless_loop (_was_seamless);
}
- ev->slave = src;
+ /* save value of seamless from before the switch */
+ _was_seamless = seamless;
+
+ ev->slave = new_slave;
queue_event (ev);
}
void
Session::request_transport_speed (double speed)
{
- Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, speed);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed);
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport speed = %1\n", speed));
queue_event (ev);
}
void
Session::request_diskstream_speed (Diskstream& ds, double speed)
{
- Event* ev = new Event (Event::SetDiskstreamSpeed, Event::Add, Event::Immediate, 0, speed);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetDiskstreamSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed);
ev->set_ptr (&ds);
queue_event (ev);
}
void
-Session::request_stop (bool abort)
+Session::request_stop (bool abort, bool clear_state)
{
- Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0, abort);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0, abort, clear_state);
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request transport stop, abort = %1, clear state = %2\n", abort, clear_state));
queue_event (ev);
}
void
Session::request_locate (nframes_t target_frame, bool with_roll)
{
- Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, false);
+ SessionEvent *ev = new SessionEvent (with_roll ? SessionEvent::LocateRoll : SessionEvent::Locate, SessionEvent::Add, SessionEvent::Immediate, target_frame, 0, false);
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request locate to %1\n", target_frame));
queue_event (ev);
}
void
-Session::force_locate (nframes_t target_frame, bool with_roll)
+Session::force_locate (nframes64_t target_frame, bool with_roll)
{
- Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, true);
+ SessionEvent *ev = new SessionEvent (with_roll ? SessionEvent::LocateRoll : SessionEvent::Locate, SessionEvent::Add, SessionEvent::Immediate, target_frame, 0, true);
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request forced locate to %1\n", target_frame));
queue_event (ev);
}
void
Session::request_play_loop (bool yn, bool leave_rolling)
{
- Event* ev;
+ SessionEvent* ev;
Location *location = _locations.auto_loop_location();
if (location == 0 && yn) {
return;
}
- ev = new Event (Event::SetLoop, Event::Add, Event::Immediate, 0, (leave_rolling ? 1.0 : 0.0), yn);
+ ev = new SessionEvent (SessionEvent::SetLoop, SessionEvent::Add, SessionEvent::Immediate, 0, (leave_rolling ? 1.0 : 0.0), yn);
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request set loop = %1, leave rolling ? %2\n", yn, leave_rolling));
queue_event (ev);
if (!leave_rolling && !yn && Config->get_seamless_loop() && transport_rolling()) {
}
void
-Session::realtime_stop (bool abort)
+Session::request_play_range (list<AudioRange>* range, bool leave_rolling)
+{
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetPlayAudioRange, SessionEvent::Add, SessionEvent::Immediate, 0, (leave_rolling ? 1.0 : 0.0));
+ if (range) {
+ ev->audio_range = *range;
+ } else {
+ ev->audio_range.clear ();
+ }
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Request play range, leave rolling ? %1\n", leave_rolling));
+ queue_event (ev);
+}
+
+void
+Session::realtime_stop (bool abort, bool clear_state)
{
+ DEBUG_TRACE (DEBUG::Transport, "realtime stop\n");
+ PostTransportWork todo = PostTransportWork (0);
+
/* assume that when we start, we'll be moving forwards */
// FIXME: where should this really be? [DR]
deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
if (_transport_speed < 0.0f) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportStop | PostTransportReverse);
+ todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
} else {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportStop);
+ todo = PostTransportWork (todo | PostTransportStop);
}
if (actively_recording()) {
/* move the transport position back to where the
request for a stop was noticed. we rolled
- past that point to pick up delayed input.
+ past that point to pick up delayed input (and/or to delick)
*/
- decrement_transport_position (_worst_output_latency);
+ if (_worst_output_latency > current_block_size) {
+ /* we rolled past the stop point to pick up data that had
+ not yet arrived. move back to where the stop occured.
+ */
+ decrement_transport_position (current_block_size + (_worst_output_latency - current_block_size));
+ } else {
+ decrement_transport_position (current_block_size);
+ }
/* the duration change is not guaranteed to have happened, but is likely */
- post_transport_work = PostTransportWork (post_transport_work | PostTransportDuration);
+ todo = PostTransportWork (todo | PostTransportDuration);
}
if (abort) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportAbort);
+ todo = PostTransportWork (todo | PostTransportAbort);
}
- _clear_event_type (Event::StopOnce);
- _clear_event_type (Event::RangeStop);
- _clear_event_type (Event::RangeLocate);
+ if (clear_state) {
+ todo = PostTransportWork (todo | PostTransportClearSubstate);
+ }
- disable_record (true);
+ if (todo) {
+ add_post_transport_work (todo);
+ }
+
+ _clear_event_type (SessionEvent::StopOnce);
+ _clear_event_type (SessionEvent::RangeStop);
+ _clear_event_type (SessionEvent::RangeLocate);
+ /* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */
+ disable_record (true, (!Config->get_latched_record_enable() && clear_state));
+
reset_slave_state ();
_transport_speed = 0;
waiting_for_sync_offset = true;
}
- transport_sub_state = ((Config->get_slave_source() == None && config.get_auto_return()) ? AutoReturning : 0);
+ transport_sub_state = ((!config.get_external_sync()&& config.get_auto_return()) ? AutoReturning : 0);
}
void
{
restart:
bool finished;
+ PostTransportWork ptw;
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);
finished = true;
+ ptw = post_transport_work();
- if (post_transport_work & PostTransportCurveRealloc) {
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw)));
+
+ if (ptw & PostTransportCurveRealloc) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->curve_reallocate();
}
}
- if (post_transport_work & PostTransportInputChange) {
+ if (ptw & PostTransportInputChange) {
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
(*i)->non_realtime_input_change ();
}
}
- if (post_transport_work & PostTransportSpeed) {
+ if (ptw & PostTransportSpeed) {
non_realtime_set_speed ();
}
- if (post_transport_work & PostTransportReverse) {
+ if (ptw & PostTransportReverse) {
clear_clicks();
cumulative_rf_motion = 0;
/* don't seek if locate will take care of that in non_realtime_stop() */
- if (!(post_transport_work & PostTransportLocate)) {
+ if (!(ptw & PostTransportLocate)) {
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (!(*i)->hidden()) {
}
}
- if (post_transport_work & PostTransportLocate) {
+ if (ptw & PostTransportLocate) {
non_realtime_locate ();
}
- if (post_transport_work & PostTransportStop) {
- non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
+ if (ptw & PostTransportStop) {
+ non_realtime_stop (ptw & PostTransportAbort, on_entry, finished);
if (!finished) {
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
goto restart;
}
}
- if (post_transport_work & PostTransportOverWrite) {
+ if (ptw & PostTransportOverWrite) {
non_realtime_overwrite (on_entry, finished);
if (!finished) {
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
}
}
- if (post_transport_work & PostTransportAudition) {
+ if (ptw & PostTransportAudition) {
non_realtime_set_audition ();
}
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
+
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n"));
}
void
time_t xnow;
bool did_record;
bool saved;
+ PostTransportWork ptw = post_transport_work();
did_record = false;
saved = false;
if (did_record) {
begin_reversible_command ("capture");
- Location* loc = _locations.end_location();
+ Location* loc = _locations.session_range_location();
bool change_end = false;
if (_transport_frame < loc->end()) {
_have_captured = true;
}
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n"));
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
(*i)->transport_stopped (*now, xnow, abort);
}
}
bool const auto_return_enabled =
- (Config->get_slave_source() == None && config.get_auto_return());
+ (!config.get_external_sync() && config.get_auto_return());
if (auto_return_enabled ||
- (post_transport_work & PostTransportLocate) ||
+ (ptw & PostTransportLocate) ||
(_requested_return_frame >= 0) ||
synced_to_jack()) {
}
if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) &&
- !(post_transport_work & PostTransportLocate)) {
+ !(ptw & PostTransportLocate)) {
- /* no explicit locate queued */
+ /* no explicit locate queued */
bool do_locate = false;
}
+ /* do this before seeking, because otherwise the Diskstreams will do the wrong thing in seamless loop mode.
+ */
+
+ if (ptw & PostTransportClearSubstate) {
+ _play_range = false;
+ unset_play_loop ();
+ }
+
/* this for() block can be put inside the previous if() and has the effect of ... ??? what */
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n"));
+
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if (!(*i)->hidden()) {
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", (*i)->name()));
(*i)->non_realtime_locate (_transport_frame);
}
+
if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
/* we will be back */
deliver_mmc (MIDI::MachineControl::cmdStop, 0);
deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
- if (did_record) {
-
- /* XXX its a little odd that we're doing this here
- when realtime_stop(), which has already executed,
- will have done this.
- JLC - so let's not because it seems unnecessary and breaks loop record
- */
-#if 0
- if (!Config->get_latched_record_enable()) {
- g_atomic_int_set (&_record_status, Disabled);
- } else {
- g_atomic_int_set (&_record_status, Enabled);
- }
- RecordStateChanged (); /* emit signal */
-#endif
- }
-
- if ((post_transport_work & PostTransportLocate) && get_record_enabled()) {
+ if ((ptw & PostTransportLocate) && get_record_enabled()) {
/* capture start has been changed, so save pending state */
save_state ("", true);
saved = true;
save_state (_current_snapshot_name);
}
- if (post_transport_work & PostTransportDuration) {
+ if (ptw & PostTransportDuration) {
DurationChanged (); /* EMIT SIGNAL */
}
- if (post_transport_work & PostTransportStop) {
+ if (ptw & PostTransportStop) {
_play_range = false;
play_loop = false;
}
- nframes_t tf = _transport_frame;
-
- PositionChanged (tf); /* EMIT SIGNAL */
+ // can't cast away volatile so copy and emit that
+ nframes64_t tframe = _transport_frame;
+ PositionChanged (tframe); /* EMIT SIGNAL */
TransportStateChange (); /* EMIT SIGNAL */
/* and start it up again if relevant */
- if ((post_transport_work & PostTransportLocate) && Config->get_slave_source() == None && pending_locate_roll) {
+ if ((ptw & PostTransportLocate) && !config.get_external_sync() && pending_locate_roll) {
request_transport_speed (1.0);
pending_locate_roll = false;
}
}
void
-Session::set_play_loop (bool yn, bool leave_rolling)
+Session::unset_play_loop ()
+{
+ play_loop = false;
+ clear_events (SessionEvent::AutoLoop);
+
+ // set all diskstreams to NOT use internal looping
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_loop (0);
+ }
+ }
+}
+
+void
+Session::set_play_loop (bool yn)
{
/* Called from event-handling context */
Location *loc;
- if (yn == play_loop) {
- return;
- }
-
- if ((actively_recording() && yn) || (loc = _locations.auto_loop_location()) == 0) {
+ if (yn == play_loop || (actively_recording() && yn) || (loc = _locations.auto_loop_location()) == 0) {
+ /* nothing to do, or can't change loop status while recording */
return;
}
-
+
set_dirty();
-
+
if (yn && Config->get_seamless_loop() && synced_to_jack()) {
- warning << _("Seamless looping cannot be supported while Ardour is using JACK transport.\n"
- "Recommend changing the configured options")
+ warning << string_compose (_("Seamless looping cannot be supported while %1 is using JACK transport.\n"
+ "Recommend changing the configured options"), PROGRAM_NAME)
<< endmsg;
return;
}
+
+ if (yn) {
-
- if ((play_loop = yn)) {
+ play_loop = true;
if (loc) {
- set_play_range (false, true);
+ unset_play_range ();
if (Config->get_seamless_loop()) {
// set all diskstreams to use internal looping
}
}
}
-
- /* stick in the loop event */
-
- Event* event = new Event (Event::AutoLoop, Event::Replace, loc->end(), loc->start(), 0.0f);
- merge_event (event);
-
- // locate to start of loop and roll
- event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, loc->start(), 0, !synced_to_jack());
+ /* put the loop event into the event list */
+
+ SessionEvent* event = new SessionEvent (SessionEvent::AutoLoop, SessionEvent::Replace, loc->end(), loc->start(), 0.0f);
merge_event (event);
- }
+ /* locate to start of loop and roll. If doing seamless loop, force a
+ locate+buffer refill even if we are positioned there already.
+ */
- } else {
- clear_events (Event::AutoLoop);
-
- // set all diskstreams to NOT use internal looping
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->set_loop (0);
- }
+ start_locate (loc->start(), true, true, false, Config->get_seamless_loop());
}
+ } else {
+
+ unset_play_loop ();
}
- TransportStateChange (); /* EMIT SIGNAL */
-}
+ TransportStateChange ();
+}
void
Session::flush_all_inserts ()
{
}
void
-Session::start_locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+Session::start_locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
{
if (synced_to_jack()) {
double sp;
- nframes_t pos;
+ nframes64_t pos;
_slave->speed_and_position (sp, pos);
}
} else {
- locate (target_frame, with_roll, with_flush, with_loop);
+ locate (target_frame, with_roll, with_flush, with_loop, force);
}
}
}
void
-Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
{
if (actively_recording() && !with_loop) {
return;
}
- if (_transport_frame == target_frame && !loop_changing && !with_loop) {
+ if (!force && _transport_frame == target_frame && !loop_changing && !with_loop) {
if (with_roll) {
set_transport_speed (1.0, false);
}
}
if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
- realtime_stop (false);
+ realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
}
- if ( !with_loop || loop_changing) {
+ if (force || !with_loop || loop_changing) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportLocate);
+ PostTransportWork todo = PostTransportLocate;
if (with_roll) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportRoll);
- }
+ todo = PostTransportWork (todo | PostTransportRoll);
+ }
+ add_post_transport_work (todo);
_butler->schedule_transport_work ();
} else {
if (al && (_transport_frame < al->start() || _transport_frame > al->end())) {
// cancel looping directly, this is called from event handling context
- set_play_loop (false, false);
+ set_play_loop (false);
}
else if (al && _transport_frame == al->start()) {
if (with_loop) {
* @param abort
*/
void
-Session::set_transport_speed (double speed, bool abort)
+Session::set_transport_speed (double speed, bool abort, bool clear_state)
{
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n", speed, abort, clear_state, _transport_speed));
+
if (_transport_speed == speed) {
return;
}
}
if (synced_to_jack ()) {
+ if (clear_state) {
+ /* do this here because our response to the slave won't
+ take care of it.
+ */
+ _play_range = false;
+ unset_play_loop ();
+ }
_engine.transport_stop ();
} else {
stop_transport (abort);
/* we are stopped and we want to start rolling at speed 1 */
- if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
- return;
- }
-
if (Config->get_monitoring_model() == HardwareMonitoring) {
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
} else {
- if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
- return;
- }
-
if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
- warning << _("Global varispeed cannot be supported while Ardour is connected to JACK transport control")
+ warning << string_compose (_("Global varispeed cannot be supported while %1 is connected to JACK transport control"),
+ PROGRAM_NAME)
<< endmsg;
return;
}
/* if we are reversing relative to the current speed, or relative to the speed
before the last stop, then we have to do extra work.
*/
+
+ PostTransportWork todo = PostTransportWork (0);
if ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
+ todo = PostTransportWork (todo | PostTransportReverse);
}
_last_transport_speed = _transport_speed;
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
+ todo = PostTransportWork (todo | PostTransportSpeed);
+ break;
}
}
- if (post_transport_work & (PostTransportSpeed|PostTransportReverse)) {
+ if (todo) {
+ add_post_transport_work (todo);
_butler->schedule_transport_work ();
}
}
/** Stop the transport. */
void
-Session::stop_transport (bool abort)
+Session::stop_transport (bool abort, bool clear_state)
{
if (_transport_speed == 0.0f) {
return;
}
- if (actively_recording() && !(transport_sub_state & StopPendingCapture) &&
- _worst_output_latency > current_block_size)
- {
+ if (actively_recording() && !(transport_sub_state & StopPendingCapture) && _worst_output_latency > current_block_size) {
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->prepare_to_stop (_transport_frame);
+ }
/* we need to capture the audio that has still not yet been received by the system
at the time the stop is requested, so we have to roll past that time.
and then we'll really be stopped.
*/
- Event *ev = new Event (Event::StopOnce, Event::Replace,
+ SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
_transport_frame + _worst_output_latency - current_block_size,
0, 0, abort);
if ((transport_sub_state & PendingDeclickOut) == 0) {
+
+ if (!(transport_sub_state & StopPendingCapture)) {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->prepare_to_stop (_transport_frame);
+ }
+ }
+
transport_sub_state |= PendingDeclickOut;
/* we'll be called again after the declick */
pending_abort = abort;
return;
}
- realtime_stop (abort);
+ realtime_stop (abort, clear_state);
_butler->schedule_transport_work ();
}
void
Session::post_transport ()
{
- if (post_transport_work & PostTransportAudition) {
- if (auditioner && auditioner->active()) {
+ PostTransportWork ptw = post_transport_work ();
+
+ if (ptw & PostTransportAudition) {
+ if (auditioner && auditioner->auditioning()) {
process_function = &Session::process_audition;
} else {
process_function = &Session::process_with_events;
}
}
- if (post_transport_work & PostTransportStop) {
+ if (ptw & PostTransportStop) {
transport_sub_state = 0;
}
- if (post_transport_work & PostTransportLocate) {
+ if (ptw & PostTransportLocate) {
- if (((Config->get_slave_source() == None && (auto_play_legal && config.get_auto_play())) && !_exporting) || (post_transport_work & PostTransportRoll)) {
+ if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
start_transport ();
} else {
}
set_next_event ();
-
- post_transport_work = PostTransportWork (0);
+ /* XXX is this really safe? shouldn't we just be unsetting the bits that we actually
+ know were handled ?
+ */
+ set_post_transport_work (PostTransportWork (0));
}
void
}
void
-Session::set_slave_source (SlaveSource src)
+Session::use_sync_source (Slave* new_slave)
{
- bool reverse = false;
- bool non_rt_required = false;
+ /* Runs in process() context */
- if (_transport_speed) {
- error << _("please stop the transport before adjusting slave settings") << endmsg;
- return;
- }
+ bool non_rt_required = false;
-// if (src == JACK && Config->get_jack_time_master()) {
-// return;
-// }
+ /* XXX this deletion is problematic because we're in RT context */
delete _slave;
- _slave = 0;
+ _slave = new_slave;
- if (_transport_speed < 0.0) {
- reverse = true;
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
+ non_rt_required = true;
+ }
+ (*i)->set_slaved (_slave != 0);
+ }
}
- switch (src) {
- case None:
- stop_transport ();
- break;
+ if (non_rt_required) {
+ add_post_transport_work (PostTransportSpeed);
+ _butler->schedule_transport_work ();
+ }
+
+ set_dirty();
+}
+
+void
+Session::drop_sync_source ()
+{
+ request_sync_source (0);
+}
+
+void
+Session::switch_to_sync_source (SyncSource src)
+{
+ Slave* new_slave;
+
+ DEBUG_TRACE (DEBUG::Slave, string_compose ("Setting up sync source %1\n", enum_2_string (src)));
+ switch (src) {
case MTC:
+ if (_slave && dynamic_cast<MTC_Slave*>(_slave)) {
+ return;
+ }
+
if (_mtc_port) {
try {
- _slave = new MTC_Slave (*this, *_mtc_port);
+ new_slave = new MTC_Slave (*this, *_mtc_port);
}
catch (failed_constructor& err) {
break;
case MIDIClock:
+ if (_slave && dynamic_cast<MIDIClock_Slave*>(_slave)) {
+ return;
+ }
+
if (_midi_clock_port) {
try {
- _slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24);
+ new_slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24);
}
catch (failed_constructor& err) {
break;
case JACK:
- _slave = new JACK_Slave (_engine.jack());
- break;
-
- };
-
- Config->set_slave_source (src);
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
- non_rt_required = true;
- }
- (*i)->set_slaved (_slave);
+ if (_slave && dynamic_cast<JACK_Slave*>(_slave)) {
+ return;
}
- }
- if (reverse) {
- reverse_diskstream_buffers ();
- }
-
- if (non_rt_required) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
- _butler->schedule_transport_work ();
- }
+ new_slave = new JACK_Slave (_engine.jack());
+ break;
+
+ default:
+ new_slave = 0;
+ break;
+ };
- set_dirty();
+ request_sync_source (new_slave);
}
void
Session::reverse_diskstream_buffers ()
{
- post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
+ add_post_transport_work (PostTransportReverse);
_butler->schedule_transport_work ();
}
Session::set_diskstream_speed (Diskstream* stream, double speed)
{
if (stream->realtime_set_speed (speed, false)) {
- post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
+ add_post_transport_work (PostTransportSpeed);
_butler->schedule_transport_work ();
set_dirty ();
}
}
void
-Session::set_audio_range (list<AudioRange>& range)
-{
- Event *ev = new Event (Event::SetAudioRange, Event::Add, Event::Immediate, 0, 0.0f);
- ev->audio_range = range;
- queue_event (ev);
-}
-
-void
-Session::request_play_range (bool yn, bool leave_rolling)
+Session::unset_play_range ()
{
- Event* ev = new Event (Event::SetPlayRange, Event::Add, Event::Immediate, 0, (leave_rolling ? 1.0f : 0.0f), yn);
- queue_event (ev);
+ _play_range = false;
+ _clear_event_type (SessionEvent::RangeStop);
+ _clear_event_type (SessionEvent::RangeLocate);
}
void
-Session::set_play_range (bool yn, bool leave_rolling)
+Session::set_play_range (list<AudioRange>& range, bool leave_rolling)
{
- /* Called from event-processing context */
-
- if (yn) {
- /* cancel loop play */
- set_play_range (false, true);
- }
-
- _play_range = yn;
- setup_auto_play ();
-
+ SessionEvent* ev;
- if (!_play_range && !leave_rolling) {
- /* stop transport */
- Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0f, false);
- merge_event (ev);
- }
- TransportStateChange (); /* EMIT SIGNAL */
-}
-
-void
-Session::setup_auto_play ()
-{
/* Called from event-processing context */
- Event* ev;
-
- _clear_event_type (Event::RangeStop);
- _clear_event_type (Event::RangeLocate);
-
- if (!_play_range) {
+ unset_play_range ();
+
+ if (range.empty()) {
+ /* _play_range set to false in unset_play_range()
+ */
+ if (!leave_rolling) {
+ /* stop transport */
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0f, false);
+ merge_event (ev);
+ }
return;
}
- list<AudioRange>::size_type sz = current_audio_range.size();
+ _play_range = true;
- if (sz > 1) {
+ /* cancel loop play */
+ unset_play_loop ();
- list<AudioRange>::iterator i = current_audio_range.begin();
+ list<AudioRange>::size_type sz = range.size();
+
+ if (sz > 1) {
+
+ list<AudioRange>::iterator i = range.begin();
list<AudioRange>::iterator next;
-
- while (i != current_audio_range.end()) {
-
+
+ while (i != range.end()) {
+
next = i;
++next;
-
+
/* locating/stopping is subject to delays for declicking.
*/
-
+
nframes_t requested_frame = (*i).end;
-
+
if (requested_frame > current_block_size) {
requested_frame -= current_block_size;
} else {
requested_frame = 0;
}
-
- if (next == current_audio_range.end()) {
- ev = new Event (Event::RangeStop, Event::Add, requested_frame, 0, 0.0f);
+
+ if (next == range.end()) {
+ ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f);
} else {
- ev = new Event (Event::RangeLocate, Event::Add, requested_frame, (*next).start, 0.0f);
+ ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f);
}
-
+
merge_event (ev);
-
+
i = next;
}
-
+
} else if (sz == 1) {
- ev = new Event (Event::RangeStop, Event::Add, current_audio_range.front().end, 0, 0.0f);
+ ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f);
merge_event (ev);
+
+ }
- }
+ /* save range so we can do auto-return etc. */
+
+ current_audio_range = range;
/* now start rolling at the right place */
- ev = new Event (Event::LocateRoll, Event::Add, Event::Immediate, current_audio_range.front().start, 0.0f, false);
+ ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false);
merge_event (ev);
+
+ TransportStateChange ();
}
void
-Session::request_roll_at_and_return (nframes_t start, nframes_t return_to)
+Session::request_bounded_roll (nframes_t start, nframes_t end)
{
- Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0);
- ev->target2_frame = start;
- queue_event (ev);
-}
+ AudioRange ar (start, end, 0);
+ list<AudioRange> lar;
+ lar.push_back (ar);
+ request_play_range (&lar, true);
+}
void
-Session::request_bounded_roll (nframes_t start, nframes_t end)
+Session::request_roll_at_and_return (nframes_t start, nframes_t return_to)
{
- Event *ev = new Event (Event::StopOnce, Event::Replace, end, Event::Immediate, 0.0);
+ SessionEvent *ev = new SessionEvent (SessionEvent::LocateRollLocate, SessionEvent::Add, SessionEvent::Immediate, return_to, 1.0);
+ ev->target2_frame = start;
queue_event (ev);
- request_locate (start, true);
}
void
*/
g_atomic_int_set (&_butler->should_do_transport_work, 0);
- post_transport_work = PostTransportWork (0);
+ set_post_transport_work (PostTransportWork (0));
_butler->stop ();
- realtime_stop (false);
+ realtime_stop (false, true);
non_realtime_stop (false, 0, ignored);
transport_sub_state = 0;
void
Session::xrun_recovery ()
{
- Xrun (transport_frame()); //EMIT SIGNAL
+ // can't cast away volatile so copy and emit that
+ nframes64_t tframe = _transport_frame;
+ Xrun (tframe); //EMIT SIGNAL
if (Config->get_stop_recording_on_xrun() && actively_recording()) {
}
}
+void
+Session::route_processors_changed (RouteProcessorChange c)
+{
+ if (c.type == RouteProcessorChange::MeterPointChange) {
+ return;
+ }
+
+ update_latency_compensation (false, false);
+ resort_routes ();
+}
+
void
Session::update_latency_compensation (bool with_stop, bool abort)
{
bool update_jack = false;
+ PostTransportWork ptw;
if (_state_of_the_state & Deletion) {
return;
}
_worst_track_latency = 0;
+ ptw = post_transport_work();
#undef DEBUG_LATENCY
#ifdef DEBUG_LATENCY
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (with_stop) {
- (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate),
- (!(post_transport_work & PostTransportLocate) || pending_locate_flush));
+ (*i)->handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
}
nframes_t old_latency = (*i)->output()->signal_latency ();
Session::maybe_stop (nframes_t limit)
{
if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
- if (synced_to_jack () && Config->get_jack_time_master ()) {
+ if (synced_to_jack () && config.get_jack_time_master ()) {
_engine.transport_stop ();
} else if (!synced_to_jack ()) {
stop_transport ();