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/amp.h"
34 #include "ardour/async_midi_port.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/debug.h"
37 #include "ardour/midiport_manager.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/midi_port.h"
40 #include "ardour/session.h"
41 #include "ardour/tempo.h"
43 #include "gtkmm2ext/gui_thread.h"
44 #include "gtkmm2ext/rgb_macros.h"
46 #include "canvas/colors.h"
56 #include "track_mix.h"
60 #ifdef PLATFORM_WINDOWS
61 #define random() rand()
64 using namespace ARDOUR;
68 using namespace ArdourSurface;
70 #include "pbd/abstract_ui.cc" // instantiate template
72 #define ABLETON 0x2982
75 Push2::Push2 (ARDOUR::Session& s)
76 : ControlProtocol (s, string (X_("Ableton Push 2")))
77 , AbstractUI<Push2Request> (name())
80 , _modifier_state (None)
83 , _previous_layout (0)
84 , connection_state (ConnectionState (0))
86 , _mode (MusicalMode::IonianMajor)
92 , _pressure_mode (AfterTouch)
93 , selection_color (LED::Green)
94 , contrast_color (LED::Green)
95 , in_range_select (false)
97 /* we're going to need this */
105 /* master cannot be removed, so no need to connect to going-away signal */
106 master = session->master_out ();
108 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
110 /* allocate graphics layouts, even though we're not using them yet */
112 _canvas = new Push2Canvas (*this, 960, 160);
113 mix_layout = new MixLayout (*this, *session, "globalmix");
114 scale_layout = new ScaleLayout (*this, *session, "scale");
115 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
116 splash_layout = new SplashLayout (*this, *session, "splash");
120 /* Ports exist for the life of this instance */
124 /* catch arrival and departure of Push2 itself */
125 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
127 /* Catch port connections and disconnections */
128 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
130 /* Push 2 ports might already be there */
131 port_registration_handler ();
136 selection_connection.disconnect ();
138 stop_event_loop (); /* this will call stop_using_device () in Quit request handler */
142 if (_current_layout) {
143 _canvas->root()->remove (_current_layout);
151 delete splash_layout;
156 Push2::run_event_loop ()
158 DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
163 Push2::stop_event_loop ()
165 DEBUG_TRACE (DEBUG::Push2, "stop event loop\n");
170 Push2::begin_using_device ()
172 DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
174 /* set up periodic task used to push a frame buffer to the
175 * device (25fps). The device can handle 60fps, but we don't
176 * need that frame rate.
179 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
180 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
181 vblank_timeout->attach (main_loop()->get_context());
183 connect_session_signals ();
187 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
190 /* catch current selection, if any so that we can wire up the pads if appropriate */
192 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
193 stripable_selection_change (sp);
196 request_pressure_mode ();
204 Push2::stop_using_device ()
206 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
209 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
213 init_buttons (false);
214 strip_buttons_off ();
216 vblank_connection.disconnect ();
217 session_connections.drop_connections ();
224 Push2::ports_acquire ()
226 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
230 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
231 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
233 if (_async_in == 0 || _async_out == 0) {
234 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
238 /* We do not add our ports to the input/output bundles because we don't
239 * want users wiring them by hand. They could use JACK tools if they
240 * really insist on that (and use JACK)
243 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
244 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
246 /* Create a shadow port where, depending on the state of the surface,
247 * we will make pad note on/off events appear. The surface code will
248 * automatically this port to the first selected MIDI track.
251 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));
252 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
256 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
258 _output_bundle->add_channel (
260 ARDOUR::DataType::MIDI,
261 session->engine().make_port_name_non_relative (shadow_port->name())
265 session->BundleAddedOrRemoved ();
267 connect_to_parser ();
269 /* Connect input port to event loop */
273 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
274 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
275 asp->xthread().attach (main_loop()->get_context());
281 Push2::ports_release ()
283 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
285 /* wait for button data to be flushed */
287 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
288 asp->drain (10000, 500000);
290 AudioEngine::instance()->unregister_port (_async_in);
291 AudioEngine::instance()->unregister_port (_async_out);
293 _async_in.reset ((ARDOUR::Port*) 0);
294 _async_out.reset ((ARDOUR::Port*) 0);
300 Push2::device_acquire ()
304 DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
307 DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
312 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
313 DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
317 if ((err = libusb_claim_interface (handle, 0x00))) {
318 DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
319 libusb_close (handle);
328 Push2::device_release ()
330 DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
332 libusb_release_interface (handle, 0x00);
333 libusb_close (handle);
338 list<boost::shared_ptr<ARDOUR::Bundle> >
341 list<boost::shared_ptr<ARDOUR::Bundle> > b;
343 if (_output_bundle) {
344 b.push_back (_output_bundle);
351 Push2::strip_buttons_off ()
353 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
354 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
356 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
357 Button* b = id_button_map[strip_buttons[n]];
359 b->set_color (LED::Black);
360 b->set_state (LED::OneShot24th);
361 write (b->state_msg());
367 Push2::init_buttons (bool startup)
369 /* This is a list of buttons that we want lit because they do something
370 in ardour related (loosely, sometimes) to their illuminated label.
373 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
374 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
375 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
378 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
379 Button* b = id_button_map[buttons[n]];
382 b->set_color (LED::White);
384 b->set_color (LED::Black);
386 b->set_state (LED::OneShot24th);
387 write (b->state_msg());
392 /* all other buttons are off (black) */
394 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
395 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
396 Accent, Note, Session, };
398 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
399 Button* b = id_button_map[off_buttons[n]];
401 b->set_color (LED::Black);
402 b->set_state (LED::OneShot24th);
403 write (b->state_msg());
408 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
409 Pad* pad = pi->second;
411 pad->set_color (LED::Black);
412 pad->set_state (LED::OneShot24th);
413 write (pad->state_msg());
425 Push2::request_factory (uint32_t num_requests)
427 /* AbstractUI<T>::request_buffer_factory() is a template method only
428 instantiated in this source module. To provide something visible for
429 use in the interface/descriptor, we have this static method that is
432 return request_buffer_factory (num_requests);
436 Push2::do_request (Push2Request * req)
438 if (req->type == CallSlot) {
440 call_slot (MISSING_INVALIDATOR, req->the_slot);
442 } else if (req->type == Quit) {
444 stop_using_device ();
451 set_current_layout (splash_layout);
452 splash_start = get_microseconds ();
460 /* display splash for 2 seconds */
462 if (get_microseconds() - splash_start > 2000000) {
464 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
465 set_current_layout (mix_layout);
469 if (_current_layout) {
470 _current_layout->update_meters ();
471 _current_layout->update_clocks ();
480 Push2::set_active (bool yn)
482 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
484 if (yn == active()) {
490 if (device_acquire ()) {
494 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
495 begin_using_device ();
497 /* begin_using_device () will get called once we're connected */
501 /* Control Protocol Manager never calls us with false, but
502 * insteads destroys us.
506 ControlProtocol::set_active (yn);
508 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
514 Push2::init_touch_strip ()
516 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
517 /* flags are the final byte (ignore end-of-sysex */
519 /* show bar, not point
523 msg[7] = (1<<4) | (1<<5) | (1<<6);
528 Push2::write (const MidiByteArray& data)
530 /* immediate delivery */
531 _output_port->write (&data[0], data.size(), 0);
535 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
538 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
544 DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
546 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
551 DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
553 framepos_t now = AudioEngine::instance()->sample_time();
562 Push2::connect_to_parser ()
564 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
566 MIDI::Parser* p = _input_port->parser();
569 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
570 /* V-Pot messages are Controller */
571 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
572 /* Button messages are NoteOn */
573 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
574 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
575 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
576 /* Fader messages are Pitchbend */
577 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
581 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
583 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
589 MidiByteArray msg (sz, raw_bytes);
590 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
592 if (!push2_sysex_header.compare_n (msg, 6)) {
597 case 0x1f: /* pressure mode */
599 _pressure_mode = AfterTouch;
600 PressureModeChange (AfterTouch);
601 cerr << "Pressure mode is after\n";
603 _pressure_mode = PolyPressure;
604 PressureModeChange (PolyPressure);
605 cerr << "Pressure mode is poly\n";
612 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
614 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
616 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
619 /* any press cancels any pending long press timeouts */
620 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
621 Button* bb = id_button_map[*x];
622 bb->timeout_connection.disconnect ();
626 if (b != cc_button_map.end()) {
628 Button* button = b->second;
631 buttons_down.insert (button->id);
632 start_press_timeout (*button, button->id);
634 buttons_down.erase (button->id);
635 button->timeout_connection.disconnect ();
639 set<ButtonID>::iterator c = consumed.find (button->id);
641 if (c == consumed.end()) {
642 if (ev->value == 0) {
643 (this->*button->release_method)();
645 (this->*button->press_method)();
648 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
656 int delta = ev->value;
659 delta = -(128 - delta);
662 switch (ev->controller_number) {
664 _current_layout->strip_vpot (0, delta);
667 _current_layout->strip_vpot (1, delta);
670 _current_layout->strip_vpot (2, delta);
673 _current_layout->strip_vpot (3, delta);
676 _current_layout->strip_vpot (4, delta);
679 _current_layout->strip_vpot (5, delta);
682 _current_layout->strip_vpot (6, delta);
685 _current_layout->strip_vpot (7, delta);
690 other_vpot (8, delta);
693 other_vpot (1, delta);
698 other_vpot (2, delta);
705 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
707 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
709 if (ev->velocity == 0) {
710 handle_midi_note_off_message (parser, ev);
714 switch (ev->note_number) {
716 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
719 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
722 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
725 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
728 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
731 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
734 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
737 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
742 other_vpot_touch (0, ev->velocity > 64);
745 other_vpot_touch (1, ev->velocity > 64);
750 other_vpot_touch (3, ev->velocity > 64);
755 if (ev->velocity < 64) {
761 if (ev->note_number < 11) {
765 /* Pad illuminations */
767 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
769 if (pm == nn_pad_map.end()) {
773 const Pad * const pad_pressed = pm->second;
775 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
777 if (pads_with_note.first == fn_pad_map.end()) {
781 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
782 Pad* pad = pi->second;
784 pad->set_color (contrast_color);
785 pad->set_state (LED::OneShot24th);
786 write (pad->state_msg());
791 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
793 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
795 if (ev->note_number < 11) {
796 /* theoretically related to encoder touch start/end, but
797 * actually they send note on with two different velocity
803 /* Pad illuminations */
805 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
807 if (pm == nn_pad_map.end()) {
811 const Pad * const pad_pressed = pm->second;
813 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
815 if (pads_with_note.first == fn_pad_map.end()) {
819 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
820 Pad* pad = pi->second;
822 if (pad->do_when_pressed == Pad::FlashOn) {
823 pad->set_color (LED::Black);
824 pad->set_state (LED::OneShot24th);
825 write (pad->state_msg());
826 } else if (pad->do_when_pressed == Pad::FlashOff) {
827 pad->set_color (pad->perma_color);
828 pad->set_state (LED::OneShot24th);
829 write (pad->state_msg());
835 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
840 Push2::thread_init ()
842 struct sched_param rtparam;
844 pthread_set_name (event_loop_name().c_str());
846 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
847 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
849 memset (&rtparam, 0, sizeof (rtparam));
850 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
852 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
853 // do we care? not particularly.
858 Push2::connect_session_signals()
860 // receive routes added
861 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
862 // receive VCAs added
863 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
865 // receive record state toggled
866 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
867 // receive transport state changed
868 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
869 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
870 // receive punch-in and punch-out
871 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
872 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
873 // receive rude solo changed
874 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
878 Push2::notify_record_state_changed ()
880 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
882 if (b == id_button_map.end()) {
886 switch (session->record_status ()) {
887 case Session::Disabled:
888 b->second->set_color (LED::White);
889 b->second->set_state (LED::NoTransition);
891 case Session::Enabled:
892 b->second->set_color (LED::Red);
893 b->second->set_state (LED::Blinking4th);
895 case Session::Recording:
896 b->second->set_color (LED::Red);
897 b->second->set_state (LED::OneShot24th);
901 write (b->second->state_msg());
905 Push2::notify_transport_state_changed ()
907 Button* b = id_button_map[Play];
909 if (session->transport_rolling()) {
910 b->set_state (LED::OneShot24th);
911 b->set_color (LED::Green);
914 /* disable any blink on FixedLength from pending edit range op */
915 Button* fl = id_button_map[FixedLength];
917 fl->set_color (LED::Black);
918 fl->set_state (LED::NoTransition);
919 write (fl->state_msg());
921 b->set_color (LED::White);
922 b->set_state (LED::NoTransition);
925 write (b->state_msg());
929 Push2::notify_loop_state_changed ()
934 Push2::notify_parameter_changed (std::string param)
936 IDButtonMap::iterator b;
938 if (param == "clicking") {
939 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
942 if (Config->get_clicking()) {
943 b->second->set_state (LED::Blinking4th);
944 b->second->set_color (LED::White);
946 b->second->set_color (LED::White);
947 b->second->set_state (LED::NoTransition);
949 write (b->second->state_msg ());
954 Push2::notify_solo_active_changed (bool yn)
956 IDButtonMap::iterator b = id_button_map.find (Solo);
958 if (b == id_button_map.end()) {
963 b->second->set_state (LED::Blinking4th);
964 b->second->set_color (LED::Red);
966 b->second->set_state (LED::NoTransition);
967 b->second->set_color (LED::White);
970 write (b->second->state_msg());
976 XMLNode& node (ControlProtocol::get_state());
979 child = new XMLNode (X_("Input"));
980 child->add_child_nocopy (_async_in->get_state());
981 node.add_child_nocopy (*child);
982 child = new XMLNode (X_("Output"));
983 child->add_child_nocopy (_async_out->get_state());
984 node.add_child_nocopy (*child);
986 node.add_property (X_("root"), to_string (_scale_root, std::dec));
987 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
988 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
989 node.add_property (X_("mode"), enum_2_string (_mode));
995 Push2::set_state (const XMLNode & node, int version)
997 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1001 if (ControlProtocol::set_state (node, version)) {
1007 if ((child = node.child (X_("Input"))) != 0) {
1008 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1010 _async_in->set_state (*portnode, version);
1014 if ((child = node.child (X_("Output"))) != 0) {
1015 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1017 _async_out->set_state (*portnode, version);
1021 XMLProperty const* prop;
1023 if ((prop = node.property (X_("root"))) != 0) {
1024 _scale_root = atoi (prop->value());
1027 if ((prop = node.property (X_("root_octave"))) != 0) {
1028 _root_octave = atoi (prop->value());
1031 if ((prop = node.property (X_("in_key"))) != 0) {
1032 _in_key = string_is_affirmative (prop->value());
1035 if ((prop = node.property (X_("mode"))) != 0) {
1036 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1043 Push2::other_vpot (int n, int delta)
1045 boost::shared_ptr<Amp> click_gain;
1051 /* metronome gain control */
1052 click_gain = session->click_gain();
1054 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1056 ac->set_value (ac->interface_to_internal (
1057 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1058 PBD::Controllable::UseGroup);
1063 /* master gain control */
1065 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1067 ac->set_value (ac->interface_to_internal (
1068 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1069 PBD::Controllable::UseGroup);
1077 Push2::other_vpot_touch (int n, bool touching)
1086 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1089 ac->start_touch (session->audible_frame());
1091 ac->stop_touch (true, session->audible_frame());
1099 Push2::start_shift ()
1101 cerr << "start shift\n";
1102 _modifier_state = ModifierState (_modifier_state | ModShift);
1103 Button* b = id_button_map[Shift];
1104 b->set_color (LED::White);
1105 b->set_state (LED::Blinking16th);
1106 write (b->state_msg());
1112 if (_modifier_state & ModShift) {
1113 cerr << "end shift\n";
1114 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1115 Button* b = id_button_map[Shift];
1116 b->timeout_connection.disconnect ();
1117 b->set_color (LED::White);
1118 b->set_state (LED::OneShot24th);
1119 write (b->state_msg());
1124 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1126 /* This filter is called asynchronously from a realtime process
1127 context. It must use atomics to check state, and must not block.
1130 bool matched = false;
1132 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1133 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1135 /* encoder touch start/touch end use note
1136 * 0-10. touchstrip uses note 12
1139 if ((*ev).note() > 10 && (*ev).note() != 12) {
1141 const int n = (*ev).note ();
1142 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1144 if (nni != nn_pad_map.end()) {
1145 Pad const * pad = nni->second;
1146 /* shift for output to the shadow port */
1147 if (pad->filtered >= 0) {
1148 (*ev).set_note (pad->filtered + (octave_shift*12));
1149 out.push_back (*ev);
1150 /* shift back so that the pads light correctly */
1153 /* no mapping, don't send event */
1156 out.push_back (*ev);
1161 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1162 out.push_back (*ev);
1170 Push2::port_registration_handler ()
1172 if (!_async_in && !_async_out) {
1173 /* ports not registered yet */
1177 if (_async_in->connected() && _async_out->connected()) {
1178 /* don't waste cycles here */
1183 /* the origin of the numeric magic identifiers is known only to Ableton
1184 and may change in time. This is part of how CoreMIDI works.
1186 string input_port_name = X_("system:midi_capture_1319078870");
1187 string output_port_name = X_("system:midi_playback_3409210341");
1189 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1190 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1195 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1196 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1198 if (!in.empty() && !out.empty()) {
1199 cerr << "Push2: both ports found\n";
1200 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1201 if (!_async_in->connected()) {
1202 AudioEngine::instance()->connect (_async_in->name(), in.front());
1204 if (!_async_out->connected()) {
1205 AudioEngine::instance()->connect (_async_out->name(), out.front());
1211 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1213 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1214 if (!_input_port || !_output_port) {
1218 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1219 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1221 if (ni == name1 || ni == name2) {
1223 connection_state |= InputConnected;
1225 connection_state &= ~InputConnected;
1227 } else if (no == name1 || no == name2) {
1229 connection_state |= OutputConnected;
1231 connection_state &= ~OutputConnected;
1234 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1239 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1242 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1244 /* XXX this is a horrible hack. Without a short sleep here,
1245 something prevents the device wakeup messages from being
1246 sent and/or the responses from being received.
1250 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1252 /* may not have the device open if it was just plugged
1253 in. Really need USB device detection rather than MIDI port
1254 detection for this to work well.
1258 begin_using_device ();
1261 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1262 stop_using_device ();
1265 ConnectionChange (); /* emit signal for our GUI */
1267 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1269 return true; /* connection status changed */
1272 boost::shared_ptr<Port>
1273 Push2::output_port()
1278 boost::shared_ptr<Port>
1285 Push2::pad_note (int row, int col) const
1287 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1289 if (nni != nn_pad_map.end()) {
1290 return nni->second->filtered;
1297 Push2::update_selection_color ()
1299 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1301 if (!current_midi_track) {
1305 selection_color = get_color_index (current_midi_track->presentation_info().color());
1306 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1308 reset_pad_colors ();
1312 Push2::reset_pad_colors ()
1314 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1318 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1320 MusicalMode m (mode);
1321 vector<float>::iterator interval;
1323 const int original_root = root;
1325 interval = m.steps.begin();
1326 root += (octave*12);
1329 const int root_start = root;
1331 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1332 vector<int> mode_vector; /* sorted in note order */
1334 mode_map.insert (note);
1335 mode_vector.push_back (note);
1337 /* build a map of all notes in the mode, from the root to 127 */
1339 while (note < 128) {
1341 if (interval == m.steps.end()) {
1343 /* last distance was the end of the scale,
1344 so wrap, adding the next note at one
1345 octave above the last root.
1348 interval = m.steps.begin();
1350 mode_map.insert (root);
1351 mode_vector.push_back (root);
1354 note = (int) floor (root + (2.0 * (*interval)));
1356 mode_map.insert (note);
1357 mode_vector.push_back (note);
1361 fn_pad_map.clear ();
1365 vector<int>::iterator notei;
1368 for (int row = 0; row < 8; ++row) {
1370 /* Ableton's grid layout wraps the available notes in the scale
1371 * by offsetting 3 notes per row (from the bottom)
1374 notei = mode_vector.begin();
1375 notei += row_offset;
1378 for (int col = 0; col < 8; ++col) {
1379 int index = 36 + (row*8) + col;
1380 Pad* pad = nn_pad_map[index];
1382 if (notei != mode_vector.end()) {
1385 pad->filtered = notenum;
1387 fn_pad_map.insert (make_pair (notenum, pad));
1389 if ((notenum % 12) == original_root) {
1390 pad->set_color (selection_color);
1391 pad->perma_color = selection_color;
1393 pad->set_color (LED::White);
1394 pad->perma_color = LED::White;
1397 pad->do_when_pressed = Pad::FlashOff;
1402 pad->set_color (LED::Black);
1403 pad->do_when_pressed = Pad::Nothing;
1407 pad->set_state (LED::OneShot24th);
1408 write (pad->state_msg());
1414 /* chromatic: all notes available, but highlight those in the scale */
1416 for (note = 36; note < 100; ++note) {
1418 Pad* pad = nn_pad_map[note];
1420 /* Chromatic: all pads play, half-tone steps. Light
1421 * those in the scale, and highlight root notes
1424 pad->filtered = root_start + (note - 36);
1426 fn_pad_map.insert (make_pair (pad->filtered, pad));
1428 if (mode_map.find (note) != mode_map.end()) {
1430 if ((note % 12) == original_root) {
1431 pad->set_color (selection_color);
1432 pad->perma_color = selection_color;
1434 pad->set_color (LED::White);
1435 pad->perma_color = LED::White;
1438 pad->do_when_pressed = Pad::FlashOff;
1442 /* note is not in mode, turn it off */
1444 pad->do_when_pressed = Pad::FlashOn;
1445 pad->set_color (LED::Black);
1449 pad->set_state (LED::OneShot24th);
1450 write (pad->state_msg());
1456 bool changed = false;
1458 if (_scale_root != original_root) {
1459 _scale_root = original_root;
1462 if (_root_octave != octave) {
1463 _root_octave = octave;
1466 if (_in_key != inkey) {
1470 if (_mode != mode) {
1476 ScaleChange (); /* EMIT SIGNAL */
1481 Push2::set_percussive_mode (bool yn)
1484 cerr << "back to scale\n";
1485 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1492 fn_pad_map.clear ();
1494 for (int row = 0; row < 8; ++row) {
1496 for (int col = 0; col < 4; ++col) {
1498 int index = 36 + (row*8) + col;
1499 Pad* pad = nn_pad_map[index];
1501 pad->filtered = drum_note;
1506 for (int row = 0; row < 8; ++row) {
1508 for (int col = 4; col < 8; ++col) {
1510 int index = 36 + (row*8) + col;
1511 Pad* pad = nn_pad_map[index];
1513 pad->filtered = drum_note;
1522 Push2::current_layout () const
1524 Glib::Threads::Mutex::Lock lm (layout_lock);
1525 return _current_layout;
1529 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1531 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1532 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1533 boost::shared_ptr<MidiTrack> new_pad_target;
1535 /* See if there's a MIDI track selected */
1537 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1539 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1541 if (new_pad_target) {
1546 if (current_midi_track == new_pad_target) {
1551 if (!new_pad_target) {
1552 /* leave existing connection alone */
1556 /* disconnect from pad port, if appropriate */
1558 if (current_midi_track && pad_port) {
1560 /* XXX this could possibly leave dangling MIDI notes.
1562 * A general libardour fix is required. It isn't obvious
1563 * how note resolution can be done unless disconnecting
1564 * becomes "slow" (i.e. deferred for as long as it takes
1565 * to resolve notes).
1567 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1570 /* now connect the pad port to this (newly) selected midi
1571 * track, if indeed there is one.
1574 if (new_pad_target && pad_port) {
1575 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1576 current_pad_target = new_pad_target;
1577 selection_color = get_color_index (new_pad_target->presentation_info().color());
1578 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1580 current_pad_target.reset ();
1581 selection_color = LED::Green;
1582 contrast_color = LED::Green;
1585 reset_pad_colors ();
1589 Push2::button_by_id (ButtonID bid)
1591 return id_button_map[bid];
1595 Push2::get_color_index (ArdourCanvas::Color rgba)
1597 ColorMap::iterator i = color_map.find (rgba);
1599 if (i != color_map.end()) {
1603 double dr, dg, db, da;
1605 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1606 int w = 126; /* not sure where/when we should get this value */
1609 r = (int) floor (255.0 * dr);
1610 g = (int) floor (255.0 * dg);
1611 b = (int) floor (255.0 * db);
1613 /* get a free index */
1617 if (color_map_free_list.empty()) {
1618 /* random replacement of any entry above zero and below 122 (where the
1619 * Ableton standard colors live)
1621 index = 1 + (random() % 121);
1623 index = color_map_free_list.top();
1624 color_map_free_list.pop();
1627 MidiByteArray palette_msg (17,
1629 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1630 0x00, /* index = 7 */
1631 0x00, 0x00, /* r = 8 & 9 */
1632 0x00, 0x00, /* g = 10 & 11 */
1633 0x00, 0x00, /* b = 12 & 13 */
1634 0x00, 0x00, /* w (a?) = 14 & 15*/
1636 palette_msg[7] = index;
1637 palette_msg[8] = r & 0x7f;
1638 palette_msg[9] = (r & 0x80) >> 7;
1639 palette_msg[10] = g & 0x7f;
1640 palette_msg[11] = (g & 0x80) >> 7;
1641 palette_msg[12] = b & 0x7f;
1642 palette_msg[13] = (b & 0x80) >> 7;
1643 palette_msg[14] = w & 0x7f;
1644 palette_msg[15] = w & 0x80;
1646 write (palette_msg);
1648 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1649 write (update_pallette_msg);
1651 color_map[rgba] = index;
1657 Push2::build_color_map ()
1659 /* These are "standard" colors that Ableton docs suggest will always be
1660 there. Put them in our color map so that when we look up these
1661 colors, we will use the Ableton indices for them.
1664 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1665 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1666 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1667 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1668 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1669 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1670 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1672 for (uint8_t n = 1; n < 122; ++n) {
1673 color_map_free_list.push (n);
1678 Push2::fill_color_table ()
1680 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1681 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1683 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1685 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1686 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1687 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1689 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1690 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1692 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1693 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1694 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1695 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1700 Push2::get_color (ColorName name)
1702 Colors::iterator c = colors.find (name);
1703 if (c != colors.end()) {
1711 Push2::set_current_layout (Push2Layout* layout)
1713 if (layout && layout == _current_layout) {
1714 _current_layout->show ();
1717 if (_current_layout) {
1718 _current_layout->hide ();
1719 _canvas->root()->remove (_current_layout);
1720 _previous_layout = _current_layout;
1723 _current_layout = layout;
1725 if (_current_layout) {
1726 _canvas->root()->add (_current_layout);
1727 _current_layout->show ();
1731 _canvas->request_redraw ();
1736 Push2::use_previous_layout ()
1738 if (_previous_layout) {
1739 set_current_layout (_previous_layout);
1744 Push2::request_pressure_mode ()
1746 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1751 Push2::set_pressure_mode (PressureMode pm)
1753 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1757 /* nothing to do, message is correct */
1767 cerr << "Sent PM message " << msg << endl;