+
+void
+Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
+{
+ MusicalMode m (mode);
+ vector<float>::iterator interval;
+ int note;
+ const int original_root = root;
+
+ interval = m.steps.begin();
+ root += (octave*12);
+ note = root;
+
+ const int root_start = root;
+
+ set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
+ vector<int> mode_vector; /* sorted in note order */
+
+ mode_map.insert (note);
+ mode_vector.push_back (note);
+
+ /* build a map of all notes in the mode, from the root to 127 */
+
+ 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);
+ mode_vector.push_back (root);
+
+ } else {
+ note = (int) floor (root + (2.0 * (*interval)));
+ interval++;
+ mode_map.insert (note);
+ mode_vector.push_back (note);
+ }
+ }
+
+ if (inkey) {
+
+ vector<int>::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());
+ }
+ }
+
+ } 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 = original_root;
+ _root_octave = octave;
+ _in_key = inkey;
+ _mode = mode;
+}
+
+void
+Push2::set_percussive_mode (bool yn)
+{
+ if (!yn) {
+ cerr << "back to scale\n";
+ set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
+ percussion = false;
+ return;
+ }
+
+ int drum_note = 36;
+
+ for (int row = 0; row < 8; ++row) {
+
+ for (int col = 0; col < 4; ++col) {
+
+ int index = 36 + (row*8) + col;
+ Pad* pad = nn_pad_map[index];
+
+ pad->filtered = drum_note;
+ drum_note++;
+ }
+ }
+
+ for (int row = 0; row < 8; ++row) {
+
+ for (int col = 4; col < 8; ++col) {
+
+ int index = 36 + (row*8) + col;
+ Pad* pad = nn_pad_map[index];
+
+ pad->filtered = drum_note;
+ drum_note++;
+ }
+ }
+
+ percussion = true;
+
+ PadChange (); /* EMIT SIGNAL */
+}
+
+Push2Layout*
+Push2::current_layout () const
+{
+ Glib::Threads::Mutex::Lock lm (layout_lock);
+ return _current_layout;
+}
+
+void
+Push2::stripable_selection_change (StripableNotificationListPtr selected)
+{
+ boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
+ boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
+ boost::shared_ptr<MidiTrack> new_pad_target;
+
+ /* See if there's a MIDI track selected */
+
+ for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
+
+ new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
+
+ if (new_pad_target) {
+ break;
+ }
+ }
+
+ if (new_pad_target) {
+ cerr << "new midi pad target " << new_pad_target->name() << endl;
+ } else {
+ cerr << "no midi pad target\n";
+ }
+
+ if (current_midi_track == new_pad_target) {
+ /* nothing to do */
+ return;
+ }
+
+ if (!new_pad_target) {
+ /* leave existing connection alone */
+ return;
+ }
+
+ /* disconnect from pad port, if appropriate */
+
+ if (current_midi_track && pad_port) {
+ cerr << "Disconnect pads from " << current_midi_track->name() << endl;
+ current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
+ }
+
+ /* now connect the pad port to this (newly) selected midi
+ * track, if indeed there is one.
+ */
+
+ if (new_pad_target && pad_port) {
+ cerr << "Reconnect pads to " << new_pad_target->name() << endl;
+ new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
+ current_pad_target = new_pad_target;
+ } else {
+ current_pad_target.reset ();
+ }
+}
+
+Push2::Button*
+Push2::button_by_id (ButtonID bid)
+{
+ return id_button_map[bid];
+}