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 "canvas/colors.h"
58 #include "track_mix.h"
62 #ifdef PLATFORM_WINDOWS
63 #define random() rand()
66 using namespace ARDOUR;
70 using namespace ArdourSurface;
72 #include "pbd/abstract_ui.cc" // instantiate template
74 #define ABLETON 0x2982
77 Push2::Push2 (ARDOUR::Session& s)
78 : ControlProtocol (s, string (X_("Ableton Push 2")))
79 , AbstractUI<Push2Request> (name())
82 , _modifier_state (None)
85 , _previous_layout (0)
86 , connection_state (ConnectionState (0))
88 , _mode (MusicalMode::IonianMajor)
94 , _pressure_mode (AfterTouch)
95 , selection_color (LED::Green)
96 , contrast_color (LED::Green)
97 , in_range_select (false)
99 /* we're going to need this */
107 /* master cannot be removed, so no need to connect to going-away signal */
108 master = session->master_out ();
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 port_reg_connection.disconnect ();
140 port_connection.disconnect ();
142 stop_using_device ();
146 if (_current_layout) {
147 _canvas->root()->remove (_current_layout);
155 delete splash_layout;
157 delete track_mix_layout;
158 track_mix_layout = 0;
165 Push2::run_event_loop ()
167 DEBUG_TRACE (DEBUG::Push2, "start event loop\n");
172 Push2::stop_event_loop ()
174 DEBUG_TRACE (DEBUG::Push2, "stop event loop\n");
179 Push2::begin_using_device ()
181 DEBUG_TRACE (DEBUG::Push2, "begin using device\n");
183 /* set up periodic task used to push a frame buffer to the
184 * device (25fps). The device can handle 60fps, but we don't
185 * need that frame rate.
188 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
189 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
190 vblank_timeout->attach (main_loop()->get_context());
192 connect_session_signals ();
196 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
199 /* catch current selection, if any so that we can wire up the pads if appropriate */
200 stripable_selection_changed ();
202 request_pressure_mode ();
210 Push2::stop_using_device ()
212 DEBUG_TRACE (DEBUG::Push2, "stop using device\n");
215 DEBUG_TRACE (DEBUG::Push2, "nothing to do, device not in use\n");
219 init_buttons (false);
220 strip_buttons_off ();
222 vblank_connection.disconnect ();
223 session_connections.drop_connections ();
230 Push2::ports_acquire ()
232 DEBUG_TRACE (DEBUG::Push2, "acquiring ports\n");
236 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
237 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
239 if (_async_in == 0 || _async_out == 0) {
240 DEBUG_TRACE (DEBUG::Push2, "cannot register ports\n");
244 /* We do not add our ports to the input/output bundles because we don't
245 * want users wiring them by hand. They could use JACK tools if they
246 * really insist on that (and use JACK)
249 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
250 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
252 /* Create a shadow port where, depending on the state of the surface,
253 * we will make pad note on/off events appear. The surface code will
254 * automatically this port to the first selected MIDI track.
257 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));
258 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
262 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
264 _output_bundle->add_channel (
266 ARDOUR::DataType::MIDI,
267 session->engine().make_port_name_non_relative (shadow_port->name())
271 session->BundleAddedOrRemoved ();
273 connect_to_parser ();
275 /* Connect input port to event loop */
279 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
280 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
281 asp->xthread().attach (main_loop()->get_context());
287 Push2::ports_release ()
289 DEBUG_TRACE (DEBUG::Push2, "releasing ports\n");
291 /* wait for button data to be flushed */
293 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
294 asp->drain (10000, 500000);
296 AudioEngine::instance()->unregister_port (_async_in);
297 AudioEngine::instance()->unregister_port (_async_out);
299 _async_in.reset ((ARDOUR::Port*) 0);
300 _async_out.reset ((ARDOUR::Port*) 0);
306 Push2::device_acquire ()
310 DEBUG_TRACE (DEBUG::Push2, "acquiring device\n");
313 DEBUG_TRACE (DEBUG::Push2, "open() called with handle already set\n");
318 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
319 DEBUG_TRACE (DEBUG::Push2, "failed to open USB handle\n");
323 if ((err = libusb_claim_interface (handle, 0x00))) {
324 DEBUG_TRACE (DEBUG::Push2, "failed to claim USB device\n");
325 libusb_close (handle);
334 Push2::device_release ()
336 DEBUG_TRACE (DEBUG::Push2, "releasing device\n");
338 libusb_release_interface (handle, 0x00);
339 libusb_close (handle);
344 list<boost::shared_ptr<ARDOUR::Bundle> >
347 list<boost::shared_ptr<ARDOUR::Bundle> > b;
349 if (_output_bundle) {
350 b.push_back (_output_bundle);
357 Push2::strip_buttons_off ()
359 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
360 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
362 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
363 Button* b = id_button_map[strip_buttons[n]];
365 b->set_color (LED::Black);
366 b->set_state (LED::OneShot24th);
367 write (b->state_msg());
373 Push2::init_buttons (bool startup)
375 /* This is a list of buttons that we want lit because they do something
376 in ardour related (loosely, sometimes) to their illuminated label.
379 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
380 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
381 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
384 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
385 Button* b = id_button_map[buttons[n]];
388 b->set_color (LED::White);
390 b->set_color (LED::Black);
392 b->set_state (LED::OneShot24th);
393 write (b->state_msg());
398 /* all other buttons are off (black) */
400 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
401 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
402 Accent, Note, Session, };
404 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
405 Button* b = id_button_map[off_buttons[n]];
407 b->set_color (LED::Black);
408 b->set_state (LED::OneShot24th);
409 write (b->state_msg());
414 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
415 Pad* pad = pi->second;
417 pad->set_color (LED::Black);
418 pad->set_state (LED::OneShot24th);
419 write (pad->state_msg());
431 Push2::request_factory (uint32_t num_requests)
433 /* AbstractUI<T>::request_buffer_factory() is a template method only
434 instantiated in this source module. To provide something visible for
435 use in the interface/descriptor, we have this static method that is
438 return request_buffer_factory (num_requests);
442 Push2::do_request (Push2Request * req)
444 if (req->type == CallSlot) {
446 call_slot (MISSING_INVALIDATOR, req->the_slot);
448 } else if (req->type == Quit) {
450 stop_using_device ();
457 set_current_layout (splash_layout);
458 splash_start = get_microseconds ();
466 /* display splash for 2 seconds */
468 if (get_microseconds() - splash_start > 2000000) {
470 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
471 set_current_layout (mix_layout);
475 if (_current_layout) {
476 _current_layout->update_meters ();
477 _current_layout->update_clocks ();
486 Push2::set_active (bool yn)
488 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
490 if (yn == active()) {
496 if (device_acquire ()) {
500 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
501 begin_using_device ();
503 /* begin_using_device () will get called once we're connected */
507 /* Control Protocol Manager never calls us with false, but
508 * insteads destroys us.
512 ControlProtocol::set_active (yn);
514 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
520 Push2::init_touch_strip ()
522 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
523 /* flags are the final byte (ignore end-of-sysex */
525 /* show bar, not point
529 msg[7] = (1<<4) | (1<<5) | (1<<6);
534 Push2::write (const MidiByteArray& data)
536 /* immediate delivery */
537 _output_port->write (&data[0], data.size(), 0);
541 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
544 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
550 DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
552 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
557 DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
559 framepos_t now = AudioEngine::instance()->sample_time();
568 Push2::connect_to_parser ()
570 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
572 MIDI::Parser* p = _input_port->parser();
575 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
576 /* V-Pot messages are Controller */
577 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
578 /* Button messages are NoteOn */
579 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
580 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
581 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
582 /* Fader messages are Pitchbend */
583 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
587 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
589 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
595 MidiByteArray msg (sz, raw_bytes);
596 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
598 if (!push2_sysex_header.compare_n (msg, 6)) {
603 case 0x1f: /* pressure mode */
605 _pressure_mode = AfterTouch;
606 PressureModeChange (AfterTouch);
607 cerr << "Pressure mode is after\n";
609 _pressure_mode = PolyPressure;
610 PressureModeChange (PolyPressure);
611 cerr << "Pressure mode is poly\n";
618 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
620 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
622 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
625 /* any press cancels any pending long press timeouts */
626 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
627 Button* bb = id_button_map[*x];
628 bb->timeout_connection.disconnect ();
632 if (b != cc_button_map.end()) {
634 Button* button = b->second;
637 buttons_down.insert (button->id);
638 start_press_timeout (*button, button->id);
640 buttons_down.erase (button->id);
641 button->timeout_connection.disconnect ();
645 set<ButtonID>::iterator c = consumed.find (button->id);
647 if (c == consumed.end()) {
648 if (ev->value == 0) {
649 (this->*button->release_method)();
651 (this->*button->press_method)();
654 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
662 int delta = ev->value;
665 delta = -(128 - delta);
668 switch (ev->controller_number) {
670 _current_layout->strip_vpot (0, delta);
673 _current_layout->strip_vpot (1, delta);
676 _current_layout->strip_vpot (2, delta);
679 _current_layout->strip_vpot (3, delta);
682 _current_layout->strip_vpot (4, delta);
685 _current_layout->strip_vpot (5, delta);
688 _current_layout->strip_vpot (6, delta);
691 _current_layout->strip_vpot (7, delta);
696 other_vpot (8, delta);
699 other_vpot (1, delta);
704 other_vpot (2, delta);
711 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
713 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
715 if (ev->velocity == 0) {
716 handle_midi_note_off_message (parser, ev);
720 switch (ev->note_number) {
722 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
725 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
728 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
731 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
734 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
737 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
740 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
743 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
748 other_vpot_touch (0, ev->velocity > 64);
751 other_vpot_touch (1, ev->velocity > 64);
756 other_vpot_touch (3, ev->velocity > 64);
761 if (ev->velocity < 64) {
767 if (ev->note_number < 11) {
771 /* Pad illuminations */
773 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
775 if (pm == nn_pad_map.end()) {
779 const Pad * const pad_pressed = pm->second;
781 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
783 if (pads_with_note.first == fn_pad_map.end()) {
787 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
788 Pad* pad = pi->second;
790 pad->set_color (contrast_color);
791 pad->set_state (LED::OneShot24th);
792 write (pad->state_msg());
797 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
799 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
801 if (ev->note_number < 11) {
802 /* theoretically related to encoder touch start/end, but
803 * actually they send note on with two different velocity
809 /* Pad illuminations */
811 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
813 if (pm == nn_pad_map.end()) {
817 const Pad * const pad_pressed = pm->second;
819 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
821 if (pads_with_note.first == fn_pad_map.end()) {
825 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
826 Pad* pad = pi->second;
828 if (pad->do_when_pressed == Pad::FlashOn) {
829 pad->set_color (LED::Black);
830 pad->set_state (LED::OneShot24th);
831 write (pad->state_msg());
832 } else if (pad->do_when_pressed == Pad::FlashOff) {
833 pad->set_color (pad->perma_color);
834 pad->set_state (LED::OneShot24th);
835 write (pad->state_msg());
841 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
846 Push2::thread_init ()
848 struct sched_param rtparam;
850 pthread_set_name (event_loop_name().c_str());
852 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
853 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
855 memset (&rtparam, 0, sizeof (rtparam));
856 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
858 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
859 // do we care? not particularly.
864 Push2::connect_session_signals()
866 // receive routes added
867 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
868 // receive VCAs added
869 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
871 // receive record state toggled
872 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
873 // receive transport state changed
874 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
875 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
876 // receive punch-in and punch-out
877 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
878 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
879 // receive rude solo changed
880 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
884 Push2::notify_record_state_changed ()
886 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
888 if (b == id_button_map.end()) {
892 switch (session->record_status ()) {
893 case Session::Disabled:
894 b->second->set_color (LED::White);
895 b->second->set_state (LED::NoTransition);
897 case Session::Enabled:
898 b->second->set_color (LED::Red);
899 b->second->set_state (LED::Blinking4th);
901 case Session::Recording:
902 b->second->set_color (LED::Red);
903 b->second->set_state (LED::OneShot24th);
907 write (b->second->state_msg());
911 Push2::notify_transport_state_changed ()
913 Button* b = id_button_map[Play];
915 if (session->transport_rolling()) {
916 b->set_state (LED::OneShot24th);
917 b->set_color (LED::Green);
920 /* disable any blink on FixedLength from pending edit range op */
921 Button* fl = id_button_map[FixedLength];
923 fl->set_color (LED::Black);
924 fl->set_state (LED::NoTransition);
925 write (fl->state_msg());
927 b->set_color (LED::White);
928 b->set_state (LED::NoTransition);
931 write (b->state_msg());
935 Push2::notify_loop_state_changed ()
940 Push2::notify_parameter_changed (std::string param)
942 IDButtonMap::iterator b;
944 if (param == "clicking") {
945 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
948 if (Config->get_clicking()) {
949 b->second->set_state (LED::Blinking4th);
950 b->second->set_color (LED::White);
952 b->second->set_color (LED::White);
953 b->second->set_state (LED::NoTransition);
955 write (b->second->state_msg ());
960 Push2::notify_solo_active_changed (bool yn)
962 IDButtonMap::iterator b = id_button_map.find (Solo);
964 if (b == id_button_map.end()) {
969 b->second->set_state (LED::Blinking4th);
970 b->second->set_color (LED::Red);
972 b->second->set_state (LED::NoTransition);
973 b->second->set_color (LED::White);
976 write (b->second->state_msg());
982 XMLNode& node (ControlProtocol::get_state());
985 child = new XMLNode (X_("Input"));
986 child->add_child_nocopy (_async_in->get_state());
987 node.add_child_nocopy (*child);
988 child = new XMLNode (X_("Output"));
989 child->add_child_nocopy (_async_out->get_state());
990 node.add_child_nocopy (*child);
992 node.set_property (X_("root"), _scale_root);
993 node.set_property (X_("root-octave"), _root_octave);
994 node.set_property (X_("in-key"), _in_key);
995 node.set_property (X_("mode"), _mode);
1001 Push2::set_state (const XMLNode & node, int version)
1003 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1007 if (ControlProtocol::set_state (node, version)) {
1013 if ((child = node.child (X_("Input"))) != 0) {
1014 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1016 _async_in->set_state (*portnode, version);
1020 if ((child = node.child (X_("Output"))) != 0) {
1021 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1023 _async_out->set_state (*portnode, version);
1027 node.get_property (X_("root"), _scale_root);
1028 node.get_property (X_("root-octave"), _root_octave);
1029 node.get_property (X_("in-key"), _in_key);
1030 node.get_property (X_("mode"), _mode);
1036 Push2::other_vpot (int n, int delta)
1038 boost::shared_ptr<Amp> click_gain;
1044 /* metronome gain control */
1045 click_gain = session->click_gain();
1047 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1049 ac->set_value (ac->interface_to_internal (
1050 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1051 PBD::Controllable::UseGroup);
1056 /* master gain control */
1058 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1060 ac->set_value (ac->interface_to_internal (
1061 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1062 PBD::Controllable::UseGroup);
1070 Push2::other_vpot_touch (int n, bool touching)
1079 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1082 ac->start_touch (session->audible_frame());
1084 ac->stop_touch (true, session->audible_frame());
1092 Push2::start_shift ()
1094 cerr << "start shift\n";
1095 _modifier_state = ModifierState (_modifier_state | ModShift);
1096 Button* b = id_button_map[Shift];
1097 b->set_color (LED::White);
1098 b->set_state (LED::Blinking16th);
1099 write (b->state_msg());
1105 if (_modifier_state & ModShift) {
1106 cerr << "end shift\n";
1107 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1108 Button* b = id_button_map[Shift];
1109 b->timeout_connection.disconnect ();
1110 b->set_color (LED::White);
1111 b->set_state (LED::OneShot24th);
1112 write (b->state_msg());
1117 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1119 /* This filter is called asynchronously from a realtime process
1120 context. It must use atomics to check state, and must not block.
1123 bool matched = false;
1125 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1126 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1128 /* encoder touch start/touch end use note
1129 * 0-10. touchstrip uses note 12
1132 if ((*ev).note() > 10 && (*ev).note() != 12) {
1134 const int n = (*ev).note ();
1135 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1137 if (nni != nn_pad_map.end()) {
1138 Pad const * pad = nni->second;
1139 /* shift for output to the shadow port */
1140 if (pad->filtered >= 0) {
1141 (*ev).set_note (pad->filtered + (octave_shift*12));
1142 out.push_back (*ev);
1143 /* shift back so that the pads light correctly */
1146 /* no mapping, don't send event */
1149 out.push_back (*ev);
1154 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1155 out.push_back (*ev);
1163 Push2::port_registration_handler ()
1165 if (!_async_in && !_async_out) {
1166 /* ports not registered yet */
1170 if (_async_in->connected() && _async_out->connected()) {
1171 /* don't waste cycles here */
1176 /* the origin of the numeric magic identifiers is known only to Ableton
1177 and may change in time. This is part of how CoreMIDI works.
1179 string input_port_name = X_("system:midi_capture_1319078870");
1180 string output_port_name = X_("system:midi_playback_3409210341");
1182 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
1183 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
1188 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
1189 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
1191 if (!in.empty() && !out.empty()) {
1192 cerr << "Push2: both ports found\n";
1193 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
1194 if (!_async_in->connected()) {
1195 AudioEngine::instance()->connect (_async_in->name(), in.front());
1197 if (!_async_out->connected()) {
1198 AudioEngine::instance()->connect (_async_out->name(), out.front());
1204 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1206 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1207 if (!_input_port || !_output_port) {
1211 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1212 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1214 if (ni == name1 || ni == name2) {
1216 connection_state |= InputConnected;
1218 connection_state &= ~InputConnected;
1220 } else if (no == name1 || no == name2) {
1222 connection_state |= OutputConnected;
1224 connection_state &= ~OutputConnected;
1227 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1232 DEBUG_TRACE (DEBUG::Push2, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
1235 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1237 /* XXX this is a horrible hack. Without a short sleep here,
1238 something prevents the device wakeup messages from being
1239 sent and/or the responses from being received.
1243 DEBUG_TRACE (DEBUG::Push2, "device now connected for both input and output\n");
1245 /* may not have the device open if it was just plugged
1246 in. Really need USB device detection rather than MIDI port
1247 detection for this to work well.
1251 begin_using_device ();
1254 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1255 stop_using_device ();
1258 ConnectionChange (); /* emit signal for our GUI */
1260 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1262 return true; /* connection status changed */
1265 boost::shared_ptr<Port>
1266 Push2::output_port()
1271 boost::shared_ptr<Port>
1278 Push2::pad_note (int row, int col) const
1280 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1282 if (nni != nn_pad_map.end()) {
1283 return nni->second->filtered;
1290 Push2::update_selection_color ()
1292 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1294 if (!current_midi_track) {
1298 selection_color = get_color_index (current_midi_track->presentation_info().color());
1299 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1301 reset_pad_colors ();
1305 Push2::reset_pad_colors ()
1307 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1311 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1313 MusicalMode m (mode);
1314 vector<float>::iterator interval;
1316 const int original_root = root;
1318 interval = m.steps.begin();
1319 root += (octave*12);
1322 const int root_start = root;
1324 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1325 vector<int> mode_vector; /* sorted in note order */
1327 mode_map.insert (note);
1328 mode_vector.push_back (note);
1330 /* build a map of all notes in the mode, from the root to 127 */
1332 while (note < 128) {
1334 if (interval == m.steps.end()) {
1336 /* last distance was the end of the scale,
1337 so wrap, adding the next note at one
1338 octave above the last root.
1341 interval = m.steps.begin();
1343 mode_map.insert (root);
1344 mode_vector.push_back (root);
1347 note = (int) floor (root + (2.0 * (*interval)));
1349 mode_map.insert (note);
1350 mode_vector.push_back (note);
1354 fn_pad_map.clear ();
1358 vector<int>::iterator notei;
1361 for (int row = 0; row < 8; ++row) {
1363 /* Ableton's grid layout wraps the available notes in the scale
1364 * by offsetting 3 notes per row (from the bottom)
1367 notei = mode_vector.begin();
1368 notei += row_offset;
1371 for (int col = 0; col < 8; ++col) {
1372 int index = 36 + (row*8) + col;
1373 Pad* pad = nn_pad_map[index];
1375 if (notei != mode_vector.end()) {
1378 pad->filtered = notenum;
1380 fn_pad_map.insert (make_pair (notenum, pad));
1382 if ((notenum % 12) == original_root) {
1383 pad->set_color (selection_color);
1384 pad->perma_color = selection_color;
1386 pad->set_color (LED::White);
1387 pad->perma_color = LED::White;
1390 pad->do_when_pressed = Pad::FlashOff;
1395 pad->set_color (LED::Black);
1396 pad->do_when_pressed = Pad::Nothing;
1400 pad->set_state (LED::OneShot24th);
1401 write (pad->state_msg());
1407 /* chromatic: all notes available, but highlight those in the scale */
1409 for (note = 36; note < 100; ++note) {
1411 Pad* pad = nn_pad_map[note];
1413 /* Chromatic: all pads play, half-tone steps. Light
1414 * those in the scale, and highlight root notes
1417 pad->filtered = root_start + (note - 36);
1419 fn_pad_map.insert (make_pair (pad->filtered, pad));
1421 if (mode_map.find (note) != mode_map.end()) {
1423 if ((note % 12) == original_root) {
1424 pad->set_color (selection_color);
1425 pad->perma_color = selection_color;
1427 pad->set_color (LED::White);
1428 pad->perma_color = LED::White;
1431 pad->do_when_pressed = Pad::FlashOff;
1435 /* note is not in mode, turn it off */
1437 pad->do_when_pressed = Pad::FlashOn;
1438 pad->set_color (LED::Black);
1442 pad->set_state (LED::OneShot24th);
1443 write (pad->state_msg());
1449 bool changed = false;
1451 if (_scale_root != original_root) {
1452 _scale_root = original_root;
1455 if (_root_octave != octave) {
1456 _root_octave = octave;
1459 if (_in_key != inkey) {
1463 if (_mode != mode) {
1469 ScaleChange (); /* EMIT SIGNAL */
1474 Push2::set_percussive_mode (bool yn)
1477 cerr << "back to scale\n";
1478 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1485 fn_pad_map.clear ();
1487 for (int row = 0; row < 8; ++row) {
1489 for (int col = 0; col < 4; ++col) {
1491 int index = 36 + (row*8) + col;
1492 Pad* pad = nn_pad_map[index];
1494 pad->filtered = drum_note;
1499 for (int row = 0; row < 8; ++row) {
1501 for (int col = 4; col < 8; ++col) {
1503 int index = 36 + (row*8) + col;
1504 Pad* pad = nn_pad_map[index];
1506 pad->filtered = drum_note;
1515 Push2::current_layout () const
1517 Glib::Threads::Mutex::Lock lm (layout_lock);
1518 return _current_layout;
1522 Push2::stripable_selection_changed ()
1524 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1525 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1526 boost::shared_ptr<MidiTrack> new_pad_target;
1527 StripableNotificationList const & selected (last_selected());
1529 /* See if there's a MIDI track selected */
1531 for (StripableNotificationList::const_iterator si = selected.begin(); si != selected.end(); ++si) {
1533 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1535 if (new_pad_target) {
1540 if (current_midi_track == new_pad_target) {
1545 if (!new_pad_target) {
1546 /* leave existing connection alone */
1550 /* disconnect from pad port, if appropriate */
1552 if (current_midi_track && pad_port) {
1554 /* XXX this could possibly leave dangling MIDI notes.
1556 * A general libardour fix is required. It isn't obvious
1557 * how note resolution can be done unless disconnecting
1558 * becomes "slow" (i.e. deferred for as long as it takes
1559 * to resolve notes).
1561 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1564 /* now connect the pad port to this (newly) selected midi
1565 * track, if indeed there is one.
1568 if (new_pad_target && pad_port) {
1569 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1570 current_pad_target = new_pad_target;
1571 selection_color = get_color_index (new_pad_target->presentation_info().color());
1572 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1574 current_pad_target.reset ();
1575 selection_color = LED::Green;
1576 contrast_color = LED::Green;
1579 reset_pad_colors ();
1581 TrackMixLayout* tml = dynamic_cast<TrackMixLayout*> (mix_layout);
1583 tml->set_stripable (first_selected_stripable());
1587 Push2::button_by_id (ButtonID bid)
1589 return id_button_map[bid];
1593 Push2::get_color_index (ArdourCanvas::Color rgba)
1595 ColorMap::iterator i = color_map.find (rgba);
1597 if (i != color_map.end()) {
1601 double dr, dg, db, da;
1603 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1604 int w = 126; /* not sure where/when we should get this value */
1607 r = (int) floor (255.0 * dr);
1608 g = (int) floor (255.0 * dg);
1609 b = (int) floor (255.0 * db);
1611 /* get a free index */
1615 if (color_map_free_list.empty()) {
1616 /* random replacement of any entry above zero and below 122 (where the
1617 * Ableton standard colors live)
1619 index = 1 + (random() % 121);
1621 index = color_map_free_list.top();
1622 color_map_free_list.pop();
1625 MidiByteArray palette_msg (17,
1627 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1628 0x00, /* index = 7 */
1629 0x00, 0x00, /* r = 8 & 9 */
1630 0x00, 0x00, /* g = 10 & 11 */
1631 0x00, 0x00, /* b = 12 & 13 */
1632 0x00, 0x00, /* w (a?) = 14 & 15*/
1634 palette_msg[7] = index;
1635 palette_msg[8] = r & 0x7f;
1636 palette_msg[9] = (r & 0x80) >> 7;
1637 palette_msg[10] = g & 0x7f;
1638 palette_msg[11] = (g & 0x80) >> 7;
1639 palette_msg[12] = b & 0x7f;
1640 palette_msg[13] = (b & 0x80) >> 7;
1641 palette_msg[14] = w & 0x7f;
1642 palette_msg[15] = w & 0x80;
1644 write (palette_msg);
1646 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1647 write (update_pallette_msg);
1649 color_map[rgba] = index;
1655 Push2::build_color_map ()
1657 /* These are "standard" colors that Ableton docs suggest will always be
1658 there. Put them in our color map so that when we look up these
1659 colors, we will use the Ableton indices for them.
1662 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1663 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1664 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1665 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1666 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1667 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1668 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1670 for (uint8_t n = 1; n < 122; ++n) {
1671 color_map_free_list.push (n);
1676 Push2::fill_color_table ()
1678 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1679 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1681 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1683 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1684 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1685 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1687 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1688 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1690 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1691 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1692 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1693 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1698 Push2::get_color (ColorName name)
1700 Colors::iterator c = colors.find (name);
1701 if (c != colors.end()) {
1709 Push2::set_current_layout (Push2Layout* layout)
1711 if (layout && layout == _current_layout) {
1712 _current_layout->show ();
1715 if (_current_layout) {
1716 _current_layout->hide ();
1717 _canvas->root()->remove (_current_layout);
1718 _previous_layout = _current_layout;
1721 _current_layout = layout;
1723 if (_current_layout) {
1724 _canvas->root()->add (_current_layout);
1725 _current_layout->show ();
1729 _canvas->request_redraw ();
1734 Push2::use_previous_layout ()
1736 if (_previous_layout) {
1737 set_current_layout (_previous_layout);
1742 Push2::request_pressure_mode ()
1744 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1749 Push2::set_pressure_mode (PressureMode pm)
1751 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1755 /* nothing to do, message is correct */
1765 cerr << "Sent PM message " << msg << endl;