X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fsurfaces%2Fpush2%2Fpush2.cc;h=57160abee3269d04bc5ae74f6b9c2d7a6c3c9888;hb=6c4627b1b71efa9f8a04fdb1634f996865ffc640;hp=6887bb73a5881e2f9675014092058759d18702da;hpb=b83548617077eecacd4e65c3c10ee154494f9823;p=ardour.git diff --git a/libs/surfaces/push2/push2.cc b/libs/surfaces/push2/push2.cc index 6887bb73a5..57160abee3 100644 --- a/libs/surfaces/push2/push2.cc +++ b/libs/surfaces/push2/push2.cc @@ -30,6 +30,7 @@ #include "timecode/time.h" #include "timecode/bbt_time.h" +#include "ardour/amp.h" #include "ardour/async_midi_port.h" #include "ardour/audioengine.h" #include "ardour/debug.h" @@ -56,6 +57,10 @@ #include "pbd/i18n.h" +#ifdef PLATFORM_WINDOWS +#define random() rand() +#endif + using namespace ARDOUR; using namespace std; using namespace PBD; @@ -74,50 +79,10 @@ register_enums () 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) @@ -127,6 +92,7 @@ Push2::Push2 (ARDOUR::Session& s) , _modifier_state (None) , splash_start (0) , _current_layout (0) + , _previous_layout (0) , connection_state (ConnectionState (0)) , gui (0) , _mode (MusicalMode::IonianMajor) @@ -136,6 +102,8 @@ Push2::Push2 (ARDOUR::Session& s) , octave_shift (0) , percussion (false) , _pressure_mode (AfterTouch) + , selection_color (LED::Green) + , contrast_color (LED::Green) { build_maps (); @@ -229,14 +197,15 @@ Push2::open () try { _canvas = new Push2Canvas (*this, 960, 160); - mix_layout = new MixLayout (*this, *session); - scale_layout = new ScaleLayout (*this, *session); - track_mix_layout = new TrackMixLayout (*this, *session); - splash_layout = new SplashLayout (*this, *session); + mix_layout = new MixLayout (*this, *session, "globalmix"); + scale_layout = new ScaleLayout (*this, *session, "scale"); + track_mix_layout = new TrackMixLayout (*this, *session, "trackmix"); + splash_layout = new SplashLayout (*this, *session, "splash"); } catch (...) { error << _("Cannot construct Canvas for display") << endmsg; libusb_release_interface (handle, 0x00); libusb_close (handle); + handle = 0; return -1; } @@ -299,6 +268,7 @@ int Push2::close () { init_buttons (false); + strip_buttons_off (); /* wait for button data to be flushed */ AsyncMIDIPort* asp; @@ -337,6 +307,22 @@ Push2::close () return 0; } +void +Push2::strip_buttons_off () +{ + ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8, + Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, }; + + for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) { + Button* b = id_button_map[strip_buttons[n]]; + + b->set_color (LED::Black); + b->set_state (LED::OneShot24th); + write (b->state_msg()); + } +} + + void Push2::init_buttons (bool startup) { @@ -345,7 +331,7 @@ Push2::init_buttons (bool startup) */ ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo, - Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop, + Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale }; @@ -361,21 +347,6 @@ Push2::init_buttons (bool startup) write (b->state_msg()); } - /* Strip buttons should all be off (black) by default. They will change - * color to reflect various conditions - */ - - ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8, - Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, }; - - for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) { - Button* b = id_button_map[strip_buttons[n]]; - - b->set_color (LED::Black); - b->set_state (LED::OneShot24th); - write (b->state_msg()); - } - if (startup) { /* all other buttons are off (black) */ @@ -434,7 +405,6 @@ Push2::request_factory (uint32_t num_requests) void Push2::do_request (Push2Request * req) { - DEBUG_TRACE (DEBUG::Push2, string_compose ("doing request type %1\n", req->type)); if (req->type == CallSlot) { call_slot (MISSING_INVALIDATOR, req->the_slot); @@ -466,15 +436,20 @@ Push2::vblank () { if (splash_start) { - /* display splash for 3 seconds */ + /* display splash for 2 seconds */ - if (get_microseconds() - splash_start > 3000000) { + if (get_microseconds() - splash_start > 2000000) { splash_start = 0; DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n"); set_current_layout (mix_layout); } } + if (_current_layout) { + _current_layout->update_meters (); + _current_layout->update_clocks (); + } + _canvas->vblank(); return true; @@ -618,18 +593,30 @@ void Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz) { DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz)); + + if (sz < 8) { + return; + } + MidiByteArray msg (sz, raw_bytes); - MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7); - MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7); - - if (msg == aftertouch_mode_response) { - _pressure_mode = AfterTouch; - PressureModeChange (AfterTouch); - cerr << "Pressure mod eis after\n"; - } else if (msg == polypress_mode_response) { - _pressure_mode = PolyPressure; - PressureModeChange (PolyPressure); - cerr << "Pressure mod eis poly\n"; + MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01); + + if (!push2_sysex_header.compare_n (msg, 6)) { + return; + } + + switch (msg[6]) { + case 0x1f: /* pressure mode */ + if (msg[7] == 0x0) { + _pressure_mode = AfterTouch; + PressureModeChange (AfterTouch); + cerr << "Pressure mode is after\n"; + } else { + _pressure_mode = PolyPressure; + PressureModeChange (PolyPressure); + cerr << "Pressure mode is poly\n"; + } + break; } } @@ -729,7 +716,7 @@ Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev) void Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev) { - DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity)); + // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity)); if (ev->velocity == 0) { handle_midi_note_off_message (parser, ev); @@ -787,22 +774,26 @@ Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* e return; } - /* Pad */ + /* Pad illuminations */ - NNPadMap::iterator pi = nn_pad_map.find (ev->note_number); + NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number); - if (pi == nn_pad_map.end()) { + if (pm == nn_pad_map.end()) { return; } - Pad* pad = pi->second; + const Pad * const pad_pressed = pm->second; - 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); + pair pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered); + + if (pads_with_note.first == fn_pad_map.end()) { + return; + } + + for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) { + Pad* pad = pi->second; + + pad->set_color (contrast_color); pad->set_state (LED::OneShot24th); write (pad->state_msg()); } @@ -811,7 +802,7 @@ Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* e void Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev) { - DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity)); + // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity)); if (ev->note_number < 11) { /* theoretically related to encoder touch start/end, but @@ -821,22 +812,34 @@ Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev) return; } - NNPadMap::iterator pi = nn_pad_map.find (ev->note_number); + /* Pad illuminations */ + + NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number); - if (pi == nn_pad_map.end()) { + if (pm == nn_pad_map.end()) { return; } - Pad* pad = pi->second; + const Pad * const pad_pressed = pm->second; - 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()); + pair pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered); + + if (pads_with_note.first == fn_pad_map.end()) { + return; + } + + for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) { + Pad* pad = pi->second; + + 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()); + } } } @@ -1051,17 +1054,31 @@ Push2::set_state (const XMLNode & node, int version) void Push2::other_vpot (int n, int delta) { + boost::shared_ptr click_gain; switch (n) { case 0: + /* tempo control */ break; case 1: + /* metronome gain control */ + click_gain = session->click_gain(); + if (click_gain) { + boost::shared_ptr ac = click_gain->gain_control(); + if (ac) { + ac->set_value (ac->interface_to_internal ( + min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))), + PBD::Controllable::UseGroup); + } + } break; case 2: /* master gain control */ if (master) { boost::shared_ptr ac = master->gain_control(); if (ac) { - ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup); + ac->set_value (ac->interface_to_internal ( + min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))), + PBD::Controllable::UseGroup); } } break; @@ -1242,6 +1259,27 @@ Push2::pad_note (int row, int col) const return 0; } +void +Push2::update_selection_color () +{ + boost::shared_ptr current_midi_track = current_pad_target.lock(); + + if (!current_midi_track) { + return; + } + + selection_color = get_color_index (current_midi_track->presentation_info().color()); + contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color()); + + reset_pad_colors (); +} + +void +Push2::reset_pad_colors () +{ + set_pad_scale (_scale_root, _root_octave, _mode, _in_key); +} + void Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) { @@ -1286,10 +1324,13 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) } } + fn_pad_map.clear (); + if (inkey) { 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 @@ -1309,9 +1350,11 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) notenum = *notei; pad->filtered = notenum; + fn_pad_map.insert (make_pair (notenum, pad)); + if ((notenum % 12) == original_root) { - pad->set_color (LED::Green); - pad->perma_color = LED::Green; + pad->set_color (selection_color); + pad->perma_color = selection_color; } else { pad->set_color (LED::White); pad->perma_color = LED::White; @@ -1327,6 +1370,7 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) pad->filtered = -1; } + pad->set_state (LED::OneShot24th); write (pad->state_msg()); } } @@ -1345,11 +1389,13 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) pad->filtered = root_start + (note - 36); + fn_pad_map.insert (make_pair (pad->filtered, pad)); + if (mode_map.find (note) != mode_map.end()) { if ((note % 12) == original_root) { - pad->set_color (LED::Green); - pad->perma_color = LED::Green; + pad->set_color (selection_color); + pad->perma_color = selection_color; } else { pad->set_color (LED::White); pad->perma_color = LED::White; @@ -1366,18 +1412,35 @@ Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey) } + pad->set_state (LED::OneShot24th); write (pad->state_msg()); } } - PadChange (); /* EMIT SIGNAL */ - /* store state */ - _scale_root = original_root; - _root_octave = octave; - _in_key = inkey; - _mode = mode; + bool changed = false; + + if (_scale_root != original_root) { + _scale_root = original_root; + changed = true; + } + if (_root_octave != octave) { + _root_octave = octave; + changed = true; + } + if (_in_key != inkey) { + _in_key = inkey; + changed = true; + } + if (_mode != mode) { + _mode = mode; + changed = true; + } + + if (changed) { + ScaleChange (); /* EMIT SIGNAL */ + } } void @@ -1392,6 +1455,8 @@ Push2::set_percussive_mode (bool yn) int drum_note = 36; + fn_pad_map.clear (); + for (int row = 0; row < 8; ++row) { for (int col = 0; col < 4; ++col) { @@ -1417,8 +1482,6 @@ Push2::set_percussive_mode (bool yn) } percussion = true; - - PadChange (); /* EMIT SIGNAL */ } Push2Layout* @@ -1446,12 +1509,6 @@ Push2::stripable_selection_change (StripableNotificationListPtr selected) } } - 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; @@ -1465,7 +1522,14 @@ Push2::stripable_selection_change (StripableNotificationListPtr selected) /* disconnect from pad port, if appropriate */ if (current_midi_track && pad_port) { - cerr << "Disconnect pads from " << current_midi_track->name() << endl; + + /* XXX this could possibly leave dangling MIDI notes. + * + * A general libardour fix is required. It isn't obvious + * how note resolution can be done unless disconnecting + * becomes "slow" (i.e. deferred for as long as it takes + * to resolve notes). + */ current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this); } @@ -1474,12 +1538,17 @@ Push2::stripable_selection_change (StripableNotificationListPtr selected) */ 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; + selection_color = get_color_index (new_pad_target->presentation_info().color()); + contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color()); } else { current_pad_target.reset (); + selection_color = LED::Green; + contrast_color = LED::Green; } + + reset_pad_colors (); } Push2::Button* @@ -1489,17 +1558,23 @@ Push2::button_by_id (ButtonID bid) } uint8_t -Push2::get_color_index (uint32_t rgb) +Push2::get_color_index (ArdourCanvas::Color rgba) { - ColorMap::iterator i = color_map.find (rgb); + ColorMap::iterator i = color_map.find (rgba); if (i != color_map.end()) { return i->second; } - int r, g, b, a; - UINT_TO_RGBA (rgb, &r, &g, &b, &a); - int w = 204; /* not sure where/when we should get this value */ + double dr, dg, db, da; + int r, g, b; + ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da); + int w = 126; /* not sure where/when we should get this value */ + + + r = (int) floor (255.0 * dr); + g = (int) floor (255.0 * dg); + b = (int) floor (255.0 * db); /* get a free index */ @@ -1515,23 +1590,31 @@ Push2::get_color_index (uint32_t rgb) color_map_free_list.pop(); } - MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7); - MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7); - + MidiByteArray palette_msg (17, + 0xf0, + 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */ + 0x00, /* index = 7 */ + 0x00, 0x00, /* r = 8 & 9 */ + 0x00, 0x00, /* g = 10 & 11 */ + 0x00, 0x00, /* b = 12 & 13 */ + 0x00, 0x00, /* w (a?) = 14 & 15*/ + 0xf7); palette_msg[7] = index; palette_msg[8] = r & 0x7f; - palette_msg[9] = r & 0x1; + palette_msg[9] = (r & 0x80) >> 7; palette_msg[10] = g & 0x7f; - palette_msg[11] = g & 0x1; + palette_msg[11] = (g & 0x80) >> 7; palette_msg[12] = b & 0x7f; - palette_msg[13] = b & 0x1; + palette_msg[13] = (b & 0x80) >> 7; palette_msg[14] = w & 0x7f; - palette_msg[15] = w & 0x1; + palette_msg[15] = w & 0x80; write (palette_msg); + + MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7); write (update_pallette_msg); - color_map[index] = rgb; + color_map[rgba] = index; return index; } @@ -1593,19 +1676,34 @@ Push2::get_color (ColorName name) void Push2::set_current_layout (Push2Layout* layout) { - if (_current_layout) { - _current_layout->hide (); - _canvas->root()->remove (_current_layout); - } + if (layout && layout == _current_layout) { + _current_layout->show (); + } else { - _current_layout = layout; + if (_current_layout) { + _current_layout->hide (); + _canvas->root()->remove (_current_layout); + _previous_layout = _current_layout; + } - if (_current_layout) { - _current_layout->show (); - _canvas->root()->add (_current_layout); + _current_layout = layout; + + if (_current_layout) { + _canvas->root()->add (_current_layout); + _current_layout->show (); + } + + + _canvas->request_redraw (); } +} - _canvas->request_redraw (); +void +Push2::use_previous_layout () +{ + if (_previous_layout) { + set_current_layout (_previous_layout); + } } void