#include "ardour/debug.h"
#include "ardour/midi_track.h"
#include "ardour/midiport_manager.h"
+#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
#include "ardour/processor.h"
#include "ardour/rc_configuration.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/session_configuration.h"
+#include "ardour/tempo.h"
#include "ardour/vca.h"
#include "faderport8.h"
, _connection_state (ConnectionState (0))
, _device_active (false)
, _ctrls (*this)
- , _channel_off (0)
, _plugin_off (0)
, _parameter_off (0)
+ , _show_presets (false)
+ , _showing_well_known (0)
, _blink_onoff (false)
, _shift_lock (false)
- , _shift_pressed (false)
+ , _shift_pressed (0)
, gui (0)
+ , _link_enabled (false)
+ , _link_locked (false)
+ , _clock_mode (1)
+ , _scribble_mode (2)
+ , _two_line_text (false)
+ , _auto_pluginui (true)
{
boost::shared_ptr<ARDOUR::Port> inp;
boost::shared_ptr<ARDOUR::Port> outp;
session->engine().make_port_name_non_relative (outp->name())
);
- ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::connection_handler, this, _1, _2, _3, _4, _5), this);
-
- StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::gui_track_selection_changed, this), this);
+ ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::connection_handler, this, _2, _4), this);
+ ARDOUR::AudioEngine::instance()->Stopped.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
+ ARDOUR::Port::PortDrop.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::engine_reset, this), this);
+ /* bind button events to call libardour actions */
setup_actions ();
+
_ctrls.FaderModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::notify_fader_mode_changed, this));
- _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this, true));
+ _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this));
}
FaderPort8::~FaderPort8 ()
{
- cerr << "~FP8\n";
- stop_midi_handling ();
- close ();
+ /* this will be called from the main UI thread. during Session::destroy().
+ * There can be concurrent activity from BaseUI::main_thread -> AsyncMIDIPort
+ * -> MIDI::Parser::signal -> ... to any of the midi_connections
+ *
+ * stop event loop early and join thread */
+ stop ();
if (_input_port) {
DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
+ Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
AudioEngine::instance()->unregister_port (_input_port);
_input_port.reset ();
}
+ disconnected (); // zero faders, turn lights off, clear strips
+
if (_output_port) {
_output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
+ Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
AudioEngine::instance()->unregister_port (_output_port);
_output_port.reset ();
}
tear_down_gui ();
-
- /* stop event loop */
- DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
- BaseUI::quit ();
}
/* ****************************************************************************
call_slot (MISSING_INVALIDATOR, req->the_slot);
} else if (req->type == Quit) {
stop ();
+ disconnected ();
}
}
-int
+void
FaderPort8::stop ()
{
+ DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
BaseUI::quit ();
- return 0;
+ close (); // drop references, disconnect from session signals
}
void
FaderPort8::thread_init ()
{
- struct sched_param rtparam;
-
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
- memset (&rtparam, 0, sizeof (rtparam));
- rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
-
- if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
- // do we care? not particularly.
- }
+ set_thread_priority ();
}
bool
FaderPort8::periodic ()
{
- /* prepare TC display -- handled by stripable Periodic () */
- if (_ctrls.display_timecode ()) {
- // TODO allow BBT, HHMMSS
- // used in FP8Strip::periodic_update_timecode
+ /* prepare TC display -- handled by stripable Periodic ()
+ * in FP8Strip::periodic_update_timecode
+ */
+ if (_ctrls.display_timecode () && clock_mode ()) {
Timecode::Time TC;
session->timecode_time (TC);
_timecode = Timecode::timecode_format_time(TC);
+
+ char buf[16];
+ Timecode::BBT_Time BBT = session->tempo_map ().bbt_at_frame (session->transport_frame ());
+ snprintf (buf, sizeof (buf),
+ " %02" PRIu32 "|%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32,
+ BBT.bars % 100, BBT.beats %100,
+ (BBT.ticks/ 100) %100, BBT.ticks %100);
+ _musical_time = std::string (buf);
} else {
_timecode.clear ();
+ _musical_time.clear ();
}
/* update stripables */
BaseUI::run ();
connect_session_signals ();
} else {
- BaseUI::quit ();
- close ();
+ stop ();
}
ControlProtocol::set_active (yn);
void
FaderPort8::close ()
{
- _assigned_strips.clear ();
+ DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::close\n");
stop_midi_handling ();
session_connections.drop_connections ();
automation_state_connections.drop_connections ();
assigned_stripable_connections.drop_connections ();
+ _assigned_strips.clear ();
drop_ctrl_connections ();
- port_connection.disconnect ();
+ port_connections.drop_connections ();
selection_connection.disconnect ();
}
FaderPort8::connected ()
{
DEBUG_TRACE (DEBUG::FaderPort8, "initializing\n");
+ assert (!_device_active);
+
+ if (_device_active) {
+ stop_midi_handling (); // re-init
+ }
+
// ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
// but we don't have a handle to the underlying USB device here.
- _channel_off = _plugin_off = _parameter_off = 0;
+ memset (_channel_off, 0, sizeof (_channel_off));
+ _plugin_off = _parameter_off = 0;
_blink_onoff = false;
_shift_lock = false;
- _shift_pressed = false;
+ _shift_pressed = 0;
start_midi_handling ();
_ctrls.initialize ();
tx_midi3 (0x90, 0x46, 0x00);
send_session_state ();
- assign_strips (true);
+ assign_strips ();
Glib::RefPtr<Glib::TimeoutSource> blink_timer =
Glib::TimeoutSource::create (200);
FaderPort8::disconnected ()
{
stop_midi_handling ();
- for (uint8_t id = 0; id < 8; ++id) {
- _ctrls.strip(id).unset_controllables ();
+ if (_device_active) {
+ for (uint8_t id = 0; id < 8; ++id) {
+ _ctrls.strip(id).unset_controllables ();
+ }
+ _ctrls.all_lights_off ();
}
}
+void
+FaderPort8::engine_reset ()
+{
+ /* Port::PortDrop is called when the engine is halted or stopped */
+ DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::engine_reset\n");
+ _connection_state = 0;
+ _device_active = false;
+ disconnected ();
+}
+
bool
-FaderPort8::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
+FaderPort8::connection_handler (std::string name1, std::string name2)
{
#ifdef VERBOSE_DEBUG
DEBUG_TRACE (DEBUG::FaderPort8, "FaderPort8::connection_handler: start\n");
string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_output_port)->name());
if (ni == name1 || ni == name2) {
- if (yn) {
+ DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
+ if (_input_port->connected ()) {
+ if (_connection_state & InputConnected) {
+ return false;
+ }
_connection_state |= InputConnected;
} else {
_connection_state &= ~InputConnected;
}
} else if (no == name1 || no == name2) {
- if (yn) {
+ DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("Connection notify %1 and %2\n", name1, name2));
+ if (_output_port->connected ()) {
+ if (_connection_state & OutputConnected) {
+ return false;
+ }
_connection_state |= OutputConnected;
} else {
_connection_state &= ~OutputConnected;
} else {
DEBUG_TRACE (DEBUG::FaderPort8, "Device disconnected (input or output or both) or not yet fully connected\n");
- disconnected ();
+ if (_device_active) {
+ disconnected ();
+ }
_device_active = false;
}
{
boost::shared_ptr<AsyncMIDIPort> port (wport.lock());
- if (!port) {
+ if (!port || !_input_port) {
return false;
}
/* fader 0..16368 (0x3ff0 -- 1024 steps) */
bool handled = _ctrls.midi_fader (chan, pb);
/* if Shift key is held while moving a fader (group override), don't lock shift. */
- if (_shift_pressed && handled) {
+ if ((_shift_pressed > 0) && handled) {
_shift_connection.disconnect ();
_shift_lock = false;
}
/* special case shift */
if (tb->note_number == 0x06 || tb->note_number == 0x46) {
- _shift_pressed = true;
+ _shift_pressed |= (tb->note_number == 0x06) ? 1 : 2;
+ if (_shift_pressed == 3) {
+ return;
+ }
_shift_connection.disconnect ();
if (_shift_lock) {
_shift_lock = false;
/* special case shift */
if (tb->note_number == 0x06 || tb->note_number == 0x46) {
- _shift_pressed = false;
+ _shift_pressed &= (tb->note_number == 0x06) ? 2 : 1;
+ if (_shift_pressed > 0) {
+ return;
+ }
if (_shift_lock) {
return;
}
bool handled = _ctrls.midi_event (tb->note_number, tb->velocity);
/* if Shift key is held while activating an action, don't lock shift. */
- if (_shift_pressed && handled) {
+ if ((_shift_pressed > 0) && handled) {
_shift_connection.disconnect ();
_shift_lock = false;
}
child->add_child_nocopy (boost::shared_ptr<ARDOUR::Port>(_output_port)->get_state());
node.add_child_nocopy (*child);
+ node.set_property (X_("clock-mode"), _clock_mode);
+ node.set_property (X_("scribble-mode"), _scribble_mode);
+ node.set_property (X_("two-line-text"), _two_line_text);
+
for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
if (i->second.empty()) {
continue;
continue;
}
XMLNode* btn = new XMLNode (X_("Button"));
- btn->add_property (X_("id"), name);
+ btn->set_property (X_("id"), name);
if (!i->second.action(true).empty ()) {
- btn->add_property ("press", i->second.action(true)._action_name);
+ btn->set_property ("press", i->second.action(true)._action_name);
}
if (!i->second.action(false).empty ()) {
- btn->add_property ("release", i->second.action(false)._action_name);
+ btn->set_property ("release", i->second.action(false)._action_name);
}
node.add_child_nocopy (*btn);
}
}
}
+ node.get_property (X_("clock-mode"), _clock_mode);
+ node.get_property (X_("scribble-mode"), _scribble_mode);
+ node.get_property (X_("two-line-text"), _two_line_text);
+
_user_action_map.clear ();
// TODO: When re-loading state w/o surface re-init becomes possible,
// unset lights and reset colors of user buttons.
if ((*n)->name() != X_("Button")) {
continue;
}
- XMLProperty const* prop = (*n)->property (X_("id"));
- if (!prop) {
+
+ std::string id_str;
+ if (!(*n)->get_property (X_("id"), id_str)) {
continue;
}
FP8Controls::ButtonId id;
- if (!_ctrls.button_name_to_enum (prop->value(), id)) {
+ if (!_ctrls.button_name_to_enum (id_str, id)) {
continue;
}
- prop = (*n)->property (X_("press"));
- if (prop) {
- set_button_action (id, true, prop->value());
+ std::string action_str;
+ if ((*n)->get_property (X_("press"), action_str)) {
+ set_button_action (id, true, action_str);
}
- prop = (*n)->property (X_("release"));
- if (prop) {
- set_button_action (id, false, prop->value());
+ if ((*n)->get_property (X_("release"), action_str)) {
+ set_button_action (id, false, action_str);
}
}
return 0 != r->the_instrument ();
}
-struct FP8SortByNewDisplayOrder
-{
- // return (a < b)
- bool operator () (const boost::shared_ptr<Stripable> & a, const boost::shared_ptr<Stripable> & b) const
- {
- if (a->presentation_info().flags () == b->presentation_info().flags ()) {
- return a->presentation_info().order() < b->presentation_info().order();
- }
-
- int cmp_a = 0;
- int cmp_b = 0;
-
- if (a->presentation_info().flags () & ARDOUR::PresentationInfo::VCA) {
- cmp_a = 1;
- }
-#ifdef MIXBUS
- else if (a->presentation_info().flags () & ARDOUR::PresentationInfo::MasterOut) {
- cmp_a = 3;
- }
- else if (a->presentation_info().flags () & ARDOUR::PresentationInfo::Mixbus || a->mixbus()) {
- cmp_a = 2;
- }
-#endif
-
- if (b->presentation_info().flags () & ARDOUR::PresentationInfo::VCA) {
- cmp_b = 1;
- }
-#ifdef MIXBUS
- else if (b->presentation_info().flags () & ARDOUR::PresentationInfo::MasterOut) {
- cmp_b = 3;
- }
- else if (b->presentation_info().flags () & ARDOUR::PresentationInfo::Mixbus || b->mixbus()) {
- cmp_b = 2;
- }
-#endif
-
-#ifdef MIXBUS
- // this can happen with older MB sessions (no PresentationInfo::Mixbus flag)
- if (cmp_a == cmp_b) {
- return a->presentation_info().order() < b->presentation_info().order();
- }
-#endif
- return cmp_a < cmp_b;
- }
-};
-
void
FaderPort8::filter_stripables (StripableList& strips) const
{
case MixFX:
flt = &flt_auxbus;
break;
+ default:
+ assert (0);
+ // fall through
case MixAll:
allow_master = true;
flt = &flt_all;
strips.push_back (*s);
}
}
- strips.sort (FP8SortByNewDisplayOrder());
+ strips.sort (Stripable::Sorter(true));
}
-/* Track/Pan mode: assign stripable to strips */
+/* Track/Pan mode: assign stripable to strips, Send-mode: selection */
void
FaderPort8::assign_stripables (bool select_only)
{
}
int n_strips = strips.size();
- _channel_off = std::min (_channel_off, n_strips - 8);
- _channel_off = std::max (0, _channel_off);
+ int channel_off = get_channel_off (_ctrls.mix_mode ());
+ channel_off = std::min (channel_off, n_strips - 8);
+ channel_off = std::max (0, channel_off);
+ set_channel_off (_ctrls.mix_mode (), channel_off);
uint8_t id = 0;
- int skip = _channel_off;
+ int skip = channel_off;
for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
if (skip > 0) {
--skip;
boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
if (select_only) {
+ /* used in send mode */
_ctrls.strip(id).set_text_line (3, (*s)->name (), true);
_ctrls.strip(id).select_button ().set_color ((*s)->presentation_info ().color());
/* update selection lights */
}
for (; id < 8; ++id) {
_ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
+ _ctrls.strip(id).set_periodic_display_mode (FP8Strip::Stripables);
+ }
+}
+
+/* ****************************************************************************
+ * Control Link/Lock
+ */
+
+void
+FaderPort8::unlock_link (bool drop)
+{
+ link_locked_connection.disconnect ();
+
+ if (drop) {
+ stop_link (); // calls back here with drop = false
+ return;
+ }
+
+ _link_locked = false;
+
+ if (_link_enabled) {
+ assert (_ctrls.button (FP8Controls::BtnLink).is_active ());
+ _link_control.reset ();
+ start_link (); // re-connect & update LED colors
+ } else {
+ _ctrls.button (FP8Controls::BtnLink).set_active (false);
+ _ctrls.button (FP8Controls::BtnLink).set_color (0x888888ff);
+ _ctrls.button (FP8Controls::BtnLock).set_active (false);
+ _ctrls.button (FP8Controls::BtnLock).set_color (0x888888ff);
+ }
+}
+
+void
+FaderPort8::lock_link ()
+{
+ boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
+ if (!ac) {
+ return;
+ }
+ ac->DropReferences.connect (link_locked_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::unlock_link, this, true), this);
+
+ // stop watching for focus events
+ link_connection.disconnect ();
+
+ _link_locked = true;
+
+ _ctrls.button (FP8Controls::BtnLock).set_color (0x00ff00ff);
+ _ctrls.button (FP8Controls::BtnLink).set_color (0x00ff00ff);
+}
+
+void
+FaderPort8::stop_link ()
+{
+ if (!_link_enabled) {
+ return;
+ }
+ link_connection.disconnect ();
+ _link_control.reset ();
+ _link_enabled = false;
+ unlock_link (); // also updates button colors
+}
+
+void
+FaderPort8::start_link ()
+{
+ assert (!_link_locked);
+
+ _link_enabled = true;
+ _ctrls.button (FP8Controls::BtnLink).set_active (true);
+ _ctrls.button (FP8Controls::BtnLock).set_active (true);
+ nofity_focus_control (_link_control); // update BtnLink, BtnLock colors
+
+ PBD::Controllable::GUIFocusChanged.connect (link_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::nofity_focus_control, this, _1), this);
+}
+
+
+/* ****************************************************************************
+ * Plugin selection and parameters
+ */
+
+void
+FaderPort8::toggle_preset_param_mode ()
+{
+ FaderMode fadermode = _ctrls.fader_mode ();
+ if (fadermode != ModePlugins || _proc_params.size() == 0) {
+ return;
+ }
+ _show_presets = ! _show_presets;
+ assign_processor_ctrls ();
+}
+
+void
+FaderPort8::preset_changed ()
+{
+ if (_show_presets) {
+ assign_processor_ctrls ();
}
}
}
set_periodic_display_mode (FP8Strip::PluginParam);
+ if (_show_presets) {
+ if (assign_plugin_presets (_plugin_insert.lock ())) {
+ return;
+ }
+ _show_presets = false;
+ }
+
std::vector <ProcessorCtrl*> toggle_params;
std::vector <ProcessorCtrl*> slider_params;
uint8_t id = 0;
for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
if (i >= toggle_params.size ()) {
- _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1);
+ _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT2);
}
else if (i >= slider_params.size ()) {
_ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
} else {
- _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_SELECT & ~FP8Strip::CTRL_TEXT3);
+ _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
}
if (i < slider_params.size ()) {
_ctrls.strip(id).set_fader_controllable (slider_params[i]->ac);
- _ctrls.strip(id).set_text_line (0, slider_params[i]->name);
+ std::string param_name = slider_params[i]->name;
+ _ctrls.strip(id).set_text_line (0, param_name.substr (0, 9));
+ _ctrls.strip(id).set_text_line (1, param_name.length () > 9 ? param_name.substr (9) : "");
}
if (i < toggle_params.size ()) {
_ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
_ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
}
- if (++id == 8) {
- break;
- }
+ if (++id == 8) {
+ break;
+ }
}
// clear remaining
}
}
+bool
+FaderPort8::assign_plugin_presets (boost::shared_ptr<PluginInsert> pi)
+{
+ if (!pi) {
+ return false;
+ }
+ boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
+
+ std::vector<ARDOUR::Plugin::PresetRecord> presets = plugin->get_presets ();
+ if (presets.size () == 0) {
+ return false;
+ }
+
+ int n_parameters = presets.size ();
+
+ _parameter_off = std::min (_parameter_off, n_parameters - 7);
+ _parameter_off = std::max (0, _parameter_off);
+ Plugin::PresetRecord active = plugin->last_preset ();
+
+ uint8_t id = 0;
+ for (size_t i = _parameter_off; i < (size_t)n_parameters; ++i) {
+ _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
+ boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, i));
+ _ctrls.strip(id).set_select_cb (cb);
+ _ctrls.strip(id).select_button ().set_active (true);
+ if (active != presets.at(i)) {
+ _ctrls.strip(id).select_button ().set_color (0x0000ffff);
+ _ctrls.strip(id).select_button ().set_blinking (false);
+ } else {
+ _ctrls.strip(id).select_button ().set_color (0x00ffffff);
+ _ctrls.strip(id).select_button ().set_blinking (plugin->parameter_changed_since_last_preset ());
+ }
+ std::string label = presets.at(i).label;
+ _ctrls.strip(id).set_text_line (0, label.substr (0, 9));
+ _ctrls.strip(id).set_text_line (1, label.length () > 9 ? label.substr (9) : "");
+ _ctrls.strip(id).set_text_line (3, "PRESET", true);
+ if (++id == 7) {
+ break;
+ }
+ }
+
+ // clear remaining
+ for (; id < 7; ++id) {
+ _ctrls.strip(id).unset_controllables ();
+ }
+
+ // pin clear-preset to the last slot
+ assert (id == 7);
+ _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
+ boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, SIZE_MAX));
+ _ctrls.strip(id).set_select_cb (cb);
+ _ctrls.strip(id).select_button ().set_blinking (false);
+ _ctrls.strip(id).select_button ().set_color (active.uri.empty() ? 0x00ffffff : 0x0000ffff);
+ _ctrls.strip(id).select_button ().set_active (true);
+ _ctrls.strip(id).set_text_line (0, _("(none)"));
+ _ctrls.strip(id).set_text_line (3, "PRESET", true);
+ return true;
+}
+
void
FaderPort8::build_well_known_processor_ctrls (boost::shared_ptr<Stripable> s, bool eq)
{
_proc_params.clear ();
if (eq) {
int cnt = s->eq_band_cnt();
- PUSH_BACK_NON_NULL ("Enable", s->eq_enable_controllable ());
- PUSH_BACK_NON_NULL ("HPF", s->eq_hpf_controllable ());
+
+#ifdef MIXBUS32C
+ PUSH_BACK_NON_NULL ("Flt In", s->filter_enable_controllable (true)); // both HP/LP
+ PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
+ PUSH_BACK_NON_NULL ("LP Freq", s->filter_freq_controllable (false));
+ PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
+#elif defined (MIXBUS)
+ PUSH_BACK_NON_NULL ("EQ In", s->eq_enable_controllable ());
+ PUSH_BACK_NON_NULL ("HP Freq", s->filter_freq_controllable (true));
+#endif
+
for (int band = 0; band < cnt; ++band) {
std::string bn = s->eq_band_name (band);
PUSH_BACK_NON_NULL (string_compose ("Gain %1", bn), s->eq_gain_controllable (band));
PUSH_BACK_NON_NULL (string_compose ("Shape %1", bn), s->eq_shape_controllable (band));
}
} else {
- PUSH_BACK_NON_NULL ("Enable", s->comp_enable_controllable ());
+ PUSH_BACK_NON_NULL ("Comp In", s->comp_enable_controllable ());
PUSH_BACK_NON_NULL ("Threshold", s->comp_threshold_controllable ());
PUSH_BACK_NON_NULL ("Speed", s->comp_speed_controllable ());
PUSH_BACK_NON_NULL ("Mode", s->comp_mode_controllable ());
FaderPort8::select_plugin (int num)
{
// make sure drop_ctrl_connections() was called
- assert (_proc_params.size() == 0 && _showing_well_known == 0);
+ assert (_proc_params.size() == 0 && _showing_well_known == 0 && _plugin_insert.expired());
boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (first_selected_stripable());
if (!r) {
_ctrls.set_fader_mode (ModeTrack);
return;
}
+
+ // Toggle Bypass
+ if (shift_mod ()) {
+ if (num >= 0) {
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (r->nth_plugin (num));
+#ifdef MIXBUS
+ if (pi && !pi->is_channelstrip () && pi->display_to_user ())
+#else
+ if (pi && pi->display_to_user ())
+#endif
+ {
+ pi->enable (! pi->enabled ());
+ }
+ }
+ return;
+ }
+
if (num < 0) {
build_well_known_processor_ctrls (r, num == -1);
assign_processor_ctrls ();
return;
}
+ // disconnect signals from spill_plugins: processors_changed and ActiveChanged
+ processor_connections.drop_connections ();
+ r->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
+
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
+ assert (pi); // nth_plugin() always returns a PI.
+ /* _plugin_insert is used for Bypass/Enable & presets */
+#ifdef MIXBUS
+ if (!pi->is_channelstrip () && pi->display_to_user ())
+#else
+ if (pi->display_to_user ())
+#endif
+ {
+ _plugin_insert = boost::weak_ptr<ARDOUR::PluginInsert> (pi);
+ pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::notify_plugin_active_changed, this), this);
+ boost::shared_ptr<ARDOUR::Plugin> plugin = pi->plugin ();
+
+ plugin->PresetAdded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
+ plugin->PresetRemoved.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
+ plugin->PresetLoaded.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
+ plugin->PresetDirty.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::preset_changed, this), this);
+
+ if (_auto_pluginui) {
+ pi->ShowUI (); /* EMIT SIGNAL */
+ }
+ }
+
// switching to "Mode Track" -> calls FaderPort8::notify_fader_mode_changed()
// which drops the references, disconnects the signal and re-spills tracks
proc->DropReferences.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FP8Controls::set_fader_mode, &_ctrls, ModeTrack), this);
// display
assign_processor_ctrls ();
+ notify_plugin_active_changed ();
+}
+
+void
+FaderPort8::select_plugin_preset (size_t num)
+{
+ assert (_proc_params.size() > 0);
+ boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock();
+ if (!pi) {
+ _ctrls.set_fader_mode (ModeTrack);
+ return;
+ }
+ if (num == SIZE_MAX) {
+ pi->plugin ()->clear_preset ();
+ } else {
+ std::vector<ARDOUR::Plugin::PresetRecord> presets = pi->plugin ()->get_presets ();
+ if (num < presets.size ()) {
+ pi->load_preset (presets.at (num));
+ }
+ }
+ _show_presets = false;
+ assign_processor_ctrls ();
}
/* short 4 chars at most */
for (uint32_t i = 0; 0 != (proc = r->nth_plugin (i)); ++i) {
if (!proc->display_to_user ()) {
+#ifdef MIXBUS
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
+ if (pi->is_channelstrip ()) // don't skip MB PRE
+#endif
continue;
}
int n_controls = 0;
set<Evoral::Parameter> p = proc->what_can_be_automated ();
- for (set<Evoral::Parameter>::iterator i = p.begin(); i != p.end(); ++i) {
- std::string n = proc->describe_parameter (*i);
+ for (set<Evoral::Parameter>::iterator j = p.begin(); j != p.end(); ++j) {
+ std::string n = proc->describe_parameter (*j);
if (n == "hidden") {
continue;
}
break;
}
boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (proc);
- boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, i));
+ boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, procs[i]));
_ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
_ctrls.strip(id).set_select_cb (cb);
- _ctrls.strip(id).select_button ().set_color (0x00ff00ff);
- _ctrls.strip(id).select_button ().set_active (true /*proc->enabled()*/);
+ _ctrls.strip(id).select_button ().set_color (proc->enabled () ? 0x00ff00ff : 0xff0000ff);
+ _ctrls.strip(id).select_button ().set_active (true);
_ctrls.strip(id).select_button ().set_blinking (false);
_ctrls.strip(id).set_text_line (0, proc->name());
_ctrls.strip(id).set_text_line (1, pi->plugin()->maker());
_ctrls.strip(id).set_text_line (2, plugintype (pi->type()));
_ctrls.strip(id).set_text_line (3, "");
+ pi->ActiveChanged.connect (processor_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort8::spill_plugins, this), this);
+
if (++id == spillwidth) {
break;
}
assert (id == 8);
}
+/* ****************************************************************************
+ * Aux Sends and Mixbus assigns
+ */
+
void
FaderPort8::assign_sends ()
{
break;
}
- _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT1 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
+ _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_FADER & ~FP8Strip::CTRL_TEXT01 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
_ctrls.strip(id).set_fader_controllable (send);
_ctrls.strip(id).set_text_line (0, s->send_name (i));
_ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
- _ctrls.strip(id).set_solo_controllable (s->master_send_enable_controllable ()); // XXX
if (++id == 8) {
break;
for (; id < 8; ++id) {
_ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
}
+#ifdef MIXBUS // master-assign on last solo
+ _ctrls.strip(7).set_solo_controllable (s->master_send_enable_controllable ());
+#endif
/* set select buttons */
+ assigned_stripable_connections.drop_connections ();
+ _assigned_strips.clear ();
assign_stripables (true);
}
-void
-FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
-{
- for (uint8_t id = 0; id < 8; ++id) {
- _ctrls.strip(id).set_periodic_display_mode (m);
- }
-}
+/* ****************************************************************************
+ * Main stripable assignment (dispatch depending on mode)
+ */
void
-FaderPort8::assign_strips (bool reset_bank)
+FaderPort8::assign_strips ()
{
- if (reset_bank) {
- _channel_off = 0;
- }
-
- _assigned_strips.clear ();
assigned_stripable_connections.drop_connections ();
+ _assigned_strips.clear ();
FaderMode fadermode = _ctrls.fader_mode ();
switch (fadermode) {
case ModeTrack:
case ModePan:
assign_stripables ();
- gui_track_selection_changed (); // update selection, automation-state
+ stripable_selection_changed (); // update selection, automation-state
break;
case ModePlugins:
if (_proc_params.size() > 0) {
}
}
+/* ****************************************************************************
+ * some helper functions
+ */
+
+void
+FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
+{
+ for (uint8_t id = 0; id < 8; ++id) {
+ _ctrls.strip(id).set_periodic_display_mode (m);
+ }
+}
void
FaderPort8::drop_ctrl_connections ()
{
_proc_params.clear();
+ if (_auto_pluginui) {
+ boost::shared_ptr<PluginInsert> pi = _plugin_insert.lock ();
+ if (pi) {
+ pi->HideUI (); /* EMIT SIGNAL */
+ }
+ }
+ _plugin_insert.reset ();
+ _show_presets = false;
processor_connections.drop_connections ();
_showing_well_known = 0;
+ notify_plugin_active_changed ();
}
+/* functor for FP8Strip's select button */
+void
+FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
+{
+ boost::shared_ptr<Stripable> s = ws.lock();
+ if (!s) {
+ return;
+ }
+#if 1 /* single exclusive selection by default, toggle via shift */
+
+# if 1 /* selecting a selected strip -> move fader to unity */
+ if (s == first_selected_stripable () && !shift_mod ()) {
+ if (_ctrls.fader_mode () == ModeTrack) {
+ boost::shared_ptr<AutomationControl> ac = s->gain_control ();
+ ac->start_touch (ac->session().transport_frame());
+ ac->set_value (ac->normal (), PBD::Controllable::UseGroup);
+ }
+ return;
+ }
+# endif
+
+ if (shift_mod ()) {
+ ToggleStripableSelection (s);
+ } else {
+ SetStripableSelection (s);
+ }
+#else
+ /* tri-state selection: This allows to set the "first selected"
+ * with a single click without clearing the selection.
+ * Single de/select via shift.
+ */
+ if (shift_mod ()) {
+ if (s->is_selected ()) {
+ RemoveStripableFromSelection (s);
+ } else {
+ SetStripableSelection (s);
+ }
+ return;
+ }
+ if (s->is_selected () && s != first_selected_stripable ()) {
+ set_first_selected_stripable (s);
+ stripable_selection_changed ();
+ } else {
+ ToggleStripableSelection (s);
+ }
+#endif
+}
+
+/* ****************************************************************************
+ * Assigned Stripable Callbacks
+ */
+
void
FaderPort8::notify_fader_mode_changed ()
{
case ModeSend:
_plugin_off = 0;
_parameter_off = 0;
+ stop_link ();
// force unset rec-arm button, see also FaderPort8::button_arm
_ctrls.button (FP8Controls::BtnArm).set_active (false);
ARMButtonChange (false);
break;
}
- assign_strips (false);
+ assign_strips ();
notify_automation_mode_changed ();
}
-/* ****************************************************************************
- * Assigned Stripable Callbacks
- */
-
void
FaderPort8::notify_stripable_added_or_removed ()
{
* - Properties::hidden
* - Properties::order
*/
- assign_strips (false);
-}
-
-/* functor for FP8Strip's select button */
-void
-FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
-{
- boost::shared_ptr<Stripable> s = ws.lock();
- if (!s) {
- return;
- }
- if (shift_mod ()) {
- if (s->is_selected ()) {
- RemoveStripableFromSelection (s);
- } else {
- SetStripableSelection (s);
- }
- return;
- }
- if (s->is_selected () && s != first_selected_stripable ()) {
- set_first_selected_stripable (s);
- gui_track_selection_changed ();
- } else {
- ToggleStripableSelection (s);
- }
+ assign_strips ();
}
/* called from static PresentationInfo::Change */
assert (0); // this should not happen
return;
}
- assert (_assigned_strips.find (s) != _assigned_strips.end());
+ if (_assigned_strips.find (s) == _assigned_strips.end()) {
+ /* it can happen that signal emission is delayed.
+ * A signal may already be in the queue but the
+ * _assigned_strips has meanwhile changed.
+ *
+ * before _assigned_strips changes, the connections are dropped
+ * but that does not seem to invalidate pending requests :(
+ *
+ * Seen when creating a new MB session and Mixbusses are added
+ * incrementally.
+ */
+ return;
+ }
uint8_t id = _assigned_strips[s];
if (what_changed.contains (Properties::color)) {
if (what_changed.contains (Properties::name)) {
switch (_ctrls.fader_mode ()) {
case ModeSend:
- _ctrls.strip(id).set_text_line (0, s->name());
+ _ctrls.strip(id).set_text_line (3, s->name(), true);
+ break;
case ModeTrack:
case ModePan:
- _ctrls.strip(id).set_text_line (3, s->name(), true);
+ _ctrls.strip(id).set_text_line (0, s->name());
break;
case ModePlugins:
assert (0);
}
void
-FaderPort8::gui_track_selection_changed (/*ARDOUR::StripableNotificationListPtr*/)
+FaderPort8::stripable_selection_changed ()
{
+ if (!_device_active) {
+ /* this can be called anytime from the static
+ * ControlProtocol::StripableSelectionChanged
+ */
+ return;
+ }
automation_state_connections.drop_connections();
switch (_ctrls.fader_mode ()) {
}
int off = std::distance (strips.begin(), it);
- if (_channel_off <= off && off < _channel_off + 8) {
+ int channel_off = get_channel_off (_ctrls.mix_mode ());
+ if (channel_off <= off && off < channel_off + 8) {
return;
}
- if (_channel_off > off) {
- _channel_off = off;
+ if (channel_off > off) {
+ channel_off = off;
} else {
- _channel_off = off - 7;
+ channel_off = off - 7;
}
- assign_strips (false);
+ set_channel_off (_ctrls.mix_mode (), channel_off);
+ assign_strips ();
}
void
if (down) {
dt *= -1;
}
- _channel_off += dt;
- assign_strips (false);
+ set_channel_off (_ctrls.mix_mode (), get_channel_off (_ctrls.mix_mode ()) + dt);
+ assign_strips ();
}
void
if (down) {
dt *= -1;
}
- _channel_off += dt;
switch (_ctrls.fader_mode ()) {
case ModePlugins:
if (_proc_params.size() > 0) {
}
break;
case ModeSend:
+ _plugin_off += dt;
assign_sends ();
break;
default: