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"
43 #include "ardour/types_convert.h"
45 #include "gtkmm2ext/gui_thread.h"
46 #include "gtkmm2ext/rgb_macros.h"
48 #include "gtkmm2ext/colors.h"
58 #include "track_mix.h"
62 #ifdef PLATFORM_WINDOWS
63 #define random() rand()
66 using namespace ARDOUR;
70 using namespace ArdourSurface;
71 using namespace Gtkmm2ext;
73 #include "pbd/abstract_ui.cc" // instantiate template
75 #define ABLETON 0x2982
78 Push2::Push2 (ARDOUR::Session& s)
79 : ControlProtocol (s, string (X_("Ableton Push 2")))
80 , AbstractUI<Push2Request> (name())
83 , _modifier_state (None)
86 , _previous_layout (0)
87 , connection_state (ConnectionState (0))
89 , _mode (MusicalMode::IonianMajor)
95 , _pressure_mode (AfterTouch)
96 , selection_color (LED::Green)
97 , contrast_color (LED::Green)
98 , in_range_select (false)
100 /* we're going to need this */
108 /* master cannot be removed, so no need to connect to going-away signal */
109 master = session->master_out ();
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 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 */
201 stripable_selection_changed ();
203 request_pressure_mode ();
211 Push2::stop_using_device ()
213 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
216 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
220 init_buttons (false);
221 strip_buttons_off ();
223 vblank_connection.disconnect ();
224 session_connections.drop_connections ();
231 Push2::ports_acquire ()
233 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
237 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
238 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
240 if (_async_in == 0 || _async_out == 0) {
241 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
245 /* We do not add our ports to the input/output bundles because we don't
246 * want users wiring them by hand. They could use JACK tools if they
247 * really insist on that (and use JACK)
250 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
251 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
253 /* Create a shadow port where, depending on the state of the surface,
254 * we will make pad note on/off events appear. The surface code will
255 * automatically this port to the first selected MIDI track.
258 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));
259 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
263 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
265 _output_bundle->add_channel (
267 ARDOUR::DataType::MIDI,
268 session->engine().make_port_name_non_relative (shadow_port->name())
272 session->BundleAddedOrRemoved ();
274 connect_to_parser ();
276 /* Connect input port to event loop */
280 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
281 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
282 asp->xthread().attach (main_loop()->get_context());
288 Push2::ports_release ()
290 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
292 /* wait for button data to be flushed */
294 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
295 asp->drain (10000, 500000);
297 AudioEngine::instance()->unregister_port (_async_in);
298 AudioEngine::instance()->unregister_port (_async_out);
300 _async_in.reset ((ARDOUR::Port*) 0);
301 _async_out.reset ((ARDOUR::Port*) 0);
307 Push2::device_acquire ()
311 DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
314 DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
319 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
320 DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
324 if ((err = libusb_claim_interface (handle, 0x00))) {
325 DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
326 libusb_close (handle);
335 Push2::device_release ()
337 DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
339 libusb_release_interface (handle, 0x00);
340 libusb_close (handle);
345 list<boost::shared_ptr<ARDOUR::Bundle> >
348 list<boost::shared_ptr<ARDOUR::Bundle> > b;
350 if (_output_bundle) {
351 b.push_back (_output_bundle);
358 Push2::strip_buttons_off ()
360 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
361 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
363 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
364 Button* b = id_button_map[strip_buttons[n]];
366 b->set_color (LED::Black);
367 b->set_state (LED::OneShot24th);
368 write (b->state_msg());
374 Push2::init_buttons (bool startup)
376 /* This is a list of buttons that we want lit because they do something
377 in ardour related (loosely, sometimes) to their illuminated label.
380 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
381 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
382 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
385 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
386 Button* b = id_button_map[buttons[n]];
389 b->set_color (LED::White);
391 b->set_color (LED::Black);
393 b->set_state (LED::OneShot24th);
394 write (b->state_msg());
399 /* all other buttons are off (black) */
401 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
402 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
403 Accent, Note, Session, };
405 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
406 Button* b = id_button_map[off_buttons[n]];
408 b->set_color (LED::Black);
409 b->set_state (LED::OneShot24th);
410 write (b->state_msg());
415 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
416 Pad* pad = pi->second;
418 pad->set_color (LED::Black);
419 pad->set_state (LED::OneShot24th);
420 write (pad->state_msg());
432 Push2::request_factory (uint32_t num_requests)
434 /* AbstractUI<T>::request_buffer_factory() is a template method only
435 instantiated in this source module. To provide something visible for
436 use in the interface/descriptor, we have this static method that is
439 return request_buffer_factory (num_requests);
443 Push2::do_request (Push2Request * req)
445 if (req->type == CallSlot) {
447 call_slot (MISSING_INVALIDATOR, req->the_slot);
449 } else if (req->type == Quit) {
451 stop_using_device ();
458 set_current_layout (splash_layout);
459 splash_start = get_microseconds ();
467 /* display splash for 2 seconds */
469 if (get_microseconds() - splash_start > 2000000) {
471 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
472 set_current_layout (mix_layout);
476 if (_current_layout) {
477 _current_layout->update_meters ();
478 _current_layout->update_clocks ();
487 Push2::set_active (bool yn)
489 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
491 if (yn == active()) {
497 if (device_acquire ()) {
501 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
502 begin_using_device ();
504 /* begin_using_device () will get called once we're connected */
508 /* Control Protocol Manager never calls us with false, but
509 * insteads destroys us.
513 ControlProtocol::set_active (yn);
515 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
521 Push2::init_touch_strip ()
523 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
524 /* flags are the final byte (ignore end-of-sysex */
526 /* show bar, not point
530 msg[7] = (1<<4) | (1<<5) | (1<<6);
535 Push2::write (const MidiByteArray& data)
537 /* immediate delivery */
538 _output_port->write (&data[0], data.size(), 0);
542 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
545 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
551 DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
553 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
558 DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
560 framepos_t now = AudioEngine::instance()->sample_time();
569 Push2::connect_to_parser ()
571 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
573 MIDI::Parser* p = _input_port->parser();
576 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
577 /* V-Pot messages are Controller */
578 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
579 /* Button messages are NoteOn */
580 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
581 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
582 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
583 /* Fader messages are Pitchbend */
584 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
588 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
590 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
596 MidiByteArray msg (sz, raw_bytes);
597 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
599 if (!push2_sysex_header.compare_n (msg, 6)) {
604 case 0x1f: /* pressure mode */
606 _pressure_mode = AfterTouch;
607 PressureModeChange (AfterTouch);
608 cerr << "Pressure mode is after\n";
610 _pressure_mode = PolyPressure;
611 PressureModeChange (PolyPressure);
612 cerr << "Pressure mode is poly\n";
619 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
621 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
623 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
626 /* any press cancels any pending long press timeouts */
627 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
628 Button* bb = id_button_map[*x];
629 bb->timeout_connection.disconnect ();
633 if (b != cc_button_map.end()) {
635 Button* button = b->second;
638 buttons_down.insert (button->id);
639 start_press_timeout (*button, button->id);
641 buttons_down.erase (button->id);
642 button->timeout_connection.disconnect ();
646 set<ButtonID>::iterator c = consumed.find (button->id);
648 if (c == consumed.end()) {
649 if (ev->value == 0) {
650 (this->*button->release_method)();
652 (this->*button->press_method)();
655 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
663 int delta = ev->value;
666 delta = -(128 - delta);
669 switch (ev->controller_number) {
671 _current_layout->strip_vpot (0, delta);
674 _current_layout->strip_vpot (1, delta);
677 _current_layout->strip_vpot (2, delta);
680 _current_layout->strip_vpot (3, delta);
683 _current_layout->strip_vpot (4, delta);
686 _current_layout->strip_vpot (5, delta);
689 _current_layout->strip_vpot (6, delta);
692 _current_layout->strip_vpot (7, delta);
697 other_vpot (8, delta);
700 other_vpot (1, delta);
705 other_vpot (2, delta);
712 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
714 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
716 if (ev->velocity == 0) {
717 handle_midi_note_off_message (parser, ev);
721 switch (ev->note_number) {
723 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
726 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
729 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
732 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
735 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
738 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
741 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
744 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
749 other_vpot_touch (0, ev->velocity > 64);
752 other_vpot_touch (1, ev->velocity > 64);
757 other_vpot_touch (3, ev->velocity > 64);
762 if (ev->velocity < 64) {
768 if (ev->note_number < 11) {
772 /* Pad illuminations */
774 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
776 if (pm == nn_pad_map.end()) {
780 const Pad * const pad_pressed = pm->second;
782 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
784 if (pads_with_note.first == fn_pad_map.end()) {
788 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
789 Pad* pad = pi->second;
791 pad->set_color (contrast_color);
792 pad->set_state (LED::OneShot24th);
793 write (pad->state_msg());
798 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
800 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
802 if (ev->note_number < 11) {
803 /* theoretically related to encoder touch start/end, but
804 * actually they send note on with two different velocity
810 /* Pad illuminations */
812 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
814 if (pm == nn_pad_map.end()) {
818 const Pad * const pad_pressed = pm->second;
820 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
822 if (pads_with_note.first == fn_pad_map.end()) {
826 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
827 Pad* pad = pi->second;
829 if (pad->do_when_pressed == Pad::FlashOn) {
830 pad->set_color (LED::Black);
831 pad->set_state (LED::OneShot24th);
832 write (pad->state_msg());
833 } else if (pad->do_when_pressed == Pad::FlashOff) {
834 pad->set_color (pad->perma_color);
835 pad->set_state (LED::OneShot24th);
836 write (pad->state_msg());
842 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
847 Push2::thread_init ()
849 struct sched_param rtparam;
851 pthread_set_name (event_loop_name().c_str());
853 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
854 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
856 memset (&rtparam, 0, sizeof (rtparam));
857 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
859 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
860 // do we care? not particularly.
865 Push2::connect_session_signals()
867 // receive routes added
868 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
869 // receive VCAs added
870 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
872 // receive record state toggled
873 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
874 // receive transport state changed
875 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
876 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
877 // receive punch-in and punch-out
878 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
879 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
880 // receive rude solo changed
881 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
885 Push2::notify_record_state_changed ()
887 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
889 if (b == id_button_map.end()) {
893 switch (session->record_status ()) {
894 case Session::Disabled:
895 b->second->set_color (LED::White);
896 b->second->set_state (LED::NoTransition);
898 case Session::Enabled:
899 b->second->set_color (LED::Red);
900 b->second->set_state (LED::Blinking4th);
902 case Session::Recording:
903 b->second->set_color (LED::Red);
904 b->second->set_state (LED::OneShot24th);
908 write (b->second->state_msg());
912 Push2::notify_transport_state_changed ()
914 Button* b = id_button_map[Play];
916 if (session->transport_rolling()) {
917 b->set_state (LED::OneShot24th);
918 b->set_color (LED::Green);
921 /* disable any blink on FixedLength from pending edit range op */
922 Button* fl = id_button_map[FixedLength];
924 fl->set_color (LED::Black);
925 fl->set_state (LED::NoTransition);
926 write (fl->state_msg());
928 b->set_color (LED::White);
929 b->set_state (LED::NoTransition);
932 write (b->state_msg());
936 Push2::notify_loop_state_changed ()
941 Push2::notify_parameter_changed (std::string param)
943 IDButtonMap::iterator b;
945 if (param == "clicking") {
946 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
949 if (Config->get_clicking()) {
950 b->second->set_state (LED::Blinking4th);
951 b->second->set_color (LED::White);
953 b->second->set_color (LED::White);
954 b->second->set_state (LED::NoTransition);
956 write (b->second->state_msg ());
961 Push2::notify_solo_active_changed (bool yn)
963 IDButtonMap::iterator b = id_button_map.find (Solo);
965 if (b == id_button_map.end()) {
970 b->second->set_state (LED::Blinking4th);
971 b->second->set_color (LED::Red);
973 b->second->set_state (LED::NoTransition);
974 b->second->set_color (LED::White);
977 write (b->second->state_msg());
983 XMLNode& node (ControlProtocol::get_state());
986 child = new XMLNode (X_("Input"));
987 child->add_child_nocopy (_async_in->get_state());
988 node.add_child_nocopy (*child);
989 child = new XMLNode (X_("Output"));
990 child->add_child_nocopy (_async_out->get_state());
991 node.add_child_nocopy (*child);
993 node.set_property (X_("root"), _scale_root);
994 node.set_property (X_("root-octave"), _root_octave);
995 node.set_property (X_("in-key"), _in_key);
996 node.set_property (X_("mode"), _mode);
1002 Push2::set_state (const XMLNode & node, int version)
1004 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1008 if (ControlProtocol::set_state (node, version)) {
1014 if ((child = node.child (X_("Input"))) != 0) {
1015 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1017 _async_in->set_state (*portnode, version);
1021 if ((child = node.child (X_("Output"))) != 0) {
1022 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1024 _async_out->set_state (*portnode, version);
1028 node.get_property (X_("root"), _scale_root);
1029 node.get_property (X_("root-octave"), _root_octave);
1030 node.get_property (X_("in-key"), _in_key);
1031 node.get_property (X_("mode"), _mode);
1037 Push2::other_vpot (int n, int delta)
1039 boost::shared_ptr<Amp> click_gain;
1045 /* metronome gain control */
1046 click_gain = session->click_gain();
1048 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1050 ac->set_value (ac->interface_to_internal (
1051 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1052 PBD::Controllable::UseGroup);
1057 /* master gain control */
1059 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1061 ac->set_value (ac->interface_to_internal (
1062 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1063 PBD::Controllable::UseGroup);
1071 Push2::other_vpot_touch (int n, bool touching)
1080 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1083 ac->start_touch (session->audible_frame());
1085 ac->stop_touch (session->audible_frame());
1093 Push2::start_shift ()
1095 cerr << "start shift\n";
1096 _modifier_state = ModifierState (_modifier_state | ModShift);
1097 Button* b = id_button_map[Shift];
1098 b->set_color (LED::White);
1099 b->set_state (LED::Blinking16th);
1100 write (b->state_msg());
1106 if (_modifier_state & ModShift) {
1107 cerr << "end shift\n";
1108 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1109 Button* b = id_button_map[Shift];
1110 b->timeout_connection.disconnect ();
1111 b->set_color (LED::White);
1112 b->set_state (LED::OneShot24th);
1113 write (b->state_msg());
1118 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1120 /* This filter is called asynchronously from a realtime process
1121 context. It must use atomics to check state, and must not block.
1124 bool matched = false;
1126 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1127 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1129 /* encoder touch start/touch end use note
1130 * 0-10. touchstrip uses note 12
1133 if ((*ev).note() > 10 && (*ev).note() != 12) {
1135 const int n = (*ev).note ();
1136 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1138 if (nni != nn_pad_map.end()) {
1139 Pad const * pad = nni->second;
1140 /* shift for output to the shadow port */
1141 if (pad->filtered >= 0) {
1142 (*ev).set_note (pad->filtered + (octave_shift*12));
1143 out.push_back (*ev);
1144 /* shift back so that the pads light correctly */
1147 /* no mapping, don't send event */
1150 out.push_back (*ev);
1155 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1156 out.push_back (*ev);
1164 Push2::port_registration_handler ()
1166 if (!_async_in && !_async_out) {
1167 /* ports not registered yet */
1171 if (_async_in->connected() && _async_out->connected()) {
1172 /* don't waste cycles here */
1177 /* the origin of the numeric magic identifiers is known only to Ableton
1178 and may change in time. This is part of how CoreMIDI works.
1180 string input_port_name = X_("system:midi_capture_1319078870");
1181 string output_port_name = X_("system:midi_playback_3409210341");
1183 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1184 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1189 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1190 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1192 if (!in.empty() && !out.empty()) {
1193 cerr << "Push2: both ports found\n";
1194 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1195 if (!_async_in->connected()) {
1196 AudioEngine::instance()->connect (_async_in->name(), in.front());
1198 if (!_async_out->connected()) {
1199 AudioEngine::instance()->connect (_async_out->name(), out.front());
1205 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1207 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1208 if (!_input_port || !_output_port) {
1212 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1213 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1215 if (ni == name1 || ni == name2) {
1217 connection_state |= InputConnected;
1219 connection_state &= ~InputConnected;
1221 } else if (no == name1 || no == name2) {
1223 connection_state |= OutputConnected;
1225 connection_state &= ~OutputConnected;
1228 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1233 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1236 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1238 /* XXX this is a horrible hack. Without a short sleep here,
1239 something prevents the device wakeup messages from being
1240 sent and/or the responses from being received.
1244 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1246 /* may not have the device open if it was just plugged
1247 in. Really need USB device detection rather than MIDI port
1248 detection for this to work well.
1252 begin_using_device ();
1255 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1256 stop_using_device ();
1259 ConnectionChange (); /* emit signal for our GUI */
1261 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1263 return true; /* connection status changed */
1266 boost::shared_ptr<Port>
1267 Push2::output_port()
1272 boost::shared_ptr<Port>
1279 Push2::pad_note (int row, int col) const
1281 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1283 if (nni != nn_pad_map.end()) {
1284 return nni->second->filtered;
1291 Push2::update_selection_color ()
1293 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1295 if (!current_midi_track) {
1299 selection_color = get_color_index (current_midi_track->presentation_info().color());
1300 contrast_color = get_color_index (Gtkmm2ext::HSV (current_midi_track->presentation_info().color()).opposite().color());
1302 reset_pad_colors ();
1306 Push2::reset_pad_colors ()
1308 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1312 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1314 MusicalMode m (mode);
1315 vector<float>::iterator interval;
1317 const int original_root = root;
1319 interval = m.steps.begin();
1320 root += (octave*12);
1323 const int root_start = root;
1325 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1326 vector<int> mode_vector; /* sorted in note order */
1328 mode_map.insert (note);
1329 mode_vector.push_back (note);
1331 /* build a map of all notes in the mode, from the root to 127 */
1333 while (note < 128) {
1335 if (interval == m.steps.end()) {
1337 /* last distance was the end of the scale,
1338 so wrap, adding the next note at one
1339 octave above the last root.
1342 interval = m.steps.begin();
1344 mode_map.insert (root);
1345 mode_vector.push_back (root);
1348 note = (int) floor (root + (2.0 * (*interval)));
1350 mode_map.insert (note);
1351 mode_vector.push_back (note);
1355 fn_pad_map.clear ();
1359 vector<int>::iterator notei;
1362 for (int row = 0; row < 8; ++row) {
1364 /* Ableton's grid layout wraps the available notes in the scale
1365 * by offsetting 3 notes per row (from the bottom)
1368 notei = mode_vector.begin();
1369 notei += row_offset;
1372 for (int col = 0; col < 8; ++col) {
1373 int index = 36 + (row*8) + col;
1374 Pad* pad = nn_pad_map[index];
1376 if (notei != mode_vector.end()) {
1379 pad->filtered = notenum;
1381 fn_pad_map.insert (make_pair (notenum, pad));
1383 if ((notenum % 12) == original_root) {
1384 pad->set_color (selection_color);
1385 pad->perma_color = selection_color;
1387 pad->set_color (LED::White);
1388 pad->perma_color = LED::White;
1391 pad->do_when_pressed = Pad::FlashOff;
1396 pad->set_color (LED::Black);
1397 pad->do_when_pressed = Pad::Nothing;
1401 pad->set_state (LED::OneShot24th);
1402 write (pad->state_msg());
1408 /* chromatic: all notes available, but highlight those in the scale */
1410 for (note = 36; note < 100; ++note) {
1412 Pad* pad = nn_pad_map[note];
1414 /* Chromatic: all pads play, half-tone steps. Light
1415 * those in the scale, and highlight root notes
1418 pad->filtered = root_start + (note - 36);
1420 fn_pad_map.insert (make_pair (pad->filtered, pad));
1422 if (mode_map.find (note) != mode_map.end()) {
1424 if ((note % 12) == original_root) {
1425 pad->set_color (selection_color);
1426 pad->perma_color = selection_color;
1428 pad->set_color (LED::White);
1429 pad->perma_color = LED::White;
1432 pad->do_when_pressed = Pad::FlashOff;
1436 /* note is not in mode, turn it off */
1438 pad->do_when_pressed = Pad::FlashOn;
1439 pad->set_color (LED::Black);
1443 pad->set_state (LED::OneShot24th);
1444 write (pad->state_msg());
1450 bool changed = false;
1452 if (_scale_root != original_root) {
1453 _scale_root = original_root;
1456 if (_root_octave != octave) {
1457 _root_octave = octave;
1460 if (_in_key != inkey) {
1464 if (_mode != mode) {
1470 ScaleChange (); /* EMIT SIGNAL */
1475 Push2::set_percussive_mode (bool yn)
1478 cerr << "back to scale\n";
1479 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1486 fn_pad_map.clear ();
1488 for (int row = 0; row < 8; ++row) {
1490 for (int col = 0; col < 4; ++col) {
1492 int index = 36 + (row*8) + col;
1493 Pad* pad = nn_pad_map[index];
1495 pad->filtered = drum_note;
1500 for (int row = 0; row < 8; ++row) {
1502 for (int col = 4; col < 8; ++col) {
1504 int index = 36 + (row*8) + col;
1505 Pad* pad = nn_pad_map[index];
1507 pad->filtered = drum_note;
1516 Push2::current_layout () const
1518 Glib::Threads::Mutex::Lock lm (layout_lock);
1519 return _current_layout;
1523 Push2::stripable_selection_changed ()
1525 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1526 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1527 boost::shared_ptr<MidiTrack> new_pad_target;
1528 StripableNotificationList const & selected (last_selected());
1530 /* See if there's a MIDI track selected */
1532 for (StripableNotificationList::const_iterator si = selected.begin(); si != selected.end(); ++si) {
1534 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1536 if (new_pad_target) {
1541 if (current_midi_track == new_pad_target) {
1546 if (!new_pad_target) {
1547 /* leave existing connection alone */
1551 /* disconnect from pad port, if appropriate */
1553 if (current_midi_track && pad_port) {
1555 /* XXX this could possibly leave dangling MIDI notes.
1557 * A general libardour fix is required. It isn't obvious
1558 * how note resolution can be done unless disconnecting
1559 * becomes "slow" (i.e. deferred for as long as it takes
1560 * to resolve notes).
1562 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1565 /* now connect the pad port to this (newly) selected midi
1566 * track, if indeed there is one.
1569 if (new_pad_target && pad_port) {
1570 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1571 current_pad_target = new_pad_target;
1572 selection_color = get_color_index (new_pad_target->presentation_info().color());
1573 contrast_color = get_color_index (Gtkmm2ext::HSV (new_pad_target->presentation_info().color()).opposite().color());
1575 current_pad_target.reset ();
1576 selection_color = LED::Green;
1577 contrast_color = LED::Green;
1580 reset_pad_colors ();
1582 TrackMixLayout* tml = dynamic_cast<TrackMixLayout*> (mix_layout);
1584 tml->set_stripable (first_selected_stripable());
1588 Push2::button_by_id (ButtonID bid)
1590 return id_button_map[bid];
1594 Push2::get_color_index (Color rgba)
1596 ColorMap::iterator i = color_map.find (rgba);
1598 if (i != color_map.end()) {
1602 double dr, dg, db, da;
1604 color_to_rgba (rgba, dr, dg, db, da);
1605 int w = 126; /* not sure where/when we should get this value */
1608 r = (int) floor (255.0 * dr);
1609 g = (int) floor (255.0 * dg);
1610 b = (int) floor (255.0 * db);
1612 /* get a free index */
1616 if (color_map_free_list.empty()) {
1617 /* random replacement of any entry above zero and below 122 (where the
1618 * Ableton standard colors live)
1620 index = 1 + (random() % 121);
1622 index = color_map_free_list.top();
1623 color_map_free_list.pop();
1626 MidiByteArray palette_msg (17,
1628 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1629 0x00, /* index = 7 */
1630 0x00, 0x00, /* r = 8 & 9 */
1631 0x00, 0x00, /* g = 10 & 11 */
1632 0x00, 0x00, /* b = 12 & 13 */
1633 0x00, 0x00, /* w (a?) = 14 & 15*/
1635 palette_msg[7] = index;
1636 palette_msg[8] = r & 0x7f;
1637 palette_msg[9] = (r & 0x80) >> 7;
1638 palette_msg[10] = g & 0x7f;
1639 palette_msg[11] = (g & 0x80) >> 7;
1640 palette_msg[12] = b & 0x7f;
1641 palette_msg[13] = (b & 0x80) >> 7;
1642 palette_msg[14] = w & 0x7f;
1643 palette_msg[15] = w & 0x80;
1645 write (palette_msg);
1647 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1648 write (update_pallette_msg);
1650 color_map[rgba] = index;
1656 Push2::build_color_map ()
1658 /* These are "standard" colors that Ableton docs suggest will always be
1659 there. Put them in our color map so that when we look up these
1660 colors, we will use the Ableton indices for them.
1663 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1664 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1665 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1666 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1667 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1668 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1669 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1671 for (uint8_t n = 1; n < 122; ++n) {
1672 color_map_free_list.push (n);
1677 Push2::fill_color_table ()
1679 colors.insert (make_pair (DarkBackground, Gtkmm2ext::rgba_to_color (0, 0, 0, 1)));
1680 colors.insert (make_pair (LightBackground, Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1)));
1682 colors.insert (make_pair (ParameterName, Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1)));
1684 colors.insert (make_pair (KnobArcBackground, Gtkmm2ext::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1685 colors.insert (make_pair (KnobArcStart, Gtkmm2ext::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1686 colors.insert (make_pair (KnobArcEnd, Gtkmm2ext::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1688 colors.insert (make_pair (KnobLineShadow, Gtkmm2ext::rgba_to_color (0, 0, 0, 0.3)));
1689 colors.insert (make_pair (KnobLine, Gtkmm2ext::rgba_to_color (1, 1, 1, 1)));
1691 colors.insert (make_pair (KnobForeground, Gtkmm2ext::rgba_to_color (0.2, 0.2, 0.2, 1)));
1692 colors.insert (make_pair (KnobBackground, Gtkmm2ext::rgba_to_color (0.2, 0.2, 0.2, 1)));
1693 colors.insert (make_pair (KnobShadow, Gtkmm2ext::rgba_to_color (0, 0, 0, 0.1)));
1694 colors.insert (make_pair (KnobBorder, Gtkmm2ext::rgba_to_color (0, 0, 0, 1)));
1699 Push2::get_color (ColorName name)
1701 Colors::iterator c = colors.find (name);
1702 if (c != colors.end()) {
1710 Push2::set_current_layout (Push2Layout* layout)
1712 if (layout && layout == _current_layout) {
1713 _current_layout->show ();
1716 if (_current_layout) {
1717 _current_layout->hide ();
1718 _canvas->root()->remove (_current_layout);
1719 _previous_layout = _current_layout;
1722 _current_layout = layout;
1724 if (_current_layout) {
1725 _canvas->root()->add (_current_layout);
1726 _current_layout->show ();
1730 _canvas->request_redraw ();
1735 Push2::use_previous_layout ()
1737 if (_previous_layout) {
1738 set_current_layout (_previous_layout);
1743 Push2::request_pressure_mode ()
1745 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1750 Push2::set_pressure_mode (PressureMode pm)
1752 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1756 /* nothing to do, message is correct */
1766 cerr << "Sent PM message " << msg << endl;