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 DEBUG_TRACE (DEBUG::Push2, "push2 control surface object being destroyed\n");
138 /* do this before stopping the event loop, so that we don't get any notifications */
139 selection_connection.disconnect ();
140 port_reg_connection.disconnect ();
141 port_connection.disconnect ();
143 stop_using_device ();
147 if (_current_layout) {
148 _canvas->root()->remove (_current_layout);
156 delete splash_layout;
158 delete track_mix_layout;
159 track_mix_layout = 0;
166 Push2::run_event_loop ()
168 DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
173 Push2::stop_event_loop ()
175 DEBUG_TRACE (DEBUG::Push2, "stop event loop\n");
180 Push2::begin_using_device ()
182 DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
184 /* set up periodic task used to push a frame buffer to the
185 * device (25fps). The device can handle 60fps, but we don't
186 * need that frame rate.
189 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
190 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
191 vblank_timeout->attach (main_loop()->get_context());
193 connect_session_signals ();
197 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
200 /* catch current selection, if any so that we can wire up the pads if appropriate */
202 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
203 stripable_selection_change (sp);
206 request_pressure_mode ();
214 Push2::stop_using_device ()
216 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
219 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
223 init_buttons (false);
224 strip_buttons_off ();
226 vblank_connection.disconnect ();
227 session_connections.drop_connections ();
234 Push2::ports_acquire ()
236 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
240 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
241 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
243 if (_async_in == 0 || _async_out == 0) {
244 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
248 /* We do not add our ports to the input/output bundles because we don't
249 * want users wiring them by hand. They could use JACK tools if they
250 * really insist on that (and use JACK)
253 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
254 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
256 /* Create a shadow port where, depending on the state of the surface,
257 * we will make pad note on/off events appear. The surface code will
258 * automatically this port to the first selected MIDI track.
261 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));
262 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
266 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
268 _output_bundle->add_channel (
270 ARDOUR::DataType::MIDI,
271 session->engine().make_port_name_non_relative (shadow_port->name())
275 session->BundleAddedOrRemoved ();
277 connect_to_parser ();
279 /* Connect input port to event loop */
283 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
284 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
285 asp->xthread().attach (main_loop()->get_context());
291 Push2::ports_release ()
293 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
295 /* wait for button data to be flushed */
297 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
298 asp->drain (10000, 500000);
300 AudioEngine::instance()->unregister_port (_async_in);
301 AudioEngine::instance()->unregister_port (_async_out);
303 _async_in.reset ((ARDOUR::Port*) 0);
304 _async_out.reset ((ARDOUR::Port*) 0);
310 Push2::device_acquire ()
314 DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
317 DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
322 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
323 DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
327 if ((err = libusb_claim_interface (handle, 0x00))) {
328 DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
329 libusb_close (handle);
338 Push2::device_release ()
340 DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
342 libusb_release_interface (handle, 0x00);
343 libusb_close (handle);
348 list<boost::shared_ptr<ARDOUR::Bundle> >
351 list<boost::shared_ptr<ARDOUR::Bundle> > b;
353 if (_output_bundle) {
354 b.push_back (_output_bundle);
361 Push2::strip_buttons_off ()
363 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
364 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
366 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
367 Button* b = id_button_map[strip_buttons[n]];
369 b->set_color (LED::Black);
370 b->set_state (LED::OneShot24th);
371 write (b->state_msg());
377 Push2::init_buttons (bool startup)
379 /* This is a list of buttons that we want lit because they do something
380 in ardour related (loosely, sometimes) to their illuminated label.
383 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
384 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
385 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
388 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
389 Button* b = id_button_map[buttons[n]];
392 b->set_color (LED::White);
394 b->set_color (LED::Black);
396 b->set_state (LED::OneShot24th);
397 write (b->state_msg());
402 /* all other buttons are off (black) */
404 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
405 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
406 Accent, Note, Session, };
408 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
409 Button* b = id_button_map[off_buttons[n]];
411 b->set_color (LED::Black);
412 b->set_state (LED::OneShot24th);
413 write (b->state_msg());
418 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
419 Pad* pad = pi->second;
421 pad->set_color (LED::Black);
422 pad->set_state (LED::OneShot24th);
423 write (pad->state_msg());
435 Push2::request_factory (uint32_t num_requests)
437 /* AbstractUI<T>::request_buffer_factory() is a template method only
438 instantiated in this source module. To provide something visible for
439 use in the interface/descriptor, we have this static method that is
442 return request_buffer_factory (num_requests);
446 Push2::do_request (Push2Request * req)
448 if (req->type == CallSlot) {
450 call_slot (MISSING_INVALIDATOR, req->the_slot);
452 } else if (req->type == Quit) {
454 stop_using_device ();
461 set_current_layout (splash_layout);
462 splash_start = get_microseconds ();
470 /* display splash for 2 seconds */
472 if (get_microseconds() - splash_start > 2000000) {
474 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
475 set_current_layout (mix_layout);
479 if (_current_layout) {
480 _current_layout->update_meters ();
481 _current_layout->update_clocks ();
490 Push2::set_active (bool yn)
492 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
494 if (yn == active()) {
500 if (device_acquire ()) {
504 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
505 begin_using_device ();
507 /* begin_using_device () will get called once we're connected */
511 /* Control Protocol Manager never calls us with false, but
512 * insteads destroys us.
516 ControlProtocol::set_active (yn);
518 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
524 Push2::init_touch_strip ()
526 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
527 /* flags are the final byte (ignore end-of-sysex */
529 /* show bar, not point
533 msg[7] = (1<<4) | (1<<5) | (1<<6);
538 Push2::write (const MidiByteArray& data)
540 /* immediate delivery */
541 _output_port->write (&data[0], data.size(), 0);
545 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
548 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
554 DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
556 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
561 DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
563 framepos_t now = AudioEngine::instance()->sample_time();
572 Push2::connect_to_parser ()
574 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
576 MIDI::Parser* p = _input_port->parser();
579 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
580 /* V-Pot messages are Controller */
581 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
582 /* Button messages are NoteOn */
583 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
584 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
585 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
586 /* Fader messages are Pitchbend */
587 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
591 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
593 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
599 MidiByteArray msg (sz, raw_bytes);
600 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
602 if (!push2_sysex_header.compare_n (msg, 6)) {
607 case 0x1f: /* pressure mode */
609 _pressure_mode = AfterTouch;
610 PressureModeChange (AfterTouch);
611 cerr << "Pressure mode is after\n";
613 _pressure_mode = PolyPressure;
614 PressureModeChange (PolyPressure);
615 cerr << "Pressure mode is poly\n";
622 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
624 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
626 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
629 /* any press cancels any pending long press timeouts */
630 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
631 Button* bb = id_button_map[*x];
632 bb->timeout_connection.disconnect ();
636 if (b != cc_button_map.end()) {
638 Button* button = b->second;
641 buttons_down.insert (button->id);
642 start_press_timeout (*button, button->id);
644 buttons_down.erase (button->id);
645 button->timeout_connection.disconnect ();
649 set<ButtonID>::iterator c = consumed.find (button->id);
651 if (c == consumed.end()) {
652 if (ev->value == 0) {
653 (this->*button->release_method)();
655 (this->*button->press_method)();
658 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
666 int delta = ev->value;
669 delta = -(128 - delta);
672 switch (ev->controller_number) {
674 _current_layout->strip_vpot (0, delta);
677 _current_layout->strip_vpot (1, delta);
680 _current_layout->strip_vpot (2, delta);
683 _current_layout->strip_vpot (3, delta);
686 _current_layout->strip_vpot (4, delta);
689 _current_layout->strip_vpot (5, delta);
692 _current_layout->strip_vpot (6, delta);
695 _current_layout->strip_vpot (7, delta);
700 other_vpot (8, delta);
703 other_vpot (1, delta);
708 other_vpot (2, delta);
715 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
717 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
719 if (ev->velocity == 0) {
720 handle_midi_note_off_message (parser, ev);
724 switch (ev->note_number) {
726 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
729 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
732 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
735 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
738 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
741 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
744 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
747 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
752 other_vpot_touch (0, ev->velocity > 64);
755 other_vpot_touch (1, ev->velocity > 64);
760 other_vpot_touch (3, ev->velocity > 64);
765 if (ev->velocity < 64) {
771 if (ev->note_number < 11) {
775 /* Pad illuminations */
777 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
779 if (pm == nn_pad_map.end()) {
783 const Pad * const pad_pressed = pm->second;
785 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
787 if (pads_with_note.first == fn_pad_map.end()) {
791 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
792 Pad* pad = pi->second;
794 pad->set_color (contrast_color);
795 pad->set_state (LED::OneShot24th);
796 write (pad->state_msg());
801 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
803 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
805 if (ev->note_number < 11) {
806 /* theoretically related to encoder touch start/end, but
807 * actually they send note on with two different velocity
813 /* Pad illuminations */
815 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
817 if (pm == nn_pad_map.end()) {
821 const Pad * const pad_pressed = pm->second;
823 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
825 if (pads_with_note.first == fn_pad_map.end()) {
829 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
830 Pad* pad = pi->second;
832 if (pad->do_when_pressed == Pad::FlashOn) {
833 pad->set_color (LED::Black);
834 pad->set_state (LED::OneShot24th);
835 write (pad->state_msg());
836 } else if (pad->do_when_pressed == Pad::FlashOff) {
837 pad->set_color (pad->perma_color);
838 pad->set_state (LED::OneShot24th);
839 write (pad->state_msg());
845 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
850 Push2::thread_init ()
852 struct sched_param rtparam;
854 pthread_set_name (event_loop_name().c_str());
856 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
857 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
859 memset (&rtparam, 0, sizeof (rtparam));
860 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
862 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
863 // do we care? not particularly.
868 Push2::connect_session_signals()
870 // receive routes added
871 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
872 // receive VCAs added
873 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
875 // receive record state toggled
876 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
877 // receive transport state changed
878 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
879 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
880 // receive punch-in and punch-out
881 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
882 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
883 // receive rude solo changed
884 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
888 Push2::notify_record_state_changed ()
890 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
892 if (b == id_button_map.end()) {
896 switch (session->record_status ()) {
897 case Session::Disabled:
898 b->second->set_color (LED::White);
899 b->second->set_state (LED::NoTransition);
901 case Session::Enabled:
902 b->second->set_color (LED::Red);
903 b->second->set_state (LED::Blinking4th);
905 case Session::Recording:
906 b->second->set_color (LED::Red);
907 b->second->set_state (LED::OneShot24th);
911 write (b->second->state_msg());
915 Push2::notify_transport_state_changed ()
917 Button* b = id_button_map[Play];
919 if (session->transport_rolling()) {
920 b->set_state (LED::OneShot24th);
921 b->set_color (LED::Green);
924 /* disable any blink on FixedLength from pending edit range op */
925 Button* fl = id_button_map[FixedLength];
927 fl->set_color (LED::Black);
928 fl->set_state (LED::NoTransition);
929 write (fl->state_msg());
931 b->set_color (LED::White);
932 b->set_state (LED::NoTransition);
935 write (b->state_msg());
939 Push2::notify_loop_state_changed ()
944 Push2::notify_parameter_changed (std::string param)
946 IDButtonMap::iterator b;
948 if (param == "clicking") {
949 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
952 if (Config->get_clicking()) {
953 b->second->set_state (LED::Blinking4th);
954 b->second->set_color (LED::White);
956 b->second->set_color (LED::White);
957 b->second->set_state (LED::NoTransition);
959 write (b->second->state_msg ());
964 Push2::notify_solo_active_changed (bool yn)
966 IDButtonMap::iterator b = id_button_map.find (Solo);
968 if (b == id_button_map.end()) {
973 b->second->set_state (LED::Blinking4th);
974 b->second->set_color (LED::Red);
976 b->second->set_state (LED::NoTransition);
977 b->second->set_color (LED::White);
980 write (b->second->state_msg());
986 XMLNode& node (ControlProtocol::get_state());
989 child = new XMLNode (X_("Input"));
990 child->add_child_nocopy (_async_in->get_state());
991 node.add_child_nocopy (*child);
992 child = new XMLNode (X_("Output"));
993 child->add_child_nocopy (_async_out->get_state());
994 node.add_child_nocopy (*child);
996 node.add_property (X_("root"), to_string (_scale_root, std::dec));
997 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
998 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
999 node.add_property (X_("mode"), enum_2_string (_mode));
1005 Push2::set_state (const XMLNode & node, int version)
1007 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1011 if (ControlProtocol::set_state (node, version)) {
1017 if ((child = node.child (X_("Input"))) != 0) {
1018 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1020 _async_in->set_state (*portnode, version);
1024 if ((child = node.child (X_("Output"))) != 0) {
1025 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1027 _async_out->set_state (*portnode, version);
1031 XMLProperty const* prop;
1033 if ((prop = node.property (X_("root"))) != 0) {
1034 _scale_root = atoi (prop->value());
1037 if ((prop = node.property (X_("root_octave"))) != 0) {
1038 _root_octave = atoi (prop->value());
1041 if ((prop = node.property (X_("in_key"))) != 0) {
1042 _in_key = string_is_affirmative (prop->value());
1045 if ((prop = node.property (X_("mode"))) != 0) {
1046 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1053 Push2::other_vpot (int n, int delta)
1055 boost::shared_ptr<Amp> click_gain;
1061 /* metronome gain control */
1062 click_gain = session->click_gain();
1064 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1066 ac->set_value (ac->interface_to_internal (
1067 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1068 PBD::Controllable::UseGroup);
1073 /* master gain control */
1075 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1077 ac->set_value (ac->interface_to_internal (
1078 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1079 PBD::Controllable::UseGroup);
1087 Push2::other_vpot_touch (int n, bool touching)
1096 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1099 ac->start_touch (session->audible_frame());
1101 ac->stop_touch (true, session->audible_frame());
1109 Push2::start_shift ()
1111 cerr << "start shift\n";
1112 _modifier_state = ModifierState (_modifier_state | ModShift);
1113 Button* b = id_button_map[Shift];
1114 b->set_color (LED::White);
1115 b->set_state (LED::Blinking16th);
1116 write (b->state_msg());
1122 if (_modifier_state & ModShift) {
1123 cerr << "end shift\n";
1124 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1125 Button* b = id_button_map[Shift];
1126 b->timeout_connection.disconnect ();
1127 b->set_color (LED::White);
1128 b->set_state (LED::OneShot24th);
1129 write (b->state_msg());
1134 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1136 /* This filter is called asynchronously from a realtime process
1137 context. It must use atomics to check state, and must not block.
1140 bool matched = false;
1142 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1143 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1145 /* encoder touch start/touch end use note
1146 * 0-10. touchstrip uses note 12
1149 if ((*ev).note() > 10 && (*ev).note() != 12) {
1151 const int n = (*ev).note ();
1152 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1154 if (nni != nn_pad_map.end()) {
1155 Pad const * pad = nni->second;
1156 /* shift for output to the shadow port */
1157 if (pad->filtered >= 0) {
1158 (*ev).set_note (pad->filtered + (octave_shift*12));
1159 out.push_back (*ev);
1160 /* shift back so that the pads light correctly */
1163 /* no mapping, don't send event */
1166 out.push_back (*ev);
1171 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1172 out.push_back (*ev);
1180 Push2::port_registration_handler ()
1182 if (!_async_in && !_async_out) {
1183 /* ports not registered yet */
1187 if (_async_in->connected() && _async_out->connected()) {
1188 /* don't waste cycles here */
1193 /* the origin of the numeric magic identifiers is known only to Ableton
1194 and may change in time. This is part of how CoreMIDI works.
1196 string input_port_name = X_("system:midi_capture_1319078870");
1197 string output_port_name = X_("system:midi_playback_3409210341");
1199 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1200 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1205 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1206 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1208 if (!in.empty() && !out.empty()) {
1209 cerr << "Push2: both ports found\n";
1210 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1211 if (!_async_in->connected()) {
1212 AudioEngine::instance()->connect (_async_in->name(), in.front());
1214 if (!_async_out->connected()) {
1215 AudioEngine::instance()->connect (_async_out->name(), out.front());
1221 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1223 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1224 if (!_input_port || !_output_port) {
1228 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1229 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1231 if (ni == name1 || ni == name2) {
1233 connection_state |= InputConnected;
1235 connection_state &= ~InputConnected;
1237 } else if (no == name1 || no == name2) {
1239 connection_state |= OutputConnected;
1241 connection_state &= ~OutputConnected;
1244 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1249 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1252 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1254 /* XXX this is a horrible hack. Without a short sleep here,
1255 something prevents the device wakeup messages from being
1256 sent and/or the responses from being received.
1260 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1262 /* may not have the device open if it was just plugged
1263 in. Really need USB device detection rather than MIDI port
1264 detection for this to work well.
1268 begin_using_device ();
1271 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1272 stop_using_device ();
1275 ConnectionChange (); /* emit signal for our GUI */
1277 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1279 return true; /* connection status changed */
1282 boost::shared_ptr<Port>
1283 Push2::output_port()
1288 boost::shared_ptr<Port>
1295 Push2::pad_note (int row, int col) const
1297 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1299 if (nni != nn_pad_map.end()) {
1300 return nni->second->filtered;
1307 Push2::update_selection_color ()
1309 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1311 if (!current_midi_track) {
1315 selection_color = get_color_index (current_midi_track->presentation_info().color());
1316 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1318 reset_pad_colors ();
1322 Push2::reset_pad_colors ()
1324 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1328 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1330 MusicalMode m (mode);
1331 vector<float>::iterator interval;
1333 const int original_root = root;
1335 interval = m.steps.begin();
1336 root += (octave*12);
1339 const int root_start = root;
1341 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1342 vector<int> mode_vector; /* sorted in note order */
1344 mode_map.insert (note);
1345 mode_vector.push_back (note);
1347 /* build a map of all notes in the mode, from the root to 127 */
1349 while (note < 128) {
1351 if (interval == m.steps.end()) {
1353 /* last distance was the end of the scale,
1354 so wrap, adding the next note at one
1355 octave above the last root.
1358 interval = m.steps.begin();
1360 mode_map.insert (root);
1361 mode_vector.push_back (root);
1364 note = (int) floor (root + (2.0 * (*interval)));
1366 mode_map.insert (note);
1367 mode_vector.push_back (note);
1371 fn_pad_map.clear ();
1375 vector<int>::iterator notei;
1378 for (int row = 0; row < 8; ++row) {
1380 /* Ableton's grid layout wraps the available notes in the scale
1381 * by offsetting 3 notes per row (from the bottom)
1384 notei = mode_vector.begin();
1385 notei += row_offset;
1388 for (int col = 0; col < 8; ++col) {
1389 int index = 36 + (row*8) + col;
1390 Pad* pad = nn_pad_map[index];
1392 if (notei != mode_vector.end()) {
1395 pad->filtered = notenum;
1397 fn_pad_map.insert (make_pair (notenum, pad));
1399 if ((notenum % 12) == original_root) {
1400 pad->set_color (selection_color);
1401 pad->perma_color = selection_color;
1403 pad->set_color (LED::White);
1404 pad->perma_color = LED::White;
1407 pad->do_when_pressed = Pad::FlashOff;
1412 pad->set_color (LED::Black);
1413 pad->do_when_pressed = Pad::Nothing;
1417 pad->set_state (LED::OneShot24th);
1418 write (pad->state_msg());
1424 /* chromatic: all notes available, but highlight those in the scale */
1426 for (note = 36; note < 100; ++note) {
1428 Pad* pad = nn_pad_map[note];
1430 /* Chromatic: all pads play, half-tone steps. Light
1431 * those in the scale, and highlight root notes
1434 pad->filtered = root_start + (note - 36);
1436 fn_pad_map.insert (make_pair (pad->filtered, pad));
1438 if (mode_map.find (note) != mode_map.end()) {
1440 if ((note % 12) == original_root) {
1441 pad->set_color (selection_color);
1442 pad->perma_color = selection_color;
1444 pad->set_color (LED::White);
1445 pad->perma_color = LED::White;
1448 pad->do_when_pressed = Pad::FlashOff;
1452 /* note is not in mode, turn it off */
1454 pad->do_when_pressed = Pad::FlashOn;
1455 pad->set_color (LED::Black);
1459 pad->set_state (LED::OneShot24th);
1460 write (pad->state_msg());
1466 bool changed = false;
1468 if (_scale_root != original_root) {
1469 _scale_root = original_root;
1472 if (_root_octave != octave) {
1473 _root_octave = octave;
1476 if (_in_key != inkey) {
1480 if (_mode != mode) {
1486 ScaleChange (); /* EMIT SIGNAL */
1491 Push2::set_percussive_mode (bool yn)
1494 cerr << "back to scale\n";
1495 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1502 fn_pad_map.clear ();
1504 for (int row = 0; row < 8; ++row) {
1506 for (int col = 0; col < 4; ++col) {
1508 int index = 36 + (row*8) + col;
1509 Pad* pad = nn_pad_map[index];
1511 pad->filtered = drum_note;
1516 for (int row = 0; row < 8; ++row) {
1518 for (int col = 4; col < 8; ++col) {
1520 int index = 36 + (row*8) + col;
1521 Pad* pad = nn_pad_map[index];
1523 pad->filtered = drum_note;
1532 Push2::current_layout () const
1534 Glib::Threads::Mutex::Lock lm (layout_lock);
1535 return _current_layout;
1539 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1541 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1542 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1543 boost::shared_ptr<MidiTrack> new_pad_target;
1545 /* See if there's a MIDI track selected */
1547 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1549 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1551 if (new_pad_target) {
1556 if (current_midi_track == new_pad_target) {
1561 if (!new_pad_target) {
1562 /* leave existing connection alone */
1566 /* disconnect from pad port, if appropriate */
1568 if (current_midi_track && pad_port) {
1570 /* XXX this could possibly leave dangling MIDI notes.
1572 * A general libardour fix is required. It isn't obvious
1573 * how note resolution can be done unless disconnecting
1574 * becomes "slow" (i.e. deferred for as long as it takes
1575 * to resolve notes).
1577 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1580 /* now connect the pad port to this (newly) selected midi
1581 * track, if indeed there is one.
1584 if (new_pad_target && pad_port) {
1585 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1586 current_pad_target = new_pad_target;
1587 selection_color = get_color_index (new_pad_target->presentation_info().color());
1588 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1590 current_pad_target.reset ();
1591 selection_color = LED::Green;
1592 contrast_color = LED::Green;
1595 reset_pad_colors ();
1599 Push2::button_by_id (ButtonID bid)
1601 return id_button_map[bid];
1605 Push2::get_color_index (ArdourCanvas::Color rgba)
1607 ColorMap::iterator i = color_map.find (rgba);
1609 if (i != color_map.end()) {
1613 double dr, dg, db, da;
1615 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1616 int w = 126; /* not sure where/when we should get this value */
1619 r = (int) floor (255.0 * dr);
1620 g = (int) floor (255.0 * dg);
1621 b = (int) floor (255.0 * db);
1623 /* get a free index */
1627 if (color_map_free_list.empty()) {
1628 /* random replacement of any entry above zero and below 122 (where the
1629 * Ableton standard colors live)
1631 index = 1 + (random() % 121);
1633 index = color_map_free_list.top();
1634 color_map_free_list.pop();
1637 MidiByteArray palette_msg (17,
1639 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1640 0x00, /* index = 7 */
1641 0x00, 0x00, /* r = 8 & 9 */
1642 0x00, 0x00, /* g = 10 & 11 */
1643 0x00, 0x00, /* b = 12 & 13 */
1644 0x00, 0x00, /* w (a?) = 14 & 15*/
1646 palette_msg[7] = index;
1647 palette_msg[8] = r & 0x7f;
1648 palette_msg[9] = (r & 0x80) >> 7;
1649 palette_msg[10] = g & 0x7f;
1650 palette_msg[11] = (g & 0x80) >> 7;
1651 palette_msg[12] = b & 0x7f;
1652 palette_msg[13] = (b & 0x80) >> 7;
1653 palette_msg[14] = w & 0x7f;
1654 palette_msg[15] = w & 0x80;
1656 write (palette_msg);
1658 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1659 write (update_pallette_msg);
1661 color_map[rgba] = index;
1667 Push2::build_color_map ()
1669 /* These are "standard" colors that Ableton docs suggest will always be
1670 there. Put them in our color map so that when we look up these
1671 colors, we will use the Ableton indices for them.
1674 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1675 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1676 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1677 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1678 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1679 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1680 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1682 for (uint8_t n = 1; n < 122; ++n) {
1683 color_map_free_list.push (n);
1688 Push2::fill_color_table ()
1690 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1691 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1693 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1695 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1696 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1697 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1699 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1700 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1702 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1703 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1704 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1705 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1710 Push2::get_color (ColorName name)
1712 Colors::iterator c = colors.find (name);
1713 if (c != colors.end()) {
1721 Push2::set_current_layout (Push2Layout* layout)
1723 if (layout && layout == _current_layout) {
1724 _current_layout->show ();
1727 if (_current_layout) {
1728 _current_layout->hide ();
1729 _canvas->root()->remove (_current_layout);
1730 _previous_layout = _current_layout;
1733 _current_layout = layout;
1735 if (_current_layout) {
1736 _canvas->root()->add (_current_layout);
1737 _current_layout->show ();
1741 _canvas->request_redraw ();
1746 Push2::use_previous_layout ()
1748 if (_previous_layout) {
1749 set_current_layout (_previous_layout);
1754 Push2::request_pressure_mode ()
1756 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1761 Push2::set_pressure_mode (PressureMode pm)
1763 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1767 /* nothing to do, message is correct */
1777 cerr << "Sent PM message " << msg << endl;