2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "ardour/session.h"
20 #include "gtkmm2ext/colors.h"
21 #include "gtkmm2ext/actions.h"
22 #include "gtkmm2ext/gui_thread.h"
25 #include "maschine2.h"
26 #include "m2controls.h"
28 #include "midi++/port.h"
30 #define COLOR_WHITE 0xffffffff
31 #define COLOR_GRAY 0x606060ff
32 #define COLOR_BLACK 0x000000ff
34 using namespace ARDOUR;
35 using namespace ArdourSurface;
38 Maschine2::connect_signals ()
40 // TODO: use some convenience macros here
43 session->TransportStateChange.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_transport_state_changed, this), this);
44 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_loop_state_changed, this), this);
45 session->RecordStateChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_record_state_changed, this), this);
46 Config->ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
47 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_parameter_changed, this, _1), this);
48 session->DirtyChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_session_dirty_changed, this), this);
49 session->history().Changed.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Maschine2::notify_history_changed, this), this);
52 Glib::RefPtr<Gtk::Action> act;
54 act = ActionManager::find_action (X_("Editor"), X_("ToggleMeasureVisibility"));
56 Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
57 tact->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_grid_change));
60 act = ActionManager::find_action (X_("Editor"), X_("snap-off"));
62 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
63 ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
65 act = ActionManager::find_action (X_("Editor"), X_("snap-magnetic"));
67 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
68 ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
70 act = ActionManager::find_action (X_("Editor"), X_("snap-normal"));
72 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
73 ract->signal_toggled ().connect (sigc::mem_fun (*this, &Maschine2::notify_snap_change));
77 _ctrl->button (M2Contols::Play)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_play, this));
78 _ctrl->button (M2Contols::Rec)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_record, this));
79 _ctrl->button (M2Contols::Loop)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_loop, this));
80 _ctrl->button (M2Contols::Metronom)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_metronom, this));
81 _ctrl->button (M2Contols::GotoStart)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_rewind, this));
82 _ctrl->button (M2Contols::FastRewind)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "RewindSlow"));
83 _ctrl->button (M2Contols::FastForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Transport", "ForwardSlow"));
84 _ctrl->button (M2Contols::Panic)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "MIDI", "panic"));
85 _ctrl->button (M2Contols::JumpForward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-forward-to-mark"));
86 _ctrl->button (M2Contols::JumpBackward)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "jump-backward-to-mark"));
88 _ctrl->button (M2Contols::Grid)->pressed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_pressed, this), gui_context());
89 _ctrl->button (M2Contols::Grid)->released.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_released, this), gui_context());
90 _ctrl->button (M2Contols::Grid)->changed.connect (button_connections, invalidator (*this), boost::bind (&Maschine2::button_snap_changed, this, _1), gui_context());
92 _ctrl->button (M2Contols::Save)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Common", "Save"));
93 _ctrl->button (M2Contols::Undo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "undo"));
94 _ctrl->button (M2Contols::Redo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_action, this, "Editor", "redo"));
96 _ctrl->button (M2Contols::MasterVolume)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_VOLUME));
97 _ctrl->button (M2Contols::MasterTempo)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::handle_master_change, this, MST_TEMPO));
99 _ctrl->button (M2Contols::EncoderWheel)->released.connect_same_thread (button_connections, boost::bind (&Maschine2::button_encoder, this));
100 _ctrl->encoder (0)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::encoder_master, this, _1));
102 for (unsigned int pad = 0; pad < 16; ++pad) {
103 _ctrl->pad (pad)->event.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_event, this, pad, _1, _2));
104 _ctrl->pad (pad)->changed.connect_same_thread (button_connections, boost::bind (&Maschine2::pad_change, this, pad, _1));
107 /* set initial values */
108 notify_record_state_changed ();
109 notify_transport_state_changed ();
110 notify_loop_state_changed ();
111 notify_parameter_changed ("clicking");
112 notify_snap_change ();
113 notify_session_dirty_changed ();
114 notify_history_changed ();
118 Maschine2::notify_record_state_changed ()
120 switch (session->record_status ()) {
121 case Session::Disabled:
122 _ctrl->button (M2Contols::Rec)->set_color (0);
123 _ctrl->button (M2Contols::Rec)->set_blinking (false);
125 case Session::Enabled:
126 _ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
127 _ctrl->button (M2Contols::Rec)->set_blinking (true);
129 case Session::Recording:
130 _ctrl->button (M2Contols::Rec)->set_color (COLOR_WHITE);
131 _ctrl->button (M2Contols::Rec)->set_blinking (false);
137 Maschine2::notify_transport_state_changed ()
139 if (session->transport_rolling ()) {
140 _ctrl->button (M2Contols::Play)->set_color (COLOR_WHITE);
142 _ctrl->button (M2Contols::Play)->set_color (0);
144 notify_loop_state_changed ();
148 Maschine2::notify_loop_state_changed ()
150 bool looping = false;
151 Location* looploc = session->locations ()->auto_loop_location ();
152 if (looploc && session->get_play_loop ()) {
155 _ctrl->button (M2Contols::Loop)->set_color (looping ? COLOR_GRAY : 0);
159 Maschine2::notify_parameter_changed (std::string param)
161 if (param == "clicking") {
162 _ctrl->button (M2Contols::Metronom)->set_color (Config->get_clicking () ? COLOR_GRAY : 0);
168 Maschine2::notify_grid_change ()
170 Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (X_("Editor"), X_("ToggleMeasureVisibility"));
172 Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
173 _ctrl->button (M2Contols::Grid)->set_color (tact->get_active () ? COLOR_WHITE : 0);
179 Maschine2::notify_snap_change ()
182 if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
186 Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (X_("Editor"), X_("snap-magnetic"));
188 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
189 if (ract->get_active ()) { rgba = COLOR_GRAY; }
191 act = ActionManager::find_action (X_("Editor"), X_("snap-normal"));
193 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
194 if (ract->get_active ()) { rgba = COLOR_WHITE; }
197 _ctrl->button (M2Contols::Grid)->set_color (rgba);
201 Maschine2::notify_session_dirty_changed ()
203 bool is_dirty = session->dirty ();
204 _ctrl->button (M2Contols::Save)->set_color (is_dirty ? COLOR_WHITE : COLOR_BLACK);
205 _ctrl->button (M2Contols::Save)->set_blinking (is_dirty);
209 Maschine2::notify_history_changed ()
211 _ctrl->button (M2Contols::Redo)->set_color (session->redo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
212 _ctrl->button (M2Contols::Undo)->set_color (session->undo_depth() > 0 ? COLOR_WHITE : COLOR_BLACK);
217 Maschine2::button_play ()
219 if (session->transport_rolling ()) {
227 Maschine2::button_record ()
229 set_record_enable (!get_record_enabled ());
233 Maschine2::button_loop ()
239 Maschine2::button_metronom ()
241 Config->set_clicking (!Config->get_clicking ());
245 Maschine2::button_rewind ()
247 goto_start (session->transport_rolling ());
251 Maschine2::button_action (const std::string& group, const std::string& item)
253 AccessAction (group, item);
258 Maschine2::button_grid ()
260 Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (X_("Editor"), X_("ToggleMeasureVisibility"));
262 Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
263 tact->set_active (!tact->get_active ());
269 Maschine2::button_snap_pressed ()
271 _ctrl->button (M2Contols::Grid)->set_color (COLOR_WHITE);
272 _ctrl->button (M2Contols::Grid)->set_blinking (true);
276 Maschine2::button_snap_changed (bool pressed)
279 _ctrl->button (M2Contols::Grid)->set_blinking (false);
280 notify_snap_change ();
282 notify_master_change ();
286 Maschine2::button_snap_released ()
288 _ctrl->button (M2Contols::Grid)->set_blinking (false);
290 const char* action = 0;
291 Glib::RefPtr<Gtk::Action> act = ActionManager::find_action (X_("Editor"), X_("snap-off"));
293 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
294 if (ract->get_active ()) { action = "snap-normal"; }
297 act = ActionManager::find_action (X_("Editor"), X_("snap-normal"));
299 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
300 if (ract->get_active ()) { action = "snap-magnetic"; }
303 act = ActionManager::find_action (X_("Editor"), X_("snap-magnetic"));
305 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
306 if (ract->get_active ()) { action = "snap-off"; }
314 act = ActionManager::find_action (X_("Editor"), action);
316 Glib::RefPtr<Gtk::RadioAction> ract = Glib::RefPtr<Gtk::RadioAction>::cast_dynamic (act);
317 ract->set_active (true);
321 /* Master mode + state -- main encoder fn */
324 Maschine2::handle_master_change (enum MasterMode id)
328 if (_master_state == MST_VOLUME) { _master_state = MST_NONE; } else { _master_state = MST_VOLUME; }
331 if (_master_state == MST_TEMPO) { _master_state = MST_NONE; } else { _master_state = MST_TEMPO; }
337 notify_master_change ();
341 Maschine2::notify_master_change ()
343 if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
344 _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
345 _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
348 switch (_master_state) {
350 _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
351 _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
354 _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_WHITE);
355 _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_BLACK);
358 _ctrl->button (M2Contols::MasterVolume)->set_color (COLOR_BLACK);
359 _ctrl->button (M2Contols::MasterTempo)->set_color (COLOR_WHITE);
364 static void apply_ac_delta (boost::shared_ptr<AutomationControl> ac, double d) {
368 ac->set_value (ac->interface_to_internal (min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + d))),
369 PBD::Controllable::UseGroup);
373 Maschine2::encoder_master (int delta)
375 if (_ctrl->button (M2Contols::Grid)->is_pressed ()) {
376 _ctrl->button (M2Contols::Grid)->ignore_release ();
378 AccessAction ("Editor", "next-snap-choice");
380 AccessAction ("Editor", "prev-snap-choice");
384 switch (_master_state) {
386 if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
388 AccessAction ("Editor", "temporal-zoom-in");
390 AccessAction ("Editor", "temporal-zoom-out");
394 AccessAction ("Editor", "playhead-forward-to-grid");
396 AccessAction ("Editor", "playhead-backward-to-grid");
402 boost::shared_ptr<Route> master = session->master_out ();
404 // TODO consider _ctrl->button (M2Contols::EncoderWheel)->is_pressed() for fine grained
405 const double factor = _ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active () ? 256. : 32.;
406 apply_ac_delta (master->gain_control(), delta / factor);
411 // set new tempo.. apply with "enter"
417 Maschine2::button_encoder ()
419 switch (_master_state) {
422 if (_ctrl->button (M2Contols::BtnShift, M2Contols::ModNone)->active ()) {
423 AccessAction ("Editor", "zoom-to-session");
427 // ignore -> fine gained?
436 Maschine2::pad_change (unsigned int pad, float v)
438 float lvl = v; // _ctrl->pad (pad)->value () / 4095.f;
439 Gtkmm2ext::Color c = Gtkmm2ext::hsva_to_color (270 - 270.f * lvl, 1.0, lvl * lvl, 1.0);
440 _ctrl->pad (pad)->set_color (c);
444 Maschine2::pad_event (unsigned int pad, float v, bool ev)
448 msg[0] = v > 0 ? 0x90 : 0x80;
449 msg[1] = 36 + pad; // TODO map note to scale
450 msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
451 _output_port->write (msg, 3, 0);
455 msg[1] = 36 + pad; // TODO map note to scale
456 msg[2] = ((uint8_t)floor (v * 127)) & 0x7f;
457 _output_port->write (msg, 3, 0);
459 //printf ("[%2d] %s %.1f\n", pad, ev ? "On/Off" : "Aftertouch" , v * 127);