2 Copyright (C) 2016 Paul Davis
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 of the License, or
7 (at your option) any later version.
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
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "pbd/compose.h"
22 #include "pbd/convert.h"
23 #include "pbd/debug.h"
24 #include "pbd/failed_constructor.h"
25 #include "pbd/file_utils.h"
26 #include "pbd/search_path.h"
27 #include "pbd/enumwriter.h"
29 #include "midi++/parser.h"
30 #include "timecode/time.h"
31 #include "timecode/bbt_time.h"
33 #include "ardour/async_midi_port.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/debug.h"
36 #include "ardour/midiport_manager.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/midi_port.h"
39 #include "ardour/session.h"
40 #include "ardour/tempo.h"
42 #include "gtkmm2ext/gui_thread.h"
43 #include "gtkmm2ext/rgb_macros.h"
45 #include "canvas/colors.h"
55 #include "track_mix.h"
59 using namespace ARDOUR;
63 using namespace ArdourSurface;
65 #include "pbd/abstract_ui.cc" // instantiate template
67 #define ABLETON 0x2982
70 __attribute__((constructor)) static void
73 EnumWriter& enum_writer (EnumWriter::instance());
77 MusicalMode::Type mode;
79 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
80 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
82 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
83 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
84 REGISTER_CLASS_ENUM (MusicalMode, Minor);
85 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
86 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
87 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
88 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
89 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
90 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
91 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
92 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
93 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
94 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
95 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
96 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
97 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
98 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
99 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
100 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
101 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
102 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
103 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
104 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
105 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
106 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
107 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
108 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
109 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
110 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
111 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
112 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
113 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
114 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
115 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
116 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
117 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
118 REGISTER_CLASS_ENUM (MusicalMode, Persian);
119 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
123 Push2::Push2 (ARDOUR::Session& s)
124 : ControlProtocol (s, string (X_("Ableton Push 2")))
125 , AbstractUI<Push2Request> (name())
127 , _modifier_state (None)
129 , _current_layout (0)
130 , _previous_layout (0)
131 , connection_state (ConnectionState (0))
133 , _mode (MusicalMode::IonianMajor)
139 , _pressure_mode (AfterTouch)
146 /* master cannot be removed, so no need to connect to going-away signal */
147 master = session->master_out ();
150 throw failed_constructor ();
153 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
155 /* catch current selection, if any */
157 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
158 stripable_selection_change (sp);
161 /* catch arrival and departure of Push2 itself */
162 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
164 /* Catch port connections and disconnections */
165 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
167 /* ports might already be there */
168 port_registration_handler ();
175 delete track_mix_layout;
181 Push2::port_registration_handler ()
183 if (!_async_in && !_async_out) {
184 /* ports not registered yet */
188 if (_async_in->connected() && _async_out->connected()) {
189 /* don't waste cycles here */
193 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
194 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
198 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
199 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
201 if (!in.empty() && !out.empty()) {
202 cerr << "Push2: both ports found\n";
203 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
204 if (!_async_in->connected()) {
205 AudioEngine::instance()->connect (_async_in->name(), in.front());
207 if (!_async_out->connected()) {
208 AudioEngine::instance()->connect (_async_out->name(), out.front());
223 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
227 if ((err = libusb_claim_interface (handle, 0x00))) {
232 _canvas = new Push2Canvas (*this, 960, 160);
233 mix_layout = new MixLayout (*this, *session);
234 scale_layout = new ScaleLayout (*this, *session);
235 track_mix_layout = new TrackMixLayout (*this, *session);
236 splash_layout = new SplashLayout (*this, *session);
238 error << _("Cannot construct Canvas for display") << endmsg;
239 libusb_release_interface (handle, 0x00);
240 libusb_close (handle);
246 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
247 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
249 if (_async_in == 0 || _async_out == 0) {
253 /* We do not add our ports to the input/output bundles because we don't
254 * want users wiring them by hand. They could use JACK tools if they
255 * really insist on that.
258 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
259 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
261 /* Create a shadow port where, depending on the state of the surface,
262 * we will make pad note on/off events appear. The surface code will
263 * automatically this port to the first selected MIDI track.
266 boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), X_("Push 2")), boost::bind (&Push2::pad_filter, this, _1, _2));
267 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
271 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
273 _output_bundle->add_channel (
275 ARDOUR::DataType::MIDI,
276 session->engine().make_port_name_non_relative (shadow_port->name())
280 session->BundleAddedOrRemoved ();
282 connect_to_parser ();
287 list<boost::shared_ptr<ARDOUR::Bundle> >
290 list<boost::shared_ptr<ARDOUR::Bundle> > b;
292 if (_output_bundle) {
293 b.push_back (_output_bundle);
302 init_buttons (false);
304 /* wait for button data to be flushed */
306 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
307 asp->drain (10000, 500000);
309 AudioEngine::instance()->unregister_port (_async_in);
310 AudioEngine::instance()->unregister_port (_async_out);
312 _async_in.reset ((ARDOUR::Port*) 0);
313 _async_out.reset ((ARDOUR::Port*) 0);
317 periodic_connection.disconnect ();
318 session_connections.drop_connections ();
320 if (_current_layout) {
321 _canvas->root()->remove (_current_layout);
329 delete splash_layout;
333 libusb_release_interface (handle, 0x00);
334 libusb_close (handle);
342 Push2::strip_buttons_off ()
344 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
345 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
347 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
348 Button* b = id_button_map[strip_buttons[n]];
350 b->set_color (LED::Black);
351 b->set_state (LED::OneShot24th);
352 write (b->state_msg());
358 Push2::init_buttons (bool startup)
360 /* This is a list of buttons that we want lit because they do something
361 in ardour related (loosely, sometimes) to their illuminated label.
364 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
365 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
366 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
369 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
370 Button* b = id_button_map[buttons[n]];
373 b->set_color (LED::White);
375 b->set_color (LED::Black);
377 b->set_state (LED::OneShot24th);
378 write (b->state_msg());
381 /* Strip buttons should all be off (black) by default. They will change
382 * color to reflect various conditions
385 strip_buttons_off ();
389 /* all other buttons are off (black) */
391 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
392 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
393 Accent, Note, Session, };
395 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
396 Button* b = id_button_map[off_buttons[n]];
398 b->set_color (LED::Black);
399 b->set_state (LED::OneShot24th);
400 write (b->state_msg());
405 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
406 Pad* pad = pi->second;
408 pad->set_color (LED::Black);
409 pad->set_state (LED::OneShot24th);
410 write (pad->state_msg());
418 libusb_device_handle *h;
421 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
422 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
427 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
432 Push2::request_factory (uint32_t num_requests)
434 /* AbstractUI<T>::request_buffer_factory() is a template method only
435 instantiated in this source module. To provide something visible for
436 use in the interface/descriptor, we have this static method that is
439 return request_buffer_factory (num_requests);
443 Push2::do_request (Push2Request * req)
445 if (req->type == CallSlot) {
447 call_slot (MISSING_INVALIDATOR, req->the_slot);
449 } else if (req->type == Quit) {
467 set_current_layout (splash_layout);
468 splash_start = get_microseconds ();
476 /* display splash for 2 seconds */
478 if (get_microseconds() - splash_start > 2000000) {
480 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
481 set_current_layout (mix_layout);
491 Push2::set_active (bool yn)
493 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
495 if (yn == active()) {
501 /* start event loop */
506 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
511 /* Connect input port to event loop */
515 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
516 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
517 asp->xthread().attach (main_loop()->get_context());
519 connect_session_signals ();
521 /* set up periodic task used to push a frame buffer to the
522 * device (25fps). The device can handle 60fps, but we don't
523 * need that frame rate.
526 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
527 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
528 vblank_timeout->attach (main_loop()->get_context());
531 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
532 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
533 periodic_timeout->attach (main_loop()->get_context());
537 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
546 ControlProtocol::set_active (yn);
548 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
554 Push2::init_touch_strip ()
556 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
557 /* flags are the final byte (ignore end-of-sysex */
559 /* show bar, not point
563 msg[7] = (1<<4) | (1<<5) | (1<<6);
568 Push2::write (const MidiByteArray& data)
570 /* immediate delivery */
571 _output_port->write (&data[0], data.size(), 0);
575 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
578 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
584 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
586 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
591 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
592 framepos_t now = AudioEngine::instance()->sample_time();
606 Push2::connect_to_parser ()
608 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
610 MIDI::Parser* p = _input_port->parser();
613 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
614 /* V-Pot messages are Controller */
615 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
616 /* Button messages are NoteOn */
617 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
618 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
619 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
620 /* Fader messages are Pitchbend */
621 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
625 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
627 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
628 MidiByteArray msg (sz, raw_bytes);
629 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
630 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
632 if (msg == aftertouch_mode_response) {
633 _pressure_mode = AfterTouch;
634 PressureModeChange (AfterTouch);
635 cerr << "Pressure mod eis after\n";
636 } else if (msg == polypress_mode_response) {
637 _pressure_mode = PolyPressure;
638 PressureModeChange (PolyPressure);
639 cerr << "Pressure mod eis poly\n";
644 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
646 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
648 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
651 /* any press cancels any pending long press timeouts */
652 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
653 Button* bb = id_button_map[*x];
654 bb->timeout_connection.disconnect ();
658 if (b != cc_button_map.end()) {
660 Button* button = b->second;
663 buttons_down.insert (button->id);
664 start_press_timeout (*button, button->id);
666 buttons_down.erase (button->id);
667 button->timeout_connection.disconnect ();
671 set<ButtonID>::iterator c = consumed.find (button->id);
673 if (c == consumed.end()) {
674 if (ev->value == 0) {
675 (this->*button->release_method)();
677 (this->*button->press_method)();
680 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
688 int delta = ev->value;
691 delta = -(128 - delta);
694 switch (ev->controller_number) {
696 _current_layout->strip_vpot (0, delta);
699 _current_layout->strip_vpot (1, delta);
702 _current_layout->strip_vpot (2, delta);
705 _current_layout->strip_vpot (3, delta);
708 _current_layout->strip_vpot (4, delta);
711 _current_layout->strip_vpot (5, delta);
714 _current_layout->strip_vpot (6, delta);
717 _current_layout->strip_vpot (7, delta);
722 other_vpot (8, delta);
725 other_vpot (1, delta);
730 other_vpot (2, delta);
737 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
739 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
741 if (ev->velocity == 0) {
742 handle_midi_note_off_message (parser, ev);
746 switch (ev->note_number) {
748 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
751 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
754 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
757 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
760 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
763 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
766 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
769 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
774 other_vpot_touch (0, ev->velocity > 64);
777 other_vpot_touch (1, ev->velocity > 64);
782 other_vpot_touch (3, ev->velocity > 64);
787 if (ev->velocity < 64) {
793 if (ev->note_number < 11) {
799 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
801 if (pi == nn_pad_map.end()) {
805 Pad* pad = pi->second;
807 if (pad->do_when_pressed == Pad::FlashOn) {
808 pad->set_color (LED::White);
809 pad->set_state (LED::OneShot24th);
810 write (pad->state_msg());
811 } else if (pad->do_when_pressed == Pad::FlashOff) {
812 pad->set_color (LED::Black);
813 pad->set_state (LED::OneShot24th);
814 write (pad->state_msg());
819 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
821 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
823 if (ev->note_number < 11) {
824 /* theoretically related to encoder touch start/end, but
825 * actually they send note on with two different velocity
831 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
833 if (pi == nn_pad_map.end()) {
837 Pad* pad = pi->second;
839 if (pad->do_when_pressed == Pad::FlashOn) {
840 pad->set_color (LED::Black);
841 pad->set_state (LED::OneShot24th);
842 write (pad->state_msg());
843 } else if (pad->do_when_pressed == Pad::FlashOff) {
844 pad->set_color (pad->perma_color);
845 pad->set_state (LED::OneShot24th);
846 write (pad->state_msg());
851 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
856 Push2::thread_init ()
858 struct sched_param rtparam;
860 pthread_set_name (event_loop_name().c_str());
862 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
863 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
865 memset (&rtparam, 0, sizeof (rtparam));
866 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
868 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
869 // do we care? not particularly.
874 Push2::connect_session_signals()
876 // receive routes added
877 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
878 // receive VCAs added
879 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
881 // receive record state toggled
882 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
883 // receive transport state changed
884 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
885 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
886 // receive punch-in and punch-out
887 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
888 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
889 // receive rude solo changed
890 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
894 Push2::notify_record_state_changed ()
896 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
898 if (b == id_button_map.end()) {
902 switch (session->record_status ()) {
903 case Session::Disabled:
904 b->second->set_color (LED::White);
905 b->second->set_state (LED::NoTransition);
907 case Session::Enabled:
908 b->second->set_color (LED::Red);
909 b->second->set_state (LED::Blinking4th);
911 case Session::Recording:
912 b->second->set_color (LED::Red);
913 b->second->set_state (LED::OneShot24th);
917 write (b->second->state_msg());
921 Push2::notify_transport_state_changed ()
923 Button* b = id_button_map[Play];
925 if (session->transport_rolling()) {
926 b->set_state (LED::OneShot24th);
927 b->set_color (LED::Green);
930 /* disable any blink on FixedLength from pending edit range op */
931 Button* fl = id_button_map[FixedLength];
933 fl->set_color (LED::Black);
934 fl->set_state (LED::NoTransition);
935 write (fl->state_msg());
937 b->set_color (LED::White);
938 b->set_state (LED::NoTransition);
941 write (b->state_msg());
945 Push2::notify_loop_state_changed ()
950 Push2::notify_parameter_changed (std::string param)
952 IDButtonMap::iterator b;
954 if (param == "clicking") {
955 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
958 if (Config->get_clicking()) {
959 b->second->set_state (LED::Blinking4th);
960 b->second->set_color (LED::White);
962 b->second->set_color (LED::White);
963 b->second->set_state (LED::NoTransition);
965 write (b->second->state_msg ());
970 Push2::notify_solo_active_changed (bool yn)
972 IDButtonMap::iterator b = id_button_map.find (Solo);
974 if (b == id_button_map.end()) {
979 b->second->set_state (LED::Blinking4th);
980 b->second->set_color (LED::Red);
982 b->second->set_state (LED::NoTransition);
983 b->second->set_color (LED::White);
986 write (b->second->state_msg());
992 XMLNode& node (ControlProtocol::get_state());
995 child = new XMLNode (X_("Input"));
996 child->add_child_nocopy (_async_in->get_state());
997 node.add_child_nocopy (*child);
998 child = new XMLNode (X_("Output"));
999 child->add_child_nocopy (_async_out->get_state());
1000 node.add_child_nocopy (*child);
1002 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1003 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1004 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1005 node.add_property (X_("mode"), enum_2_string (_mode));
1011 Push2::set_state (const XMLNode & node, int version)
1013 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1017 if (ControlProtocol::set_state (node, version)) {
1023 if ((child = node.child (X_("Input"))) != 0) {
1024 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1026 _async_in->set_state (*portnode, version);
1030 if ((child = node.child (X_("Output"))) != 0) {
1031 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1033 _async_out->set_state (*portnode, version);
1037 XMLProperty const* prop;
1039 if ((prop = node.property (X_("root"))) != 0) {
1040 _scale_root = atoi (prop->value());
1043 if ((prop = node.property (X_("root_octave"))) != 0) {
1044 _root_octave = atoi (prop->value());
1047 if ((prop = node.property (X_("in_key"))) != 0) {
1048 _in_key = string_is_affirmative (prop->value());
1051 if ((prop = node.property (X_("mode"))) != 0) {
1052 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1059 Push2::other_vpot (int n, int delta)
1067 /* master gain control */
1069 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1071 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1079 Push2::other_vpot_touch (int n, bool touching)
1088 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1091 ac->start_touch (session->audible_frame());
1093 ac->stop_touch (true, session->audible_frame());
1101 Push2::start_shift ()
1103 cerr << "start shift\n";
1104 _modifier_state = ModifierState (_modifier_state | ModShift);
1105 Button* b = id_button_map[Shift];
1106 b->set_color (LED::White);
1107 b->set_state (LED::Blinking16th);
1108 write (b->state_msg());
1114 if (_modifier_state & ModShift) {
1115 cerr << "end shift\n";
1116 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1117 Button* b = id_button_map[Shift];
1118 b->timeout_connection.disconnect ();
1119 b->set_color (LED::White);
1120 b->set_state (LED::OneShot24th);
1121 write (b->state_msg());
1126 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1128 /* This filter is called asynchronously from a realtime process
1129 context. It must use atomics to check state, and must not block.
1132 bool matched = false;
1134 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1135 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1137 /* encoder touch start/touch end use note
1138 * 0-10. touchstrip uses note 12
1141 if ((*ev).note() > 10 && (*ev).note() != 12) {
1143 const int n = (*ev).note ();
1144 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1146 if (nni != nn_pad_map.end()) {
1147 Pad const * pad = nni->second;
1148 /* shift for output to the shadow port */
1149 if (pad->filtered >= 0) {
1150 (*ev).set_note (pad->filtered + (octave_shift*12));
1151 out.push_back (*ev);
1152 /* shift back so that the pads light correctly */
1155 /* no mapping, don't send event */
1158 out.push_back (*ev);
1163 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1164 out.push_back (*ev);
1172 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1174 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1175 if (!_input_port || !_output_port) {
1179 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1180 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1182 if (ni == name1 || ni == name2) {
1184 connection_state |= InputConnected;
1186 connection_state &= ~InputConnected;
1188 } else if (no == name1 || no == name2) {
1190 connection_state |= OutputConnected;
1192 connection_state &= ~OutputConnected;
1195 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1200 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1202 /* XXX this is a horrible hack. Without a short sleep here,
1203 something prevents the device wakeup messages from being
1204 sent and/or the responses from being received.
1208 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1212 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1215 ConnectionChange (); /* emit signal for our GUI */
1217 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1219 return true; /* connection status changed */
1225 request_pressure_mode ();
1228 boost::shared_ptr<Port>
1229 Push2::output_port()
1234 boost::shared_ptr<Port>
1241 Push2::pad_note (int row, int col) const
1243 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1245 if (nni != nn_pad_map.end()) {
1246 return nni->second->filtered;
1253 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1255 MusicalMode m (mode);
1256 vector<float>::iterator interval;
1258 const int original_root = root;
1260 interval = m.steps.begin();
1261 root += (octave*12);
1264 const int root_start = root;
1266 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1267 vector<int> mode_vector; /* sorted in note order */
1269 mode_map.insert (note);
1270 mode_vector.push_back (note);
1272 /* build a map of all notes in the mode, from the root to 127 */
1274 while (note < 128) {
1276 if (interval == m.steps.end()) {
1278 /* last distance was the end of the scale,
1279 so wrap, adding the next note at one
1280 octave above the last root.
1283 interval = m.steps.begin();
1285 mode_map.insert (root);
1286 mode_vector.push_back (root);
1289 note = (int) floor (root + (2.0 * (*interval)));
1291 mode_map.insert (note);
1292 mode_vector.push_back (note);
1298 vector<int>::iterator notei;
1300 for (int row = 0; row < 8; ++row) {
1302 /* Ableton's grid layout wraps the available notes in the scale
1303 * by offsetting 3 notes per row (from the bottom)
1306 notei = mode_vector.begin();
1307 notei += row_offset;
1310 for (int col = 0; col < 8; ++col) {
1311 int index = 36 + (row*8) + col;
1312 Pad* pad = nn_pad_map[index];
1314 if (notei != mode_vector.end()) {
1317 pad->filtered = notenum;
1319 if ((notenum % 12) == original_root) {
1320 pad->set_color (LED::Green);
1321 pad->perma_color = LED::Green;
1323 pad->set_color (LED::White);
1324 pad->perma_color = LED::White;
1327 pad->do_when_pressed = Pad::FlashOff;
1332 pad->set_color (LED::Black);
1333 pad->do_when_pressed = Pad::Nothing;
1337 write (pad->state_msg());
1343 /* chromatic: all notes available, but highlight those in the scale */
1345 for (note = 36; note < 100; ++note) {
1347 Pad* pad = nn_pad_map[note];
1349 /* Chromatic: all pads play, half-tone steps. Light
1350 * those in the scale, and highlight root notes
1353 pad->filtered = root_start + (note - 36);
1355 if (mode_map.find (note) != mode_map.end()) {
1357 if ((note % 12) == original_root) {
1358 pad->set_color (LED::Green);
1359 pad->perma_color = LED::Green;
1361 pad->set_color (LED::White);
1362 pad->perma_color = LED::White;
1365 pad->do_when_pressed = Pad::FlashOff;
1369 /* note is not in mode, turn it off */
1371 pad->do_when_pressed = Pad::FlashOn;
1372 pad->set_color (LED::Black);
1376 write (pad->state_msg());
1382 _scale_root = original_root;
1383 _root_octave = octave;
1387 ScaleChange (); /* EMIT SIGNAL */
1391 Push2::set_percussive_mode (bool yn)
1394 cerr << "back to scale\n";
1395 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1402 for (int row = 0; row < 8; ++row) {
1404 for (int col = 0; col < 4; ++col) {
1406 int index = 36 + (row*8) + col;
1407 Pad* pad = nn_pad_map[index];
1409 pad->filtered = drum_note;
1414 for (int row = 0; row < 8; ++row) {
1416 for (int col = 4; col < 8; ++col) {
1418 int index = 36 + (row*8) + col;
1419 Pad* pad = nn_pad_map[index];
1421 pad->filtered = drum_note;
1430 Push2::current_layout () const
1432 Glib::Threads::Mutex::Lock lm (layout_lock);
1433 return _current_layout;
1437 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1439 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1440 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1441 boost::shared_ptr<MidiTrack> new_pad_target;
1443 /* See if there's a MIDI track selected */
1445 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1447 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1449 if (new_pad_target) {
1454 if (current_midi_track == new_pad_target) {
1459 if (!new_pad_target) {
1460 /* leave existing connection alone */
1464 /* disconnect from pad port, if appropriate */
1466 if (current_midi_track && pad_port) {
1467 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1470 /* now connect the pad port to this (newly) selected midi
1471 * track, if indeed there is one.
1474 if (new_pad_target && pad_port) {
1475 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1476 current_pad_target = new_pad_target;
1478 current_pad_target.reset ();
1483 Push2::button_by_id (ButtonID bid)
1485 return id_button_map[bid];
1489 Push2::get_color_index (uint32_t rgb)
1491 ColorMap::iterator i = color_map.find (rgb);
1493 if (i != color_map.end()) {
1498 UINT_TO_RGBA (rgb, &r, &g, &b, &a);
1499 int w = 204; /* not sure where/when we should get this value */
1501 /* get a free index */
1505 if (color_map_free_list.empty()) {
1506 /* random replacement of any entry above zero and below 122 (where the
1507 * Ableton standard colors live)
1509 index = 1 + (random() % 121);
1511 index = color_map_free_list.top();
1512 color_map_free_list.pop();
1515 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1516 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1518 palette_msg[7] = index;
1519 palette_msg[8] = r & 0x7f;
1520 palette_msg[9] = r & 0x1;
1521 palette_msg[10] = g & 0x7f;
1522 palette_msg[11] = g & 0x1;
1523 palette_msg[12] = b & 0x7f;
1524 palette_msg[13] = b & 0x1;
1525 palette_msg[14] = w & 0x7f;
1526 palette_msg[15] = w & 0x1;
1528 write (palette_msg);
1529 write (update_pallette_msg);
1531 color_map[index] = rgb;
1537 Push2::build_color_map ()
1539 /* These are "standard" colors that Ableton docs suggest will always be
1540 there. Put them in our color map so that when we look up these
1541 colors, we will use the Ableton indices for them.
1544 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1545 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1546 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1547 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1548 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1549 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1550 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1552 for (uint8_t n = 1; n < 122; ++n) {
1553 color_map_free_list.push (n);
1558 Push2::fill_color_table ()
1560 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1561 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1563 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1565 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1566 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1567 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1569 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1570 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1572 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1573 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1574 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1575 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1580 Push2::get_color (ColorName name)
1582 Colors::iterator c = colors.find (name);
1583 if (c != colors.end()) {
1591 Push2::set_current_layout (Push2Layout* layout)
1593 if (_current_layout) {
1594 _current_layout->hide ();
1595 _canvas->root()->remove (_current_layout);
1596 _previous_layout = _current_layout;
1599 /* turn off all strip buttons - let new layout set them if it
1603 strip_buttons_off ();
1605 _current_layout = layout;
1607 if (_current_layout) {
1608 _canvas->root()->add (_current_layout);
1609 _current_layout->show ();
1612 _canvas->request_redraw ();
1616 Push2::use_previous_layout ()
1618 if (_previous_layout) {
1619 set_current_layout (_previous_layout);
1624 Push2::request_pressure_mode ()
1626 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1631 Push2::set_pressure_mode (PressureMode pm)
1633 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1637 /* nothing to do, message is correct */
1647 cerr << "Sent PM message " << msg << endl;