#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/scene_changer.h"
#include "ardour/session.h"
#include "ardour/slave.h"
+#include "ardour/tempo.h"
#include "ardour/operations.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
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 (_slave && yn) {
+ // don't attempt to loop when not using Internal Transport
+ // see also gtk2_ardour/ardour_ui_options.cc parameter_changed()
+ return;
+ }
+
SessionEvent* ev;
Location *location = _locations->auto_loop_location();
double target_speed;
/* don't seek if locate will take care of that in non_realtime_stop() */
if (!(ptw & PostTransportLocate)) {
-
for (RouteList::iterator i = r->begin(); i != r->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) {
- (*i)->non_realtime_locate (_transport_frame);
+ {
+ boost::shared_ptr<RouteList> rl = routes.reader();
+ for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+ (*i)->non_realtime_locate (_transport_frame);
+ }
}
_scene_changer->locate (_transport_frame);
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 */
- 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()));
- (*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()));
+ (*i)->non_realtime_locate (_transport_frame);
- if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
- finished = false;
- /* we will be back */
- return;
+ if (on_entry != g_atomic_int_get (&_butler->should_do_transport_work)) {
+ finished = false;
+ /* we will be back */
+ return;
+ }
}
}
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 */
}
// Update Timecode time
- // [DR] FIXME: find out exactly where this should go below
_transport_frame = target_frame;
_last_roll_or_reversal_location = target_frame;
timecode_time(_transport_frame, transmitting_timecode_time);
- outbound_mtc_timecode_frame = _transport_frame;
- next_quarter_frame_to_send = 0;
/* do "stopped" stuff if:
*
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 ()) {
+ if (tr && tr->rec_enable_control()->get_value()) {
// tell it we've looped, so it can deal with the record state
tr->transport_looped (_transport_frame);
}
}
_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 ();
}
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;
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;
void
Session::route_processors_changed (RouteProcessorChange c)
{
- if (ignore_route_processor_changes) {
+ if (g_atomic_int_get (&_ignore_route_processor_changes) > 0) {
return;
}