diff options
Diffstat (limited to 'libs/surfaces/maschine2/callbacks.cc')
-rw-r--r-- | libs/surfaces/maschine2/callbacks.cc | 460 |
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); +} |