summaryrefslogtreecommitdiff
path: root/libs/surfaces/maschine2/callbacks.cc
diff options
context:
space:
mode:
Diffstat (limited to 'libs/surfaces/maschine2/callbacks.cc')
-rw-r--r--libs/surfaces/maschine2/callbacks.cc460
1 files changed, 460 insertions, 0 deletions
diff --git a/libs/surfaces/maschine2/callbacks.cc b/libs/surfaces/maschine2/callbacks.cc
new file mode 100644
index 0000000000..32466fc7be
--- /dev/null
+++ b/libs/surfaces/maschine2/callbacks.cc
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
+ *
+ * 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "ardour/session.h"
+#include "gtkmm2ext/colors.h"
+#include "gtkmm2ext/actions.h"
+#include "gtkmm2ext/gui_thread.h"
+#include "pbd/i18n.h"
+
+#include "maschine2.h"
+#include "m2controls.h"
+
+#include "midi++/port.h"
+
+#define COLOR_WHITE 0xffffffff
+#define COLOR_GRAY 0x606060ff
+#define COLOR_BLACK 0x000000ff
+
+using namespace ARDOUR;
+using namespace ArdourSurface;
+
+void
+Maschine2::connect_signals ()
+{
+ // TODO: use some convenience macros here
+
+ /* Signals */
+ session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_transport_state_changed, this), this);
+ session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_loop_state_changed, this), this);
+ session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_record_state_changed, this), this);
+ Config->ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
+ session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
+ session->DirtyChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_session_dirty_changed, this), this);
+ session->history().Changed.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_history_changed, this), this);
+
+ /* Actions */
+ Glib::RefPtr<Gtk::Action> act;
+#if 0
+ act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
+ if (act) {
+ Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
+ tact->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_grid_change));
+ }
+#endif
+ act = ActionManager::get_action (X_("Editor"), X_("snap-off"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
+ }
+ act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
+ }
+ act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
+ }
+
+ /* Surface events */
+ _ctrl->button (M2Contols::Play)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_play, this));
+ _ctrl->button (M2Contols::Rec)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_record, this));
+ _ctrl->button (M2Contols::Loop)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_loop, this));
+ _ctrl->button (M2Contols::Metronom)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_metronom, this));
+ _ctrl->button (M2Contols::GotoStart)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_rewind, this));
+ _ctrl->button (M2Contols::FastRewind)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "RewindSlow"));
+ _ctrl->button (M2Contols::FastForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "ForwardSlow"));
+ _ctrl->button (M2Contols::Panic)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "MIDI", "panic"));
+ _ctrl->button (M2Contols::JumpForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-forward-to-mark"));
+ _ctrl->button (M2Contols::JumpBackward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-backward-to-mark"));
+
+ _ctrl->button (M2Contols::Grid)->pressed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_pressed, this), gui_context());
+ _ctrl->button (M2Contols::Grid)->released.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_released, this), gui_context());
+ _ctrl->button (M2Contols::Grid)->changed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_changed, this, _1), gui_context());
+
+ _ctrl->button (M2Contols::Save)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Common", "Save"));
+ _ctrl->button (M2Contols::Undo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "undo"));
+ _ctrl->button (M2Contols::Redo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "redo"));
+
+ _ctrl->button (M2Contols::MasterVolume)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_VOLUME));
+ _ctrl->button (M2Contols::MasterTempo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_TEMPO));
+
+ _ctrl->button (M2Contols::EncoderWheel)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_encoder, this));
+ _ctrl->encoder (0)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::encoder_master, this, _1));
+
+ for (unsigned int pad = 0; pad < 16; ++pad) {
+ _ctrl->pad (pad)->event.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_event, this, pad, _1, _2));
+ _ctrl->pad (pad)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_change, this, pad, _1));
+ }
+
+ /* set initial values */
+ notify_record_state_changed ();
+ notify_transport_state_changed ();
+ notify_loop_state_changed ();
+ notify_parameter_changed ("clicking");
+ notify_snap_change ();
+ notify_session_dirty_changed ();
+ notify_history_changed ();
+}
+
+void
+Maschine2::notify_record_state_changed ()
+{
+ switch (session->record_status ()) {
+ case Session::Disabled:
+ _ctrl->button (M2Contols::Rec)->set_color (0);
+ _ctrl->button (M2Contols::Rec)->set_blinking (false);
+ break;
+ case Session::Enabled:
+ _ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
+ _ctrl->button (M2Contols::Rec)->set_blinking (true);
+ break;
+ case Session::Recording:
+ _ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
+ _ctrl->button (M2Contols::Rec)->set_blinking (false);
+ break;
+ }
+}
+
+void
+Maschine2::notify_transport_state_changed ()
+{
+ if (session->transport_rolling ()) {
+ _ctrl->button (M2Contols::Play)->set_color (COLOR_WHITE);
+ } else {
+ _ctrl->button (M2Contols::Play)->set_color (0);
+ }
+ notify_loop_state_changed ();
+}
+
+void
+Maschine2::notify_loop_state_changed ()
+{
+ bool looping = false;
+ Location* looploc = session->locations ()->auto_loop_location ();
+ if (looploc && session->get_play_loop ()) {
+ looping = true;
+ }
+ _ctrl->button (M2Contols::Loop)->set_color (looping ? COLOR_GRAY : 0);
+}
+
+void
+Maschine2::notify_parameter_changed (std::string param)
+{
+ if (param == "clicking") {
+ _ctrl->button (M2Contols::Metronom)->set_color (Config->get_clicking () ? COLOR_GRAY : 0);
+ }
+}
+
+#if 0
+void
+Maschine2::notify_grid_change ()
+{
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
+ if (act) {
+ Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
+ _ctrl->button (M2Contols::Grid)->set_color (tact->get_active () ? COLOR_WHITE : 0);
+ }
+}
+#endif
+
+void
+Maschine2::notify_snap_change ()
+{
+ uint32_t rgba = 0;
+ if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
+ return;
+ }
+
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ if (ract->get_active ()) { rgba = COLOR_GRAY; }
+ }
+ act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ if (ract->get_active ()) { rgba = COLOR_WHITE; }
+ }
+
+ _ctrl->button (M2Contols::Grid)->set_color (rgba);
+}
+
+void
+Maschine2::notify_session_dirty_changed ()
+{
+ bool is_dirty = session->dirty ();
+ _ctrl->button (M2Contols::Save)->set_color (is_dirty ? COLOR_WHITE : COLOR_BLACK);
+ _ctrl->button (M2Contols::Save)->set_blinking (is_dirty);
+}
+
+void
+Maschine2::notify_history_changed ()
+{
+ _ctrl->button (M2Contols::Redo)->set_color (session->redo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
+ _ctrl->button (M2Contols::Undo)->set_color (session->undo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
+}
+
+
+void
+Maschine2::button_play ()
+{
+ if (session->transport_rolling ()) {
+ transport_stop ();
+ } else {
+ transport_play ();
+ }
+}
+
+void
+Maschine2::button_record ()
+{
+ set_record_enable (!get_record_enabled ());
+}
+
+void
+Maschine2::button_loop ()
+{
+ loop_toggle ();
+}
+
+void
+Maschine2::button_metronom ()
+{
+ Config->set_clicking (!Config->get_clicking ());
+}
+
+void
+Maschine2::button_rewind ()
+{
+ goto_start (session->transport_rolling ());
+}
+
+void
+Maschine2::button_action (const std::string& group, const std::string& item)
+{
+ AccessAction (group, item);
+}
+
+#if 0
+void
+Maschine2::button_grid ()
+{
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
+ if (act) {
+ Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
+ tact->set_active (!tact->get_active ());
+ }
+}
+#endif
+
+void
+Maschine2::button_snap_pressed ()
+{
+ _ctrl->button (M2Contols::Grid)->set_color (COLOR_WHITE);
+ _ctrl->button (M2Contols::Grid)->set_blinking (true);
+}
+
+void
+Maschine2::button_snap_changed (bool pressed)
+{
+ if (!pressed) {
+ _ctrl->button (M2Contols::Grid)->set_blinking (false);
+ notify_snap_change ();
+ }
+ notify_master_change ();
+}
+
+void
+Maschine2::button_snap_released ()
+{
+ _ctrl->button (M2Contols::Grid)->set_blinking (false);
+
+ const char* action = 0;
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (X_("Editor"), X_("snap-off"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ if (ract->get_active ()) { action = "snap-normal"; }
+ }
+
+ act = ActionManager::get_action (X_("Editor"), X_("snap-normal"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ if (ract->get_active ()) { action = "snap-magnetic"; }
+ }
+
+ act = ActionManager::get_action (X_("Editor"), X_("snap-magnetic"));
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ if (ract->get_active ()) { action = "snap-off"; }
+ }
+
+ if (!action) {
+ assert (0);
+ return;
+ }
+
+ act = ActionManager::get_action (X_("Editor"), action);
+ if (act) {
+ Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
+ ract->set_active (true);
+ }
+}
+
+/* Master mode + state -- main encoder fn */
+
+void
+Maschine2::handle_master_change (enum MasterMode id)
+{
+ switch (id) {
+ case MST_VOLUME:
+ if (_master_state == MST_VOLUME) { _master_state = MST_NONE; } else { _master_state = MST_VOLUME; }
+ break;
+ case MST_TEMPO:
+ if (_master_state == MST_TEMPO) { _master_state = MST_NONE; } else { _master_state = MST_TEMPO; }
+ break;
+ default:
+ return;
+ break;
+ }
+ notify_master_change ();
+}
+
+void
+Maschine2::notify_master_change ()
+{
+ if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
+ _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
+ _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
+ return;
+ }
+ switch (_master_state) {
+ case MST_NONE:
+ _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
+ _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
+ break;
+ case MST_VOLUME:
+ _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_WHITE);
+ _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
+ break;
+ case MST_TEMPO:
+ _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
+ _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_WHITE);
+ break;
+ }
+}
+
+static void apply_ac_delta (boost::shared_ptr<AutomationControl> ac, double d) {
+ if (!ac) {
+ return;
+ }
+ ac->set_value (ac->interface_to_internal (min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + d))),
+ PBD::Controllable::UseGroup);
+}
+
+void
+Maschine2::encoder_master (int delta)
+{
+ if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
+ _ctrl->button (M2Contols::Grid)->ignore_release ();
+ if (delta > 0) {
+ AccessAction ("Editor", "next-snap-choice");
+ } else {
+ AccessAction ("Editor", "prev-snap-choice");
+ }
+ return;
+ }
+ switch (_master_state) {
+ case MST_NONE:
+ if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
+ if (delta > 0) {
+ AccessAction ("Editor", "temporal-zoom-in");
+ } else {
+ AccessAction ("Editor", "temporal-zoom-out");
+ }
+ } else {
+ if (delta > 0) {
+ AccessAction ("Editor", "playhead-forward-to-grid");
+ } else {
+ AccessAction ("Editor", "playhead-backward-to-grid");
+ }
+ }
+ break;
+ case MST_VOLUME:
+ {
+ boost::shared_ptr<Route> master = session->master_out ();
+ if (master) {
+ // TODO consider _ctrl->button (M2Contols::EncoderWheel)->is_pressed() for fine grained
+ const double factor = _ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? 256. : 32.;
+ apply_ac_delta (master->gain_control(), delta / factor);
+ }
+ }
+ break;
+ case MST_TEMPO:
+ // set new tempo.. apply with "enter"
+ break;
+ }
+}
+
+void
+Maschine2::button_encoder ()
+{
+ switch (_master_state) {
+ case MST_NONE:
+ // OR: add marker ??
+ if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
+ AccessAction ("Editor", "zoom-to-session");
+ }
+ break;
+ case MST_VOLUME:
+ // ignore -> fine gained?
+ break;
+ case MST_TEMPO:
+ // add new tempo.. ?
+ break;
+ }
+}
+
+void
+Maschine2::pad_change (unsigned int pad, float v)
+{
+ float lvl = v; // _ctrl->pad (pad)->value () / 4095.f;
+ Gtkmm2ext::Color c = Gtkmm2ext::hsva_to_color (270 - 270.f * lvl, 1.0, lvl * lvl, 1.0);
+ _ctrl->pad (pad)->set_color (c);
+}
+
+void
+Maschine2::pad_event (unsigned int pad, float v, bool ev)
+{
+ if (ev) {
+ uint8_t msg[3];
+ msg[0] = v > 0 ? 0x90 : 0x80;
+ msg[1] = 36 + pad; // TODO map note to scale
+ msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
+ _output_port->write (msg, 3, 0);
+ } else {
+ uint8_t msg[3];
+ msg[0] = 0xa0;
+ msg[1] = 36 + pad; // TODO map note to scale
+ msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
+ _output_port->write (msg, 3, 0);
+ }
+ //printf ("[%2d] %s %.1f\n", pad, ev ? "On/Off" : "Aftertouch" , v * 127);
+}