#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
-#include "pbd/controllable_descriptor.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
#include "pbd/file_utils.h"
#include "ardour/audioengine.h"
#include "ardour/amp.h"
#include "ardour/bundle.h"
+#include "ardour/controllable_descriptor.h"
#include "ardour/debug.h"
#include "ardour/filesystem_paths.h"
#include "ardour/midi_port.h"
#include "ardour/monitor_processor.h"
#include "ardour/profile.h"
#include "ardour/rc_configuration.h"
-#include "ardour/route.h"
+#include "ardour/record_enable_control.h"
+#include "ardour/stripable.h"
#include "ardour/session.h"
#include "ardour/session_configuration.h"
#include "ardour/track.h"
#include "pbd/abstract_ui.cc" // instantiate template
FaderPort::FaderPort (Session& s)
- : ControlProtocol (s, _("Faderport"))
+ : ControlProtocol (s, _("PreSonus FaderPort"))
, AbstractUI<FaderPortRequest> (name())
, gui (0)
, connection_state (ConnectionState (0))
, fader_is_touched (false)
, button_state (ButtonState (0))
, blink_state (false)
+ , rec_enable_state (false)
{
last_encoder_time = 0;
);
- TrackSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::gui_track_selection_changed, this, _1), this);
+ StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::gui_track_selection_changed, this, _1), this);
/* Catch port connections and disconnections */
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort::connection_handler, this, _1, _2, _3, _4, _5), this);
buttons.insert (std::make_pair (Stop, Button (*this, _("Stop"), Stop, 2)));
buttons.insert (std::make_pair (Play, Button (*this, _("Play"), Play, 1)));
buttons.insert (std::make_pair (RecEnable, Button (*this, _("RecEnable"), RecEnable, 0)));
+ buttons.insert (std::make_pair (Footswitch, Button (*this, _("Footswitch"), Footswitch, -1)));
buttons.insert (std::make_pair (FaderTouch, Button (*this, _("Fader (touch)"), FaderTouch, -1)));
get_button (Shift).set_flash (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);
FaderPort::~FaderPort ()
{
+ all_lights_out ();
+
if (_input_port) {
DEBUG_TRACE (DEBUG::FaderPort, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
AudioEngine::instance()->unregister_port (_input_port);
}
if (_output_port) {
-// _output_port->drain (10000); //ToDo: is this necessary? It hangs the shutdown, for me
+ _output_port->drain (10000, 250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
DEBUG_TRACE (DEBUG::FaderPort, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
AudioEngine::instance()->unregister_port (_output_port);
_output_port.reset ();
}
tear_down_gui ();
+
+ /* stop event loop */
+ DEBUG_TRACE (DEBUG::FaderPort, "BaseUI::quit ()\n");
+ BaseUI::quit ();
}
void*
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", tb->controller_number, tb->value ? "yes" : "no"));
+ 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);
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;
break;
case FaderTouch:
fader_is_touched = tb->value;
- if (_current_route) {
- boost::shared_ptr<AutomationControl> gain = _current_route->gain_control ();
+ if (_current_stripable) {
+ boost::shared_ptr<AutomationControl> gain = _current_stripable->gain_control ();
if (gain) {
framepos_t now = session->engine().sample_time();
if (tb->value) {
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()) {
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 (!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 (!tb->value && (id != User)) {
- consumed.insert (User);
- DEBUG_TRACE (DEBUG::FaderPort, "clear user modifier\n");
- }
}
void
last_good_encoder_delta = delta;
}
- if (_current_route) {
+ if (_current_stripable) {
ButtonState trim_modifier;
ButtonState width_modifier;
}
if ((button_state & trim_modifier) == trim_modifier ) { // mod+encoder = input trim
- boost::shared_ptr<AutomationControl> trim = _current_route->trim()->gain_control ();
+ boost::shared_ptr<AutomationControl> trim = _current_stripable->trim_control ();
if (trim) {
float val = trim->get_user(); //for gain elements, the "user" value is in dB
val += delta;
}
if (was_fader) {
- if (_current_route) {
- boost::shared_ptr<AutomationControl> gain = _current_route->gain_control ();
+ if (_current_stripable) {
+ boost::shared_ptr<AutomationControl> gain = _current_stripable->gain_control ();
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 stripable at a time, allow the fader to
+ modify the group, if appropriate.
+ */
+ _current_stripable->gain_control()->set_value (val, Controllable::UseGroup);
}
}
}
/* catch up on state */
+ /* make sure that rec_enable_state is consistent with current device state */
+ get_button (RecEnable).set_led_state (_output_port, rec_enable_state);
+
map_transport_state ();
map_recenable_state ();
}
bool
FaderPort::periodic ()
{
- if (!_current_route) {
+ if (!_current_stripable) {
return true;
}
- ARDOUR::AutoState gain_state = _current_route->gain_control()->automation_state();
+ ARDOUR::AutoState gain_state = _current_stripable->gain_control()->automation_state();
if (gain_state == ARDOUR::Touch || gain_state == ARDOUR::Play) {
map_gain ();
get_button(*b).set_led_state (_output_port, blink_state);
}
+ map_recenable_state ();
+
return true;
}
port_connection.disconnect ();
blink_connection.disconnect ();
selection_connection.disconnect ();
- route_connections.drop_connections ();
+ stripable_connections.drop_connections ();
#if 0
- route_connections.drop_connections ();
+ stripable_connections.drop_connections ();
#endif
}
void
FaderPort::map_recenable_state ()
{
+ /* special case for RecEnable because its status can change as a
+ * confluence of unrelated parameters: (a) session rec-enable state (b)
+ * rec-enabled tracks. So we don't add the button to the blinkers list,
+ * we just call this:
+ *
+ * * from the blink callback
+ * * when the session tells us about a status change
+ *
+ * We do the last one so that the button changes state promptly rather
+ * than waiting for the next blink callback. The change in "blinking"
+ * based on having record-enabled tracks isn't urgent, and that happens
+ * during the blink callback.
+ */
+
+ bool onoff;
+
switch (session->record_status()) {
case Session::Disabled:
- stop_blinking (RecEnable);
+ onoff = false;
break;
case Session::Enabled:
- start_blinking (RecEnable);
+ onoff = blink_state;
break;
case Session::Recording:
- stop_blinking (RecEnable);
+ if (session->have_rec_enabled_track ()) {
+ onoff = true;
+ } else {
+ onoff = blink_state;
+ }
break;
}
+
+ if (onoff != rec_enable_state) {
+ get_button(RecEnable).set_led_state (_output_port, onoff);
+ rec_enable_state = onoff;
+ }
}
void
node.add_child_nocopy (get_button (Proj).get_state());
node.add_child_nocopy (get_button (Trns).get_state());
node.add_child_nocopy (get_button (User).get_state());
+ node.add_child_nocopy (get_button (Footswitch).get_state());
return node;
}
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;
}
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::gui_track_selection_changed (RouteNotificationListPtr routes)
+FaderPort::gui_track_selection_changed (StripableNotificationListPtr stripables)
{
- boost::shared_ptr<Route> r;
+ boost::shared_ptr<Stripable> r;
- if (!routes->empty()) {
- r = routes->front().lock();
+ if (!stripables->empty()) {
+ r = stripables->front().lock();
}
- set_current_route (r);
+ set_current_stripable (r);
}
void
-FaderPort::drop_current_route ()
+FaderPort::drop_current_stripable ()
{
- if (_current_route) {
- if (_current_route == session->monitor_out()) {
- set_current_route (session->master_out());
+ if (_current_stripable) {
+ if (_current_stripable == session->monitor_out()) {
+ set_current_stripable (session->master_out());
} else {
- set_current_route (boost::shared_ptr<Route>());
+ set_current_stripable (boost::shared_ptr<Stripable>());
}
}
}
void
-FaderPort::set_current_route (boost::shared_ptr<Route> r)
+FaderPort::set_current_stripable (boost::shared_ptr<Stripable> r)
{
- route_connections.drop_connections ();
+ stripable_connections.drop_connections ();
- _current_route = r;
+ _current_stripable = r;
/* turn this off. It will be turned on back on in use_master() or
use_monitor() as appropriate.
*/
get_button(Output).set_led_state (_output_port, false);
- if (_current_route) {
- _current_route->DropReferences.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::drop_current_route, this), this);
+ if (_current_stripable) {
+ _current_stripable->DropReferences.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::drop_current_stripable, this), this);
- _current_route->mute_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_mute, this, _1), this);
- _current_route->solo_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_solo, this, _1, _2, _3), this);
- _current_route->listen_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_listen, this, _1, _2), this);
+ _current_stripable->mute_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_mute, this), this);
+ _current_stripable->solo_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_solo, this), this);
- boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route);
+ boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_stripable);
if (t) {
- t->RecordEnableChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this);
+ t->rec_enable_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this);
}
- boost::shared_ptr<AutomationControl> control = _current_route->gain_control ();
+ boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
if (control) {
- control->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_gain, this), this);
- control->alist()->automation_state_changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_auto, this), this);
+ control->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_gain, this), this);
+ control->alist()->automation_state_changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_auto, this), this);
}
- boost::shared_ptr<MonitorProcessor> mp = _current_route->monitor_control();
+ boost::shared_ptr<MonitorProcessor> mp = _current_stripable->monitor_control();
if (mp) {
- mp->cut_control()->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_cut, this), this);
+ mp->cut_control()->Changed.connect (stripable_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_cut, this), this);
}
}
//ToDo: subscribe to the fader automation modes so we can light the LEDs
- map_route_state ();
+ map_stripable_state ();
}
void
* the Off button, because this will disable the fader.
*/
- boost::shared_ptr<AutomationControl> control = _current_route->gain_control ();
+ boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
const AutoState as = control->automation_state ();
switch (as) {
void
FaderPort::map_cut ()
{
- boost::shared_ptr<MonitorProcessor> mp = _current_route->monitor_control();
+ boost::shared_ptr<MonitorProcessor> mp = _current_stripable->monitor_control();
if (mp) {
bool yn = mp->cut_all ();
}
void
-FaderPort::map_mute (void*)
+FaderPort::map_mute ()
{
- if (_current_route) {
- if (_current_route->muted()) {
+ if (_current_stripable) {
+ if (_current_stripable->mute_control()->muted()) {
stop_blinking (Mute);
get_button (Mute).set_led_state (_output_port, true);
- } else if (_current_route->muted_by_others()) {
+ } else if (_current_stripable->mute_control()->muted_by_others_soloing () || _current_stripable->mute_control()->muted_by_masters()) {
start_blinking (Mute);
} else {
stop_blinking (Mute);
}
void
-FaderPort::map_solo (bool, void*, bool)
-{
- 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)
+FaderPort::map_solo ()
{
- if (_current_route) {
- get_button (Solo).set_led_state (_output_port, _current_route->listening_via_monitor());
+ if (_current_stripable) {
+ get_button (Solo).set_led_state (_output_port, _current_stripable->solo_control()->soloed());
} else {
get_button (Solo).set_led_state (_output_port, false);
}
void
FaderPort::map_recenable ()
{
- boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_route);
+ boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (_current_stripable);
if (t) {
- get_button (Rec).set_led_state (_output_port, t->record_enabled());
+ get_button (Rec).set_led_state (_output_port, t->rec_enable_control()->get_value());
} else {
get_button (Rec).set_led_state (_output_port, false);
}
return;
}
- if (!_current_route) {
+ if (!_current_stripable) {
return;
}
- boost::shared_ptr<AutomationControl> control = _current_route->gain_control ();
+ boost::shared_ptr<AutomationControl> control = _current_stripable->gain_control ();
double val;
if (!control) {
}
void
-FaderPort::map_route_state ()
+FaderPort::map_stripable_state ()
{
- if (!_current_route) {
+ if (!_current_stripable) {
stop_blinking (Mute);
stop_blinking (Solo);
get_button (Rec).set_led_state (_output_port, false);
} else {
- /* arguments to these map_*() methods are all ignored */
- map_solo (false, 0, false);
+ map_solo ();
map_recenable ();
map_gain ();
map_auto ();
- if (_current_route == session->monitor_out()) {
+ if (_current_stripable == session->monitor_out()) {
map_cut ();
} else {
- map_mute (0);
+ map_mute ();
}
}
}