#include "fader.h"
#include "surface.h"
#include "control_group.h"
+#include "mackie_control_protocol.h"
using namespace Mackie;
MidiByteArray
Fader::update_message ()
{
+ if (MackieControlProtocol::instance()->flip_mode() == MackieControlProtocol::Zero) {
+ /* do not send messages to move the faders when in this mode */
+ return MidiByteArray();
+ }
+
int posi = int (0x3fff * position);
return MidiByteArray (3, 0xe0 | id(), posi & 0x7f, posi >> 7);
}
, _gui (0)
, _zoom_mode (false)
, _scrub_mode (false)
- , _flip_mode (false)
+ , _flip_mode (Normal)
+ , _view_mode (Global)
, _current_selected_track (-1)
{
DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n");
access_action (action);
}
}
+
+void
+MackieControlProtocol::set_view_mode (ViewMode m)
+{
+ _view_mode = m;
+
+ if (surfaces.empty()) {
+ return;
+ }
+
+ boost::shared_ptr<Surface> surface = surfaces.front();
+
+ if (surface->type() != mcu) {
+ return;
+ }
+
+ switch (_view_mode) {
+ case Global:
+ surface->write (surface->two_char_display ("Gl"));
+ break;
+ case Dynamics:
+ surface->write (surface->two_char_display ("Dy"));
+ break;
+ case EQ:
+ surface->write (surface->two_char_display ("EQ"));
+ break;
+ case Loop:
+ surface->write (surface->two_char_display ("LP"));
+ break;
+ case AudioTracks:
+ surface->write (surface->two_char_display ("AT"));
+ break;
+ case MidiTracks:
+ surface->write (surface->two_char_display ("MT"));
+ break;
+ case Busses:
+ surface->write (surface->two_char_display ("Bs"));
+ break;
+ case Sends:
+ surface->write (surface->two_char_display ("Sn"));
+ break;
+ }
+}
static const int MODIFIER_SHIFT;
static const int MODIFIER_CMDALT;
+ enum ViewMode {
+ Global,
+ Dynamics,
+ EQ,
+ Loop,
+ AudioTracks,
+ MidiTracks,
+ Busses,
+ Sends,
+ };
+
+ enum FlipMode {
+ Normal,
+ Mirror,
+ Swap,
+ Zero,
+ };
+
MackieControlProtocol(ARDOUR::Session &);
virtual ~MackieControlProtocol();
int set_active (bool yn);
+ FlipMode flip_mode () const { return _flip_mode; }
+ ViewMode view_mode () const { return _view_mode; }
+
+ void set_view_mode (ViewMode);
+
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
void update_global_button(const std::string & name, Mackie::LedState);
void update_global_led(const std::string & name, Mackie::LedState);
+ ARDOUR::Session & get_session() { return *session; }
+
+ void add_in_use_timeout (Mackie::Surface& surface, Mackie::Control& in_use_control, Mackie::Control* touch_control);
+
+ int modifier_state() const { return _modifier_state; }
+
+ protected:
+ // shut down the surface
+ void close();
+
+ // This sets up the notifications and sets the
+ // controls to the correct values
+ void update_surfaces();
+
+ // connects global (not strip) signals from the Session to here
+ // so the surface can be notified of changes from the other UIs.
+ void connect_session_signals();
+
+ // set all controls to their zero position
+ void zero_all();
+
+ /**
+ Fetch the set of routes to be considered for control by the
+ surface. Excluding master, hidden and control routes, and inactive routes
+ */
+ typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
+ Sorted get_sorted_routes();
+
+ // bank switching
+ void switch_banks (uint32_t first_remote_id, bool force = false);
+ void prev_track ();
+ void next_track ();
+
+ // also called from poll_automation to update timecode display
+ void update_timecode_display();
+
+ std::string format_bbt_timecode (ARDOUR::framepos_t now_frame);
+ std::string format_timecode_timecode (ARDOUR::framepos_t now_frame);
+
+ void do_request (MackieControlUIRequest*);
+ int stop ();
+
+ void thread_init ();
+
+ /* handling function key presses */
+
+ std::string f_action (uint32_t fn);
+ void f_press (uint32_t fn);
+
+ private:
+
+ static MackieControlProtocol* _instance;
+
+ void create_surfaces ();
+ void port_connected_or_disconnected (std::string, std::string, bool);
+ bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *);
+
+ bool periodic();
+ sigc::connection periodic_connection;
+
+ /// The initial remote_id of the currently switched in bank.
+ uint32_t _current_initial_bank;
+
+ /// protects the port list
+ Glib::Mutex update_mutex;
+
+ PBD::ScopedConnectionList audio_engine_connections;
+ PBD::ScopedConnectionList session_connections;
+ PBD::ScopedConnectionList port_connections;
+ PBD::ScopedConnectionList route_connections;
+
+ bool _transport_previously_rolling;
+
+ // timer for two quick marker left presses
+ Mackie::Timer _frm_left_last;
+
+ // last written timecode string
+ std::string _timecode_last;
+
+ // Which timecode are we displaying? BBT or Timecode
+ ARDOUR::AnyTime::Type _timecode_type;
+
+ // Bundle to represent our input ports
+ boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
+ // Bundle to represent our output ports
+ boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
+
+ void build_gui ();
+ void* _gui;
+
+ bool _zoom_mode;
+ bool _scrub_mode;
+ FlipMode _flip_mode;
+ ViewMode _view_mode;
+ int _current_selected_track;
+ int _modifier_state;
+
+ typedef std::list<GSource*> PortSources;
+ PortSources port_sources;
+
+ bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
+ void clear_ports ();
+
+ struct ButtonHandlers {
+ Mackie::LedState (MackieControlProtocol::*press) (Mackie::Button&);
+ Mackie::LedState (MackieControlProtocol::*release) (Mackie::Button&);
+
+ ButtonHandlers (Mackie::LedState (MackieControlProtocol::*p) (Mackie::Button&),
+ Mackie::LedState (MackieControlProtocol::*r) (Mackie::Button&))
+ : press (p)
+ , release (r) {}
+ };
+
+ typedef std::map<int,ButtonHandlers> ButtonMap;
+ ButtonMap button_map;
+
+ void build_button_map ();
+
+ std::vector<std::string> _f_actions;
+
/* implemented button handlers */
Mackie::LedState frm_left_press(Mackie::Button &);
Mackie::LedState frm_left_release(Mackie::Button &);
Mackie::LedState user_b_release (Mackie::Button &);
Mackie::LedState fader_touch_press (Mackie::Button &);
Mackie::LedState fader_touch_release (Mackie::Button &);
-
- ARDOUR::Session & get_session() { return *session; }
-
- void add_in_use_timeout (Mackie::Surface& surface, Mackie::Control& in_use_control, Mackie::Control* touch_control);
-
- int modifier_state() const { return _modifier_state; }
-
- protected:
- // shut down the surface
- void close();
-
- // This sets up the notifications and sets the
- // controls to the correct values
- void update_surfaces();
-
- // connects global (not strip) signals from the Session to here
- // so the surface can be notified of changes from the other UIs.
- void connect_session_signals();
-
- // set all controls to their zero position
- void zero_all();
-
- /**
- Fetch the set of routes to be considered for control by the
- surface. Excluding master, hidden and control routes, and inactive routes
- */
- typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
- Sorted get_sorted_routes();
-
- // bank switching
- void switch_banks (uint32_t first_remote_id, bool force = false);
- void prev_track ();
- void next_track ();
-
- // also called from poll_automation to update timecode display
- void update_timecode_display();
-
- std::string format_bbt_timecode (ARDOUR::framepos_t now_frame);
- std::string format_timecode_timecode (ARDOUR::framepos_t now_frame);
-
- void do_request (MackieControlUIRequest*);
- int stop ();
-
- void thread_init ();
-
- /* handling function key presses */
-
- std::string f_action (uint32_t fn);
- void f_press (uint32_t fn);
-
- private:
-
- static MackieControlProtocol* _instance;
-
- void create_surfaces ();
- void port_connected_or_disconnected (std::string, std::string, bool);
- bool control_in_use_timeout (Mackie::Surface*, Mackie::Control *, Mackie::Control *);
-
- bool periodic();
- sigc::connection periodic_connection;
-
- /// The initial remote_id of the currently switched in bank.
- uint32_t _current_initial_bank;
-
- /// protects the port list
- Glib::Mutex update_mutex;
-
- PBD::ScopedConnectionList audio_engine_connections;
- PBD::ScopedConnectionList session_connections;
- PBD::ScopedConnectionList port_connections;
- PBD::ScopedConnectionList route_connections;
-
- bool _transport_previously_rolling;
-
- // timer for two quick marker left presses
- Mackie::Timer _frm_left_last;
-
- // last written timecode string
- std::string _timecode_last;
-
- // Which timecode are we displaying? BBT or Timecode
- ARDOUR::AnyTime::Type _timecode_type;
-
- // Bundle to represent our input ports
- boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
- // Bundle to represent our output ports
- boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
-
- void build_gui ();
- void* _gui;
-
- bool _zoom_mode;
- bool _scrub_mode;
- bool _flip_mode;
- int _current_selected_track;
- int _modifier_state;
-
- typedef std::list<GSource*> PortSources;
- PortSources port_sources;
-
- bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
- void clear_ports ();
-
- struct ButtonHandlers {
- Mackie::LedState (MackieControlProtocol::*press) (Mackie::Button&);
- Mackie::LedState (MackieControlProtocol::*release) (Mackie::Button&);
-
- ButtonHandlers (Mackie::LedState (MackieControlProtocol::*p) (Mackie::Button&),
- Mackie::LedState (MackieControlProtocol::*r) (Mackie::Button&))
- : press (p)
- , release (r) {}
- };
-
- typedef std::map<int,ButtonHandlers> ButtonMap;
- ButtonMap button_map;
-
- void build_button_map ();
-
- std::vector<std::string> _f_actions;
};
{
}
-void JogWheel::jog_event (SurfacePort &, Control &, const ControlState & state)
+void JogWheel::jog_event (SurfacePort &, Control &, float delta)
{
// TODO use current snap-to setting?
switch (jog_wheel_state())
{
case scroll:
- _mcp.ScrollTimeline (state.delta * state.sign);
+ _mcp.ScrollTimeline (delta);
break;
case zoom:
// Chunky Zoom.
// TODO implement something similar to ScrollTimeline which
// ends up in Editor::control_scroll for smoother zooming.
- if (state.sign > 0)
- for (unsigned int i = 0; i < state.ticks; ++i) _mcp.ZoomIn();
- else
- for (unsigned int i = 0; i < state.ticks; ++i) _mcp.ZoomOut();
+ if (delta > 0) {
+ for (unsigned int i = 0; i < fabs (delta); ++i) {
+ _mcp.ZoomIn();
+ }
+ } else {
+ for (unsigned int i = 0; i < fabs (delta); ++i) {
+ _mcp.ZoomOut();
+ }
+ }
break;
case speed:
// locally, _transport_speed is an positive value
- _transport_speed += _mcp.surfaces.front()->scaled_delta (state, _mcp.get_session().transport_speed());
+ _transport_speed += _mcp.surfaces.front()->scaled_delta (delta, _mcp.get_session().transport_speed());
// make sure no weirdness gets to the session
if (_transport_speed < 0 || isnan (_transport_speed))
case scrub:
{
- if (state.sign != 0)
- {
+ if (delta != 0) {
add_scrub_interval (_scrub_timer.restart());
// x clicks per second => speed == 1.0
- float speed = _mcp.surfaces.front()->scrub_scaling_factor() / average_scrub_interval() * state.ticks;
- _mcp.get_session().request_transport_speed_nonzero (speed * state.sign);
+ float speed = _mcp.surfaces.front()->scrub_scaling_factor() / average_scrub_interval() * delta;
+ _mcp.get_session().request_transport_speed_nonzero (speed);
}
else
{
case shuttle:
_shuttle_speed = _mcp.get_session().transport_speed();
- _shuttle_speed += _mcp.surfaces.front()->scaled_delta (state, _mcp.get_session().transport_speed());
+ _shuttle_speed += _mcp.surfaces.front()->scaled_delta (delta, _mcp.get_session().transport_speed());
_mcp.get_session().request_transport_speed_nonzero (_shuttle_speed);
break;
public:
enum State { scroll, zoom, speed, scrub, shuttle, select };
- JogWheel( MackieControlProtocol & mcp );
+ JogWheel (MackieControlProtocol & mcp);
/// As the wheel turns...
- void jog_event( SurfacePort & port, Control & control, const ControlState & state );
+ void jog_event (SurfacePort & port, Control & control, float delta);
// These are for incoming button presses that change the internal state
// but they're not actually used at the moment.
- void zoom_event( SurfacePort & port, Control & control, const ControlState & state );
- void scrub_event( SurfacePort & port, Control & control, const ControlState & state );
- void speed_event( SurfacePort & port, Control & control, const ControlState & state );
- void scroll_event( SurfacePort & port, Control & control, const ControlState & state );
+ void zoom_event (SurfacePort & port, Control & control, const ControlState & state);
+ void scrub_event (SurfacePort & port, Control & control, const ControlState & state);
+ void speed_event (SurfacePort & port, Control & control, const ControlState & state);
+ void scroll_event (SurfacePort & port, Control & control, const ControlState & state);
/// Return the current jog wheel mode, which defaults to Scroll
State jog_wheel_state() const;
/// one of -1,0,1
int transport_direction() const { return _transport_direction; }
- void transport_direction( int rhs ) { _transport_direction = rhs; }
+ void transport_direction (int rhs) { _transport_direction = rhs; }
- void push( State state );
+ void push (State state);
void pop();
/// Turn zoom mode on and off
void check_scrubbing();
protected:
- void add_scrub_interval( unsigned long elapsed );
+ void add_scrub_interval (unsigned long elapsed);
float average_scrub_interval();
float std_dev_scrub_interval();
LedState
MackieControlProtocol::loop_press (Button &)
{
- session->request_play_loop (!session->get_play_loop());
- return none;
+ if (_modifier_state & MODIFIER_CONTROL) {
+ set_view_mode (Loop);
+ return on;
+ } else {
+ session->request_play_loop (!session->get_play_loop());
+ return none;
+ }
}
LedState
LedState
MackieControlProtocol::sends_press (Button &)
{
- return off;
+ set_view_mode (Sends);
+ return on;
}
LedState
MackieControlProtocol::sends_release (Button &)
{
- return off;
+ return none;
}
LedState
MackieControlProtocol::pan_press (Button &)
LedState
MackieControlProtocol::eq_press (Button &)
{
- return off;
+ set_view_mode (EQ);
+ return on;
}
LedState
MackieControlProtocol::eq_release (Button &)
{
- return off;
+ return none;
}
LedState
MackieControlProtocol::dyn_press (Button &)
{
- return off;
+ set_view_mode (Dynamics);
+ return on;
}
LedState
MackieControlProtocol::dyn_release (Button &)
{
- return off;
+ return none;
}
LedState
MackieControlProtocol::flip_press (Button &)
{
- _flip_mode = !_flip_mode;
- return (_flip_mode ? on : off);
+ if (_modifier_state == 0) {
+ if (_flip_mode != Normal) {
+ _flip_mode = Normal;
+ } else {
+ _flip_mode = Swap;
+ }
+ } else if (_modifier_state & MODIFIER_CONTROL) {
+ _flip_mode = Zero;
+ }
+
+ return (_flip_mode != Normal ? on : off);
}
LedState
MackieControlProtocol::flip_release (Button &)
{
- return (_flip_mode ? on : off);
+ return none;
}
LedState
MackieControlProtocol::edit_press (Button &)
{
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader to %1\n", position));
+ switch (_surface->mcp().flip_mode()) {
+ case MackieControlProtocol::Normal:
+ break;
+ case MackieControlProtocol::Zero:
+ break;
+ case MackieControlProtocol::Mirror:
+ break;
+ case MackieControlProtocol::Swap:
+ if (_vpot) {
+ handle_pot (*_vpot, 1.0);
+ }
+ return;
+ }
+
if (_route) {
_route->gain_control()->set_value (slider_position_to_gain (position));
}
}
void
-Strip::handle_pot (Pot& pot, ControlState& state)
+Strip::handle_pot (Pot& pot, float delta)
{
if (!_route) {
_surface->write (pot.set_onoff (false));
double p = ac->get_value();
// calculate new value, and adjust
- p += state.delta * state.sign;
+ p += delta;
p = min (1.0, p);
p = max (0.0, p);
void handle_button (Button&, ButtonState bs);
void handle_fader (Fader&, float position);
- void handle_pot (Pot&, ControlState&);
+ void handle_pot (Pot&, float delta);
void periodic ();
}
float
-Surface::scaled_delta (const ControlState & state, float current_speed)
+Surface::scaled_delta (float delta, float current_speed)
{
- return state.sign * (std::pow (float(state.ticks + 1), 2) + current_speed) / 100.0;
+ /* XXX needs work before use */
+ return (std::pow (float(delta + 1), 2) + current_speed) / 100.0;
}
void
if (pot) {
ControlState state;
- // bytes[2] & 0b01000000 (0x40) give sign
- state.sign = (ev->value & 0x40) == 0 ? 1 : -1;
- // bytes[2] & 0b00111111 (0x3f) gives delta
- state.ticks = (ev->value & 0x3f);
- if (state.ticks == 0) {
+ // bit 6 gives the sign
+ float sign = (ev->value & 0x40) == 0 ? 1.0 : -1.0;
+ // bits 0..3 give the velocity. we interpret this as "ticks
+ // moved before this message was sent"
+ float ticks = (ev->value & 0xf);
+ if (ticks == 0) {
/* euphonix and perhaps other devices send zero
when they mean 1, we think.
*/
- state.ticks = 1;
+ ticks = 1;
}
- state.delta = float (state.ticks) / float (0x3f);
+ float delta = sign * (ticks / (float) 0x3f);
/* Pots only emit events when they move, not when they
stop moving. So to get a stop event, we need to use a timeout.
*/
-
+
_mcp.add_in_use_timeout (*this, *pot, pot);
Strip* strip = dynamic_cast<Strip*> (&pot->group());
if (strip) {
- strip->handle_pot (*pot, state);
+ strip->handle_pot (*pot, delta);
} else {
JogWheel* wheel = dynamic_cast<JogWheel*> (pot);
if (wheel) {
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
- wheel->jog_event (*_port, *pot, state);
+ wheel->jog_event (*_port, *pot, delta);
} else {
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
- cout << "external controller" << state.ticks * state.sign << endl;
+ cout << "external controller" << delta << endl;
}
}
} else {
high definition control at low speeds and quick speed changes to/from
higher speeds.
*/
- float scaled_delta (const ControlState & state, float current_speed);
+ float scaled_delta (float delta, float current_speed);
// display the first 2 chars of the msg in the 2 char display
// . is appended to the previous character, so A.B. would