push2: initial sort of working pad remapping
authorPaul Davis <paul@linuxaudiosystems.com>
Thu, 7 Jul 2016 18:26:55 +0000 (14:26 -0400)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 27 Sep 2016 19:59:30 +0000 (14:59 -0500)
libs/surfaces/push2/buttons.cc
libs/surfaces/push2/gui.cc
libs/surfaces/push2/gui.h
libs/surfaces/push2/mode.cc
libs/surfaces/push2/mode.h
libs/surfaces/push2/push2.cc
libs/surfaces/push2/push2.h
libs/surfaces/push2/wscript

index 0c443cb1ec7bfe51cfd9b40d57aad108742bf138..7bcdff0d77442b29d9a2ab7f70804fcecf78be0a 100644 (file)
@@ -42,13 +42,14 @@ Push2::build_maps ()
        nn_pad_map.insert (std::make_pair (pad->extra(), pad)); \
        coord_pad_map.insert (std::make_pair (pad->coord(), pad));
 
+       MAKE_PAD (0, 0, 92);
        MAKE_PAD (0, 1, 93);
        MAKE_PAD (0, 2, 94);
        MAKE_PAD (0, 3, 95);
        MAKE_PAD (0, 4, 96);
        MAKE_PAD (0, 5, 97);
        MAKE_PAD (0, 6, 98);
-       MAKE_PAD (0, 7, 90);
+       MAKE_PAD (0, 7, 99);
        MAKE_PAD (1, 0, 84);
        MAKE_PAD (1, 1, 85);
        MAKE_PAD (1, 2, 86);
index 78772aeefd252fc03e7bc2f229621c6e5177c27a..8398132d3e8e2706f423e6d03dae93e60bd62337 100644 (file)
@@ -84,6 +84,12 @@ P2GUI::P2GUI (Push2& p)
        , action_table (5, 4)
        , ignore_active_change (false)
        , 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)
 {
        set_border_width (12);
 
@@ -132,17 +138,32 @@ P2GUI::P2GUI (Push2& p)
 
        root_note_selector.set_model (build_note_columns());
        root_note_selector.pack_start (note_columns.name);
+       root_note_selector.set_active (0);
 
        mode_selector.set_model (build_mode_columns());
        mode_selector.pack_start (mode_columns.name);
+       mode_selector.set_active (0);
 
-       mode_packer.pack_start (root_note_selector, false, false);
-       mode_packer.pack_start (mode_selector, false, false);
+       mode_packer.set_border_width (12);
+       mode_packer.set_spacings (12);
+
+       mode_packer.attach (root_note_label, 0, 1, 0, 1, AttachOptions (FILL|EXPAND), SHRINK);
+       mode_packer.attach (root_note_selector, 1, 2, 0, 1, AttachOptions (FILL|EXPAND), SHRINK);
+
+       mode_packer.attach (root_note_octave_label, 0, 1, 1, 2, AttachOptions (FILL|EXPAND), SHRINK);
+       mode_packer.attach (root_note_octave, 1, 2, 1, 2, AttachOptions (FILL|EXPAND), SHRINK);
+
+       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);
 
        pad_notebook.append_page (pad_table, _("Pad Layout"));
        pad_notebook.append_page (mode_packer, _("Modes/Scales"));
        pad_notebook.append_page (custom_packer, _("Custom"));
 
+       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));
+
        set_spacing (12);
 
        pack_start (hpacker, false, false);
@@ -445,17 +466,29 @@ P2GUI::build_mode_columns ()
        Glib::RefPtr<Gtk::ListStore> store = ListStore::create (mode_columns);
        TreeModel::Row row;
 
-       row = *store->append();
-       row[mode_columns.name] = _("Random");
-       row[mode_columns.mode] = MusicalMode::Random;
-
        row = *store->append();
        row[mode_columns.name] = _("Dorian");
        row[mode_columns.mode] = MusicalMode::Dorian;
 
        row = *store->append();
-       row[mode_columns.name] = _("Ionian (\"Minor\")");
-       row[mode_columns.mode] = MusicalMode::Ionian;
+       row[mode_columns.name] = _("Ionian (\"Major\")");
+       row[mode_columns.mode] = MusicalMode::IonianMajor;
+
+       row = *store->append();
+       row[mode_columns.name] = _("Minor");
+       row[mode_columns.mode] = MusicalMode::Minor;
+
+       row = *store->append();
+       row[mode_columns.name] = _("Harmonic Minor");
+       row[mode_columns.mode] = MusicalMode::HarmonicMinor;
+
+       row = *store->append();
+       row[mode_columns.name] = _("Melodic Minor Ascending");
+       row[mode_columns.mode] = MusicalMode::MelodicMinorAscending;
+
+       row = *store->append();
+       row[mode_columns.name] = _("Melodic Minor Descending");
+       row[mode_columns.mode] = MusicalMode::MelodicMinorDescending;
 
        row = *store->append();
        row[mode_columns.name] = _("Phrygian");
