#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 "midi++/manager.h"
#include "ardour/ardour.h"
-#include "ardour/audio_diskstream.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)
+Session::request_track_speed (Track* tr, double speed)
{
- Event* ev = new Event (Event::SetDiskstreamSpeed, Event::Add, Event::Immediate, 0, speed);
- ev->set_ptr (&ds);
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTrackSpeed, SessionEvent::Add, SessionEvent::Immediate, 0, speed);
+ ev->set_ptr (tr);
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)
+Session::request_locate (framepos_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)
+Session::force_locate (framepos_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;
- Location *location = _locations.auto_loop_location();
+ SessionEvent* ev;
+ Location *location = _locations->auto_loop_location();
if (location == 0 && yn) {
error << _("Cannot loop - no loop range defined")
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()) {
- // request an immediate locate to refresh the diskstreams
+ // request an immediate locate to refresh the tracks
// after disabling looping
request_locate (_transport_frame-1, false);
}
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 */
- // FIXME: where should this really be? [DR]
- //send_full_time_code();
- deliver_mmc (MIDI::MachineControl::cmdStop, 0);
- deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
-
if (_transport_speed < 0.0f) {
todo = (PostTransportWork (todo | PostTransportStop | PostTransportReverse));
} else {
/* 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;
_target_transport_speed = 0;
+ g_atomic_int_set (&_playback_load, 100);
+ g_atomic_int_set (&_capture_load, 100);
+
if (config.get_use_video_sync()) {
waiting_for_sync_offset = true;
}
- transport_sub_state = ((Config->get_slave_source() == None && config.get_auto_return()) ? AutoReturning : 0);
+ transport_sub_state = 0;
}
void
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();
+
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler transport work, todo = %1\n", enum_2_string (ptw)));
+
+ if (ptw & PostTransportAdjustPlaybackBuffering) {
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->adjust_playback_buffering ();
+ /* and refill those buffers ... */
+ tr->non_realtime_locate (_transport_frame);
+ }
+ }
+
+ }
+
+ if (ptw & PostTransportAdjustCaptureBuffering) {
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->adjust_capture_buffering ();
+ }
+ }
+ }
+
if (ptw & PostTransportCurveRealloc) {
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->curve_reallocate();
}
if (ptw & PostTransportInputChange) {
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->non_realtime_input_change ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->non_realtime_input_change ();
+ }
}
}
if (!(ptw & PostTransportLocate)) {
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->non_realtime_locate (_transport_frame);
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ tr->non_realtime_locate (_transport_frame);
}
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);
+
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler transport work all done\n"));
}
void
Session::non_realtime_set_speed ()
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->non_realtime_set_speed ();
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->non_realtime_set_speed ();
+ }
}
}
void
Session::non_realtime_overwrite (int on_entry, bool& finished)
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->pending_overwrite) {
- (*i)->overwrite_existing_buffers ();
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->pending_overwrite ()) {
+ tr->overwrite_existing_buffers ();
}
if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
void
Session::non_realtime_locate ()
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->non_realtime_locate (_transport_frame);
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->non_realtime_locate (_transport_frame);
+ }
}
}
did_record = false;
saved = false;
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->get_captured_frames () != 0) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->get_captured_frames () != 0) {
did_record = true;
break;
}
if (did_record) {
begin_reversible_command ("capture");
+ _have_captured = true;
+ }
- Location* loc = _locations.end_location();
- bool change_end = false;
-
- if (_transport_frame < loc->end()) {
-
- /* stopped recording before current end */
-
- if (config.get_end_marker_is_free()) {
-
- /* first capture for this session, move end back to where we are */
-
- change_end = true;
- }
-
- } else if (_transport_frame > loc->end()) {
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: DS stop\n"));
- /* stopped recording after the current end, extend it */
+ if (abort && did_record) {
+ /* no reason to save the session file when we remove sources
+ */
+ _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
+ }
- change_end = true;
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->transport_stopped_wallclock (*now, xnow, abort);
}
-
- if (change_end) {
- XMLNode &before = loc->get_state();
- loc->set_end(_transport_frame);
- XMLNode &after = loc->get_state();
- add_command (new MementoCommand<Location>(*loc, &before, &after));
- }
-
- config.set_end_marker_is_free (false);
- _have_captured = true;
}
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->transport_stopped (*now, xnow, abort);
- }
+ if (abort && did_record) {
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
+ }
boost::shared_ptr<RouteList> r = routes.reader ();
}
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;
if (!synced_to_jack()) {
- Location *location = _locations.auto_loop_location();
+ Location *location = _locations->auto_loop_location();
if (location != 0) {
_transport_frame = location->start();
}
- /* do this before seeking, because otherwise the Diskstreams will do the wrong thing in seamless loop mode.
+ /* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
*/
if (ptw & PostTransportClearSubstate) {
/* this for() block can be put inside the previous if() and has the effect of ... ??? what */
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->non_realtime_locate (_transport_frame);
+ DEBUG_TRACE (DEBUG::Transport, X_("Butler PTW: locate\n"));
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("Butler PTW: locate on %1\n", (*i)->name()));
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ tr->non_realtime_locate (_transport_frame);
}
+
if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
finished = false;
/* we will be back */
have_looped = false;
- send_full_time_code (0);
- deliver_mmc (MIDI::MachineControl::cmdStop, 0);
- deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
+ send_full_time_code (_transport_frame);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ send_mmc_locate (_transport_frame);
if ((ptw & PostTransportLocate) && get_record_enabled()) {
/* capture start has been changed, so save pending state */
save_state (_current_snapshot_name);
}
- if (ptw & PostTransportDuration) {
- DurationChanged (); /* EMIT SIGNAL */
- }
-
if (ptw & PostTransportStop) {
_play_range = false;
play_loop = false;
}
- PositionChanged ((nframes64_t) _transport_frame); /* EMIT SIGNAL */
+ PositionChanged (_transport_frame); /* 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();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->set_loop (0);
+ // set all tracks to NOT use internal looping
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ tr->set_loop (0);
}
}
}
Location *loc;
- if (yn == play_loop || (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;
}
unset_play_range ();
if (Config->get_seamless_loop()) {
- // set all diskstreams to 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 (loc);
+ // set all tracks to use internal looping
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ tr->set_loop (loc);
}
}
}
else {
- // 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);
+ // set all tracks to NOT use internal looping
+ boost::shared_ptr<RouteList> rl = routes.reader ();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ tr->set_loop (0);
}
}
}
/* 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::start_locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
+Session::start_locate (framepos_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
{
if (synced_to_jack()) {
double sp;
- nframes64_t pos;
+ framepos_t pos;
_slave->speed_and_position (sp, pos);
}
int
-Session::micro_locate (nframes_t distance)
+Session::micro_locate (framecnt_t distance)
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (!(*i)->can_internal_playback_seek (distance)) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->can_internal_playback_seek (distance)) {
return -1;
}
}
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->internal_playback_seek (distance);
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->internal_playback_seek (distance);
+ }
}
_transport_frame += distance;
return 0;
}
+/** @param with_mmc true to send a MMC locate command when the locate is done */
void
-Session::locate (nframes64_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force)
+Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool with_loop, bool force, bool with_mmc)
{
if (actively_recording() && !with_loop) {
return;
set_transport_speed (1.0, false);
}
loop_changing = false;
+ Located (); /* EMIT SIGNAL */
return;
}
/* switch from input if we're going to roll */
if (Config->get_monitoring_model() == HardwareMonitoring) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->record_enabled ()) {
//cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (!config.get_auto_input());
+ tr->monitor_input (!config.get_auto_input());
}
}
}
} else {
/* otherwise we're going to stop, so do the opposite */
if (Config->get_monitoring_model() == HardwareMonitoring) {
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->record_enabled ()) {
//cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (true);
+ tr->monitor_input (true);
}
}
}
/* cancel looped playback if transport pos outside of loop range */
if (play_loop) {
- Location* al = _locations.auto_loop_location();
+ Location* al = _locations->auto_loop_location();
if (al && (_transport_frame < al->start() || _transport_frame > al->end())) {
// cancel looping directly, this is called from event handling context
if (with_loop) {
// this is only necessary for seamless looping
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->record_enabled ()) {
// tell it we've looped, so it can deal with the record state
- (*i)->transport_looped(_transport_frame);
+ tr->transport_looped(_transport_frame);
}
}
}
_send_timecode_update = true;
+ if (with_mmc) {
+ send_mmc_locate (_transport_frame);
+ }
+
Located (); /* EMIT SIGNAL */
}
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;
}
if (Config->get_monitoring_model() == HardwareMonitoring)
{
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if ((*i)->record_enabled ()) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->record_enabled ()) {
//cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (true);
+ tr->monitor_input (true);
}
}
}
/* 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();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- if (config.get_auto_input() && (*i)->record_enabled ()) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (config.get_auto_input() && tr && tr->record_enabled ()) {
//cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
- (*i)->monitor_input (false);
+ tr->monitor_input (false);
}
}
}
} 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 ((_transport_speed && speed * _transport_speed < 0.0) || (_last_transport_speed * speed < 0.0) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
todo = PostTransportWork (todo | PostTransportReverse);
+ _last_roll_or_reversal_location = _transport_frame;
}
_last_transport_speed = _transport_speed;
_transport_speed = 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)) {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && tr->realtime_set_speed (tr->speed(), true)) {
todo = PostTransportWork (todo | PostTransportSpeed);
break;
}
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<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->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<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->prepare_to_stop (_transport_frame);
+ }
+ }
+ }
+
transport_sub_state |= PendingDeclickOut;
/* we'll be called again after the declick */
pending_abort = abort;
Session::start_transport ()
{
_last_roll_location = _transport_frame;
+ _last_roll_or_reversal_location = _transport_frame;
+
have_looped = false;
/* if record status is Enabled, move it to Recording. if its
_transport_speed = 1.0;
_target_transport_speed = 1.0;
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->realtime_set_speed ((*i)->speed(), true);
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->realtime_set_speed (tr->speed(), true);
+ }
+ (*i)->automation_snapshot (_transport_frame, true);
}
- deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame);
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
TransportStateChange (); /* EMIT SIGNAL */
}
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::reset_rf_scale (nframes_t motion)
+Session::reset_rf_scale (framecnt_t motion)
{
cumulative_rf_motion += motion;
}
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<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr && !tr->hidden()) {
+ if (tr->realtime_set_speed (tr->speed(), true)) {
+ non_rt_required = true;
+ }
+ tr->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 (_mtc_port) {
- try {
- _slave = new MTC_Slave (*this, *_mtc_port);
- }
+ if (_slave && dynamic_cast<MTC_Slave*>(_slave)) {
+ return;
+ }
- catch (failed_constructor& err) {
- return;
- }
+ try {
+ new_slave = new MTC_Slave (*this, *MIDI::Manager::instance()->mtc_input_port());
+ }
- } else {
- error << _("No MTC port defined: MTC slaving is impossible.") << endmsg;
+ catch (failed_constructor& err) {
return;
}
break;
case MIDIClock:
- if (_midi_clock_port) {
- try {
- _slave = new MIDIClock_Slave (*this, *_midi_clock_port, 24);
- }
-
- catch (failed_constructor& err) {
- return;
- }
+ if (_slave && dynamic_cast<MIDIClock_Slave*>(_slave)) {
+ return;
+ }
- } else {
- error << _("No MIDI Clock port defined: MIDI Clock slaving is impossible.") << endmsg;
+ try {
+ new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
+ }
+
+ catch (failed_constructor& err) {
return;
}
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 (config.get_video_pullup() != 0.0f) {
+ return;
+ }
- 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::reverse_diskstream_buffers ()
+Session::reverse_track_buffers ()
{
add_post_transport_work (PostTransportReverse);
_butler->schedule_transport_work ();
}
void
-Session::set_diskstream_speed (Diskstream* stream, double speed)
+Session::set_track_speed (Track* track, double speed)
{
- if (stream->realtime_set_speed (speed, false)) {
+ if (track->realtime_set_speed (speed, false)) {
add_post_transport_work (PostTransportSpeed);
_butler->schedule_transport_work ();
set_dirty ();
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;
/* locating/stopping is subject to delays for declicking.
*/
- nframes_t requested_frame = (*i).end;
+ framepos_t requested_frame = i->end;
if (requested_frame > current_block_size) {
requested_frame -= current_block_size;
}
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_bounded_roll (nframes_t start, nframes_t end)
+Session::request_bounded_roll (framepos_t start, framepos_t end)
{
AudioRange ar (start, end, 0);
list<AudioRange> lar;
request_play_range (&lar, true);
}
void
-Session::request_roll_at_and_return (nframes_t start, nframes_t return_to)
+Session::request_roll_at_and_return (framepos_t start, framepos_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
+ Xrun (_transport_frame); //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)
{
(*i)->handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
}
- nframes_t old_latency = (*i)->output()->signal_latency ();
- nframes_t track_latency = (*i)->update_total_latency ();
+ framecnt_t old_latency = (*i)->output()->signal_latency ();
+ framecnt_t track_latency = (*i)->update_total_latency ();
if (old_latency != track_latency) {
(*i)->input()->update_port_total_latencies ();
/* reflect any changes in latencies into capture offsets
*/
-
- boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
-
- for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
- (*i)->set_capture_offset ();
+
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+ if (tr) {
+ tr->set_capture_offset ();
+ }
}
}
}
bool
-Session::maybe_stop (nframes_t limit)
+Session::maybe_stop (framepos_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 ()) {
}
return false;
}
+
+void
+Session::send_mmc_locate (framepos_t t)
+{
+ Timecode::Time time;
+ timecode_time_subframes (t, time);
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+}
+
+/** Ask the transport to not send timecode until further notice. The suspension
+ * will come into effect some finite time after this call, and timecode_transmission_suspended()
+ * should be checked by the caller to find out when.
+ */
+void
+Session::request_suspend_timecode_transmission ()
+{
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTimecodeTransmission, SessionEvent::Add, SessionEvent::Immediate, 0, 0, false);
+ queue_event (ev);
+}
+
+void
+Session::request_resume_timecode_transmission ()
+{
+ SessionEvent* ev = new SessionEvent (SessionEvent::SetTimecodeTransmission, SessionEvent::Add, SessionEvent::Immediate, 0, 0, true);
+ queue_event (ev);
+}
+
+bool
+Session::timecode_transmission_suspended () const
+{
+ return g_atomic_int_get (&_suspend_timecode_transmission) == 1;
+}