} else {
framepos_t c = current_time();
- if (c > frames) {
+ if (c > frames || _negative_allowed) {
set (c - frames, true);
} else {
set (0, true);
void
AudioClock::session_configuration_changed (std::string p)
{
+ if (_negative_allowed) {
+ /* session option editor clock */
+ return;
+ }
+
if (p == "sync-source" || p == "external-sync") {
set (current_time(), true);
return;
AudioClock::timecode_validate_edit (const string&)
{
Timecode::Time TC;
+ int hours;
char ignored[2];
if (sscanf (_layout->get_text().c_str(), "%" PRId32 ":%" PRId32 ":%" PRId32 "%[:;]%" PRId32,
- &TC.hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+ &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+ return false;
+ }
+
+ if (hours < 0) {
+ TC.hours = hours * -1;
+ TC.negative = true;
+ } else {
+ TC.hours = hours;
+ TC.negative = false;
+ }
+
+ if (TC.negative && !_negative_allowed) {
return false;
}
Timecode::Time TC;
framepos_t sample;
char ignored[2];
+ int hours;
- if (sscanf (str.c_str(), "%d:%d:%d%[:;]%d", &TC.hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
+ if (sscanf (str.c_str(), "%d:%d:%d%[:;]%d", &hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 5) {
error << string_compose (_("programming error: %1 %2"), "badly formatted timecode clock string", str) << endmsg;
return 0;
}
-
- TC.negative = edit_is_negative;
+ TC.hours = abs(hours);
TC.rate = _session->timecode_frames_per_second();
TC.drop= _session->timecode_drop_frames();
_session->timecode_to_sample (TC, sample, false /* use_offset */, false /* use_subframes */ );
// timecode_tester ();
+ if (edit_is_negative) {
+ sample = - sample;
+ }
return sample;
}
#include "ardour/rc_configuration.h"
#include "ardour/utils.h"
#include "ardour/dB.h"
+#include "ardour/session.h"
#include "option_editor.h"
#include "gui_thread.h"
add_widgets_to_page (p, &_label, &_box);
}
-ClockOption::ClockOption (string const & i, string const & n, sigc::slot<framecnt_t> g, sigc::slot<bool, framecnt_t> s)
+ClockOption::ClockOption (string const & i, string const & n, sigc::slot<std::string> g, sigc::slot<bool, std::string> s)
: Option (i, n)
, _clock (X_("timecode-offset"), false, X_(""), true, false, true, false)
, _get (g)
void
ClockOption::set_state_from_config ()
{
- _clock.set (_get (), true);
+ Timecode::Time TC;
+ framepos_t when;
+ if (!Timecode::parse_timecode_format(_get(), TC)) {
+ _clock.set (0, true);
+ }
+ TC.rate = _session->frames_per_timecode_frame();
+ TC.drop = _session->timecode_drop_frames();
+ _session->timecode_to_sample(TC, when, false, false);
+ if (TC.negative) { when=-when; }
+ _clock.set (when, true);
}
void
ClockOption::save_clock_time ()
{
- _set (_clock.current_time());
+ Timecode::Time TC;
+ _session->sample_to_timecode(_clock.current_time(), TC, false, false);
+ _set (Timecode::timecode_format_time(TC));
}
void
void
ClockOption::set_session (Session* s)
{
+ _session = s;
_clock.set_session (s);
}
class ClockOption : public Option
{
public:
- ClockOption (std::string const &, std::string const &, sigc::slot<ARDOUR::framecnt_t>, sigc::slot<bool, ARDOUR::framecnt_t>);
+ ClockOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
void set_state_from_config ();
void add_to_page (OptionEditorPage *);
void set_session (ARDOUR::Session *);
void save_clock_time ();
Gtk::Label _label;
AudioClock _clock;
- sigc::slot<ARDOUR::framecnt_t> _get;
- sigc::slot<bool, ARDOUR::framecnt_t> _set;
+ sigc::slot<std::string> _get;
+ sigc::slot<bool, std::string> _set;
+ ARDOUR::Session *_session;
};
class DirectoryOption : public Option
add_option (_("Timecode"), _vpu);
- ClockOption* co = new ClockOption (
- "timecode-offset",
- _("Timecode offset"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_offset),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_offset)
+
+ add_option (_("Timecode"), new OptionEditorHeading (_("Ext Timecode Offsets")));
+
+ ClockOption* sco = new ClockOption (
+ "slave-timecode-offset",
+ _("Slave Timecode offset"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_slave_timecode_offset),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_slave_timecode_offset)
);
- co->set_session (_session);
- co->clock().set_negative_allowed (true);
+ sco->set_session (_session);
+ sco->clock().set_negative_allowed (true);
- add_option (_("Timecode"), co);
+ add_option (_("Timecode"), sco);
- add_option (_("Timecode"), new BoolOption (
- "timecode-offset-negative",
- _("Timecode Offset Negative"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_offset_negative),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_offset_negative)
- ));
+ ClockOption* gco = new ClockOption (
+ "timecode-generator-offset",
+ _("Timecode Generator offset"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_timecode_generator_offset),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_timecode_generator_offset)
+ );
+
+ gco->set_session (_session);
+ gco->clock().set_negative_allowed (true);
+
+ add_option (_("Timecode"), gco);
add_option (_("Timecode"), new OptionEditorHeading (_("JACK Transport/Time Settings")));
_vpu->set_sensitive(true);
}
}
-
+ if (p == "timecode-format") {
+ /* update offset clocks */
+ parameter_changed("timecode-generator-offset");
+ parameter_changed("slave-timecode-offset");
+ }
}
/* the presence of absence of a monitor section is not really a regular session
framepos_t ltc_enc_off;
bool restarting;
+ framepos_t ltc_timecode_offset;
+ bool ltc_timecode_negative_offset;
+
jack_latency_range_t ltc_out_latency;
void ltc_tx_initialize();
void ltc_tx_reset();
void ltc_tx_resync_latency();
void ltc_tx_recalculate_position();
+ void ltc_tx_parse_offset();
void ltc_tx_send_time_code_for_cycle (framepos_t, framepos_t, double, double, pframes_t nframes);
#endif
CONFIG_VARIABLE (InsertMergePolicy, insert_merge_policy, "insert-merge-policy", InsertMergeRelax)
CONFIG_VARIABLE (framecnt_t, timecode_offset, "timecode-offset", 0)
CONFIG_VARIABLE (bool, timecode_offset_negative, "timecode-offset-negative", true)
-CONFIG_VARIABLE (framecnt_t, slave_timecode_offset, "slave-timecode-offset", 0)
-CONFIG_VARIABLE (bool, slave_timecode_offset_negative, "slave-timecode-offset-negative", true)
-CONFIG_VARIABLE (framecnt_t, timecode_generator_offset, "timecode-generator-offset", 0)
-CONFIG_VARIABLE (bool, timecode_generator_offset_negative, "timecode-generator-offset-negative", true)
+CONFIG_VARIABLE (std::string, slave_timecode_offset, "slave-timecode-offset", " 00:00:00:00")
+CONFIG_VARIABLE (std::string, timecode_generator_offset, "timecode-generator-offset", " 00:00:00:00")
CONFIG_VARIABLE (bool, glue_new_markers_to_bars_and_beats, "glue-new-markers-to-bars-and-beats", false)
CONFIG_VARIABLE (bool, midi_copy_is_fork, "midi-copy-is-fork", false)
CONFIG_VARIABLE (bool, glue_new_regions_to_bars_and_beats, "glue-new-regions-to-bars-and-beats", false)
of the TC source position.
*/
virtual std::string approximate_current_position() const = 0;
+
+ framepos_t timecode_offset;
+ bool timecode_negative_offset;
};
class MTC_Slave : public TimecodeSlave {
Session& session;
MIDI::Port* port;
PBD::ScopedConnectionList port_connections;
+ PBD::ScopedConnection config_connection;
bool can_notify_on_unknown_rate;
static const int frame_tolerance;
bool outside_window (framepos_t) const;
void init_mtc_dll(framepos_t, double);
void init_engine_dll (framepos_t, framepos_t);
+ void parse_timecode_offset();
+ void parameter_changed(std::string const & p);
};
#ifdef HAVE_LTC
void reset();
void resync_xrun();
void resync_latency();
+ void parse_timecode_offset();
+ void parameter_changed(std::string const & p);
Session& session;
bool did_reset_tc_format;
Timecode::TimecodeFormat a3e_timecode;
PBD::ScopedConnectionList port_connections;
+ PBD::ScopedConnection config_connection;
jack_latency_range_t ltc_slave_latency;
/* DLL - chase LTC */
memset(&prev_frame, 0, sizeof(LTCFrameExt));
decoder = ltc_decoder_create((int) frames_per_ltc_frame, 128 /*queue size*/);
+
+ session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (<C_Slave::parameter_changed, this, _1));
+ parse_timecode_offset();
reset();
resync_latency();
session.Xrun.connect_same_thread (port_connections, boost::bind (<C_Slave::resync_xrun, this));
LTC_Slave::~LTC_Slave()
{
port_connections.drop_connections();
+ config_connection.disconnect();
if (did_reset_tc_format) {
session.config.set_timecode_format (saved_tc_format);
ltc_decoder_free(decoder);
}
+void
+LTC_Slave::parse_timecode_offset() {
+ Timecode::Time offset_tc;
+ Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
+ offset_tc.rate = session.timecode_frames_per_second();
+ offset_tc.drop = session.timecode_drop_frames();
+ session.timecode_to_sample(offset_tc, timecode_offset, false, false);
+ timecode_negative_offset = offset_tc.negative;
+}
+
+void
+LTC_Slave::parameter_changed (std::string const & p)
+{
+ if (p == "slave-timecode-offset"
+ || p == "subframes-per-frame"
+ || p == "timecode-format"
+ ) {
+ parse_timecode_offset();
+ }
+}
+
ARDOUR::framecnt_t
LTC_Slave::resolution () const
{
Timecode::timecode_to_sample (timecode, ltc_frame, true, false,
double(session.frame_rate()),
session.config.get_subframes_per_frame(),
- session.config.get_slave_timecode_offset_negative(), session.config.get_slave_timecode_offset()
+ timecode_negative_offset, timecode_offset
);
framepos_t cur_timestamp = frame.off_end + 1;
a3e_timecode = session.config.get_timecode_format();
printed_timecode_warning = false;
+ session.config.ParameterChanged.connect_same_thread (config_connection, boost::bind (&MTC_Slave::parameter_changed, this, _1));
+ parse_timecode_offset();
reset (true);
rebind (p);
}
MTC_Slave::~MTC_Slave()
{
port_connections.drop_connections();
+ config_connection.disconnect();
while (busy_guard1 != busy_guard2) {
/* make sure MIDI parser is not currently calling any callbacks in here,
port->parser()->mtc_status.connect_same_thread (port_connections, boost::bind (&MTC_Slave::update_mtc_status, this, _1));
}
+void
+MTC_Slave::parse_timecode_offset() {
+ Timecode::Time offset_tc;
+ Timecode::parse_timecode_format(session.config.get_slave_timecode_offset(), offset_tc);
+ offset_tc.rate = session.timecode_frames_per_second();
+ offset_tc.drop = session.timecode_drop_frames();
+ session.timecode_to_sample(offset_tc, timecode_offset, false, false);
+ timecode_negative_offset = offset_tc.negative;
+}
+
+void
+MTC_Slave::parameter_changed (std::string const & p)
+{
+ if (p == "slave-timecode-offset"
+ || p == "subframes-per-frame"
+ || p == "timecode-format"
+ ) {
+ parse_timecode_offset();
+ }
+}
+
bool
MTC_Slave::give_slave_full_control_over_transport_speed() const
{
Timecode::timecode_to_sample (timecode, mtc_frame, true, false,
double(session.frame_rate()),
session.config.get_subframes_per_frame(),
- session.config.get_slave_timecode_offset_negative(), session.config.get_slave_timecode_offset()
+ timecode_negative_offset, timecode_offset
);
DEBUG_TRACE (DEBUG::MTC, string_compose ("MTC at %1 TC %2 = mtc_frame %3 (from full message ? %4) tc-ratio %5\n",
{
ltc_enc_tcformat = config.get_timecode_format();
+ ltc_tx_parse_offset();
DEBUG_TRACE (DEBUG::LTC, string_compose("LTC TX init sr: %1 fps: %2\n", nominal_frame_rate(), timecode_to_frames_per_second(ltc_enc_tcformat)));
ltc_encoder = ltc_encoder_create(nominal_frame_rate(),
timecode_to_frames_per_second(ltc_enc_tcformat),
ltc_encoder_reset(ltc_encoder);
}
+void
+Session::ltc_tx_parse_offset() {
+ Timecode::Time offset_tc;
+ Timecode::parse_timecode_format(config.get_timecode_generator_offset(), offset_tc);
+ offset_tc.rate = timecode_frames_per_second();
+ offset_tc.drop = timecode_drop_frames();
+ timecode_to_sample(offset_tc, ltc_timecode_offset, false, false);
+ ltc_timecode_negative_offset = !offset_tc.negative;
+}
+
void
Session::ltc_tx_recalculate_position()
{
Timecode::timecode_to_sample (a3tc, ltc_enc_pos, true, false,
(double)frame_rate(),
config.get_subframes_per_frame(),
- config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+ ltc_timecode_negative_offset, ltc_timecode_offset
);
restarting = false;
}
}
ltc_encoder_set_filter(ltc_encoder, LTC_RISE_TIME(ltc_speed));
ltc_enc_tcformat = cur_timecode;
+ ltc_tx_parse_offset();
ltc_tx_reset();
}
timecode_drop_frames(),
(double)frame_rate(),
config.get_subframes_per_frame(),
- config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+ ltc_timecode_negative_offset, ltc_timecode_offset
);
/* convert timecode back to sample-position */
Timecode::timecode_to_sample (tc_start, tc_sample_start, true, false,
(double)frame_rate(),
config.get_subframes_per_frame(),
- config.get_timecode_generator_offset_negative(), config.get_timecode_generator_offset()
+ ltc_timecode_negative_offset, ltc_timecode_offset
);
/* difference between current frame and TC frame in samples */
reconnect_ltc_input ();
} else if (p == "ltc-sink-port") {
reconnect_ltc_output ();
+#ifdef HAVE_LTC
+ } else if (p == "timecode-generator-offset") {
+ ltc_tx_parse_offset();
+#endif
}
set_dirty ();
}
break;
};
+#ifdef HAVE_LTC
+ ltc_tx_parse_offset();
+#endif
}
void
#include <math.h>
#include <stdio.h>
+#include <stdlib.h>
#include "timecode/time.h"
int64_t offset_sample;
if (!use_offset) {
- offset_sample = sample;
- timecode.negative = false;
+ timecode.negative = (sample < 0);
+ offset_sample = llabs(sample);
} else {
if (offset_is_negative) {
offset_sample = sample + offset_samples;