@@ -485,22 +518,6 @@ P2GUI::build_mode_columns ()
        row[mode_columns.name] = _("Pentatonic Minor");
        row[mode_columns.mode] = MusicalMode::PentatonicMinor;
 
-       row = *store->append();
-       row[mode_columns.name] = _("Major Chord");
-       row[mode_columns.mode] = MusicalMode::MajorChord;
-
-       row = *store->append();
-       row[mode_columns.name] = _("Minor Chord");
-       row[mode_columns.mode] = MusicalMode::MinorChord;
-
-       row = *store->append();
-       row[mode_columns.name] = _("Min7");
-       row[mode_columns.mode] = MusicalMode::Min7;
-
-       row = *store->append();
-       row[mode_columns.name] = _("Sus4");
-       row[mode_columns.mode] = MusicalMode::Sus4;
-
        row = *store->append();
        row[mode_columns.name] = _("Chromatic");
        row[mode_columns.mode] = MusicalMode::Chromatic;
@@ -610,11 +627,89 @@ P2GUI::build_note_columns ()
        Glib::RefPtr<Gtk::ListStore> store = ListStore::create (note_columns);
        TreeModel::Row row;
 
-       for (int n = 0; n < 127; ++n) {
-               row = *store->append ();
-               row[note_columns.number] = n;
-               row[note_columns.name] = Evoral::midi_note_name (n);
-       }
+       row = *store->append ();
+       row[note_columns.number] = 0;
+       row[note_columns.name] = "C";
+
+       row = *store->append ();
+       row[note_columns.number] = 1;
+       row[note_columns.name] = "C#";
+
+       row = *store->append ();
+       row[note_columns.number] = 2;
+       row[note_columns.name] = "D";
+
+       row = *store->append ();
+       row[note_columns.number] = 3;
+       row[note_columns.name] = "D#";
+
+       row = *store->append ();
+       row[note_columns.number] = 4;
+       row[note_columns.name] = "E";
+
+       row = *store->append ();
+       row[note_columns.number] = 5;
+       row[note_columns.name] = "F";
+
+       row = *store->append ();
+       row[note_columns.number] = 6;
+       row[note_columns.name] = "F#";
+
+       row = *store->append ();
+       row[note_columns.number] = 7;
+       row[note_columns.name] = "G";
+
+       row = *store->append ();
+       row[note_columns.number] = 8;
+       row[note_columns.name] = "G#";
+
+       row = *store->append ();
+       row[note_columns.number] = 9;
+       row[note_columns.name] = "A";
+
+       row = *store->append ();
+       row[note_columns.number] = 10;
+       row[note_columns.name] = "A#";
+
+       row = *store->append ();
+       row[note_columns.number] = 11;
+       row[note_columns.name] = "B";
 
        return store;
 }
+
+void
+P2GUI::reprogram_pad_scale ()
+{
+       int root;
+       int octave;
+       MusicalMode::Type mode;
+
+       Gtk::TreeModel::iterator iter = root_note_selector.get_active();
+       if (iter) {
+               Gtk::TreeModel::Row row = *iter;
+               if (row) {
+                       root = row[note_columns.number];
+               } else {
+                       root = 5;
+               }
+       } else {
+               root = 5;
+       }
+
+       octave = (int) floor (root_note_octave_adjustment.get_value ());
+
+       iter = mode_selector.get_active();
+       if (iter) {
+               Gtk::TreeModel::Row row = *iter;
+               if (row) {
+                       mode = row[mode_columns.mode];
+               } else {
+                       mode = MusicalMode::IonianMajor;
+               }
+       } else {
+               mode = MusicalMode::IonianMajor;
+       }
+
+       p2.set_pad_scale (root, octave, mode);
+}
index 2ec6281a52da201426d571b3e6659e2b7714b7e6..1c38a16c2d7aae5f69ae5dab13f0d354a737e34a 100644 (file)
@@ -28,6 +28,7 @@
 #include <gtkmm/image.h>
 #include <gtkmm/table.h>
 #include <gtkmm/treestore.h>
