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.
22 #include "pbd/compose.h"
23 #include "pbd/convert.h"
24 #include "pbd/debug.h"
25 #include "pbd/failed_constructor.h"
26 #include "pbd/file_utils.h"
27 #include "pbd/search_path.h"
28 #include "pbd/enumwriter.h"
30 #include "midi++/parser.h"
31 #include "timecode/time.h"
32 #include "timecode/bbt_time.h"
34 #include "ardour/amp.h"
35 #include "ardour/async_midi_port.h"
36 #include "ardour/audioengine.h"
37 #include "ardour/debug.h"
38 #include "ardour/midiport_manager.h"
39 #include "ardour/midi_track.h"
40 #include "ardour/midi_port.h"
41 #include "ardour/session.h"
42 #include "ardour/tempo.h"
44 #include "gtkmm2ext/gui_thread.h"
45 #include "gtkmm2ext/rgb_macros.h"
47 #include "canvas/colors.h"
57 #include "track_mix.h"
61 #ifdef PLATFORM_WINDOWS
62 #define random() rand()
65 using namespace ARDOUR;
69 using namespace ArdourSurface;
71 #include "pbd/abstract_ui.cc" // instantiate template
73 #define ABLETON 0x2982
76 Push2::Push2 (ARDOUR::Session& s)
77 : ControlProtocol (s, string (X_("Ableton Push 2")))
78 , AbstractUI<Push2Request> (name())
81 , _modifier_state (None)
84 , _previous_layout (0)
85 , connection_state (ConnectionState (0))
87 , _mode (MusicalMode::IonianMajor)
93 , _pressure_mode (AfterTouch)
94 , selection_color (LED::Green)
95 , contrast_color (LED::Green)
96 , in_range_select (false)
98 /* we're going to need this */
106 /* master cannot be removed, so no need to connect to going-away signal */
107 master = session->master_out ();
109 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
111 /* allocate graphics layouts, even though we're not using them yet */
113 _canvas = new Push2Canvas (*this, 960, 160);
114 mix_layout = new MixLayout (*this, *session, "globalmix");
115 scale_layout = new ScaleLayout (*this, *session, "scale");
116 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
117 splash_layout = new SplashLayout (*this, *session, "splash");
121 /* Ports exist for the life of this instance */
125 /* catch arrival and departure of Push2 itself */
126 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
128 /* Catch port connections and disconnections */
129 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
131 /* Push 2 ports might already be there */
132 port_registration_handler ();
137 DEBUG_TRACE (DEBUG::Push2, "push2 control surface object being destroyed\n");
139 /* do this before stopping the event loop, so that we don't get any notifications */
140 selection_connection.disconnect ();
141 port_reg_connection.disconnect ();
142 port_connection.disconnect ();
144 stop_using_device ();
148 if (_current_layout) {
149 _canvas->root()->remove (_current_layout);
157 delete splash_layout;
159 delete track_mix_layout;
160 track_mix_layout = 0;
167 Push2::run_event_loop ()
169 DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
174 Push2::stop_event_loop ()
176 DEBUG_TRACE (DEBUG::Push2, "stop event loop\n");
181 Push2::begin_using_device ()
183 DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
185 /* set up periodic task used to push a frame buffer to the
186 * device (25fps). The device can handle 60fps, but we don't
187 * need that frame rate.
190 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
191 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
192 vblank_timeout->attach (main_loop()->get_context());
194 connect_session_signals ();
198 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
201 /* catch current selection, if any so that we can wire up the pads if appropriate */
203 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
204 stripable_selection_change (sp);
207 request_pressure_mode ();
215 Push2::stop_using_device ()
217 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
220 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
224 init_buttons (false);
225 strip_buttons_off ();
227 vblank_connection.disconnect ();
228 session_connections.drop_connections ();
235 Push2::ports_acquire ()
237 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
241 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
242 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
244 if (_async_in == 0 || _async_out == 0) {
245 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
249 /* We do not add our ports to the input/output bundles because we don't
250 * want users wiring them by hand. They could use JACK tools if they
251 * really insist on that (and use JACK)
254 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
255 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
257 /* Create a shadow port where, depending on the state of the surface,
258 * we will make pad note on/off events appear. The surface code will
259 * automatically this port to the first selected MIDI track.
262 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));
263 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
267 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
269 _output_bundle->add_channel (
271 ARDOUR::DataType::MIDI,
272 session->engine().make_port_name_non_relative (shadow_port->name())
276 session->BundleAddedOrRemoved ();
278 connect_to_parser ();
280 /* Connect input port to event loop */
284 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
285 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
286 asp->xthread().attach (main_loop()->get_context());
292 Push2::ports_release ()
294 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
296 /* wait for button data to be flushed */
298 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
299 asp->drain (10000, 500000);
301 AudioEngine::instance()->unregister_port (_async_in);
302 AudioEngine::instance()->unregister_port (_async_out);
304 _async_in.reset ((ARDOUR::Port*) 0);
305 _async_out.reset ((ARDOUR::Port*) 0);
311 Push2::device_acquire ()
315 DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
318 DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
323 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
324 DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
328 if ((err = libusb_claim_interface (handle, 0x00))) {
329 DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
330 libusb_close (handle);
339 Push2::device_release ()
341 DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
343 libusb_release_interface (handle, 0x00);
344 libusb_close (handle);
349 list<boost::shared_ptr<ARDOUR::Bundle> >
352 list<boost::shared_ptr<ARDOUR::Bundle> > b;
354 if (_output_bundle) {
355 b.push_back (_output_bundle);
362 Push2::strip_buttons_off ()
364 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
365 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
367 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
368 Button* b = id_button_map[strip_buttons[n]];
370 b->set_color (LED::Black);
371 b->set_state (LED::OneShot24th);
372 write (b->state_msg());
378 Push2::init_buttons (bool startup)
380 /* This is a list of buttons that we want lit because they do something
381 in ardour related (loosely, sometimes) to their illuminated label.
384 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
385 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
386 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
389 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
390 Button* b = id_button_map[buttons[n]];
393 b->set_color (LED::White);
395 b->set_color (LED::Black);
397 b->set_state (LED::OneShot24th);
398 write (b->state_msg());
403 /* all other buttons are off (black) */
405 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
406 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
407 Accent, Note, Session, };
409 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
410 Button* b = id_button_map[off_buttons[n]];
412 b->set_color (LED::Black);
413 b->set_state (LED::OneShot24th);
414 write (b->state_msg());
419 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
420 Pad* pad = pi->second;
422 pad->set_color (LED::Black);
423 pad->set_state (LED::OneShot24th);
424 write (pad->state_msg());
436 Push2::request_factory (uint32_t num_requests)
438 /* AbstractUI<T>::request_buffer_factory() is a template method only
439 instantiated in this source module. To provide something visible for
440 use in the interface/descriptor, we have this static method that is
443 return request_buffer_factory (num_requests);
447 Push2::do_request (Push2Request * req)
449 if (req->type == CallSlot) {
451 call_slot (MISSING_INVALIDATOR, req->the_slot);
453 } else if (req->type == Quit) {
455 stop_using_device ();
462 set_current_layout (splash_layout);
463 splash_start = get_microseconds ();
471 /* display splash for 2 seconds */
473 if (get_microseconds() - splash_start > 2000000) {
475 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
476 set_current_layout (mix_layout);
480 if (_current_layout) {
481 _current_layout->update_meters ();
482 _current_layout->update_clocks ();
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 if (device_acquire ()) {
505 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
506 begin_using_device ();
508 /* begin_using_device () will get called once we're connected */
512 /* Control Protocol Manager never calls us with false, but
513 * insteads destroys us.
517 ControlProtocol::set_active (yn);
519 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
525 Push2::init_touch_strip ()
527 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
528 /* flags are the final byte (ignore end-of-sysex */
530 /* show bar, not point
534 msg[7] = (1<<4) | (1<<5) | (1<<6);
539 Push2::write (const MidiByteArray& data)
541 /* immediate delivery */
542 _output_port->write (&data[0], data.size(), 0);
546 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
549 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
555 DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
557 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
562 DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
564 framepos_t now = AudioEngine::instance()->sample_time();
573 Push2::connect_to_parser ()
575 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
577 MIDI::Parser* p = _input_port->parser();
580 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
581 /* V-Pot messages are Controller */
582 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
583 /* Button messages are NoteOn */
584 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
585 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
586 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
587 /* Fader messages are Pitchbend */
588 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
592 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
594 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
600 MidiByteArray msg (sz, raw_bytes);
601 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
603 if (!push2_sysex_header.compare_n (msg, 6)) {
608 case 0x1f: /* pressure mode */
610 _pressure_mode = AfterTouch;
611 PressureModeChange (AfterTouch);
612 cerr << "Pressure mode is after\n";
614 _pressure_mode = PolyPressure;
615 PressureModeChange (PolyPressure);
616 cerr << "Pressure mode is poly\n";
623 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
625 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
627 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
630 /* any press cancels any pending long press timeouts */
631 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
632 Button* bb = id_button_map[*x];
633 bb->timeout_connection.disconnect ();
637 if (b != cc_button_map.end()) {
639 Button* button = b->second;
642 buttons_down.insert (button->id);
643 start_press_timeout (*button, button->id);
645 buttons_down.erase (button->id);
646 button->timeout_connection.disconnect ();
650 set<ButtonID>::iterator c = consumed.find (button->id);
652 if (c == consumed.end()) {
653 if (ev->value == 0) {
654 (this->*button->release_method)();
656 (this->*button->press_method)();
659 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
667 int delta = ev->value;
670 delta = -(128 - delta);
673 switch (ev->controller_number) {
675 _current_layout->strip_vpot (0, delta);
678 _current_layout->strip_vpot (1, delta);
681 _current_layout->strip_vpot (2, delta);
684 _current_layout->strip_vpot (3, delta);
687 _current_layout->strip_vpot (4, delta);
690 _current_layout->strip_vpot (5, delta);
693 _current_layout->strip_vpot (6, delta);
696 _current_layout->strip_vpot (7, delta);
701 other_vpot (8, delta);
704 other_vpot (1, delta);
709 other_vpot (2, delta);
716 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
718 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
720 if (ev->velocity == 0) {
721 handle_midi_note_off_message (parser, ev);
725 switch (ev->note_number) {
727 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
730 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
733 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
736 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
739 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
742 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
745 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
748 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
753 other_vpot_touch (0, ev->velocity > 64);
756 other_vpot_touch (1, ev->velocity > 64);
761 other_vpot_touch (3, ev->velocity > 64);
766 if (ev->velocity < 64) {
772 if (ev->note_number < 11) {
776 /* Pad illuminations */
778 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
780 if (pm == nn_pad_map.end()) {
784 const Pad * const pad_pressed = pm->second;
786 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
788 if (pads_with_note.first == fn_pad_map.end()) {
792 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
793 Pad* pad = pi->second;
795 pad->set_color (contrast_color);
796 pad->set_state (LED::OneShot24th);
797 write (pad->state_msg());
802 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
804 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
806 if (ev->note_number < 11) {
807 /* theoretically related to encoder touch start/end, but
808 * actually they send note on with two different velocity
814 /* Pad illuminations */
816 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
818 if (pm == nn_pad_map.end()) {
822 const Pad * const pad_pressed = pm->second;
824 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
826 if (pads_with_note.first == fn_pad_map.end()) {
830 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
831 Pad* pad = pi->second;
833 if (pad->do_when_pressed == Pad::FlashOn) {
834 pad->set_color (LED::Black);
835 pad->set_state (LED::OneShot24th);
836 write (pad->state_msg());
837 } else if (pad->do_when_pressed == Pad::FlashOff) {
838 pad->set_color (pad->perma_color);
839 pad->set_state (LED::OneShot24th);
840 write (pad->state_msg());
846 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
851 Push2::thread_init ()
853 struct sched_param rtparam;
855 pthread_set_name (event_loop_name().c_str());
857 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
858 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
860 memset (&rtparam, 0, sizeof (rtparam));
861 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
863 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
864 // do we care? not particularly.
869 Push2::connect_session_signals()
871 // receive routes added
872 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
873 // receive VCAs added
874 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
876 // receive record state toggled
877 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
878 // receive transport state changed
879 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
880 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
881 // receive punch-in and punch-out
882 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
883 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
884 // receive rude solo changed
885 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
889 Push2::notify_record_state_changed ()
891 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
893 if (b == id_button_map.end()) {
897 switch (session->record_status ()) {
898 case Session::Disabled:
899 b->second->set_color (LED::White);
900 b->second->set_state (LED::NoTransition);
902 case Session::Enabled:
903 b->second->set_color (LED::Red);
904 b->second->set_state (LED::Blinking4th);
906 case Session::Recording:
907 b->second->set_color (LED::Red);
908 b->second->set_state (LED::OneShot24th);
912 write (b->second->state_msg());
916 Push2::notify_transport_state_changed ()
918 Button* b = id_button_map[Play];
920 if (session->transport_rolling()) {
921 b->set_state (LED::OneShot24th);
922 b->set_color (LED::Green);
925 /* disable any blink on FixedLength from pending edit range op */
926 Button* fl = id_button_map[FixedLength];
928 fl->set_color (LED::Black);
929 fl->set_state (LED::NoTransition);
930 write (fl->state_msg());
932 b->set_color (LED::White);
933 b->set_state (LED::NoTransition);
936 write (b->state_msg());
940 Push2::notify_loop_state_changed ()
945 Push2::notify_parameter_changed (std::string param)
947 IDButtonMap::iterator b;
949 if (param == "clicking") {
950 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
953 if (Config->get_clicking()) {
954 b->second->set_state (LED::Blinking4th);
955 b->second->set_color (LED::White);
957 b->second->set_color (LED::White);
958 b->second->set_state (LED::NoTransition);
960 write (b->second->state_msg ());
965 Push2::notify_solo_active_changed (bool yn)
967 IDButtonMap::iterator b = id_button_map.find (Solo);
969 if (b == id_button_map.end()) {
974 b->second->set_state (LED::Blinking4th);
975 b->second->set_color (LED::Red);
977 b->second->set_state (LED::NoTransition);
978 b->second->set_color (LED::White);
981 write (b->second->state_msg());
987 XMLNode& node (ControlProtocol::get_state());
990 child = new XMLNode (X_("Input"));
991 child->add_child_nocopy (_async_in->get_state());
992 node.add_child_nocopy (*child);
993 child = new XMLNode (X_("Output"));
994 child->add_child_nocopy (_async_out->get_state());
995 node.add_child_nocopy (*child);
997 node.add_property (X_("root"), to_string (_scale_root, std::dec));
998 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
999 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1000 node.add_property (X_("mode"), enum_2_string (_mode));
1006 Push2::set_state (const XMLNode & node, int version)
1008 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1012 if (ControlProtocol::set_state (node, version)) {
1018 if ((child = node.child (X_("Input"))) != 0) {
1019 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1021 _async_in->set_state (*portnode, version);
1025 if ((child = node.child (X_("Output"))) != 0) {
1026 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1028 _async_out->set_state (*portnode, version);
1032 XMLProperty const* prop;
1034 if ((prop = node.property (X_("root"))) != 0) {
1035 _scale_root = atoi (prop->value());
1038 if ((prop = node.property (X_("root_octave"))) != 0) {
1039 _root_octave = atoi (prop->value());
1042 if ((prop = node.property (X_("in_key"))) != 0) {
1043 _in_key = string_is_affirmative (prop->value());
1046 if ((prop = node.property (X_("mode"))) != 0) {
1047 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1054 Push2::other_vpot (int n, int delta)
1056 boost::shared_ptr<Amp> click_gain;
1062 /* metronome gain control */
1063 click_gain = session->click_gain();
1065 boost::shared_ptr<AutomationControl> ac = click_gain->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);
1074 /* master gain control */
1076 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1078 ac->set_value (ac->interface_to_internal (
1079 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1080 PBD::Controllable::UseGroup);
1088 Push2::other_vpot_touch (int n, bool touching)
1097 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1100 ac->start_touch (session->audible_frame());
1102 ac->stop_touch (true, session->audible_frame());
1110 Push2::start_shift ()
1112 cerr << "start shift\n";
1113 _modifier_state = ModifierState (_modifier_state | ModShift);
1114 Button* b = id_button_map[Shift];
1115 b->set_color (LED::White);
1116 b->set_state (LED::Blinking16th);
1117 write (b->state_msg());
1123 if (_modifier_state & ModShift) {
1124 cerr << "end shift\n";
1125 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1126 Button* b = id_button_map[Shift];
1127 b->timeout_connection.disconnect ();
1128 b->set_color (LED::White);
1129 b->set_state (LED::OneShot24th);
1130 write (b->state_msg());
1135 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1137 /* This filter is called asynchronously from a realtime process
1138 context. It must use atomics to check state, and must not block.
1141 bool matched = false;
1143 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1144 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1146 /* encoder touch start/touch end use note
1147 * 0-10. touchstrip uses note 12
1150 if ((*ev).note() > 10 && (*ev).note() != 12) {
1152 const int n = (*ev).note ();
1153 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1155 if (nni != nn_pad_map.end()) {
1156 Pad const * pad = nni->second;
1157 /* shift for output to the shadow port */
1158 if (pad->filtered >= 0) {
1159 (*ev).set_note (pad->filtered + (octave_shift*12));
1160 out.push_back (*ev);
1161 /* shift back so that the pads light correctly */
1164 /* no mapping, don't send event */
1167 out.push_back (*ev);
1172 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1173 out.push_back (*ev);
1181 Push2::port_registration_handler ()
1183 if (!_async_in && !_async_out) {
1184 /* ports not registered yet */
1188 if (_async_in->connected() && _async_out->connected()) {
1189 /* don't waste cycles here */
1194 /* the origin of the numeric magic identifiers is known only to Ableton
1195 and may change in time. This is part of how CoreMIDI works.
1197 string input_port_name = X_("system:midi_capture_1319078870");
1198 string output_port_name = X_("system:midi_playback_3409210341");
1200 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1201 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1206 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1207 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1209 if (!in.empty() && !out.empty()) {
1210 cerr << "Push2: both ports found\n";
1211 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1212 if (!_async_in->connected()) {
1213 AudioEngine::instance()->connect (_async_in->name(), in.front());
1215 if (!_async_out->connected()) {
1216 AudioEngine::instance()->connect (_async_out->name(), out.front());
1222 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1224 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1225 if (!_input_port || !_output_port) {
1229 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1230 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1232 if (ni == name1 || ni == name2) {
1234 connection_state |= InputConnected;
1236 connection_state &= ~InputConnected;
1238 } else if (no == name1 || no == name2) {
1240 connection_state |= OutputConnected;
1242 connection_state &= ~OutputConnected;
1245 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1250 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1253 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1255 /* XXX this is a horrible hack. Without a short sleep here,
1256 something prevents the device wakeup messages from being
1257 sent and/or the responses from being received.
1261 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1263 /* may not have the device open if it was just plugged
1264 in. Really need USB device detection rather than MIDI port
1265 detection for this to work well.
1269 begin_using_device ();
1272 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1273 stop_using_device ();
1276 ConnectionChange (); /* emit signal for our GUI */
1278 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1280 return true; /* connection status changed */
1283 boost::shared_ptr<Port>
1284 Push2::output_port()
1289 boost::shared_ptr<Port>
1296 Push2::pad_note (int row, int col) const
1298 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1300 if (nni != nn_pad_map.end()) {
1301 return nni->second->filtered;
1308 Push2::update_selection_color ()
1310 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1312 if (!current_midi_track) {
1316 selection_color = get_color_index (current_midi_track->presentation_info().color());
1317 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1319 reset_pad_colors ();
1323 Push2::reset_pad_colors ()
1325 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1329 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1331 MusicalMode m (mode);
1332 vector<float>::iterator interval;
1334 const int original_root = root;
1336 interval = m.steps.begin();
1337 root += (octave*12);
1340 const int root_start = root;
1342 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1343 vector<int> mode_vector; /* sorted in note order */
1345 mode_map.insert (note);
1346 mode_vector.push_back (note);
1348 /* build a map of all notes in the mode, from the root to 127 */
1350 while (note < 128) {
1352 if (interval == m.steps.end()) {
1354 /* last distance was the end of the scale,
1355 so wrap, adding the next note at one
1356 octave above the last root.
1359 interval = m.steps.begin();
1361 mode_map.insert (root);
1362 mode_vector.push_back (root);
1365 note = (int) floor (root + (2.0 * (*interval)));
1367 mode_map.insert (note);
1368 mode_vector.push_back (note);
1372 fn_pad_map.clear ();
1376 vector<int>::iterator notei;
1379 for (int row = 0; row < 8; ++row) {
1381 /* Ableton's grid layout wraps the available notes in the scale
1382 * by offsetting 3 notes per row (from the bottom)
1385 notei = mode_vector.begin();
1386 notei += row_offset;
1389 for (int col = 0; col < 8; ++col) {
1390 int index = 36 + (row*8) + col;
1391 Pad* pad = nn_pad_map[index];
1393 if (notei != mode_vector.end()) {
1396 pad->filtered = notenum;
1398 fn_pad_map.insert (make_pair (notenum, pad));
1400 if ((notenum % 12) == original_root) {
1401 pad->set_color (selection_color);
1402 pad->perma_color = selection_color;
1404 pad->set_color (LED::White);
1405 pad->perma_color = LED::White;
1408 pad->do_when_pressed = Pad::FlashOff;
1413 pad->set_color (LED::Black);
1414 pad->do_when_pressed = Pad::Nothing;
1418 pad->set_state (LED::OneShot24th);
1419 write (pad->state_msg());
1425 /* chromatic: all notes available, but highlight those in the scale */
1427 for (note = 36; note < 100; ++note) {
1429 Pad* pad = nn_pad_map[note];
1431 /* Chromatic: all pads play, half-tone steps. Light
1432 * those in the scale, and highlight root notes
1435 pad->filtered = root_start + (note - 36);
1437 fn_pad_map.insert (make_pair (pad->filtered, pad));
1439 if (mode_map.find (note) != mode_map.end()) {
1441 if ((note % 12) == original_root) {
1442 pad->set_color (selection_color);
1443 pad->perma_color = selection_color;
1445 pad->set_color (LED::White);
1446 pad->perma_color = LED::White;
1449 pad->do_when_pressed = Pad::FlashOff;
1453 /* note is not in mode, turn it off */
1455 pad->do_when_pressed = Pad::FlashOn;
1456 pad->set_color (LED::Black);
1460 pad->set_state (LED::OneShot24th);
1461 write (pad->state_msg());
1467 bool changed = false;
1469 if (_scale_root != original_root) {
1470 _scale_root = original_root;
1473 if (_root_octave != octave) {
1474 _root_octave = octave;
1477 if (_in_key != inkey) {
1481 if (_mode != mode) {
1487 ScaleChange (); /* EMIT SIGNAL */
1492 Push2::set_percussive_mode (bool yn)
1495 cerr << "back to scale\n";
1496 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1503 fn_pad_map.clear ();
1505 for (int row = 0; row < 8; ++row) {
1507 for (int col = 0; col < 4; ++col) {
1509 int index = 36 + (row*8) + col;
1510 Pad* pad = nn_pad_map[index];
1512 pad->filtered = drum_note;
1517 for (int row = 0; row < 8; ++row) {
1519 for (int col = 4; col < 8; ++col) {
1521 int index = 36 + (row*8) + col;
1522 Pad* pad = nn_pad_map[index];
1524 pad->filtered = drum_note;
1533 Push2::current_layout () const
1535 Glib::Threads::Mutex::Lock lm (layout_lock);
1536 return _current_layout;
1540 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1542 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1543 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1544 boost::shared_ptr<MidiTrack> new_pad_target;
1546 /* See if there's a MIDI track selected */
1548 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1550 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1552 if (new_pad_target) {
1557 if (current_midi_track == new_pad_target) {
1562 if (!new_pad_target) {
1563 /* leave existing connection alone */
1567 /* disconnect from pad port, if appropriate */
1569 if (current_midi_track && pad_port) {
1571 /* XXX this could possibly leave dangling MIDI notes.
1573 * A general libardour fix is required. It isn't obvious
1574 * how note resolution can be done unless disconnecting
1575 * becomes "slow" (i.e. deferred for as long as it takes
1576 * to resolve notes).
1578 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1581 /* now connect the pad port to this (newly) selected midi
1582 * track, if indeed there is one.
1585 if (new_pad_target && pad_port) {
1586 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1587 current_pad_target = new_pad_target;
1588 selection_color = get_color_index (new_pad_target->presentation_info().color());
1589 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1591 current_pad_target.reset ();
1592 selection_color = LED::Green;
1593 contrast_color = LED::Green;
1596 reset_pad_colors ();
1600 Push2::button_by_id (ButtonID bid)
1602 return id_button_map[bid];
1606 Push2::get_color_index (ArdourCanvas::Color rgba)
1608 ColorMap::iterator i = color_map.find (rgba);
1610 if (i != color_map.end()) {
1614 double dr, dg, db, da;
1616 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1617 int w = 126; /* not sure where/when we should get this value */
1620 r = (int) floor (255.0 * dr);
1621 g = (int) floor (255.0 * dg);
1622 b = (int) floor (255.0 * db);
1624 /* get a free index */
1628 if (color_map_free_list.empty()) {
1629 /* random replacement of any entry above zero and below 122 (where the
1630 * Ableton standard colors live)
1632 index = 1 + (random() % 121);
1634 index = color_map_free_list.top();
1635 color_map_free_list.pop();
1638 MidiByteArray palette_msg (17,
1640 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1641 0x00, /* index = 7 */
1642 0x00, 0x00, /* r = 8 & 9 */
1643 0x00, 0x00, /* g = 10 & 11 */
1644 0x00, 0x00, /* b = 12 & 13 */
1645 0x00, 0x00, /* w (a?) = 14 & 15*/
1647 palette_msg[7] = index;
1648 palette_msg[8] = r & 0x7f;
1649 palette_msg[9] = (r & 0x80) >> 7;
1650 palette_msg[10] = g & 0x7f;
1651 palette_msg[11] = (g & 0x80) >> 7;
1652 palette_msg[12] = b & 0x7f;
1653 palette_msg[13] = (b & 0x80) >> 7;
1654 palette_msg[14] = w & 0x7f;
1655 palette_msg[15] = w & 0x80;
1657 write (palette_msg);
1659 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1660 write (update_pallette_msg);
1662 color_map[rgba] = index;
1668 Push2::build_color_map ()
1670 /* These are "standard" colors that Ableton docs suggest will always be
1671 there. Put them in our color map so that when we look up these
1672 colors, we will use the Ableton indices for them.
1675 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1676 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1677 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1678 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1679 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1680 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1681 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1683 for (uint8_t n = 1; n < 122; ++n) {
1684 color_map_free_list.push (n);
1689 Push2::fill_color_table ()
1691 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1692 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1694 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1696 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1697 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1698 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1700 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1701 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1703 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1704 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1705 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1706 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1711 Push2::get_color (ColorName name)
1713 Colors::iterator c = colors.find (name);
1714 if (c != colors.end()) {
1722 Push2::set_current_layout (Push2Layout* layout)
1724 if (layout && layout == _current_layout) {
1725 _current_layout->show ();
1728 if (_current_layout) {
1729 _current_layout->hide ();
1730 _canvas->root()->remove (_current_layout);
1731 _previous_layout = _current_layout;
1734 _current_layout = layout;
1736 if (_current_layout) {
1737 _canvas->root()->add (_current_layout);
1738 _current_layout->show ();
1742 _canvas->request_redraw ();
1747 Push2::use_previous_layout ()
1749 if (_previous_layout) {
1750 set_current_layout (_previous_layout);
1755 Push2::request_pressure_mode ()
1757 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1762 Push2::set_pressure_mode (PressureMode pm)
1764 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1768 /* nothing to do, message is correct */
1778 cerr << "Sent PM message " << msg << endl;