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"
32 #include "temporal/time.h"
33 #include "temporal/bbt_time.h"
35 #include "ardour/amp.h"
36 #include "ardour/async_midi_port.h"
37 #include "ardour/audioengine.h"
38 #include "ardour/debug.h"
39 #include "ardour/midiport_manager.h"
40 #include "ardour/midi_track.h"
41 #include "ardour/midi_port.h"
42 #include "ardour/session.h"
43 #include "ardour/tempo.h"
44 #include "ardour/types_convert.h"
46 #include "gtkmm2ext/gui_thread.h"
47 #include "gtkmm2ext/rgb_macros.h"
49 #include "gtkmm2ext/colors.h"
59 #include "track_mix.h"
63 #ifdef PLATFORM_WINDOWS
64 #define random() rand()
67 using namespace ARDOUR;
71 using namespace ArdourSurface;
72 using namespace Gtkmm2ext;
74 #include "pbd/abstract_ui.cc" // instantiate template
76 #define ABLETON 0x2982
79 Push2::Push2 (ARDOUR::Session& s)
80 : ControlProtocol (s, string (X_("Ableton Push 2")))
81 , AbstractUI<Push2Request> (name())
84 , _modifier_state (None)
87 , _previous_layout (0)
88 , connection_state (ConnectionState (0))
90 , _mode (MusicalMode::IonianMajor)
96 , _pressure_mode (AfterTouch)
97 , selection_color (LED::Green)
98 , contrast_color (LED::Green)
99 , in_range_select (false)
101 /* we're going to need this */
109 /* master cannot be removed, so no need to connect to going-away signal */
110 master = session->master_out ();
112 /* allocate graphics layouts, even though we're not using them yet */
114 _canvas = new Push2Canvas (*this, 960, 160);
115 mix_layout = new MixLayout (*this, *session, "globalmix");
116 scale_layout = new ScaleLayout (*this, *session, "scale");
117 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
118 splash_layout = new SplashLayout (*this, *session, "splash");
122 /* Ports exist for the life of this instance */
126 /* catch arrival and departure of Push2 itself */
127 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
129 /* Catch port connections and disconnections */
130 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
132 /* Push 2 ports might already be there */
133 port_registration_handler ();
138 DEBUG_TRACE (DEBUG::Push2, "push2 control surface object being destroyed\n");
140 /* do this before stopping the event loop, so that we don't get any notifications */
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 sample 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 */
202 stripable_selection_changed ();
204 request_pressure_mode ();
212 Push2::stop_using_device ()
214 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
217 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
221 init_buttons (false);
222 strip_buttons_off ();
224 vblank_connection.disconnect ();
225 session_connections.drop_connections ();
232 Push2::ports_acquire ()
234 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
238 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
239 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
241 if (_async_in == 0 || _async_out == 0) {
242 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
246 /* We do not add our ports to the input/output bundles because we don't
247 * want users wiring them by hand. They could use JACK tools if they
248 * really insist on that (and use JACK)
251 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
252 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
254 /* Create a shadow port where, depending on the state of the surface,
255 * we will make pad note on/off events appear. The surface code will
256 * automatically this port to the first selected MIDI track.
259 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));
260 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
264 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
266 _output_bundle->add_channel (
268 ARDOUR::DataType::MIDI,
269 session->engine().make_port_name_non_relative (shadow_port->name())
273 session->BundleAddedOrRemoved ();
275 connect_to_parser ();
277 /* Connect input port to event loop */
281 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
282 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
283 asp->xthread().attach (main_loop()->get_context());
289 Push2::ports_release ()
291 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
293 /* wait for button data to be flushed */
295 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
296 asp->drain (10000, 500000);
299 Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
300 AudioEngine::instance()->unregister_port (_async_in);
301 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 boost::shared_ptr<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 boost::shared_ptr<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 boost::shared_ptr<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 boost::shared_ptr<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 samplepos_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 boost::shared_ptr<Button> bb = id_button_map[*x];
633 bb->timeout_connection.disconnect ();
637 if (b != cc_button_map.end()) {
639 boost::shared_ptr<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 boost::shared_ptr<const Pad> 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 boost::shared_ptr<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 boost::shared_ptr<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 boost::shared_ptr<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 pthread_set_name (event_loop_name().c_str());
855 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
856 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
858 set_thread_priority ();
862 Push2::connect_session_signals()
864 // receive routes added
865 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
866 // receive VCAs added
867 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
869 // receive record state toggled
870 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
871 // receive transport state changed
872 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
873 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
874 // receive punch-in and punch-out
875 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
876 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
877 // receive rude solo changed
878 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
882 Push2::notify_record_state_changed ()
884 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
886 if (b == id_button_map.end()) {
890 switch (session->record_status ()) {
891 case Session::Disabled:
892 b->second->set_color (LED::White);
893 b->second->set_state (LED::NoTransition);
895 case Session::Enabled:
896 b->second->set_color (LED::Red);
897 b->second->set_state (LED::Blinking4th);
899 case Session::Recording:
900 b->second->set_color (LED::Red);
901 b->second->set_state (LED::OneShot24th);
905 write (b->second->state_msg());
909 Push2::notify_transport_state_changed ()
911 boost::shared_ptr<Button> b = id_button_map[Play];
913 if (session->transport_rolling()) {
914 b->set_state (LED::OneShot24th);
915 b->set_color (LED::Green);
918 /* disable any blink on FixedLength from pending edit range op */
919 boost::shared_ptr<Button> fl = id_button_map[FixedLength];
921 fl->set_color (LED::Black);
922 fl->set_state (LED::NoTransition);
923 write (fl->state_msg());
925 b->set_color (LED::White);
926 b->set_state (LED::NoTransition);
929 write (b->state_msg());
933 Push2::notify_loop_state_changed ()
938 Push2::notify_parameter_changed (std::string param)
940 IDButtonMap::iterator b;
942 if (param == "clicking") {
943 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
946 if (Config->get_clicking()) {
947 b->second->set_state (LED::Blinking4th);
948 b->second->set_color (LED::White);
950 b->second->set_color (LED::White);
951 b->second->set_state (LED::NoTransition);
953 write (b->second->state_msg ());
958 Push2::notify_solo_active_changed (bool yn)
960 IDButtonMap::iterator b = id_button_map.find (Solo);
962 if (b == id_button_map.end()) {
967 b->second->set_state (LED::Blinking4th);
968 b->second->set_color (LED::Red);
970 b->second->set_state (LED::NoTransition);
971 b->second->set_color (LED::White);
974 write (b->second->state_msg());
980 XMLNode& node (ControlProtocol::get_state());
983 child = new XMLNode (X_("Input"));
984 child->add_child_nocopy (_async_in->get_state());
985 node.add_child_nocopy (*child);
986 child = new XMLNode (X_("Output"));
987 child->add_child_nocopy (_async_out->get_state());
988 node.add_child_nocopy (*child);
990 node.set_property (X_("root"), _scale_root);
991 node.set_property (X_("root-octave"), _root_octave);
992 node.set_property (X_("in-key"), _in_key);
993 node.set_property (X_("mode"), _mode);
999 Push2::set_state (const XMLNode & node, int version)
1001 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1005 if (ControlProtocol::set_state (node, version)) {
1011 if ((child = node.child (X_("Input"))) != 0) {
1012 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1014 _async_in->set_state (*portnode, version);
1018 if ((child = node.child (X_("Output"))) != 0) {
1019 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1021 _async_out->set_state (*portnode, version);
1025 node.get_property (X_("root"), _scale_root);
1026 node.get_property (X_("root-octave"), _root_octave);
1027 node.get_property (X_("in-key"), _in_key);
1028 node.get_property (X_("mode"), _mode);
1034 Push2::other_vpot (int n, int delta)
1036 boost::shared_ptr<Amp> click_gain;
1042 /* metronome gain control */
1043 click_gain = session->click_gain();
1045 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1047 ac->set_value (ac->interface_to_internal (
1048 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1049 PBD::Controllable::UseGroup);
1054 /* master gain control */
1056 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1058 ac->set_value (ac->interface_to_internal (
1059 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1060 PBD::Controllable::UseGroup);
1068 Push2::other_vpot_touch (int n, bool touching)
1077 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1080 ac->start_touch (session->audible_sample());
1082 ac->stop_touch (session->audible_sample());
1090 Push2::start_shift ()
1092 cerr << "start shift\n";
1093 _modifier_state = ModifierState (_modifier_state | ModShift);
1094 boost::shared_ptr<Button> b = id_button_map[Shift];
1095 b->set_color (LED::White);
1096 b->set_state (LED::Blinking16th);
1097 write (b->state_msg());
1103 if (_modifier_state & ModShift) {
1104 cerr << "end shift\n";
1105 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1106 boost::shared_ptr<Button> b = id_button_map[Shift];
1107 b->timeout_connection.disconnect ();
1108 b->set_color (LED::White);
1109 b->set_state (LED::OneShot24th);
1110 write (b->state_msg());
1115 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1117 /* This filter is called asynchronously from a realtime process
1118 context. It must use atomics to check state, and must not block.
1121 bool matched = false;
1123 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1124 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1126 /* encoder touch start/touch end use note
1127 * 0-10. touchstrip uses note 12
1130 if ((*ev).note() > 10 && (*ev).note() != 12) {
1132 const int n = (*ev).note ();
1133 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1135 if (nni != nn_pad_map.end()) {
1136 boost::shared_ptr<const Pad> pad = nni->second;
1137 /* shift for output to the shadow port */
1138 if (pad->filtered >= 0) {
1139 (*ev).set_note (pad->filtered + (octave_shift*12));
1140 out.push_back (*ev);
1141 /* shift back so that the pads light correctly */
1144 /* no mapping, don't send event */
1147 out.push_back (*ev);
1152 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1153 out.push_back (*ev);
1161 Push2::port_registration_handler ()
1163 if (!_async_in || !_async_out) {
1164 /* ports not registered yet */
1168 if (_async_in->connected() && _async_out->connected()) {
1169 /* don't waste cycles here */
1174 /* the origin of the numeric magic identifiers is known only to Ableton
1175 and may change in time. This is part of how CoreMIDI works.
1177 string input_port_name = X_("system:midi_capture_1319078870");
1178 string output_port_name = X_("system:midi_playback_3409210341");
1180 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1181 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1186 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1187 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1189 if (!in.empty() && !out.empty()) {
1190 cerr << "Push2: both ports found\n";
1191 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1192 if (!_async_in->connected()) {
1193 AudioEngine::instance()->connect (_async_in->name(), in.front());
1195 if (!_async_out->connected()) {
1196 AudioEngine::instance()->connect (_async_out->name(), out.front());
1202 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1204 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1205 if (!_input_port || !_output_port) {
1209 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1210 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1212 if (ni == name1 || ni == name2) {
1214 connection_state |= InputConnected;
1216 connection_state &= ~InputConnected;
1218 } else if (no == name1 || no == name2) {
1220 connection_state |= OutputConnected;
1222 connection_state &= ~OutputConnected;
1225 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1230 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1233 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1235 /* XXX this is a horrible hack. Without a short sleep here,
1236 something prevents the device wakeup messages from being
1237 sent and/or the responses from being received.
1241 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1243 /* may not have the device open if it was just plugged
1244 in. Really need USB device detection rather than MIDI port
1245 detection for this to work well.
1249 begin_using_device ();
1252 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1253 stop_using_device ();
1256 ConnectionChange (); /* emit signal for our GUI */
1258 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1260 return true; /* connection status changed */
1263 boost::shared_ptr<Port>
1264 Push2::output_port()
1269 boost::shared_ptr<Port>
1276 Push2::pad_note (int row, int col) const
1278 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1280 if (nni != nn_pad_map.end()) {
1281 return nni->second->filtered;
1288 Push2::update_selection_color ()
1290 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1292 if (!current_midi_track) {
1296 selection_color = get_color_index (current_midi_track->presentation_info().color());
1297 contrast_color = get_color_index (Gtkmm2ext::HSV (current_midi_track->presentation_info().color()).opposite().color());
1299 reset_pad_colors ();
1303 Push2::reset_pad_colors ()
1305 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1309 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1311 MusicalMode m (mode);
1312 vector<float>::iterator interval;
1314 const int original_root = root;
1316 interval = m.steps.begin();
1317 root += (octave*12);
1320 const int root_start = root;
1322 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1323 vector<int> mode_vector; /* sorted in note order */
1325 mode_map.insert (note);
1326 mode_vector.push_back (note);
1328 /* build a map of all notes in the mode, from the root to 127 */
1330 while (note < 128) {
1332 if (interval == m.steps.end()) {
1334 /* last distance was the end of the scale,
1335 so wrap, adding the next note at one
1336 octave above the last root.
1339 interval = m.steps.begin();
1341 mode_map.insert (root);
1342 mode_vector.push_back (root);
1345 note = (int) floor (root + (2.0 * (*interval)));
1347 mode_map.insert (note);
1348 mode_vector.push_back (note);
1352 fn_pad_map.clear ();
1356 vector<int>::iterator notei;
1359 for (int row = 0; row < 8; ++row) {
1361 /* Ableton's grid layout wraps the available notes in the scale
1362 * by offsetting 3 notes per row (from the bottom)
1365 notei = mode_vector.begin();
1366 notei += row_offset;
1369 for (int col = 0; col < 8; ++col) {
1370 int index = 36 + (row*8) + col;
1371 boost::shared_ptr<Pad> pad = nn_pad_map[index];
1373 if (notei != mode_vector.end()) {
1376 pad->filtered = notenum;
1378 fn_pad_map.insert (make_pair (notenum, pad));
1380 if ((notenum % 12) == original_root) {
1381 pad->set_color (selection_color);
1382 pad->perma_color = selection_color;
1384 pad->set_color (LED::White);
1385 pad->perma_color = LED::White;
1388 pad->do_when_pressed = Pad::FlashOff;
1393 pad->set_color (LED::Black);
1394 pad->do_when_pressed = Pad::Nothing;
1398 pad->set_state (LED::OneShot24th);
1399 write (pad->state_msg());
1405 /* chromatic: all notes available, but highlight those in the scale */
1407 for (note = 36; note < 100; ++note) {
1409 boost::shared_ptr<Pad> pad = nn_pad_map[note];
1411 /* Chromatic: all pads play, half-tone steps. Light
1412 * those in the scale, and highlight root notes
1415 pad->filtered = root_start + (note - 36);
1417 fn_pad_map.insert (make_pair (pad->filtered, pad));
1419 if (mode_map.find (note) != mode_map.end()) {
1421 if ((note % 12) == original_root) {
1422 pad->set_color (selection_color);
1423 pad->perma_color = selection_color;
1425 pad->set_color (LED::White);
1426 pad->perma_color = LED::White;
1429 pad->do_when_pressed = Pad::FlashOff;
1433 /* note is not in mode, turn it off */
1435 pad->do_when_pressed = Pad::FlashOn;
1436 pad->set_color (LED::Black);
1440 pad->set_state (LED::OneShot24th);
1441 write (pad->state_msg());
1447 bool changed = false;
1449 if (_scale_root != original_root) {
1450 _scale_root = original_root;
1453 if (_root_octave != octave) {
1454 _root_octave = octave;
1457 if (_in_key != inkey) {
1461 if (_mode != mode) {
1467 ScaleChange (); /* EMIT SIGNAL */
1472 Push2::set_percussive_mode (bool yn)
1475 cerr << "back to scale\n";
1476 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1483 fn_pad_map.clear ();
1485 for (int row = 0; row < 8; ++row) {
1487 for (int col = 0; col < 4; ++col) {
1489 int index = 36 + (row*8) + col;
1490 boost::shared_ptr<Pad> pad = nn_pad_map[index];
1492 pad->filtered = drum_note;
1497 for (int row = 0; row < 8; ++row) {
1499 for (int col = 4; col < 8; ++col) {
1501 int index = 36 + (row*8) + col;
1502 boost::shared_ptr<Pad> pad = nn_pad_map[index];
1504 pad->filtered = drum_note;
1513 Push2::current_layout () const
1515 Glib::Threads::Mutex::Lock lm (layout_lock);
1516 return _current_layout;
1520 Push2::stripable_selection_changed ()
1522 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1523 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1524 boost::shared_ptr<MidiTrack> new_pad_target;
1525 StripableNotificationList const & selected (last_selected());
1527 /* See if there's a MIDI track selected */
1529 for (StripableNotificationList::const_iterator si = selected.begin(); si != selected.end(); ++si) {
1531 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1533 if (new_pad_target) {
1538 if (current_midi_track == new_pad_target) {
1543 if (!new_pad_target) {
1544 /* leave existing connection alone */
1548 /* disconnect from pad port, if appropriate */
1550 if (current_midi_track && pad_port) {
1552 /* XXX this could possibly leave dangling MIDI notes.
1554 * A general libardour fix is required. It isn't obvious
1555 * how note resolution can be done unless disconnecting
1556 * becomes "slow" (i.e. deferred for as long as it takes
1557 * to resolve notes).
1559 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1562 /* now connect the pad port to this (newly) selected midi
1563 * track, if indeed there is one.
1566 if (new_pad_target && pad_port) {
1567 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1568 current_pad_target = new_pad_target;
1569 selection_color = get_color_index (new_pad_target->presentation_info().color());
1570 contrast_color = get_color_index (Gtkmm2ext::HSV (new_pad_target->presentation_info().color()).opposite().color());
1572 current_pad_target.reset ();
1573 selection_color = LED::Green;
1574 contrast_color = LED::Green;
1577 reset_pad_colors ();
1579 TrackMixLayout* tml = dynamic_cast<TrackMixLayout*> (track_mix_layout);
1581 tml->set_stripable (first_selected_stripable());
1584 boost::shared_ptr<Push2::Button>
1585 Push2::button_by_id (ButtonID bid)
1587 return id_button_map[bid];
1591 Push2::get_color_index (Color rgba)
1593 ColorMap::iterator i = color_map.find (rgba);
1595 if (i != color_map.end()) {
1599 double dr, dg, db, da;
1601 color_to_rgba (rgba, dr, dg, db, da);
1602 int w = 126; /* not sure where/when we should get this value */
1605 r = (int) floor (255.0 * dr);
1606 g = (int) floor (255.0 * dg);
1607 b = (int) floor (255.0 * db);
1609 /* get a free index */
1613 if (color_map_free_list.empty()) {
1614 /* random replacement of any entry above zero and below 122 (where the
1615 * Ableton standard colors live)
1617 index = 1 + (random() % 121);
1619 index = color_map_free_list.top();
1620 color_map_free_list.pop();
1623 MidiByteArray palette_msg (17,
1625 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1626 0x00, /* index = 7 */
1627 0x00, 0x00, /* r = 8 & 9 */
1628 0x00, 0x00, /* g = 10 & 11 */
1629 0x00, 0x00, /* b = 12 & 13 */
1630 0x00, 0x00, /* w (a?) = 14 & 15*/
1632 palette_msg[7] = index;
1633 palette_msg[8] = r & 0x7f;
1634 palette_msg[9] = (r & 0x80) >> 7;
1635 palette_msg[10] = g & 0x7f;
1636 palette_msg[11] = (g & 0x80) >> 7;
1637 palette_msg[12] = b & 0x7f;
1638 palette_msg[13] = (b & 0x80) >> 7;
1639 palette_msg[14] = w & 0x7f;
1640 palette_msg[15] = w & 0x80;
1642 write (palette_msg);
1644 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1645 write (update_pallette_msg);
1647 color_map[rgba] = index;
1653 Push2::build_color_map ()
1655 /* These are "standard" colors that Ableton docs suggest will always be
1656 there. Put them in our color map so that when we look up these
1657 colors, we will use the Ableton indices for them.
1660 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1661 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1662 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1663 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1664 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1665 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1666 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1668 for (uint8_t n = 1; n < 122; ++n) {
1669 color_map_free_list.push (n);
1674 Push2::fill_color_table ()
1676 colors.insert (make_pair (DarkBackground, Gtkmm2ext::rgba_to_color (0, 0, 0, 1)));
1677 colors.insert (make_pair (LightBackground, Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1)));
1679 colors.insert (make_pair (ParameterName, Gtkmm2ext::rgba_to_color (0.98, 0.98, 0.98, 1)));
1681 colors.insert (make_pair (KnobArcBackground, Gtkmm2ext::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1682 colors.insert (make_pair (KnobArcStart, Gtkmm2ext::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1683 colors.insert (make_pair (KnobArcEnd, Gtkmm2ext::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1685 colors.insert (make_pair (KnobLineShadow, Gtkmm2ext::rgba_to_color (0, 0, 0, 0.3)));
1686 colors.insert (make_pair (KnobLine, Gtkmm2ext::rgba_to_color (1, 1, 1, 1)));
1688 colors.insert (make_pair (KnobForeground, Gtkmm2ext::rgba_to_color (0.2, 0.2, 0.2, 1)));
1689 colors.insert (make_pair (KnobBackground, Gtkmm2ext::rgba_to_color (0.2, 0.2, 0.2, 1)));
1690 colors.insert (make_pair (KnobShadow, Gtkmm2ext::rgba_to_color (0, 0, 0, 0.1)));
1691 colors.insert (make_pair (KnobBorder, Gtkmm2ext::rgba_to_color (0, 0, 0, 1)));
1696 Push2::get_color (ColorName name)
1698 Colors::iterator c = colors.find (name);
1699 if (c != colors.end()) {
1707 Push2::set_current_layout (Push2Layout* layout)
1709 if (layout && layout == _current_layout) {
1710 _current_layout->show ();
1713 if (_current_layout) {
1714 _current_layout->hide ();
1715 _canvas->root()->remove (_current_layout);
1716 _previous_layout = _current_layout;
1719 _current_layout = layout;
1721 if (_current_layout) {
1722 _canvas->root()->add (_current_layout);
1723 _current_layout->show ();
1727 _canvas->request_redraw ();
1732 Push2::use_previous_layout ()
1734 if (_previous_layout) {
1735 set_current_layout (_previous_layout);
1740 Push2::request_pressure_mode ()
1742 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1747 Push2::set_pressure_mode (PressureMode pm)
1749 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1753 /* nothing to do, message is correct */
1763 cerr << "Sent PM message " << msg << endl;