From 70477e6fedee80a4e785d4da65c873329d06f76a Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 7 Jul 2016 15:53:19 -0400 Subject: push2: almost fully operational ableton style pad mapping, etc --- libs/surfaces/push2/gui.cc | 23 +++-- libs/surfaces/push2/gui.h | 3 + libs/surfaces/push2/push2.cc | 215 ++++++++++++++++++++++++++++++++++--------- libs/surfaces/push2/push2.h | 10 +- 4 files changed, 195 insertions(+), 56 deletions(-) (limited to 'libs/surfaces/push2') diff --git a/libs/surfaces/push2/gui.cc b/libs/surfaces/push2/gui.cc index 8398132d3e..0c417276c4 100644 --- a/libs/surfaces/push2/gui.cc +++ b/libs/surfaces/push2/gui.cc @@ -86,10 +86,11 @@ P2GUI::P2GUI (Push2& p) , pad_table (8, 8) , root_note_octave_adjustment (3, 0, 10, 1, 1) , root_note_octave (root_note_octave_adjustment) - , root_note_octave_label (X_("Octave")) - , root_note_label (X_("Root")) - , mode_label (X_("Mode (Scale)")) - , mode_packer (3, 2) + , root_note_octave_label (_("Octave")) + , root_note_label (_("Root")) + , mode_label (_("Mode (Scale)")) + , inkey_button (_("In-Key Mode")) + , mode_packer (3, 3) { set_border_width (12); @@ -156,6 +157,8 @@ P2GUI::P2GUI (Push2& p) mode_packer.attach (mode_label, 0, 1, 2, 3, AttachOptions (FILL|EXPAND), SHRINK); mode_packer.attach (mode_selector, 1, 2, 2, 3, AttachOptions (FILL|EXPAND), SHRINK); + mode_packer.attach (inkey_button, 1, 2, 3, 4, AttachOptions (FILL|EXPAND), SHRINK); + pad_notebook.append_page (pad_table, _("Pad Layout")); pad_notebook.append_page (mode_packer, _("Modes/Scales")); pad_notebook.append_page (custom_packer, _("Custom")); @@ -163,6 +166,7 @@ P2GUI::P2GUI (Push2& p) root_note_octave_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &P2GUI::reprogram_pad_scale)); root_note_selector.signal_changed().connect (sigc::mem_fun (*this, &P2GUI::reprogram_pad_scale)); mode_selector.signal_changed().connect (sigc::mem_fun (*this, &P2GUI::reprogram_pad_scale)); + inkey_button.signal_clicked().connect (sigc::mem_fun (*this, &P2GUI::reprogram_pad_scale)); set_spacing (12); @@ -447,15 +451,15 @@ P2GUI::build_pad_table () { container_clear (pad_table); - for (int row = 0; row < 8; ++row) { + for (int row = 7; row >= 0; --row) { for (int col = 0; col < 8; ++col) { - int n = (int) p2.pad_note (row, col); + int n = p2.pad_note (row, col); Gtk::Button* b = manage (new Button (string_compose ("%1 (%2)", Evoral::midi_note_name (n), n))); b->show (); - pad_table.attach (*b, col, col+1, row, row + 1); + pad_table.attach (*b, col, col+1, (7-row), (8-row)); } } } @@ -684,6 +688,7 @@ P2GUI::reprogram_pad_scale () int root; int octave; MusicalMode::Type mode; + bool inkey; Gtk::TreeModel::iterator iter = root_note_selector.get_active(); if (iter) { @@ -711,5 +716,7 @@ P2GUI::reprogram_pad_scale () mode = MusicalMode::IonianMajor; } - p2.set_pad_scale (root, octave, mode); + inkey = inkey_button.get_active (); + + p2.set_pad_scale (root, octave, mode, inkey); } diff --git a/libs/surfaces/push2/gui.h b/libs/surfaces/push2/gui.h index 1c38a16c2d..b472698fee 100644 --- a/libs/surfaces/push2/gui.h +++ b/libs/surfaces/push2/gui.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -136,6 +137,8 @@ private: Gtk::ComboBox mode_selector; Gtk::Label mode_label; + Gtk::CheckButton inkey_button; + Gtk::Notebook pad_notebook; Gtk::Table mode_packer; Gtk::VBox custom_packer; diff --git a/libs/surfaces/push2/push2.cc b/libs/surfaces/push2/push2.cc index a9de0aee01..d5672fe886 100644 --- a/libs/surfaces/push2/push2.cc +++ b/libs/surfaces/push2/push2.cc @@ -26,6 +26,7 @@ #include "pbd/failed_constructor.h" #include "pbd/file_utils.h" #include "pbd/search_path.h" +#include "pbd/enumwriter.h" #include "midi++/parser.h" #include "timecode/time.h" @@ -61,6 +62,59 @@ const int Push2::pixels_per_row = 1024; #define ABLETON 0x2982 #define PUSH2 0x1967 +__attribute__((constructor)) static void +register_enums () +{ + EnumWriter& enum_writer (EnumWriter::instance()); + vector i; + vector s; + + MusicalMode::Type mode; + +#define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() +#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) + + REGISTER_CLASS_ENUM (MusicalMode,Dorian); + REGISTER_CLASS_ENUM (MusicalMode, IonianMajor); + REGISTER_CLASS_ENUM (MusicalMode, Minor); + REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor); + REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending); + REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending); + REGISTER_CLASS_ENUM (MusicalMode, Phrygian); + REGISTER_CLASS_ENUM (MusicalMode, Lydian); + REGISTER_CLASS_ENUM (MusicalMode, Mixolydian); + REGISTER_CLASS_ENUM (MusicalMode, Aeolian); + REGISTER_CLASS_ENUM (MusicalMode, Locrian); + REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor); + REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor); + REGISTER_CLASS_ENUM (MusicalMode, Chromatic); + REGISTER_CLASS_ENUM (MusicalMode, BluesScale); + REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor); + REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor); + REGISTER_CLASS_ENUM (MusicalMode, Oriental); + REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic); + REGISTER_CLASS_ENUM (MusicalMode, Enigmatic); + REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi); + REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor); + REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor); + REGISTER_CLASS_ENUM (MusicalMode, Kumoi); + REGISTER_CLASS_ENUM (MusicalMode, Iwato); + REGISTER_CLASS_ENUM (MusicalMode, Hindu); + REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone); + REGISTER_CLASS_ENUM (MusicalMode, Pelog); + REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy); + REGISTER_CLASS_ENUM (MusicalMode, Overtone); + REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone); + REGISTER_CLASS_ENUM (MusicalMode, Arabian); + REGISTER_CLASS_ENUM (MusicalMode, Balinese); + REGISTER_CLASS_ENUM (MusicalMode, Gypsy); + REGISTER_CLASS_ENUM (MusicalMode, Mohammedan); + REGISTER_CLASS_ENUM (MusicalMode, Javanese); + REGISTER_CLASS_ENUM (MusicalMode, Persian); + REGISTER_CLASS_ENUM (MusicalMode, Algerian); + REGISTER (mode); +} + Push2::Push2 (ARDOUR::Session& s) : ControlProtocol (s, string (X_("Ableton Push 2"))) , AbstractUI (name()) @@ -72,6 +126,10 @@ Push2::Push2 (ARDOUR::Session& s) , bank_start (0) , connection_state (ConnectionState (0)) , gui (0) + , mode (MusicalMode::IonianMajor) + , scale_root (36) + , root_octave (3) + , in_key (true) , octave_shift (0) { context = Cairo::Context::create (frame_buffer); @@ -292,12 +350,14 @@ Push2::init_buttons (bool startup) } } - for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) { - Pad* pad = pi->second; + if (!startup) { + for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) { + Pad* pad = pi->second; - pad->set_color (LED::Black); - pad->set_state (LED::OneShot24th); - write (pad->state_msg()); + pad->set_color (LED::Black); + pad->set_state (LED::OneShot24th); + write (pad->state_msg()); + } } } @@ -591,6 +651,7 @@ Push2::set_active (bool yn) init_buttons (true); init_touch_strip (); + set_pad_scale (scale_root, root_octave, mode, in_key); switch_bank (0); splash (); @@ -1053,6 +1114,11 @@ Push2::get_state() child->add_child_nocopy (_async_out->get_state()); node.add_child_nocopy (*child); + node.add_property ("root", to_string (scale_root, std::dec)); + node.add_property ("root_octave", to_string (root_octave, std::dec)); + node.add_property ("in_key", in_key ? X_("yes") : X_("no")); + node.add_property ("mode", enum_2_string (mode)); + return node; } @@ -1656,51 +1722,39 @@ Push2::build_pad_table () PadChange (); /* emit signal */ } -uint8_t +int Push2::pad_note (int row, int col) const { - map::const_iterator ni = pad_map.find (row*8+col); + NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col); - if (ni != pad_map.end()) { - return ni->second; + if (nni != nn_pad_map.end()) { + return nni->second->filtered; } return 0; } void -Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode) +Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) { cerr << "reset pad to r = " << root << " o = " << octave << " m = " << mode << endl; MusicalMode m (mode); - - if (mode == MusicalMode::Chromatic) { - /* back to "normal" */ - for (int note = 36; note < 100; ++note) { - Pad* pad = nn_pad_map[note]; - pad->do_when_pressed = Pad::FlashOn; - pad->set_color (LED::Black); - pad->perma_color = LED::Black; - pad->filtered = note; - write (pad->state_msg()); - } - - PadChange (); - return; - } - vector::iterator interval; int note; - int keep_root = root; + const int original_root = root; interval = m.steps.begin(); root += (octave*12); note = root; - set mode_map; /* contains only notes in mode */ + const int root_start = root; + + set mode_map; /* contains only notes in mode, O(logN) lookup */ + vector mode_vector; /* sorted in note order */ mode_map.insert (note); + mode_vector.push_back (note); while (note < 128) { @@ -1714,37 +1768,106 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode) interval = m.steps.begin(); root += 12; mode_map.insert (root); + mode_vector.push_back (root); } else { note = (int) floor (root + (2.0 * (*interval))); interval++; mode_map.insert (note); + mode_vector.push_back (note); } } - for (note = 36; note < 100; ++note) { - Pad* pad = nn_pad_map[note]; + if (inkey) { - if (mode_map.find (note) != mode_map.end()) { - if ((note % 12) == keep_root) { - pad->set_color (LED::Green); - pad->perma_color = LED::Green; - } else { - pad->set_color (LED::White); - pad->perma_color = LED::White; + vector::iterator notei; + int row_offset = 0; + for (int row = 0; row < 8; ++row) { + + /* Ableton's grid layout wraps the available notes in the scale + * by offsetting 3 notes per row (from the bottom) + */ + + notei = mode_vector.begin(); + notei += row_offset; + row_offset += 3; + + for (int col = 0; col < 8; ++col) { + int index = 36 + (row*8) + col; + Pad* pad = nn_pad_map[index]; + int notenum; + if (notei != mode_vector.end()) { + + notenum = *notei; + pad->filtered = notenum; + + if ((notenum % 12) == original_root) { + pad->set_color (LED::Green); + pad->perma_color = LED::Green; + } else { + pad->set_color (LED::White); + pad->perma_color = LED::White; + } + + pad->do_when_pressed = Pad::FlashOff; + notei++; + + } else { + + pad->set_color (LED::Black); + pad->do_when_pressed = Pad::Nothing; + pad->filtered = -1; + } + + write (pad->state_msg()); } - pad->do_when_pressed = Pad::FlashOff; - /* Chromatic: all pads send their own note number */ - pad->filtered = note; - } else { - /* note is not in mode, turn it off */ - pad->do_when_pressed = Pad::Nothing; - pad->set_color (LED::Black); - pad->filtered = -1; } - write (pad->state_msg()); + } else { + + /* chromatic: all notes available, but highlight those in the scale */ + + for (note = 36; note < 100; ++note) { + + Pad* pad = nn_pad_map[note]; + + /* Chromatic: all pads play, half-tone steps. Light + * those in the scale, and highlight root notes + */ + + pad->filtered = root_start + (note - 36); + + if (mode_map.find (note) != mode_map.end()) { + + if ((note % 12) == original_root) { + pad->set_color (LED::Green); + pad->perma_color = LED::Green; + } else { + pad->set_color (LED::White); + pad->perma_color = LED::White; + } + + pad->do_when_pressed = Pad::FlashOff; + + } else { + + /* note is not in mode, turn it off */ + + pad->do_when_pressed = Pad::FlashOn; + pad->set_color (LED::Black); + + } + + write (pad->state_msg()); + } } PadChange (); /* EMIT SIGNAL */ + + /* store state */ + + scale_root = root; + root_octave = octave; + in_key = inkey; + mode = mode; } diff --git a/libs/surfaces/push2/push2.h b/libs/surfaces/push2/push2.h index 806d5d0ad1..e39c07e76c 100644 --- a/libs/surfaces/push2/push2.h +++ b/libs/surfaces/push2/push2.h @@ -95,10 +95,10 @@ class Push2 : public ARDOUR::ControlProtocol boost::shared_ptr input_port(); boost::shared_ptr output_port(); - uint8_t pad_note (int row, int col) const; + int pad_note (int row, int col) const; PBD::Signal0 PadChange; - void set_pad_scale (int root, int octave, MusicalMode::Type mode); + void set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey); private: libusb_device_handle *handle; @@ -513,6 +513,12 @@ class Push2 : public ARDOUR::ControlProtocol std::map pad_map; void build_pad_table(); + + MusicalMode::Type mode; + int scale_root; + int root_octave; + bool in_key; + int octave_shift; }; -- cgit v1.2.3