#include "ardour/async_midi_port.h"
#include "ardour/audioengine.h"
#include "ardour/amp.h"
+#include "ardour/bundle.h"
#include "ardour/debug.h"
#include "ardour/filesystem_paths.h"
#include "ardour/midi_port.h"
FaderPort::FaderPort (Session& s)
: ControlProtocol (s, _("Faderport"))
- , AbstractUI<FaderPortRequest> ("faderport")
+ , AbstractUI<FaderPortRequest> (name())
, gui (0)
, connection_state (ConnectionState (0))
, _device_active (false)
throw failed_constructor();
}
+ _input_bundle.reset (new ARDOUR::Bundle (_("Faderport Support (Receive)"), true));
+ _output_bundle.reset (new ARDOUR::Bundle (_("Faderport Support (Send) "), false));
+
+ _input_bundle->add_channel (
+ inp->name(),
+ ARDOUR::DataType::MIDI,
+ session->engine().make_port_name_non_relative (inp->name())
+ );
+
+ _output_bundle->add_channel (
+ outp->name(),
+ ARDOUR::DataType::MIDI,
+ session->engine().make_port_name_non_relative (outp->name())
+ );
+
+
TrackSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::gui_track_selection_changed, this, _1), this);
/* Catch port connections and disconnections */
get_button (Undo).set_flash (true);
get_button (FP_Read).set_action (boost::bind (&FaderPort::read, this), true);
+ get_button (FP_Read).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
get_button (FP_Write).set_action (boost::bind (&FaderPort::write, this), true);
+ get_button (FP_Write).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
get_button (FP_Touch).set_action (boost::bind (&FaderPort::touch, this), true);
+ get_button (FP_Touch).set_action (boost::bind (&FaderPort::off, this), false, LongPress);
get_button (FP_Off).set_action (boost::bind (&FaderPort::off, this), true);
get_button (Play).set_action (boost::bind (&BasicUI::transport_play, this, true), true);
get_button (Loop).set_action (boost::bind (&BasicUI::add_marker, this, string()), true, ShiftDown);
get_button (Punch).set_action (boost::bind (&BasicUI::prev_marker, this), true, ShiftDown);
- get_button (User).set_action (boost::bind (&BasicUI::next_marker, this), true, ShiftDown);
+ get_button (User).set_action (boost::bind (&BasicUI::next_marker, this), true, ButtonState(ShiftDown|UserDown));
get_button (Mute).set_action (boost::bind (&FaderPort::mute, this), true);
get_button (Solo).set_action (boost::bind (&FaderPort::solo, this), true);
}
tear_down_gui ();
+
+ /* stop event loop */
+ DEBUG_TRACE (DEBUG::FaderPort, "BaseUI::quit ()\n");
+ BaseUI::quit ();
+}
+
+void*
+FaderPort::request_factory (uint32_t num_requests)
+{
+ /* AbstractUI<T>::request_buffer_factory() is a template method only
+ instantiated in this source module. To provide something visible for
+ use in the interface/descriptor, we have this static method that is
+ template-free.
+ */
+ return request_buffer_factory (num_requests);
}
void
{
struct sched_param rtparam;
- pthread_set_name (X_("FaderPort"));
+ pthread_set_name (event_loop_name().c_str());
- PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("FaderPort"), 2048);
- ARDOUR::SessionEvent::create_per_thread_pool (X_("FaderPort"), 128);
+ 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 */
FaderPort::all_lights_out ()
{
for (ButtonMap::iterator b = buttons.begin(); b != buttons.end(); ++b) {
- b->second.set_led_state (_output_port, false, true);
+ b->second.set_led_state (_output_port, false);
}
}
/* release happened and somehow we were not cancelled */
}
+ /* whichever button this was, we've used it ... don't invoke the
+ release action.
+ */
+ consumed.insert (id);
+
return false; /* don't get called again */
}
ButtonID id (ButtonID (tb->controller_number));
Button& button (get_button (id));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("button event for ID %1 press ? %2\n", (int) tb->controller_number, (tb->value ? "yes" : "no")));
+
if (tb->value) {
buttons_down.insert (id);
} else {
switch (id) {
case Shift:
- /* set this bit on press, do NOT clear it on release */
- if (tb->value) {
- bs = ShiftDown;
- }
+ bs = ShiftDown;
break;
case Stop:
bs = StopDown;
if (bs) {
button_state = (tb->value ? ButtonState (button_state|bs) : ButtonState (button_state&~bs));
- DEBUG_TRACE (DEBUG::FaderPort, string_compose ("reset button state to %1%2 using %3%4\n", hex, button_state, bs, dec));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("reset button state to %1 using %2\n", button_state, (int) bs));
}
if (button.uses_flash()) {
button.set_led_state (_output_port, (int)tb->value);
}
- button.invoke (button_state, tb->value ? true : false);
+ set<ButtonID>::iterator c = consumed.find (id);
- if (!tb->value && (id != Shift)) {
- /* non-shift key was released, clear shift modifier */
- button_state = ButtonState (button_state&~ShiftDown);
- DEBUG_TRACE (DEBUG::FaderPort, "clear shift modifier\n");
+ if (c == consumed.end()) {
+ button.invoke (button_state, tb->value ? true : false);
+ } else {
+ DEBUG_TRACE (DEBUG::FaderPort, "button was consumed, ignored\n");
+ consumed.erase (c);
}
}
}
if ((button_state & trim_modifier) == trim_modifier ) { // mod+encoder = input trim
- boost::shared_ptr<AutomationControl> gain = _current_route->trim()->gain_control ();
- if (gain) {
- float val = gain->get_user(); //for gain elements, the "user" value is in dB
+ boost::shared_ptr<AutomationControl> trim = _current_route->trim()->gain_control ();
+ if (trim) {
+ float val = trim->get_user(); //for gain elements, the "user" value is in dB
val += delta;
- gain->set_user(val);
+ trim->set_user(val);
}
} else if (width_modifier && ((button_state & width_modifier) == width_modifier)) {
ardour_pan_width (delta);
}
}
}
+
+ /* if the user button was pressed, mark it as consumed so that its
+ * release action has no effect.
+ */
+
+ if (!Profile->get_mixbus() && (button_state & UserDown)) {
+ consumed.insert (User);
+ }
}
void
if (gain) {
int ival = (fader_msb << 7) | fader_lsb;
float val = gain->interface_to_internal (ival/16384.0);
- _current_route->set_gain (val, this);
+ /* even though the faderport only controls a
+ single route at a time, allow the fader to
+ modify the group, if appropriate.
+ */
+ _current_route->set_gain (val, Controllable::UseGroup);
}
}
}
void
FaderPort::sysex_handler (MIDI::Parser &p, MIDI::byte *buf, size_t sz)
{
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("sysex message received, size = %1\n", sz));
+
if (sz < 17) {
return;
}
/* catch up on state */
- notify_transport_state_changed ();
- notify_record_state_changed ();
+ map_transport_state ();
+ map_recenable_state ();
}
int
FaderPort::set_active (bool yn)
{
- DEBUG_TRACE (DEBUG::FaderPort, string_compose("MackieControlProtocol::set_active init with yn: '%1'\n", yn));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose("Faderport::set_active init with yn: '%1'\n", yn));
if (yn == active()) {
return 0;
blink_connection = blink_timeout->connect (sigc::mem_fun (*this, &FaderPort::blink));
blink_timeout->attach (main_loop()->get_context());
+ Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
+ periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &FaderPort::periodic));
+ periodic_timeout->attach (main_loop()->get_context());
+
} else {
BaseUI::quit ();
ControlProtocol::set_active (yn);
- DEBUG_TRACE (DEBUG::FaderPort, string_compose("MackieControlProtocol::set_active done with yn: '%1'\n", yn));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose("Faderport::set_active done with yn: '%1'\n", yn));
return 0;
}
+bool
+FaderPort::periodic ()
+{
+ if (!_current_route) {
+ return true;
+ }
+
+ ARDOUR::AutoState gain_state = _current_route->gain_control()->automation_state();
+
+ if (gain_state == ARDOUR::Touch || gain_state == ARDOUR::Play) {
+ map_gain ();
+ }
+
+ return true;
+}
+
+void
+FaderPort::stop_blinking (ButtonID id)
+{
+ blinkers.remove (id);
+ get_button (id).set_led_state (_output_port, false);
+}
+
+void
+FaderPort::start_blinking (ButtonID id)
+{
+ blinkers.push_back (id);
+ get_button (id).set_led_state (_output_port, true);
+}
+
bool
FaderPort::blink ()
{
}
void
-FaderPort::notify_record_state_changed ()
+FaderPort::map_recenable_state ()
{
switch (session->record_status()) {
case Session::Disabled:
- get_button (RecEnable).set_led_state (_output_port, false);
- blinkers.remove (RecEnable);
+ stop_blinking (RecEnable);
break;
case Session::Enabled:
- get_button (RecEnable).set_led_state (_output_port, true);
- blinkers.push_back (RecEnable);
+ start_blinking (RecEnable);
break;
case Session::Recording:
- get_button (RecEnable).set_led_state (_output_port, true);
- blinkers.remove (RecEnable);
+ stop_blinking (RecEnable);
break;
}
}
void
-FaderPort::notify_transport_state_changed ()
+FaderPort::map_transport_state ()
{
get_button (Loop).set_led_state (_output_port, session->get_play_loop());
- get_button (Play).set_led_state (_output_port, session->transport_speed() == 1.0);
+
+ float ts = session->transport_speed();
+
+ if (ts == 0) {
+ stop_blinking (Play);
+ } else if (fabs (ts) == 1.0) {
+ stop_blinking (Play);
+ get_button (Play).set_led_state (_output_port, true);
+ } else {
+ start_blinking (Play);
+ }
+
get_button (Stop).set_led_state (_output_port, session->transport_stopped ());
get_button (Rewind).set_led_state (_output_port, session->transport_speed() < 0.0);
get_button (Ffwd).set_led_state (_output_port, session->transport_speed() > 1.0);
get_button (Punch).set_led_state (_output_port, true);
blinkers.remove (Punch);
} else if (in || out) {
- blinkers.push_back (Punch);
+ start_blinking (Punch);
} else {
- blinkers.remove (Punch);
- get_button (Punch).set_led_state (_output_port, false);
+ stop_blinking (Punch);
}
}
}
void
FaderPort::connect_session_signals()
{
- session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::notify_record_state_changed, this), this);
- session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::notify_transport_state_changed, this), this);
+ session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable_state, this), this);
+ session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_transport_state, this), this);
/* not session, but treat it similarly */
session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::parameter_changed, this, _1), this);
}
if (ioc & IO_IN) {
- if (port) {
- port->clear ();
- }
-
+ port->clear ();
DEBUG_TRACE (DEBUG::FaderPort, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
framepos_t now = session->engine().sample_time();
port->parse (now);
bool
FaderPort::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
{
+ DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
if (!_input_port || !_output_port) {
return false;
}
connection_state &= ~OutputConnected;
}
} else {
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
/* not our ports */
return false;
}
*/
g_usleep (100000);
+ DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
connected ();
} else {
ConnectionChange (); /* emit signal for our GUI */
+ DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
+
return true; /* connection status changed */
}
void
FaderPort::connected ()
{
- DEBUG_TRACE (DEBUG::FaderPort, "connection status changed\n");
+ DEBUG_TRACE (DEBUG::FaderPort, "sending device inquiry message...\n");
start_midi_handling ();
if (press) {
if ((x = on_press.find (bs)) == on_press.end()) {
- DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no press action for button %1 state %2%3\%4\n", id, hex, bs, dec));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no press action for button %1 state %2 @ %3 in %4\n", id, bs, this, &on_press));
return;
}
} else {
if ((x = on_release.find (bs)) == on_release.end()) {
- DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no release action for button %1 state %2%3\%4\n", id, hex, bs, dec));
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("no release action for button %1 state %2 @%3 in %4\n", id, bs, this, &on_release));
return;
}
}
todo.type = InternalFunction;
if (when_pressed) {
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 (%2) @ %5 to some functor on press + %3 in %4\n", id, name, bs, &on_press, this));
todo.function = f;
on_press[bs] = todo;
} else {
+ DEBUG_TRACE (DEBUG::FaderPort, string_compose ("set button %1 (%2) @ %5 to some functor on release + %3\n", id, name, bs, this));
todo.function = f;
on_release[bs] = todo;
}
}
void
-FaderPort::Button::set_led_state (boost::shared_ptr<MIDI::Port> port, int onoff, bool force)
+FaderPort::Button::set_led_state (boost::shared_ptr<MIDI::Port> port, bool onoff)
{
- if (!force && (led_on == (bool) onoff)) {
- /* nothing to do */
- return;
- }
-
if (out < 0) {
/* fader button ID - no LED */
return;
buf[1] = out;
buf[2] = onoff ? 1 : 0;
port->write (buf, 3, 0);
- led_on = (onoff ? true : false);
}
int
state_pairs.push_back (make_pair (string ("shift"), ShiftDown));
state_pairs.push_back (make_pair (string ("long"), LongPress));
- on_press.clear ();
- on_release.clear ();
-
for (vector<state_pair_t>::const_iterator sp = state_pairs.begin(); sp != state_pairs.end(); ++sp) {
string propname;
void
FaderPort::map_auto ()
{
+ /* Under no circumstances send a message to "enable" the LED state of
+ * the Off button, because this will disable the fader.
+ */
+
boost::shared_ptr<AutomationControl> control = _current_route->gain_control ();
const AutoState as = control->automation_state ();
get_button (FP_Read).set_led_state (_output_port, true);
get_button (FP_Write).set_led_state (_output_port, false);
get_button (FP_Touch).set_led_state (_output_port, false);
- get_button (FP_Off).set_led_state (_output_port, false);
break;
case ARDOUR::Write:
get_button (FP_Read).set_led_state (_output_port, false);
get_button (FP_Write).set_led_state (_output_port, true);
get_button (FP_Touch).set_led_state (_output_port, false);
- get_button (FP_Off).set_led_state (_output_port, false);
break;
case ARDOUR::Touch:
get_button (FP_Read).set_led_state (_output_port, false);
get_button (FP_Write).set_led_state (_output_port, false);
get_button (FP_Touch).set_led_state (_output_port, true);
- get_button (FP_Off).set_led_state (_output_port, false);
break;
case ARDOUR::Off:
get_button (FP_Read).set_led_state (_output_port, false);
get_button (FP_Write).set_led_state (_output_port, false);
get_button (FP_Touch).set_led_state (_output_port, false);
- get_button (FP_Off).set_led_state (_output_port, true);
break;
}
-
+
}
if (mp) {
bool yn = mp->cut_all ();
- get_button (Mute).set_led_state (_output_port, yn);
if (yn) {
- blinkers.push_back (Mute);
+ start_blinking (Mute);
} else {
- blinkers.remove (Mute);
+ stop_blinking (Mute);
}
} else {
- blinkers.remove (Mute);
+ stop_blinking (Mute);
}
}
void
FaderPort::map_mute (void*)
{
- get_button (Mute).set_led_state (_output_port, _current_route->muted());
+ if (_current_route) {
+ if (_current_route->muted()) {
+ stop_blinking (Mute);
+ get_button (Mute).set_led_state (_output_port, true);
+ } else if (_current_route->muted_by_others()) {
+ start_blinking (Mute);
+ } else {
+ stop_blinking (Mute);
+ }
+ } else {
+ stop_blinking (Mute);
+ }
}
void
FaderPort::map_solo (bool, void*, bool)
{
- get_button (Solo).set_led_state (_output_port, _current_route->soloed() || _current_route->listening_via_monitor());
+ if (_current_route) {
+ get_button (Solo).set_led_state (_output_port, _current_route->soloed() || _current_route->listening_via_monitor());
+ } else {
+ get_button (Solo).set_led_state (_output_port, false);
+ }
}
void
FaderPort::map_listen (void*, bool)
{
- get_button (Solo).set_led_state (_output_port, _current_route->listening_via_monitor());
+ if (_current_route) {
+ get_button (Solo).set_led_state (_output_port, _current_route->listening_via_monitor());
+ } else {
+ get_button (Solo).set_led_state (_output_port, false);
+ }
}
void
FaderPort::map_route_state ()
{
if (!_current_route) {
- get_button (Mute).set_led_state (_output_port, false);
- get_button (Solo).set_led_state (_output_port, false);
+ stop_blinking (Mute);
+ stop_blinking (Solo);
get_button (Rec).set_led_state (_output_port, false);
- blinkers.remove (Mute);
- blinkers.remove (Solo);
} else {
/* arguments to these map_*() methods are all ignored */
- map_mute (0);
map_solo (false, 0, false);
map_recenable ();
map_gain ();
- map_cut ();
map_auto ();
+
+ if (_current_route == session->monitor_out()) {
+ map_cut ();
+ } else {
+ map_mute (0);
+ }
+ }
+}
+
+list<boost::shared_ptr<ARDOUR::Bundle> >
+FaderPort::bundles ()
+{
+ list<boost::shared_ptr<ARDOUR::Bundle> > b;
+
+ if (_input_bundle) {
+ b.push_back (_input_bundle);
+ b.push_back (_output_bundle);
}
+
+ return b;
}
boost::shared_ptr<Port>