#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::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) {
+ if (dynamic_cast<JACK_Slave*>(new_slave)) {
/* JACK cannot support seamless looping at present */
Config->set_seamless_loop (false);
} else {
/* save value of seamless from before the switch */
_was_seamless = seamless;
- ev->slave = src;
+ 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, bool clear_state)
{
- Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0, abort, clear_state);
+ 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 (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::request_play_range (list<AudioRange>* range, bool leave_rolling)
{
- Event* ev = new Event (Event::SetPlayAudioRange, Event::Add, Event::Immediate, 0, (leave_rolling ? 1.0 : 0.0));
+ 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 */
/* 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 */
add_post_transport_work (todo);
}
- _clear_event_type (Event::StopOnce);
- _clear_event_type (Event::RangeStop);
- _clear_event_type (Event::RangeLocate);
-
- disable_record (true);
+ _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
int on_entry = g_atomic_int_get (&_butler->should_do_transport_work);
finished = true;
ptw = post_transport_work();
+
+ 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();
}
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
+
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n"));
}
void
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 ||
(ptw & PostTransportLocate) ||
if ((auto_return_enabled || synced_to_jack() || _requested_return_frame >= 0) &&
!(ptw & PostTransportLocate)) {
- /* no explicit locate queued */
+ /* no explicit locate queued */
bool do_locate = false;
/* 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 */
play_loop = false;
}
- PositionChanged ((nframes64_t) _transport_frame); /* 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 ((ptw & 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;
}
Session::unset_play_loop ()
{
play_loop = false;
- clear_events (Event::AutoLoop);
+ clear_events (SessionEvent::AutoLoop);
// set all diskstreams to NOT use internal looping
boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
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;
}
/* put the loop event into the event list */
- Event* event = new Event (Event::AutoLoop, Event::Replace, loc->end(), loc->start(), 0.0f);
+ 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
void
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;
}
/* 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;
}
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;
PostTransportWork ptw = post_transport_work ();
if (ptw & PostTransportAudition) {
- if (auditioner && auditioner->active()) {
+ if (auditioner && auditioner->auditioning()) {
process_function = &Session::process_audition;
} else {
process_function = &Session::process_with_events;
if (ptw & PostTransportLocate) {
- if (((Config->get_slave_source() == None && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
+ if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
start_transport ();
} else {
}
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) {
- add_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::unset_play_range ()
{
_play_range = false;
- _clear_event_type (Event::RangeStop);
- _clear_event_type (Event::RangeLocate);
+ _clear_event_type (SessionEvent::RangeStop);
+ _clear_event_type (SessionEvent::RangeLocate);
}
void
Session::set_play_range (list<AudioRange>& range, bool leave_rolling)
{
- Event* ev;
+ SessionEvent* ev;
/* Called from event-processing context */
*/
if (!leave_rolling) {
/* stop transport */
- Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0f, false);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTransportSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0f, false);
merge_event (ev);
}
return;
}
if (next == range.end()) {
- ev = new Event (Event::RangeStop, Event::Add, requested_frame, 0, 0.0f);
+ 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);
} else if (sz == 1) {
- ev = new Event (Event::RangeStop, Event::Add, range.front().end, 0, 0.0f);
+ ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, range.front().end, 0, 0.0f);
merge_event (ev);
}
/* now start rolling at the right place */
- ev = new Event (Event::LocateRoll, Event::Add, Event::Immediate, 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)
{
- Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0);
+ SessionEvent *ev = new SessionEvent (SessionEvent::LocateRollLocate, SessionEvent::Add, SessionEvent::Immediate, return_to, 1.0);
ev->target2_frame = start;
queue_event (ev);
}
void
Session::xrun_recovery ()
{
- Xrun ((nframes64_t)_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)
{