#include "ardour/audioengine.h"
#include "ardour/auditioner.h"
+#include "ardour/automation_watch.h"
#include "ardour/butler.h"
#include "ardour/click.h"
#include "ardour/debug.h"
+#include "ardour/disk_reader.h"
#include "ardour/location.h"
#include "ardour/profile.h"
#include "ardour/scene_changer.h"
#include "ardour/session.h"
#include "ardour/slave.h"
+#include "ardour/tempo.h"
#include "ardour/operations.h"
+#include "ardour/vca.h"
+#include "ardour/vca_manager.h"
#include "pbd/i18n.h"
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))) {
- SessionEvent* ev = new SessionEvent (SessionEvent::InputConfigurationChange, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
- queue_event (ev);
- }
-}
-
void
Session::request_sync_source (Slave* new_slave)
{
request_transport_speed (speed, as_default);
}
-void
-Session::request_track_speed (Track* tr, double speed)
-{
- 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)
{
queue_event (ev);
}
+void
+Session::unset_preroll_record_punch ()
+{
+ if (_preroll_record_punch_pos >= 0) {
+ remove_event (_preroll_record_punch_pos, SessionEvent::RecordStart);
+ }
+ _preroll_record_punch_pos = -1;
+}
+
+void
+Session::unset_preroll_record_trim ()
+{
+ _preroll_record_trim_len = 0;
+}
+
+void
+Session::request_preroll_record_punch (framepos_t rec_in, framecnt_t preroll)
+{
+ if (actively_recording ()) {
+ return;
+ }
+ unset_preroll_record_punch ();
+ unset_preroll_record_trim ();
+ framepos_t start = std::max ((framepos_t)0, rec_in - preroll);
+
+ _preroll_record_punch_pos = rec_in;
+ if (_preroll_record_punch_pos >= 0) {
+ replace_event (SessionEvent::RecordStart, _preroll_record_punch_pos);
+ config.set_punch_in (false);
+ config.set_punch_out (false);
+ }
+ maybe_enable_record ();
+ request_locate (start, true);
+ set_requested_return_frame (rec_in);
+}
+
+void
+Session::request_preroll_record_trim (framepos_t rec_in, framecnt_t preroll)
+{
+ if (actively_recording ()) {
+ return;
+ }
+ unset_preroll_record_punch ();
+ unset_preroll_record_trim ();
+
+ config.set_punch_in (false);
+ config.set_punch_out (false);
+
+ framepos_t pos = std::max ((framepos_t)0, rec_in - preroll);
+ _preroll_record_trim_len = preroll;
+ maybe_enable_record ();
+ request_locate (pos, true);
+ set_requested_return_frame (rec_in);
+}
+
+void
+Session::request_count_in_record ()
+{
+ if (actively_recording ()) {
+ return;
+ }
+ if (transport_rolling()) {
+ return;
+ }
+ maybe_enable_record ();
+ _count_in_once = true;
+ request_transport_speed (1.0, true);
+}
+
void
Session::request_play_loop (bool yn, bool change_transport_roll)
{
}
if (ptw & PostTransportAdjustPlaybackBuffering) {
- /* non_realtime_locate() calls Automatable::transport_located()
- * for every route. This eventually calls
- * ARDOUR::AutomationList::state () which has a LocaleGuard,
- * and would switch locales forth/back every time.
- */
- LocaleGuard lg;
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
if (tr) {
}
(*i)->non_realtime_locate (_transport_frame);
}
-
+ VCAList v = _vca_manager->vcas ();
+ for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+ (*i)->non_realtime_locate (_transport_frame);
+ }
}
if (ptw & PostTransportAdjustCaptureBuffering) {
}
}
- if (ptw & PostTransportInputChange) {
- 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 & PostTransportSpeed) {
non_realtime_set_speed ();
}
/* don't seek if locate will take care of that in non_realtime_stop() */
if (!(ptw & PostTransportLocate)) {
- LocaleGuard lg; // see note for non_realtime_locate() above
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
(*i)->non_realtime_locate (_transport_frame);
goto restart;
}
}
+ VCAList v = _vca_manager->vcas ();
+ for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+ (*i)->non_realtime_locate (_transport_frame);
+ }
}
}
g_atomic_int_dec_and_test (&_butler->should_do_transport_work);
- DEBUG_TRACE (DEBUG::Transport, string_compose (X_("Butler transport work all done after %1 usecs\n"), g_get_monotonic_time() - before));
- DEBUG_TRACE (DEBUG::Transport, X_(string_compose ("Frame %1\n", _transport_frame)));
+ DEBUG_TRACE (DEBUG::Transport, string_compose (X_("Butler transport work all done after %1 usecs @ %2 trw = %3\n"), g_get_monotonic_time() - before, _transport_frame, _butler->transport_work_requested()));
}
void
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 ();
+ tr->non_realtime_speed_change ();
}
}
}
}
+ microseconds_t begin = get_microseconds ();
+ framepos_t tf;
+
{
- LocaleGuard lg; // see note for non_realtime_locate() above
boost::shared_ptr<RouteList> rl = routes.reader();
+
+ restart:
+ gint sc = g_atomic_int_get (&_seek_counter);
+ tf = _transport_frame;
+
+ cerr << "\n\n >>> START Non-RT locate on routes to " << tf << " counter = " << sc << "\n\n";
+
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
- (*i)->non_realtime_locate (_transport_frame);
+ (*i)->non_realtime_locate (tf);
+ //::usleep (250000);
+ cerr << "\t\tcounter after track: " << g_atomic_int_get (&_seek_counter) << endl;
+ if (sc != g_atomic_int_get (&_seek_counter)) {
+ cerr << "\n\n RESTART locate, new seek delivered\n";
+ goto restart;
+ }
}
+
+ cerr << "\n\n <<< DONE Non-RT locate on routes\n\n";
}
+ {
+ /* VCAs are quick to locate because they have no data (except
+ automation) associated with them. Don't bother with a
+ restart mechanism here, but do use the same transport frame
+ that the Routes used.
+ */
+ VCAList v = _vca_manager->vcas ();
+ for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+ (*i)->non_realtime_locate (tf);
+ }
+ }
+
+ microseconds_t end = get_microseconds ();
+ cerr << "Locate took " << setprecision (3) << ((end - begin) /1000000.0) << " secs\n";
+
_scene_changer->locate (_transport_frame);
/* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
if (_engine.running()) {
PostTransportWork ptw = post_transport_work ();
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
- (*i)->nonrealtime_handle_transport_stopped (abort, (ptw & PostTransportLocate), (!(ptw & PostTransportLocate) || pending_locate_flush));
+ (*i)->non_realtime_transport_stop (_transport_frame, !(ptw & PostTransportLocate) || pending_locate_flush);
}
+ VCAList v = _vca_manager->vcas ();
+ for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+ (*i)->non_realtime_transport_stop (_transport_frame, !(ptw & PostTransportLocate) || pending_locate_flush);
+ }
+
update_latency_compensation ();
}
flush_all_inserts ();
}
+ // rg: what is the logic behind this case?
+ // _requested_return_frame should be ignored when synced_to_engine/slaved.
+ // currently worked around in MTC_Slave by forcing _requested_return_frame to -1
+ // 2016-01-10
if ((auto_return_enabled || synced_to_engine() || _requested_return_frame >= 0) &&
!(ptw & PostTransportLocate)) {
}
clear_clicks();
+ unset_preroll_record_trim ();
/* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
*/
/* this for() block can be put inside the previous if() and has the effect of ... ??? what */
{
- LocaleGuard lg; // see note for non_realtime_locate() above
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()));
}
}
+ {
+ VCAList v = _vca_manager->vcas ();
+ for (VCAList::const_iterator i = v.begin(); i != v.end(); ++i) {
+ (*i)->non_realtime_locate (_transport_frame);
+ }
+ }
+
have_looped = false;
/* don't bother with this stuff if we're disconnected from the engine,
PositionChanged (_transport_frame); /* EMIT SIGNAL */
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC with speed = %1\n", _transport_speed));
TransportStateChange (); /* EMIT SIGNAL */
+ AutomationWatch::instance().transport_stop_automation_watches (_transport_frame);
/* and start it up again if relevant */
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 && !tr->is_private_route()) {
tr->set_loop (yn ? loc : 0);
}
}
pending_locate_frame = target_frame;
pending_locate_roll = with_roll;
pending_locate_flush = with_flush;
+ cerr << "Declick scheduled ... back soon\n";
return;
}
}
+ cerr << "... now doing the actual locate\n";
+
// Update Timecode time
_transport_frame = target_frame;
+ // Bump seek counter so that any in-process locate in the butler
+ // thread(s?) can restart.
+ g_atomic_int_inc (&_seek_counter);
_last_roll_or_reversal_location = target_frame;
timecode_time(_transport_frame, transmitting_timecode_time);
}
_last_roll_location = _last_roll_or_reversal_location = _transport_frame;
- Located (); /* EMIT SIGNAL */
+ if (!synced_to_engine () || _transport_frame == _engine.transport_frame ()) {
+ Located (); /* EMIT SIGNAL */
+ }
}
/** Set the transport speed.
take care of it.
*/
_play_range = false;
+ _count_in_once = false;
unset_play_loop ();
}
_engine.transport_stop ();
if (synced_to_engine()) {
_engine.transport_start ();
+ _count_in_once = false;
} else {
start_transport ();
}
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)) {
+ if (tr && tr->realtime_speed_change()) {
todo = PostTransportWork (todo | PostTransportSpeed);
}
}
void
Session::stop_transport (bool abort, bool clear_state)
{
+ _count_in_once = false;
if (_transport_speed == 0.0f) {
return;
}
switch (record_status()) {
case Enabled:
- if (!config.get_punch_in()) {
+ if (!config.get_punch_in() && !preroll_record_punch_enabled()) {
enable_record ();
}
break;
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);
+ tr->realtime_speed_change ();
}
}
if (!dynamic_cast<MTC_Slave*>(_slave)) {
send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdDeferredPlay));
}
+
+ if (actively_recording() && click_data && (config.get_count_in () || _count_in_once)) {
+ _count_in_once = false;
+ /* calculate count-in duration (in audio samples)
+ * - use [fixed] tempo/meter at _transport_frame
+ * - calc duration of 1 bar + time-to-beat before or at transport_frame
+ */
+ const Tempo& tempo = _tempo_map->tempo_at_frame (_transport_frame);
+ const Meter& meter = _tempo_map->meter_at_frame (_transport_frame);
+
+ const double num = meter.divisions_per_bar ();
+ const double den = meter.note_divisor ();
+ const double barbeat = _tempo_map->exact_qn_at_frame (_transport_frame, 0) * den / (4. * num);
+ const double bar_fract = fmod (barbeat, 1.0); // fraction of bar elapsed.
+
+ _count_in_samples = meter.frames_per_bar (tempo, _current_frame_rate);
+
+ double dt = _count_in_samples / num;
+ if (bar_fract == 0) {
+ /* at bar boundary, count-in 2 bars before start. */
+ _count_in_samples *= 2;
+ } else {
+ /* beats left after full bar until roll position */
+ _count_in_samples *= 1. + bar_fract;
+ }
+
+ int clickbeat = 0;
+ framepos_t cf = _transport_frame - _count_in_samples;
+ while (cf < _transport_frame) {
+ add_click (cf - _worst_track_latency, clickbeat == 0);
+ cf += dt;
+ clickbeat = fmod (clickbeat + 1, num);
+ }
+ }
}
DEBUG_TRACE (DEBUG::Transport, string_compose ("send TSC4 with speed = %1\n", _transport_speed));
if (ptw & PostTransportLocate) {
if (((!config.get_external_sync() && (auto_play_legal && config.get_auto_play())) && !_exporting) || (ptw & PostTransportRoll)) {
+ _count_in_once = false;
start_transport ();
} else {
transport_sub_state = 0;
delete _slave;
_slave = new_slave;
+
+ /* slave change, reset any DiskIO block on disk output because it is no
+ longer valid with a new slave.
+ */
+ DiskReader::set_no_disk_output (false);
+
MTC_Slave* mtc_slave = dynamic_cast<MTC_Slave*>(_slave);
if (mtc_slave) {
mtc_slave->ActiveChanged.connect_same_thread (mtc_status_connection, boost::bind (&Session::mtc_status_changed, this, _1));
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)) {
+ if (tr && !tr->is_private_route()) {
+ if (tr->realtime_speed_change()) {
non_rt_required = true;
}
tr->set_slaved (_slave != 0);
request_sync_source (new_slave);
}
-void
-Session::set_track_speed (Track* track, double speed)
-{
- if (track->realtime_set_speed (speed, false)) {
- add_post_transport_work (PostTransportSpeed);
- _butler->schedule_transport_work ();
- set_dirty ();
- }
-}
-
void
Session::unset_play_range ()
{
void
Session::route_processors_changed (RouteProcessorChange c)
{
- if (ignore_route_processor_changes) {
+ if (g_atomic_int_get (&_ignore_route_processor_changes) > 0) {
return;
}