+#include <gtkmm/spinbutton.h>
 #include <gtkmm/notebook.h>
 
 namespace Gtk {
@@ -99,6 +100,12 @@ private:
 
        /* root notes */
 
+       Gtk::Adjustment root_note_octave_adjustment;
+       Gtk::SpinButton root_note_octave;
+       Gtk::Label root_note_octave_label;
+
+       void root_note_octave_adjustment_changed ();
+
        struct NoteColumns : public Gtk::TreeModel::ColumnRecord {
                NoteColumns () {
                        add (number);
@@ -110,6 +117,9 @@ private:
        NoteColumns note_columns;
        Glib::RefPtr<Gtk::ListStore> build_note_columns ();
        Gtk::ComboBox root_note_selector;
+       Gtk::Label root_note_label;
+
+       void root_note_changed ();
 
        /* modes/scales */
 
@@ -124,10 +134,13 @@ private:
        ModeColumns mode_columns;
        Glib::RefPtr<Gtk::ListStore> build_mode_columns ();
        Gtk::ComboBox mode_selector;
+       Gtk::Label mode_label;
 
        Gtk::Notebook pad_notebook;
-       Gtk::VBox     mode_packer;
+       Gtk::Table    mode_packer;
        Gtk::VBox     custom_packer;
+
+       void reprogram_pad_scale ();
 };
 
 }
index 324c53caba93d6ff8b8b9b987b37523378f73500..99efc86c021eed481d0240b8c13f35bf63a8417c 100644 (file)
@@ -19,9 +19,6 @@ MusicalMode::fill (MusicalMode& m, MusicalMode::Type t)
        */
 
        switch (t) {
-       case Random:
-               m.steps.push_back (0.0); // sekrit code for "random"
-               break;
        case Dorian:
                m.steps.push_back (1.0);
                m.steps.push_back (1.5);
@@ -29,8 +26,9 @@ MusicalMode::fill (MusicalMode& m, MusicalMode::Type t)
                m.steps.push_back (3.0);
                m.steps.push_back (4.0);
                m.steps.push_back (4.5);
+               m.steps.push_back (5.5);
                break;
-       case Ionian:
+       case IonianMajor:
                m.steps.push_back (1.0);
                m.steps.push_back (2.0);
                m.steps.push_back (2.5);
@@ -38,6 +36,48 @@ MusicalMode::fill (MusicalMode& m, MusicalMode::Type t)
                m.steps.push_back (4.5);
                m.steps.push_back (5.5);
                break;
+       case Minor:
+               m.steps.push_back (1.0);
+               m.steps.push_back (1.5);
+               m.steps.push_back (2.5);
+               m.steps.push_back (3.5);
+               m.steps.push_back (4.0);
+               m.steps.push_back (5.0);
+               break;
+       case HarmonicMinor:
+               m.steps.push_back (1.0);
+               m.steps.push_back (1.5);
+               m.steps.push_back (2.5);
+               m.steps.push_back (3.5);
+               m.steps.push_back (5.0);
+               m.steps.push_back (5.5);
+               break;
+       case  BluesScale:
+               m.steps.push_back (1.0);
+               m.steps.push_back (1.5);
+               m.steps.push_back (2.5);
+               m.steps.push_back (3);
+               m.steps.push_back (3.5);
+               m.steps.push_back (4.5);
+               m.steps.push_back (5.0);
+               m.steps.push_back (5.5);
+               break;
+       case MelodicMinorAscending:
+               m.steps.push_back (1.0);
+               m.steps.push_back (1.5);
+               m.steps.push_back (2.5);
+               m.steps.push_back (3.5);
+               m.steps.push_back (4.5);
+               m.steps.push_back (5.5);
+               break;
+       case MelodicMinorDescending:
+               m.steps.push_back (1.0);
+               m.steps.push_back (2.0);
+               m.steps.push_back (2.5);
+               m.steps.push_back (3.5);
+               m.steps.push_back (4.5);
+               m.steps.push_back (5.0);
+               break;
        case Phrygian:
                m.steps.push_back (0.5);
                m.steps.push_back (1.5);
@@ -90,23 +130,6 @@ MusicalMode::fill (MusicalMode& m, MusicalMode::Type t)
                m.steps.push_back (3.5);
                m.steps.push_back (5.0);
                break;
-       case  MajorChord:
-               m.steps.push_back (2.0);
-               m.steps.push_back (3.5);
-               break;
-       case  MinorChord:
-               m.steps.push_back (1.5);
-               m.steps.push_back (3.5);
-               break;
-       case  Min7:
-               m.steps.push_back (1.5);
-               m.steps.push_back (3.5);
-               m.steps.push_back (5.0);
-               break;
-       case  Sus4:
-               m.steps.push_back (2.5);
-               m.steps.push_back (3.5);
-               break;
        case  Chromatic:
                m.steps.push_back (0.5);
                m.steps.push_back (1.0);
@@ -120,16 +143,6 @@ MusicalMode::fill (MusicalMode& m, MusicalMode::Type t)
                m.steps.push_back (5.0);
                m.steps.push_back (5.5);
                break;
-       case  BluesScale:
-               m.steps.push_back (1.0);
-               m.steps.push_back (1.5);
-               m.steps.push_back (2.5);
-               m.steps.push_back (3);
-               m.steps.push_back (3.5);
-               m.steps.push_back (4.5);
-               m.steps.push_back (5.0);
-               m.steps.push_back (5.5);
-               break;
        case  NeapolitanMinor:
                m.steps.push_back (0.5);
                m.steps.push_back (1.5);
index e638d53164092ae31ddcb04e14e376a89e9f17f1..4f570f998d2dd4d453bb52a0920bcde19131d986 100644 (file)
@@ -7,9 +7,12 @@ class MusicalMode
 {
   public:
        enum Type {
-               Random,
                Dorian,
-               Ionian,
+               IonianMajor,
+               Minor,
+               HarmonicMinor,
+               MelodicMinorAscending,
+               MelodicMinorDescending,
                Phrygian,
                Lydian,
                Mixolydian,
@@ -17,10 +20,6 @@ class MusicalMode
                Locrian,
                PentatonicMajor,
                PentatonicMinor,
-               MajorChord,
-               MinorChord,
-               Min7,
-               Sus4,
                Chromatic,
                BluesScale,
                NeapolitanMinor,
index 40a454211f15d841d804e99a2434627e33cdeb06..a9de0aee01fc0356fa6532f7c44938a1c19e7ccb 100644 (file)
@@ -107,13 +107,13 @@ Push2::Push2 (ARDOUR::Session& s)
 
        StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
 
+       /* catch arrival and departure of Push2 itself */
        ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
 
        /* Catch port connections and disconnections */
        ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
 
        /* ports might already be there */
-
        port_registration_handler ();
 }
 
@@ -848,9 +848,15 @@ Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* e
 
        Pad* pad = pi->second;
 
-       pad->set_color (LED::White);
-       pad->set_state (LED::OneShot24th);
-       write (pad->state_msg());
+       if (pad->do_when_pressed == Pad::FlashOn) {
+               pad->set_color (LED::White);
+               pad->set_state (LED::OneShot24th);
+               write (pad->state_msg());
+       } else if (pad->do_when_pressed == Pad::FlashOff) {
+               pad->set_color (LED::Black);
+               pad->set_state (LED::OneShot24th);
+               write (pad->state_msg());
+       }
 }
 
 void
@@ -874,9 +880,15 @@ Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
 
        Pad* pad = pi->second;
 
-       pad->set_color (LED::Black);
-       pad->set_state (LED::OneShot24th);
-       write (pad->state_msg());
+       if (pad->do_when_pressed == Pad::FlashOn) {
+               pad->set_color (LED::Black);
+               pad->set_state (LED::OneShot24th);
+               write (pad->state_msg());
+       } else if (pad->do_when_pressed == Pad::FlashOff) {
+               pad->set_color (pad->perma_color);
+               pad->set_state (LED::OneShot24th);
+               write (pad->state_msg());
+       }
 }
 
 void
@@ -1545,16 +1557,20 @@ Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
 
                        if ((*ev).note() > 10 && (*ev).note() != 12) {
 
-                               int n = (*ev).note ();
-
-                               map<int,int>::const_iterator ni = pad_map.find (n);
+                               const int n = (*ev).note ();
+                               NNPadMap::const_iterator nni = nn_pad_map.find (n);
 
-                               if (ni != pad_map.end()) {
+                               if (nni != nn_pad_map.end()) {
+                                       Pad const * pad = nni->second;
                                        /* shift for output to the shadow port */
-                                       (*ev).set_note (ni->second);
-                                       out.push_back (*ev);
-                                       /* shift back so that the pads light correctly  */
-                                       (*ev).set_note (n);
+                                       if (pad->filtered >= 0) {
+                                               (*ev).set_note (pad->filtered);
+                                               out.push_back (*ev);
+                                               /* shift back so that the pads light correctly  */
+                                               (*ev).set_note (n);
+                                       } else {
+                                               /* no mapping, don't send event */
+                                       }
                                } else {
                                        out.push_back (*ev);
                                }
@@ -1633,10 +1649,8 @@ Push2::input_port()
 void
 Push2::build_pad_table ()
 {
-       for (int row = 0; row < 8; ++row) {
-               for (int col = 7; col >= 0; --col) {
-                       pad_map[row*8+col] = 99 - (row*8+(7-col)) + (octave_shift*12);
-               }
+       for (int n = 36; n < 100; ++n) {
+               pad_map[n] = n + (octave_shift*12);
        }
 
        PadChange (); /* emit signal */
@@ -1653,3 +1667,84 @@ Push2::pad_note (int row, int col) const
 
        return 0;
 }
+
+void
+Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode)
+{
+       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<float>::iterator interval;
+       int note;
+       int keep_root = root;
+
+       interval = m.steps.begin();
+       root += (octave*12);
+       note = root;
+
+       set<int> mode_map; /* contains only notes in mode */
+
+       mode_map.insert (note);
+
+       while (note < 128) {
+
+               if (interval == m.steps.end()) {
+
+                       /* last distance was the end of the scale,
+                          so wrap, adding the next note at one
+                          octave above the last root.
+                       */
+
+                       interval = m.steps.begin();
+                       root += 12;
+                       mode_map.insert (root);
+
+               } else {
+                       note = (int) floor (root + (2.0 * (*interval)));
+                       interval++;
+                       mode_map.insert (note);
+               }
+       }
+
+       for (note = 36; note < 100; ++note) {
+               Pad* pad = nn_pad_map[note];
+
+               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;
+                       }
+                       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());
+       }
+
+       PadChange (); /* EMIT SIGNAL */
+}
index e065dc590aee849f8e5ef779085887210ccef6bf..806d5d0ad10b043e8266822136f703b94aeb460f 100644 (file)
@@ -39,6 +39,7 @@
 #include "control_protocol/types.h"
 
 #include "midi_byte_array.h"
+#include "mode.h"
 
 namespace Cairo {
        class ImageSurface;
@@ -97,6 +98,8 @@ class Push2 : public ARDOUR::ControlProtocol
        uint8_t pad_note (int row, int col) const;
        PBD::Signal0<void> PadChange;
 
+       void set_pad_scale (int root, int octave, MusicalMode::Type mode);
+
    private:
        libusb_device_handle *handle;
        uint8_t   frame_header[16];
@@ -211,7 +214,7 @@ class Push2 : public ARDOUR::ControlProtocol
                        White = 122
                };
 
-               LED (uint8_t e) : _extra (e), _color_index (0), _state (NoTransition) {}
+               LED (uint8_t e) : _extra (e), _color_index (Black), _state (NoTransition) {}
                virtual ~LED() {}
 
                uint8_t extra () const { return _extra; }
@@ -230,10 +233,20 @@ class Push2 : public ARDOUR::ControlProtocol
        };
 
        struct Pad : public LED {
+               enum WhenPressed {
+                       Nothing,
+                       FlashOn,
+                       FlashOff,
+               };
+
                Pad (int xx, int yy, uint8_t ex)
                        : LED (ex)
                        , x (xx)
-                       , y (yy) {}
+                       , y (yy)
+                       , do_when_pressed (FlashOn)
+                       , filtered (ex)
+                       , perma_color (LED::Black)
+               {}
 
                MidiByteArray state_msg () const { return MidiByteArray (3, 0x90|_state, _extra, _color_index); }
 
@@ -242,6 +255,9 @@ class Push2 : public ARDOUR::ControlProtocol
 
                int x;
                int y;
+               int do_when_pressed;
+               int filtered;
+               int perma_color;
        };
 
        struct Button : public LED {
index 322b08f07e66374daafe26705215530d8b014b72..b69d4a22cea21e5b7572dca672186ccff08f00f7 100644 (file)
@@ -25,7 +25,8 @@ def build(bld):
            interface.cc
             midi_byte_array.cc
             leds.cc
-           gui.cc            
+           gui.cc
+           mode.cc
     '''
     obj.export_includes = ['.']
     obj.defines      = [ 'PACKAGE="ardour_push2"' ]