*/
-#include <cmath>
-#include <cerrno>
-#include <unistd.h>
-
#ifdef WAF_BUILD
#include "libardour-config.h"
#endif
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
#include "pbd/undo.h"
#include "pbd/error.h"
#include "midi++/port.h"
#include "midi++/manager.h"
-#include "ardour/ardour.h"
#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
#include "ardour/butler.h"
+#include "ardour/click.h"
#include "ardour/debug.h"
#include "ardour/location.h"
#include "ardour/session.h"
void
Session::realtime_stop (bool abort, bool clear_state)
{
- DEBUG_TRACE (DEBUG::Transport, "realtime stop\n");
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("realtime stop @ %1\n", _transport_frame));
PostTransportWork todo = PostTransportWork (0);
/* assume that when we start, we'll be moving forwards */
tr->non_realtime_locate (_transport_frame);
}
}
+
+ /* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
+ rather than clearing them so that the RT thread has to spend time constructing
+ them (in Session::click).
+ */
+ clear_clicks ();
}
auditioner->cancel_audition ();
}
- clear_clicks();
cumulative_rf_motion = 0;
reset_rf_scale (0);
}
if (abort && did_record) {
- _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
}
boost::shared_ptr<RouteList> r = routes.reader ();
if (_requested_return_frame >= 0) {
/* explicit return request pre-queued in event list. overrides everything else */
-
+
cerr << "explicit auto-return to " << _requested_return_frame << endl;
_transport_frame = _requested_return_frame;
if (!synced_to_jack()) {
Location *location = _locations->auto_loop_location();
-
+
if (location != 0) {
_transport_frame = location->start();
} else {
}
} else {
-
+
/* regular auto-return */
-
+
_transport_frame = _last_roll_location;
do_locate = true;
}
- }
+ }
}
- _requested_return_frame = -1;
+ _requested_return_frame = -1;
if (do_locate) {
_engine.transport_locate (_transport_frame);
}
- }
+ }
}
+ clear_clicks();
+
/* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
*/
have_looped = false;
- send_full_time_code (_transport_frame);
-
- if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
- send_mmc_locate (_transport_frame);
+ if (!_engine.freewheeling()) {
+ send_full_time_code (_transport_frame);
+
+ if (!dynamic_cast<MTC_Slave*>(_slave)) {
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdStop));
+ send_mmc_locate (_transport_frame);
+ }
}
if ((ptw & PostTransportLocate) && get_record_enabled()) {
{
play_loop = false;
clear_events (SessionEvent::AutoLoop);
-
+
// 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) {
/* nothing to do, or can't change loop status while recording */
return;
}
-
+
if (yn && Config->get_seamless_loop() && synced_to_jack()) {
warning << string_compose (
_("Seamless looping cannot be supported while %1 is using JACK transport.\n"
<< endmsg;
return;
}
-
+
if (yn) {
play_loop = true;
}
}
}
-
+
/* 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 to start of loop and roll. If doing seamless loop, force a
locate+buffer refill even if we are positioned there already.
*/
* !(playing a loop with JACK sync)
*
*/
-
+
if (transport_rolling() && (!auto_play_legal || !config.get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
realtime_stop (false, true); // XXX paul - check if the 2nd arg is really correct
} else {
if (with_roll) {
todo = PostTransportWork (todo | PostTransportRoll);
- }
+ }
add_post_transport_work (todo);
_butler->schedule_transport_work ();
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));
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("@ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4\n",
+ speed, abort, clear_state, _transport_speed, _transport_frame));
if (_transport_speed == speed) {
return;
}
+ if (actively_recording() && speed != 1.0 && speed != 0.0) {
+ /* no varispeed during recording */
+ return;
+ }
+
_target_transport_speed = fabs(speed);
/* 8.0 max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability
if (synced_to_jack ()) {
if (clear_state) {
- /* do this here because our response to the slave won't
+ /* do this here because our response to the slave won't
take care of it.
*/
_play_range = false;
} else {
stop_transport (abort);
}
-
+
unset_play_loop ();
} else if (transport_stopped() && speed == 1.0) {
}
} else {
-
+
/* not zero, not 1.0 ... varispeed */
if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
/* 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)) {
add_post_transport_work (todo);
_butler->schedule_transport_work ();
}
-
+
TransportStateChange (); /* EMIT SIGNAL */
}
}
and then we'll really be stopped.
*/
+ DEBUG_TRACE (DEBUG::Transport, string_compose ("stop transport requested @ %1, scheduled for + %2 - %3 = %4, abort = %5\n",
+ _transport_frame, _worst_input_latency, current_block_size,
+ _transport_frame - _worst_input_latency - current_block_size,
+ abort));
+
SessionEvent *ev = new SessionEvent (SessionEvent::StopOnce, SessionEvent::Replace,
_transport_frame + _worst_input_latency - current_block_size,
0, 0, abort);
_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
(*i)->automation_snapshot (_transport_frame, true);
}
- Timecode::Time time;
- timecode_time_subframes (_transport_frame, time);
- if (!dynamic_cast<MTC_Slave*>(_slave)) {
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ if (!_engine.freewheeling()) {
+ Timecode::Time time;
+ timecode_time_subframes (_transport_frame, time);
+ if (!dynamic_cast<MTC_Slave*>(_slave)) {
+ MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
+ }
}
TransportStateChange (); /* EMIT SIGNAL */
delete _slave;
_slave = new_slave;
+ send_full_time_code (_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);
try {
new_slave = new MIDIClock_Slave (*this, *MIDI::Manager::instance()->midi_clock_input_port(), 24);
}
-
+
catch (failed_constructor& err) {
return;
}
new_slave = new JACK_Slave (_engine.jack());
break;
-
+
default:
new_slave = 0;
break;
request_sync_source (new_slave);
}
-void
-Session::reverse_track_buffers ()
-{
- add_post_transport_work (PostTransportReverse);
- _butler->schedule_transport_work ();
-}
-
void
Session::set_track_speed (Track* track, double speed)
{
/* Called from event-processing context */
unset_play_range ();
-
+
if (range.empty()) {
/* _play_range set to false in unset_play_range()
*/
unset_play_loop ();
list<AudioRange>::size_type sz = range.size();
-
+
if (sz > 1) {
-
- list<AudioRange>::iterator i = range.begin();
+
+ list<AudioRange>::iterator i = range.begin();
list<AudioRange>::iterator next;
-
+
while (i != range.end()) {
-
+
next = i;
++next;
-
+
/* locating/stopping is subject to delays for declicking.
*/
-
+
framepos_t requested_frame = i->end;
-
+
if (requested_frame > current_block_size) {
requested_frame -= current_block_size;
} else {
requested_frame = 0;
}
-
+
if (next == range.end()) {
ev = new SessionEvent (SessionEvent::RangeStop, SessionEvent::Add, requested_frame, 0, 0.0f);
} else {
ev = new SessionEvent (SessionEvent::RangeLocate, SessionEvent::Add, requested_frame, (*next).start, 0.0f);
}
-
+
merge_event (ev);
-
+
i = next;
}
-
+
} else if (sz == 1) {
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. */
ev = new SessionEvent (SessionEvent::LocateRoll, SessionEvent::Add, SessionEvent::Immediate, range.front().start, 0.0f, false);
merge_event (ev);
-
+
TransportStateChange ();
}
void
Session::route_processors_changed (RouteProcessorChange c)
{
+ if (ignore_route_processor_changes) {
+ return;
+ }
+
if (c.type == RouteProcessorChange::MeterPointChange) {
return;
}
void
Session::send_mmc_locate (framepos_t t)
{
- Timecode::Time time;
- timecode_time_subframes (t, time);
- MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (time));
+ if (!_engine.freewheeling()) {
+ 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