new transport slave/master implementation, gui edition

This commit is contained in:
Paul Davis 2018-09-18 18:52:20 -04:00
parent e6915e01de
commit db385c2e3c
16 changed files with 655 additions and 167 deletions

View file

@ -556,6 +556,7 @@
#endif
<menuitem action='toggle-big-clock'/>
<menuitem action='toggle-big-transport'/>
<menuitem action='toggle-transport-masters'/>
#if 0
<menuitem action='toggle-speaker-config'/>
#endif

View file

@ -105,7 +105,8 @@
#include "ardour/session_state_utils.h"
#include "ardour/session_utils.h"
#include "ardour/source_factory.h"
#include "ardour/slave.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "ardour/system_exec.h"
#include "ardour/track.h"
#include "ardour/vca_manager.h"
@ -185,6 +186,7 @@ typedef uint64_t microseconds_t;
#include "time_axis_view_item.h"
#include "time_info_box.h"
#include "timers.h"
#include "transport_masters_dialog.h"
#include "utils.h"
#include "utils_videotl.h"
#include "video_server_dialog.h"
@ -314,6 +316,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
, export_video_dialog (X_("video-export"), _("Video Export Dialog"))
, lua_script_window (X_("script-manager"), _("Script Manager"))
, idleometer (X_("idle-o-meter"), _("Idle'o'Meter"))
, transport_masters_dialog (X_("transport-masters"), _("Transport Masters"))
, session_option_editor (X_("session-options-editor"), _("Properties"), boost::bind (&ARDOUR_UI::create_session_option_editor, this))
, add_video_dialog (X_("add-video"), _("Add Video"), boost::bind (&ARDOUR_UI::create_add_video_dialog, this))
, bundle_manager (X_("bundle-manager"), _("Bundle Manager"), boost::bind (&ARDOUR_UI::create_bundle_manager, this))
@ -473,6 +476,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
export_video_dialog.set_state (*ui_xml, 0);
lua_script_window.set_state (*ui_xml, 0);
idleometer.set_state (*ui_xml, 0);
transport_masters_dialog.set_state (*ui_xml, 0);
}
/* Separate windows */
@ -494,6 +498,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
WM::Manager::instance().register_window (&audio_port_matrix);
WM::Manager::instance().register_window (&midi_port_matrix);
WM::Manager::instance().register_window (&idleometer);
WM::Manager::instance().register_window (&transport_masters_dialog);
/* do not retain position for add route dialog */
add_route_dialog.set_state_mask (WindowProxy::Size);
@ -1801,11 +1806,11 @@ ARDOUR_UI::update_timecode_format ()
if (_session) {
bool matching;
TimecodeSlave* tcslave;
SyncSource sync_src = Config->get_sync_source();
boost::shared_ptr<TimecodeTransportMaster> tcmaster;
boost::shared_ptr<TransportMaster> tm = TransportMasterManager::instance().current();
if ((sync_src == LTC || sync_src == MTC) && (tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
if ((tm->type() == LTC || tm->type() == MTC) && (tcmaster = boost::dynamic_pointer_cast<TimecodeTransportMaster>(tm)) != 0) {
matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format());
} else {
matching = true;
}

View file

@ -101,6 +101,7 @@
#include "route_params_ui.h"
#include "session_option_editor.h"
#include "speaker_dialog.h"
#include "transport_masters_dialog.h"
#else
class About;
class AddRouteDialog;
@ -119,6 +120,7 @@ class SessionOptionEditor;
class SpeakerDialog;
class GlobalPortMatrixWindow;
class IdleOMeter;
class TransportMastersDialog;
#endif
class VideoTimeLine;
@ -680,6 +682,7 @@ private:
WM::Proxy<ExportVideoDialog> export_video_dialog;
WM::Proxy<LuaScriptManager> lua_script_window;
WM::Proxy<IdleOMeter> idleometer;
WM::Proxy<TransportMastersDialog> transport_masters_dialog;
/* Windows/Dialogs that require a creator method */

View file

@ -72,6 +72,7 @@
#include "sfdb_ui.h"
#include "time_info_box.h"
#include "timers.h"
#include "transport_masters_dialog.h"
#include "pbd/i18n.h"
@ -127,6 +128,8 @@ ARDOUR_UI::set_session (Session *s)
big_clock->set_session (s);
video_timeline->set_session (s);
lua_script_window->set_session (s);
transport_masters_dialog->set_session (s);
rc_option_editor->set_session (s);
/* sensitize menu bar options that are now valid */

View file

@ -68,7 +68,7 @@ when the pull up/down setting is non-zero."));
* This is a UI limitation, imposed by audio-clock and
* status displays which combine RC-config & session-properties.
*
* Notficy RCOptionEditor by emitting a signal if the active
* Notify RCOptionEditor by emitting a signal if the active
* status changed:
*/
Config->ParameterChanged("sync-source");
@ -349,6 +349,8 @@ ARDOUR_UI::parameter_changed (std::string p)
{
if (p == "external-sync") {
/* session parameter */
ActionManager::map_some_state ("Transport", "ToggleExternalSync", sigc::mem_fun (_session->config, &SessionConfiguration::get_external_sync));
if (!_session->config.get_external_sync()) {
@ -357,19 +359,27 @@ ARDOUR_UI::parameter_changed (std::string p)
ActionManager::get_action ("Transport", "ToggleAutoReturn")->set_sensitive (true);
ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (true);
} else {
sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
if (_session && _session->locations()->auto_loop_location()) {
// disable looping with external sync.
// This is not necessary because session-transport ignores the loop-state,
// but makes it clear to the user that it's disabled.
_session->request_play_loop (false, false);
}
/* XXX we need to make sure that auto-play is off as well as insensitive */
ActionManager::get_action ("Transport", "ToggleAutoPlay")->set_sensitive (false);
ActionManager::get_action ("Transport", "ToggleAutoReturn")->set_sensitive (false);
ActionManager::get_action ("Transport", "ToggleFollowEdits")->set_sensitive (false);
}
} else if (p == "sync-source") {
/* app parameter (RC config) */
if (_session) {
if (!_session->config.get_external_sync()) {
sync_button.set_text (S_("SyncSource|Int."));
} else {
sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
}
} else {
/* changing sync source without a session is unlikely/impossible , except during startup */
sync_button.set_text (sync_source_to_string (Config->get_sync_source(), true));
}
} else if (p == "follow-edits") {
ActionManager::map_some_state ("Transport", "ToggleFollowEdits", &UIConfiguration::get_follow_edits);
@ -598,4 +608,3 @@ ARDOUR_UI::synchronize_sync_source_and_video_pullup ()
}
}

View file

@ -34,7 +34,7 @@
#include "ardour/profile.h"
#include "ardour/lmath.h"
#include "ardour/session.h"
#include "ardour/slave.h"
#include "ardour/transport_master.h"
#include "ardour/tempo.h"
#include "ardour/types.h"
@ -938,20 +938,21 @@ AudioClock::set_slave_info ()
return;
}
SyncSource sync_src = Config->get_sync_source();
const SyncSource sync_src = Config->get_sync_source();
if (_session->config.get_external_sync()) {
Slave* slave = _session->slave();
if (_session->transport_master_is_external()) {
switch (sync_src) {
boost::shared_ptr<TransportMaster> tm = _session->transport_master();
switch (tm->type()) {
case Engine:
_left_btn.set_text (sync_source_to_string (sync_src, true), true);
_left_btn.set_text (tm->name(), true);
_right_btn.set_text ("", true);
break;
case MIDIClock:
if (slave) {
_left_btn.set_text (sync_source_to_string (sync_src, true), true);
_right_btn.set_text (slave->approximate_current_delta (), true);
if (tm) {
_left_btn.set_text (sync_source_to_string (tm->type(), true), true);
_right_btn.set_text (tm->delta_string (), true);
} else {
_left_btn.set_text (_("--pending--"), true);
_right_btn.set_text ("", true);
@ -959,17 +960,17 @@ AudioClock::set_slave_info ()
break;
case LTC:
case MTC:
if (slave) {
if (tm) {
bool matching;
TimecodeSlave* tcslave;
if ((tcslave = dynamic_cast<TimecodeSlave*>(_session->slave())) != 0) {
matching = (tcslave->apparent_timecode_format() == _session->config.get_timecode_format());
boost::shared_ptr<TimecodeTransportMaster> tcmaster;
if ((tcmaster = boost::dynamic_pointer_cast<TimecodeTransportMaster>(tm)) != 0) {
matching = (tcmaster->apparent_timecode_format() == _session->config.get_timecode_format());
_left_btn.set_text (string_compose ("%1<span face=\"monospace\" foreground=\"%3\">%2</span>",
sync_source_to_string(sync_src, true)[0],
dynamic_cast<TimecodeSlave*>(slave)->approximate_current_position (),
sync_source_to_string(tm->type(), true)[0],
tcmaster->position_string (),
matching ? "#66ff66" : "#ff3333"
), true);
_right_btn.set_text (slave->approximate_current_delta (), true);
_right_btn.set_text (tm->delta_string (), true);
}
} else {
_left_btn.set_text (_("--pending--"), true);
@ -978,8 +979,7 @@ AudioClock::set_slave_info ()
break;
}
} else {
_left_btn.set_text (string_compose ("%1/%2",
_("INT"), sync_source_to_string(sync_src, true)), true);
_left_btn.set_text (string_compose ("%1/%2", _("INT"), sync_source_to_string (sync_src, true)), true);
_right_btn.set_text ("", true);
}
}

View file

@ -46,6 +46,7 @@ MidiTracer::MidiTracer ()
, line_count_adjustment (200, 1, 2000, 1, 10)
, line_count_spinner (line_count_adjustment)
, line_count_label (_("Line history: "))
, _last_receipt (0)
, autoscroll (true)
, show_hex (true)
, show_delta_time (false)
@ -60,9 +61,6 @@ MidiTracer::MidiTracer ()
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect
(_manager_connection, invalidator (*this), boost::bind (&MidiTracer::ports_changed, this), gui_context());
_last_receipt.tv_sec = 0;
_last_receipt.tv_usec = 0;
VBox* vbox = manage (new VBox);
vbox->set_spacing (4);
@ -121,7 +119,6 @@ MidiTracer::MidiTracer ()
port_changed ();
}
MidiTracer::~MidiTracer()
{
}
@ -178,13 +175,13 @@ MidiTracer::port_changed ()
boost::shared_ptr<ARDOUR::MidiPort> mp = boost::dynamic_pointer_cast<ARDOUR::MidiPort> (p);
if (mp) {
mp->self_parser().any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
mp->set_trace_on (true);
my_parser.any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4));
mp->set_trace (&my_parser);
traced_port = mp;
}
} else {
async->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3));
async->parser()->any.connect_same_thread (_parser_connection, boost::bind (&MidiTracer::tracer, this, _1, _2, _3, _4));
}
}
@ -194,40 +191,33 @@ MidiTracer::disconnect ()
_parser_connection.disconnect ();
if (traced_port) {
traced_port->set_trace_on (false);
traced_port->set_trace (0);
traced_port.reset ();
}
}
void
MidiTracer::tracer (Parser&, byte* msg, size_t len)
MidiTracer::tracer (Parser&, byte* msg, size_t len, samplecnt_t now)
{
stringstream ss;
struct timeval tv;
char* buf;
struct tm now;
size_t bufsize;
size_t s;
gettimeofday (&tv, 0);
std::cerr << "tracer msg " << len << " bytes, first = " << hex << (int) msg[0] << dec << std::endl;
buf = (char *) buffer_pool.alloc ();
bufsize = buffer_size;
if (_last_receipt.tv_sec != 0 && show_delta_time) {
struct timeval delta;
timersub (&tv, &_last_receipt, &delta);
s = snprintf (buf, bufsize, "+%02" PRId64 ":%06" PRId64, (int64_t) delta.tv_sec, (int64_t) delta.tv_usec);
if (_last_receipt != 0 && show_delta_time) {
s = snprintf (buf, bufsize, "+%12ld", now - _last_receipt);
bufsize -= s;
} else {
localtime_r ((const time_t*)&tv.tv_sec, &now);
s = strftime (buf, bufsize, "%H:%M:%S", &now);
bufsize -= s;
s += snprintf (&buf[s], bufsize, ".%06" PRId64, (int64_t) tv.tv_usec);
s = snprintf (buf, bufsize, "%12ld", now);
bufsize -= s;
}
_last_receipt = tv;
_last_receipt = now;
switch ((eventType) msg[0]&0xf0) {
case off:

View file

@ -56,7 +56,7 @@ private:
Gtk::SpinButton line_count_spinner;
Gtk::Label line_count_label;
Gtk::HBox line_count_box;
struct timeval _last_receipt;
MIDI::samplecnt_t _last_receipt;
bool autoscroll;
bool show_hex;
@ -72,7 +72,7 @@ private:
Pool buffer_pool;
static const size_t buffer_size = 256;
void tracer (MIDI::Parser&, MIDI::byte*, size_t);
void tracer (MIDI::Parser&, MIDI::byte*, size_t, MIDI::samplecnt_t);
void update ();
Gtk::CheckButton autoscroll_button;
@ -91,6 +91,7 @@ private:
void disconnect ();
PBD::ScopedConnection _parser_connection;
PBD::ScopedConnection _manager_connection;
MIDI::Parser my_parser;
boost::shared_ptr<ARDOUR::MidiPort> traced_port;
};

View file

@ -608,6 +608,20 @@ ClockOption::set_session (Session* s)
/*--------------------------*/
WidgetOption::WidgetOption (string const & i, string const & n, Gtk::Widget& w)
: Option (i, n)
, _widget (&w)
{
}
void
WidgetOption::add_to_page (OptionEditorPage* p)
{
add_widget_to_page (p, _widget);
}
/*--------------------------*/
OptionEditorPage::OptionEditorPage ()
: table (1, 3)
{

View file

@ -558,6 +558,21 @@ private:
sigc::slot<bool, ARDOUR::gain_t> _set;
};
class WidgetOption : public Option
{
public:
WidgetOption (std::string const & i, std::string const & n, Gtk::Widget& w);
void add_to_page (OptionEditorPage*);
void parameter_changed (std::string const &) {}
void set_state_from_config () {}
Gtk::Widget& tip_widget() { return *_widget; }
private:
Gtk::Widget* _widget;
};
class ClockOption : public Option
{
public:

View file

@ -436,9 +436,10 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
ltc->add_channel (_("LTC Out"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_output_port()->name()));
program->add_bundle (ltc);
} else {
boost::shared_ptr<Bundle> ltc (new Bundle (_("LTC In"), inputs));
ltc->add_channel (_("LTC In"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_input_port()->name()));
program->add_bundle (ltc);
// XXX TRANSPORTMASTERS
//boost::shared_ptr<Bundle> ltc (new Bundle (_("LTC In"), inputs));
// ltc->add_channel (_("LTC In"), DataType::AUDIO, session->engine().make_port_name_non_relative (session->ltc_input_port()->name()));
// program->add_bundle (ltc);
}
}
@ -470,12 +471,13 @@ PortGroupList::gather (ARDOUR::Session* session, ARDOUR::DataType type, bool inp
AudioEngine* ae = AudioEngine::instance();
if (inputs) {
sync->add_channel (
_("MTC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_input_port()->name())
);
sync->add_channel (
_("MIDI clock in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_input_port()->name())
);
// XXX TRANSPORTMASTER
// sync->add_channel (
// _("MTC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mtc_input_port()->name())
// );
// sync->add_channel (
// _("MIDI clock in"), DataType::MIDI, ae->make_port_name_non_relative (session->midi_clock_input_port()->name())
//);
sync->add_channel (
_("MMC in"), DataType::MIDI, ae->make_port_name_non_relative (session->mmc_input_port()->name())
);

View file

@ -43,12 +43,14 @@
#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/profile.h"
#include "ardour/dB.h"
#include "ardour/rc_configuration.h"
#include "ardour/control_protocol_manager.h"
#include "ardour/dB.h"
#include "ardour/port_manager.h"
#include "ardour/plugin_manager.h"
#include "ardour/profile.h"
#include "ardour/rc_configuration.h"
#include "ardour/transport_master_manager.h"
#include "control_protocol/control_protocol.h"
#include "waveview/wave_view.h"
@ -66,6 +68,7 @@
#include "midi_tracer.h"
#include "rc_option_editor.h"
#include "sfdb_ui.h"
#include "transport_masters_dialog.h"
#include "ui_config.h"
#include "utils.h"
@ -2109,14 +2112,7 @@ MidiPortOptions::pretty_name_edit (std::string const & path, string const & new_
return;
}
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
if (backend) {
ARDOUR::PortEngine::PortHandle ph = backend->get_port_by_name ((*iter)[midi_port_columns.name]);
if (ph) {
backend->set_port_property (ph, "http://jackaudio.org/metadata/pretty-name", new_text, "");
(*iter)[midi_port_columns.pretty_name] = new_text;
}
}
AudioEngine::instance()->set_midi_port_pretty_name ((*iter)[midi_port_columns.name], new_text);
}
/*============*/
@ -3213,16 +3209,9 @@ RCOptionEditor::RCOptionEditor ()
/* SYNC */
add_option (_("Sync"), new OptionEditorHeading (_("External Synchronization")));
add_option (_("Sync"), new OptionEditorHeading (_("Transport Masters")));
_sync_source = new ComboOption<SyncSource> (
"sync-source",
_("External timecode source"),
sigc::mem_fun (*_rc_config, &RCConfiguration::get_sync_source),
sigc::mem_fun (*_rc_config, &RCConfiguration::set_sync_source)
);
add_option (_("Sync"), _sync_source);
add_option (_("Sync"), new WidgetOption (X_("foo"), X_("Transport Masters"), _transport_masters_widget));
_sync_framerate = new BoolOption (
"timecode-sync-frame-rate",
@ -3240,45 +3229,6 @@ RCOptionEditor::RCOptionEditor ()
add_option (_("Sync"), _sync_framerate);
_sync_genlock = new BoolOption (
"timecode-source-is-synced",
_("Sync-lock timecode to clock (disable drift compensation)"),
sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_source_is_synced),
sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_source_is_synced)
);
Gtkmm2ext::UI::instance()->set_tip
(_sync_genlock->tip_widget(),
string_compose (_("<b>When enabled</b> %1 will never varispeed when slaved to external timecode. "
"Sync Lock indicates that the selected external timecode source shares clock-sync "
"(Black &amp; Burst, Wordclock, etc) with the audio interface. "
"This option disables drift compensation. The transport speed is fixed at 1.0. "
"Vari-speed LTC will be ignored and cause drift."
"\n\n"
"<b>When disabled</b> %1 will compensate for potential drift, regardless if the "
"timecode sources shares clock sync."
), PROGRAM_NAME));
add_option (_("Sync"), _sync_genlock);
_sync_source_2997 = new BoolOption (
"timecode-source-2997",
_("Lock to 29.9700 fps instead of 30000/1001"),
sigc::mem_fun (*_rc_config, &RCConfiguration::get_timecode_source_2997),
sigc::mem_fun (*_rc_config, &RCConfiguration::set_timecode_source_2997)
);
Gtkmm2ext::UI::instance()->set_tip
(_sync_source_2997->tip_widget(),
_("<b>When enabled</b> the external timecode source is assumed to use 29.97 fps instead of 30000/1001.\n"
"SMPTE 12M-1999 specifies 29.97df as 30000/1001. The spec further mentions that "
"drop-sample timecode has an accumulated error of -86ms over a 24-hour period.\n"
"Drop-sample timecode would compensate exactly for a NTSC color frame rate of 30 * 0.9990 (ie 29.970000). "
"That is not the actual rate. However, some vendors use that rate - despite it being against the specs - "
"because the variant of using exactly 29.97 fps has zero timecode drift.\n"
));
add_option (_("Sync"), _sync_source_2997);
add_option (_("Sync/LTC"), new OptionEditorHeading (_("Linear Timecode (LTC) Reader")));
_ltc_port = new ComboStringOption (
@ -3293,9 +3243,6 @@ RCOptionEditor::RCOptionEditor ()
AudioEngine::instance()->get_physical_inputs (DataType::AUDIO, physical_inputs);
_ltc_port->set_popdown_strings (physical_inputs);
populate_sync_options ();
AudioEngine::instance()->Running.connect (engine_started_connection, MISSING_INVALIDATOR, boost::bind (&RCOptionEditor::populate_sync_options, this), gui_context());
add_option (_("Sync/LTC"), _ltc_port);
add_option (_("Sync/LTC"), new OptionEditorHeading (_("Linear Timecode (LTC) Generator")));
@ -4058,6 +4005,13 @@ These settings will only take effect after %1 is restarted.\n\
set_current_page (_("General"));
}
void
RCOptionEditor::set_session (Session *s)
{
SessionHandlePtr::set_session (s);
_transport_masters_widget.set_session (s);
}
void
RCOptionEditor::parameter_changed (string const & p)
{
@ -4072,22 +4026,11 @@ RCOptionEditor::parameter_changed (string const & p)
_solo_control_is_listen_control->set_sensitive (s);
_listen_position->set_sensitive (s);
} else if (p == "sync-source") {
_sync_source->set_sensitive (true);
if (_session) {
_sync_source->set_sensitive (!_session->config.get_external_sync());
}
switch(Config->get_sync_source()) {
case ARDOUR::MTC:
case ARDOUR::LTC:
_sync_genlock->set_sensitive (true);
boost::shared_ptr<TransportMaster> tm (TransportMasterManager::instance().current());
if (boost::dynamic_pointer_cast<TimecodeTransportMaster> (tm)) {
_sync_framerate->set_sensitive (true);
_sync_source_2997->set_sensitive (true);
break;
default:
_sync_genlock->set_sensitive (false);
} else {
_sync_framerate->set_sensitive (false);
_sync_source_2997->set_sensitive (false);
break;
}
} else if (p == "send-ltc") {
bool const s = Config->get_send_ltc ();
@ -4164,29 +4107,6 @@ void RCOptionEditor::edit_vst_path () {
delete pd;
}
void
RCOptionEditor::populate_sync_options ()
{
vector<SyncSource> sync_opts = ARDOUR::get_available_sync_options ();
_sync_source->clear ();
for (vector<SyncSource>::iterator i = sync_opts.begin(); i != sync_opts.end(); ++i) {
_sync_source->add (*i, sync_source_to_string (*i));
}
if (sync_opts.empty()) {
_sync_source->set_sensitive(false);
} else {
if (std::find(sync_opts.begin(), sync_opts.end(), _rc_config->get_sync_source()) == sync_opts.end()) {
_rc_config->set_sync_source(sync_opts.front());
}
}
parameter_changed ("sync-source");
}
Gtk::Window*
RCOptionEditor::use_own_window (bool and_fill_it)
{

View file

@ -24,6 +24,7 @@
#include "option_editor.h"
#include "visibility_group.h"
#include "transport_masters_dialog.h"
/** @file rc_option_editor.h
* @brief Editing of options which are obtained from and written back to one of the .rc files.
@ -39,7 +40,7 @@ class RCOptionEditor : public OptionEditorContainer, public ARDOUR::SessionHandl
public:
RCOptionEditor ();
void populate_sync_options ();
void set_session (ARDOUR::Session*);
Gtk::Window* use_own_window (bool and_fill_it);
XMLNode& get_state ();
@ -53,13 +54,12 @@ private:
VisibilityGroup _mixer_strip_visibility;
ComboOption<ARDOUR::SyncSource>* _sync_source;
BoolOption* _sync_framerate;
BoolOption* _sync_genlock;
BoolOption* _sync_source_2997;
ComboStringOption* _ltc_port;
HSliderOption* _ltc_volume_slider;
Gtk::Adjustment* _ltc_volume_adjustment;
BoolOption* _ltc_send_continuously;
BoolOption* _plugin_prefer_inline;
TransportMastersWidget _transport_masters_widget;
PBD::ScopedConnection parameter_change_connection;
PBD::ScopedConnection engine_started_connection;

View file

@ -0,0 +1,395 @@
/*
Copyright (C) 2018 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "pbd/enumwriter.h"
#include "pbd/i18n.h"
#include "temporal/time.h"
#include "ardour/audioengine.h"
#include "ardour/session.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "widgets/tooltips.h"
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/gui_thread.h"
#include "ardour_ui.h"
#include "transport_masters_dialog.h"
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace ARDOUR;
using namespace PBD;
using namespace ArdourWidgets;
TransportMastersWidget::TransportMastersWidget ()
: table (4, 9)
{
pack_start (table, PACK_EXPAND_WIDGET, 12);
col_title[0].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Name")));
col_title[0].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Name")));
col_title[1].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Type")));
col_title[2].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Format")));
col_title[3].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Current")));
col_title[4].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Timestamp")));
col_title[5].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Delta")));
col_title[6].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Collect")));
col_title[7].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Use")));
col_title[8].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Data Source")));
col_title[9].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Accept")));
col_title[10].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Clock Synced")));
col_title[11].set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("29.97/30")));
set_tooltip (col_title[11], _("<b>When enabled</b> the external timecode source is assumed to use 29.97 fps instead of 30000/1001.\n"
"SMPTE 12M-1999 specifies 29.97df as 30000/1001. The spec further mentions that "
"drop-sample timecode has an accumulated error of -86ms over a 24-hour period.\n"
"Drop-sample timecode would compensate exactly for a NTSC color frame rate of 30 * 0.9990 (ie 29.970000). "
"That is not the actual rate. However, some vendors use that rate - despite it being against the specs - "
"because the variant of using exactly 29.97 fps has zero timecode drift.\n"
));
table.set_spacings (6);
TransportMasterManager::instance().CurrentChanged.connect (current_connection, invalidator (*this), boost::bind (&TransportMastersWidget::current_changed, this, _1, _2), gui_context());
rebuild ();
}
TransportMastersWidget::~TransportMastersWidget ()
{
for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
delete *r;
}
}
void
TransportMastersWidget::current_changed (boost::shared_ptr<TransportMaster> old_master, boost::shared_ptr<TransportMaster> new_master)
{
cerr << "master changed to " << new_master << endl;
}
void
TransportMastersWidget::rebuild ()
{
TransportMasterManager::TransportMasters const & masters (TransportMasterManager::instance().transport_masters());
container_clear (table);
for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
delete *r;
}
rows.clear ();
table.resize (masters.size()+1, 12);
table.attach (col_title[0], 0, 1, 0, 1);
table.attach (col_title[1], 1, 2, 0, 1);
table.attach (col_title[2], 2, 3, 0, 1);
table.attach (col_title[3], 3, 4, 0, 1);
table.attach (col_title[4], 4, 5, 0, 1);
table.attach (col_title[5], 5, 6, 0, 1);
table.attach (col_title[6], 6, 7, 0, 1);
table.attach (col_title[7], 7, 8, 0, 1);
table.attach (col_title[8], 8, 9, 0, 1);
table.attach (col_title[9], 9, 10, 0, 1);
table.attach (col_title[10], 10, 11, 0, 1);
table.attach (col_title[11], 11, 12, 0, 1);
uint32_t n = 1;
for (TransportMasterManager::TransportMasters::const_iterator m = masters.begin(); m != masters.end(); ++m, ++n) {
Row* r = new Row;
rows.push_back (r);
r->tm = *m;
r->label.set_text ((*m)->name());
r->type.set_text (enum_2_string ((*m)->type()));
r->use_button.set_group (use_button_group);
if (TransportMasterManager::instance().current() == r->tm) {
r->use_button.set_active (true);
}
table.attach (r->type, 0, 1, n, n+1);
table.attach (r->label, 1, 2, n, n+1);
table.attach (r->format, 2, 3, n, n+1);
table.attach (r->current, 3, 4, n, n+1);
table.attach (r->timestamp, 4, 5, n, n+1);
table.attach (r->delta, 5, 6, n, n+1);
table.attach (r->collect_button, 6, 7, n, n+1);
table.attach (r->use_button, 7, 8, n, n+1);
table.attach (r->port_combo, 8, 9, n, n+1);
table.attach (r->request_options, 9, 10, n, n+1);
if (boost::dynamic_pointer_cast<TimecodeTransportMaster> (r->tm)) {
table.attach (r->sclock_synced_button, 10, 11, n, n+1);
r->sclock_synced_button.set_active (r->tm->sample_clock_synced());
r->sclock_synced_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::sync_button_toggled));
table.attach (r->fps_299730_button, 11, 12, n, n+1);
}
r->port_combo.signal_changed().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::port_choice_changed));
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (*r, invalidator (*this), boost::bind (&TransportMastersWidget::Row::connection_handler, r), gui_context());
r->collect_button.set_active (r->tm->collect());
r->use_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::use_button_toggled));
r->collect_button.signal_toggled().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::collect_button_toggled));
r->request_options.signal_button_press_event().connect (sigc::mem_fun (*r, &TransportMastersWidget::Row::request_option_press), false);
}
}
TransportMastersWidget::Row::Row ()
: request_option_menu (0)
, ignore_active_change (false)
{
}
void
TransportMastersWidget::Row::use_button_toggled ()
{
if (use_button.get_active()) {
Config->set_sync_source (tm->type());
}
}
void
TransportMastersWidget::Row::collect_button_toggled ()
{
tm->set_collect (collect_button.get_active());
}
void
TransportMastersWidget::Row::sync_button_toggled ()
{
tm->set_sample_clock_synced (sclock_synced_button.get_active());
}
bool
TransportMastersWidget::Row::request_option_press (GdkEventButton* ev)
{
if (ev->button == 1) {
if (!request_option_menu) {
build_request_options ();
}
request_option_menu->popup (1, ev->time);
return true;
}
return false;
}
void
TransportMastersWidget::Row::build_request_options ()
{
using namespace Gtk::Menu_Helpers;
request_option_menu = manage (new Menu);
MenuList& items (request_option_menu->items());
items.push_back (CheckMenuElem (_("Accept speed-changing commands (start/stop)")));
CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back ());
i->set_active (tm->request_mask() & TR_Speed);
items.push_back (CheckMenuElem (_("Accept locate commands")));
i = dynamic_cast<CheckMenuItem *> (&items.back ());
i->set_active (tm->request_mask() & TR_Locate);
}
void
TransportMastersWidget::Row::connection_handler ()
{
}
Glib::RefPtr<Gtk::ListStore>
TransportMastersWidget::Row::build_port_list (vector<string> const & ports)
{
Glib::RefPtr<Gtk::ListStore> store = ListStore::create (port_columns);
TreeModel::Row row;
row = *store->append ();
row[port_columns.full_name] = string();
row[port_columns.short_name] = _("Disconnected");
for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
if (AudioEngine::instance()->port_is_mine (*p)) {
continue;
}
row = *store->append ();
row[port_columns.full_name] = *p;
std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
if (pn.empty ()) {
pn = (*p).substr ((*p).find (':') + 1);
}
row[port_columns.short_name] = pn;
}
return store;
}
void
TransportMastersWidget::Row::populate_port_combo ()
{
if (!tm->port()) {
port_combo.hide ();
return;
} else {
port_combo.show ();
}
vector<string> inputs;
if (tm->port()->type() == DataType::MIDI) {
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput), inputs);
} else {
ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::AUDIO, ARDOUR::PortFlags (ARDOUR::IsOutput), inputs);
}
Glib::RefPtr<Gtk::ListStore> input = build_port_list (inputs);
bool input_found = false;
int n;
port_combo.set_model (input);
Gtk::TreeModel::Children children = input->children();
Gtk::TreeModel::Children::iterator i;
i = children.begin();
++i; /* skip "Disconnected" */
for (n = 1; i != children.end(); ++i, ++n) {
string port_name = (*i)[port_columns.full_name];
if (tm->port()->connected_to (port_name)) {
port_combo.set_active (n);
input_found = true;
break;
}
}
if (!input_found) {
port_combo.set_active (0); /* disconnected */
}
}
void
TransportMastersWidget::Row::port_choice_changed ()
{
if (ignore_active_change) {
return;
}
TreeModel::iterator active = port_combo.get_active ();
string new_port = (*active)[port_columns.full_name];
if (new_port.empty()) {
tm->port()->disconnect_all ();
return;
}
if (!tm->port()->connected_to (new_port)) {
tm->port()->disconnect_all ();
tm->port()->connect (new_port);
}
}
void
TransportMastersWidget::Row::update (Session* s, samplepos_t now)
{
using namespace Timecode;
samplepos_t pos;
double speed;
stringstream ss;
Time t;
boost::shared_ptr<TimecodeTransportMaster> ttm;
boost::shared_ptr<MIDIClock_TransportMaster> mtm;
if (s) {
if (tm->speed_and_position (speed, pos, now)) {
sample_to_timecode (pos, t, false, false, 25, false, AudioEngine::instance()->sample_rate(), 100, false, 0);
if ((ttm = boost::dynamic_pointer_cast<TimecodeTransportMaster> (tm))) {
format.set_text (timecode_format_name (ttm->apparent_timecode_format()));
} else if ((mtm = boost::dynamic_pointer_cast<MIDIClock_TransportMaster> (tm))) {
char buf[8];
snprintf (buf, sizeof (buf), "%.1f", mtm->bpm());
format.set_text (buf);
} else {
format.set_text ("");
}
current.set_text (Timecode::timecode_format_time (t));
timestamp.set_markup (tm->position_string());
delta.set_markup (tm->delta_string ());
}
}
populate_port_combo ();
}
void
TransportMastersWidget::update (samplepos_t audible)
{
samplepos_t now = AudioEngine::instance()->sample_time ();
for (vector<Row*>::iterator r = rows.begin(); r != rows.end(); ++r) {
(*r)->update (_session, now);
}
}
void
TransportMastersWidget::on_map ()
{
update_connection = ARDOUR_UI::Clock.connect (sigc::mem_fun (*this, &TransportMastersWidget::update));
Gtk::VBox::on_map ();
}
void
TransportMastersWidget::on_unmap ()
{
update_connection.disconnect ();
Gtk::VBox::on_unmap ();
}
TransportMastersDialog::TransportMastersDialog ()
: ArdourDialog (_("Transport Masters"))
{
get_vbox()->pack_start (w);
w.show ();
}
void
TransportMastersDialog::set_session (ARDOUR::Session* s)
{
ArdourDialog::set_session (s);
w.set_session (s);
}

View file

@ -0,0 +1,129 @@
/*
Copyright (C) 2018 Paul Davis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_gtk_transport_masters_dialog_h__
#define __ardour_gtk_transport_masters_dialog_h__
#include <vector>
#include <string>
#include <gtkmm/button.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/label.h>
#include <gtkmm/table.h>
#include <gtkmm/treestore.h>
#include "ardour_dialog.h"
namespace Gtk {
class Menu;
}
namespace ARDOUR {
class TransportMaster;
}
class TransportMastersWidget : public Gtk::VBox, public ARDOUR::SessionHandlePtr
{
public:
TransportMastersWidget ();
~TransportMastersWidget ();
void update (ARDOUR::samplepos_t);
protected:
void on_map ();
void on_unmap ();
private:
struct Row : sigc::trackable, PBD::ScopedConnectionList {
Gtk::Label label;
Gtk::Label type;
Gtk::Label format;
Gtk::Label current;
Gtk::Label timestamp;
Gtk::Label delta;
Gtk::CheckButton collect_button;
Gtk::RadioButton use_button;
Gtk::ComboBoxText port_combo;
Gtk::CheckButton sclock_synced_button;
Gtk::CheckButton fps_299730_button;
Gtk::Button request_options;
Gtk::Menu* request_option_menu;
void build_request_options();
boost::shared_ptr<ARDOUR::TransportMaster> tm;
void update (ARDOUR::Session*, ARDOUR::samplepos_t);
Row ();
struct PortColumns : public Gtk::TreeModel::ColumnRecord {
PortColumns() {
add (short_name);
add (full_name);
}
Gtk::TreeModelColumn<std::string> short_name;
Gtk::TreeModelColumn<std::string> full_name;
};
PortColumns port_columns;
void populate_port_combo ();
Glib::RefPtr<Gtk::ListStore> build_port_list (std::vector<std::string> const & ports);
void use_button_toggled ();
void collect_button_toggled ();
void sync_button_toggled ();
void port_choice_changed ();
void connection_handler ();
bool request_option_press (GdkEventButton*);
bool ignore_active_change;
};
std::vector<Row*> rows;
Gtk::RadioButtonGroup use_button_group;
Gtk::Table table;
Gtk::Label col_title[12];
sigc::connection update_connection;
PBD::ScopedConnection current_connection;
void rebuild ();
void current_changed (boost::shared_ptr<ARDOUR::TransportMaster> old_master, boost::shared_ptr<ARDOUR::TransportMaster> new_master);
};
class TransportMastersDialog : public ArdourDialog
{
public:
TransportMastersDialog ();
void set_session (ARDOUR::Session*);
private:
TransportMastersWidget w;
};
#endif /* __ardour_gtk_transport_masters_dialog_h__ */

View file

@ -262,6 +262,7 @@ gtk2_ardour_sources = [
'transform_dialog.cc',
'transport_control.cc',
'transport_control_ui.cc',
'transport_masters_dialog.cc',
'transpose_dialog.cc',
'ui_config.cc',
'utils.cc',