2 Copyright (C) 2016 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "pbd/compose.h"
22 #include "pbd/convert.h"
23 #include "pbd/debug.h"
24 #include "pbd/failed_constructor.h"
25 #include "pbd/file_utils.h"
26 #include "pbd/search_path.h"
27 #include "pbd/enumwriter.h"
29 #include "midi++/parser.h"
30 #include "timecode/time.h"
31 #include "timecode/bbt_time.h"
33 #include "ardour/async_midi_port.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/debug.h"
36 #include "ardour/midiport_manager.h"
37 #include "ardour/midi_track.h"
38 #include "ardour/midi_port.h"
39 #include "ardour/session.h"
40 #include "ardour/tempo.h"
42 #include "gtkmm2ext/gui_thread.h"
43 #include "gtkmm2ext/rgb_macros.h"
53 #include "track_mix.h"
57 using namespace ARDOUR;
61 using namespace ArdourSurface;
63 #include "pbd/abstract_ui.cc" // instantiate template
65 #define ABLETON 0x2982
68 __attribute__((constructor)) static void
71 EnumWriter& enum_writer (EnumWriter::instance());
75 MusicalMode::Type mode;
77 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
78 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
80 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
81 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
82 REGISTER_CLASS_ENUM (MusicalMode, Minor);
83 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
84 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
85 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
86 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
87 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
88 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
89 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
90 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
91 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
92 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
93 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
94 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
95 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
96 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
97 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
98 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
99 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
100 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
101 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
102 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
103 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
104 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
105 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
106 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
107 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
108 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
109 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
110 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
111 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
112 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
113 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
114 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
115 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
116 REGISTER_CLASS_ENUM (MusicalMode, Persian);
117 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
121 Push2::Push2 (ARDOUR::Session& s)
122 : ControlProtocol (s, string (X_("Ableton Push 2")))
123 , AbstractUI<Push2Request> (name())
125 , _modifier_state (None)
127 , _current_layout (0)
128 , _previous_layout (0)
129 , connection_state (ConnectionState (0))
131 , _mode (MusicalMode::IonianMajor)
137 , _pressure_mode (AfterTouch)
144 /* master cannot be removed, so no need to connect to going-away signal */
145 master = session->master_out ();
148 throw failed_constructor ();
151 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
153 /* catch current selection, if any */
155 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
156 stripable_selection_change (sp);
159 /* catch arrival and departure of Push2 itself */
160 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
162 /* Catch port connections and disconnections */
163 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
165 /* ports might already be there */
166 port_registration_handler ();
173 delete track_mix_layout;
179 Push2::port_registration_handler ()
181 if (!_async_in && !_async_out) {
182 /* ports not registered yet */
186 if (_async_in->connected() && _async_out->connected()) {
187 /* don't waste cycles here */
191 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
192 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
196 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
197 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
199 if (!in.empty() && !out.empty()) {
200 cerr << "Push2: both ports found\n";
201 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
202 if (!_async_in->connected()) {
203 AudioEngine::instance()->connect (_async_in->name(), in.front());
205 if (!_async_out->connected()) {
206 AudioEngine::instance()->connect (_async_out->name(), out.front());
221 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
225 if ((err = libusb_claim_interface (handle, 0x00))) {
230 _canvas = new Push2Canvas (*this, 960, 160);
231 mix_layout = new MixLayout (*this, *session);
232 scale_layout = new ScaleLayout (*this, *session);
233 track_mix_layout = new TrackMixLayout (*this, *session);
234 splash_layout = new SplashLayout (*this, *session);
236 error << _("Cannot construct Canvas for display") << endmsg;
237 libusb_release_interface (handle, 0x00);
238 libusb_close (handle);
244 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
245 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
247 if (_async_in == 0 || _async_out == 0) {
251 /* We do not add our ports to the input/output bundles because we don't
252 * want users wiring them by hand. They could use JACK tools if they
253 * really insist on that.
256 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
257 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
259 /* Create a shadow port where, depending on the state of the surface,
260 * we will make pad note on/off events appear. The surface code will
261 * automatically this port to the first selected MIDI track.
264 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));
265 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
269 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
271 _output_bundle->add_channel (
273 ARDOUR::DataType::MIDI,
274 session->engine().make_port_name_non_relative (shadow_port->name())
278 session->BundleAddedOrRemoved ();
280 connect_to_parser ();
285 list<boost::shared_ptr<ARDOUR::Bundle> >
288 list<boost::shared_ptr<ARDOUR::Bundle> > b;
290 if (_output_bundle) {
291 b.push_back (_output_bundle);
300 init_buttons (false);
302 /* wait for button data to be flushed */
304 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
305 asp->drain (10000, 500000);
307 AudioEngine::instance()->unregister_port (_async_in);
308 AudioEngine::instance()->unregister_port (_async_out);
310 _async_in.reset ((ARDOUR::Port*) 0);
311 _async_out.reset ((ARDOUR::Port*) 0);
315 periodic_connection.disconnect ();
316 session_connections.drop_connections ();
318 if (_current_layout) {
319 _canvas->root()->remove (_current_layout);
327 delete splash_layout;
331 libusb_release_interface (handle, 0x00);
332 libusb_close (handle);
340 Push2::strip_buttons_off ()
342 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
343 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
345 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
346 Button* b = id_button_map[strip_buttons[n]];
348 b->set_color (LED::Black);
349 b->set_state (LED::OneShot24th);
350 write (b->state_msg());
356 Push2::init_buttons (bool startup)
358 /* This is a list of buttons that we want lit because they do something
359 in ardour related (loosely, sometimes) to their illuminated label.
362 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
363 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
364 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
367 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
368 Button* b = id_button_map[buttons[n]];
371 b->set_color (LED::White);
373 b->set_color (LED::Black);
375 b->set_state (LED::OneShot24th);
376 write (b->state_msg());
379 /* Strip buttons should all be off (black) by default. They will change
380 * color to reflect various conditions
383 strip_buttons_off ();
387 /* all other buttons are off (black) */
389 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
390 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
391 Accent, Note, Session, };
393 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
394 Button* b = id_button_map[off_buttons[n]];
396 b->set_color (LED::Black);
397 b->set_state (LED::OneShot24th);
398 write (b->state_msg());
403 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
404 Pad* pad = pi->second;
406 pad->set_color (LED::Black);
407 pad->set_state (LED::OneShot24th);
408 write (pad->state_msg());
416 libusb_device_handle *h;
419 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
420 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
425 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
430 Push2::request_factory (uint32_t num_requests)
432 /* AbstractUI<T>::request_buffer_factory() is a template method only
433 instantiated in this source module. To provide something visible for
434 use in the interface/descriptor, we have this static method that is
437 return request_buffer_factory (num_requests);
441 Push2::do_request (Push2Request * req)
443 if (req->type == CallSlot) {
445 call_slot (MISSING_INVALIDATOR, req->the_slot);
447 } else if (req->type == Quit) {
465 set_current_layout (splash_layout);
466 splash_start = get_microseconds ();
474 /* display splash for 2 seconds */
476 if (get_microseconds() - splash_start > 2000000) {
478 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
479 set_current_layout (mix_layout);
489 Push2::set_active (bool yn)
491 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
493 if (yn == active()) {
499 /* start event loop */
504 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
509 /* Connect input port to event loop */
513 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
514 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
515 asp->xthread().attach (main_loop()->get_context());
517 connect_session_signals ();
519 /* set up periodic task used to push a frame buffer to the
520 * device (25fps). The device can handle 60fps, but we don't
521 * need that frame rate.
524 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
525 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
526 vblank_timeout->attach (main_loop()->get_context());
529 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
530 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
531 periodic_timeout->attach (main_loop()->get_context());
535 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
544 ControlProtocol::set_active (yn);
546 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
552 Push2::init_touch_strip ()
554 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
555 /* flags are the final byte (ignore end-of-sysex */
557 /* show bar, not point
561 msg[7] = (1<<4) | (1<<5) | (1<<6);
566 Push2::write (const MidiByteArray& data)
568 /* immediate delivery */
569 _output_port->write (&data[0], data.size(), 0);
573 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
576 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
582 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
584 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
589 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
590 framepos_t now = AudioEngine::instance()->sample_time();
604 Push2::connect_to_parser ()
606 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
608 MIDI::Parser* p = _input_port->parser();
611 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
612 /* V-Pot messages are Controller */
613 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
614 /* Button messages are NoteOn */
615 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
616 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
617 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
618 /* Fader messages are Pitchbend */
619 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
623 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
625 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
626 MidiByteArray msg (sz, raw_bytes);
627 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
628 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
630 if (msg == aftertouch_mode_response) {
631 _pressure_mode = AfterTouch;
632 PressureModeChange (AfterTouch);
633 cerr << "Pressure mod eis after\n";
634 } else if (msg == polypress_mode_response) {
635 _pressure_mode = PolyPressure;
636 PressureModeChange (PolyPressure);
637 cerr << "Pressure mod eis poly\n";
642 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
644 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
646 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
649 /* any press cancels any pending long press timeouts */
650 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
651 Button* bb = id_button_map[*x];
652 bb->timeout_connection.disconnect ();
656 if (b != cc_button_map.end()) {
658 Button* button = b->second;
661 buttons_down.insert (button->id);
662 start_press_timeout (*button, button->id);
664 buttons_down.erase (button->id);
665 button->timeout_connection.disconnect ();
669 set<ButtonID>::iterator c = consumed.find (button->id);
671 if (c == consumed.end()) {
672 if (ev->value == 0) {
673 (this->*button->release_method)();
675 (this->*button->press_method)();
678 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
686 int delta = ev->value;
689 delta = -(128 - delta);
692 switch (ev->controller_number) {
694 _current_layout->strip_vpot (0, delta);
697 _current_layout->strip_vpot (1, delta);
700 _current_layout->strip_vpot (2, delta);
703 _current_layout->strip_vpot (3, delta);
706 _current_layout->strip_vpot (4, delta);
709 _current_layout->strip_vpot (5, delta);
712 _current_layout->strip_vpot (6, delta);
715 _current_layout->strip_vpot (7, delta);
720 other_vpot (8, delta);
723 other_vpot (1, delta);
728 other_vpot (2, delta);
735 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
737 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
739 if (ev->velocity == 0) {
740 handle_midi_note_off_message (parser, ev);
744 switch (ev->note_number) {
746 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
749 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
752 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
755 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
758 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
761 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
764 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
767 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
772 other_vpot_touch (0, ev->velocity > 64);
775 other_vpot_touch (1, ev->velocity > 64);
780 other_vpot_touch (3, ev->velocity > 64);
785 if (ev->velocity < 64) {
791 if (ev->note_number < 11) {
797 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
799 if (pi == nn_pad_map.end()) {
803 Pad* pad = pi->second;
805 if (pad->do_when_pressed == Pad::FlashOn) {
806 pad->set_color (LED::White);
807 pad->set_state (LED::OneShot24th);
808 write (pad->state_msg());
809 } else if (pad->do_when_pressed == Pad::FlashOff) {
810 pad->set_color (LED::Black);
811 pad->set_state (LED::OneShot24th);
812 write (pad->state_msg());
817 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
819 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
821 if (ev->note_number < 11) {
822 /* theoretically related to encoder touch start/end, but
823 * actually they send note on with two different velocity
829 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
831 if (pi == nn_pad_map.end()) {
835 Pad* pad = pi->second;
837 if (pad->do_when_pressed == Pad::FlashOn) {
838 pad->set_color (LED::Black);
839 pad->set_state (LED::OneShot24th);
840 write (pad->state_msg());
841 } else if (pad->do_when_pressed == Pad::FlashOff) {
842 pad->set_color (pad->perma_color);
843 pad->set_state (LED::OneShot24th);
844 write (pad->state_msg());
849 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
854 Push2::thread_init ()
856 struct sched_param rtparam;
858 pthread_set_name (event_loop_name().c_str());
860 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
861 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
863 memset (&rtparam, 0, sizeof (rtparam));
864 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
866 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
867 // do we care? not particularly.
872 Push2::connect_session_signals()
874 // receive routes added
875 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
876 // receive VCAs added
877 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
879 // receive record state toggled
880 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
881 // receive transport state changed
882 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
883 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
884 // receive punch-in and punch-out
885 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
886 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
887 // receive rude solo changed
888 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
892 Push2::notify_record_state_changed ()
894 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
896 if (b == id_button_map.end()) {
900 switch (session->record_status ()) {
901 case Session::Disabled:
902 b->second->set_color (LED::White);
903 b->second->set_state (LED::NoTransition);
905 case Session::Enabled:
906 b->second->set_color (LED::Red);
907 b->second->set_state (LED::Blinking4th);
909 case Session::Recording:
910 b->second->set_color (LED::Red);
911 b->second->set_state (LED::OneShot24th);
915 write (b->second->state_msg());
919 Push2::notify_transport_state_changed ()
921 Button* b = id_button_map[Play];
923 if (session->transport_rolling()) {
924 b->set_state (LED::OneShot24th);
925 b->set_color (LED::Green);
928 /* disable any blink on FixedLength from pending edit range op */
929 Button* fl = id_button_map[FixedLength];
931 fl->set_color (LED::Black);
932 fl->set_state (LED::NoTransition);
933 write (fl->state_msg());
935 b->set_color (LED::White);
936 b->set_state (LED::NoTransition);
939 write (b->state_msg());
943 Push2::notify_loop_state_changed ()
948 Push2::notify_parameter_changed (std::string param)
950 IDButtonMap::iterator b;
952 if (param == "clicking") {
953 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
956 if (Config->get_clicking()) {
957 b->second->set_state (LED::Blinking4th);
958 b->second->set_color (LED::White);
960 b->second->set_color (LED::White);
961 b->second->set_state (LED::NoTransition);
963 write (b->second->state_msg ());
968 Push2::notify_solo_active_changed (bool yn)
970 IDButtonMap::iterator b = id_button_map.find (Solo);
972 if (b == id_button_map.end()) {
977 b->second->set_state (LED::Blinking4th);
978 b->second->set_color (LED::Red);
980 b->second->set_state (LED::NoTransition);
981 b->second->set_color (LED::White);
984 write (b->second->state_msg());
990 XMLNode& node (ControlProtocol::get_state());
993 child = new XMLNode (X_("Input"));
994 child->add_child_nocopy (_async_in->get_state());
995 node.add_child_nocopy (*child);
996 child = new XMLNode (X_("Output"));
997 child->add_child_nocopy (_async_out->get_state());
998 node.add_child_nocopy (*child);
1000 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1001 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1002 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1003 node.add_property (X_("mode"), enum_2_string (_mode));
1009 Push2::set_state (const XMLNode & node, int version)
1011 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1015 if (ControlProtocol::set_state (node, version)) {
1021 if ((child = node.child (X_("Input"))) != 0) {
1022 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1024 _async_in->set_state (*portnode, version);
1028 if ((child = node.child (X_("Output"))) != 0) {
1029 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1031 _async_out->set_state (*portnode, version);
1035 XMLProperty const* prop;
1037 if ((prop = node.property (X_("root"))) != 0) {
1038 _scale_root = atoi (prop->value());
1041 if ((prop = node.property (X_("root_octave"))) != 0) {
1042 _root_octave = atoi (prop->value());
1045 if ((prop = node.property (X_("in_key"))) != 0) {
1046 _in_key = string_is_affirmative (prop->value());
1049 if ((prop = node.property (X_("mode"))) != 0) {
1050 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1057 Push2::other_vpot (int n, int delta)
1065 /* master gain control */
1067 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1069 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1077 Push2::other_vpot_touch (int n, bool touching)
1086 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1089 ac->start_touch (session->audible_frame());
1091 ac->stop_touch (true, session->audible_frame());
1099 Push2::start_shift ()
1101 cerr << "start shift\n";
1102 _modifier_state = ModifierState (_modifier_state | ModShift);
1103 Button* b = id_button_map[Shift];
1104 b->set_color (LED::White);
1105 b->set_state (LED::Blinking16th);
1106 write (b->state_msg());
1112 if (_modifier_state & ModShift) {
1113 cerr << "end shift\n";
1114 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1115 Button* b = id_button_map[Shift];
1116 b->timeout_connection.disconnect ();
1117 b->set_color (LED::White);
1118 b->set_state (LED::OneShot24th);
1119 write (b->state_msg());
1124 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1126 /* This filter is called asynchronously from a realtime process
1127 context. It must use atomics to check state, and must not block.
1130 bool matched = false;
1132 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1133 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1135 /* encoder touch start/touch end use note
1136 * 0-10. touchstrip uses note 12
1139 if ((*ev).note() > 10 && (*ev).note() != 12) {
1141 const int n = (*ev).note ();
1142 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1144 if (nni != nn_pad_map.end()) {
1145 Pad const * pad = nni->second;
1146 /* shift for output to the shadow port */
1147 if (pad->filtered >= 0) {
1148 (*ev).set_note (pad->filtered + (octave_shift*12));
1149 out.push_back (*ev);
1150 /* shift back so that the pads light correctly */
1153 /* no mapping, don't send event */
1156 out.push_back (*ev);
1161 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1162 out.push_back (*ev);
1170 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1172 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1173 if (!_input_port || !_output_port) {
1177 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1178 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1180 if (ni == name1 || ni == name2) {
1182 connection_state |= InputConnected;
1184 connection_state &= ~InputConnected;
1186 } else if (no == name1 || no == name2) {
1188 connection_state |= OutputConnected;
1190 connection_state &= ~OutputConnected;
1193 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1198 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1200 /* XXX this is a horrible hack. Without a short sleep here,
1201 something prevents the device wakeup messages from being
1202 sent and/or the responses from being received.
1206 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1210 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1213 ConnectionChange (); /* emit signal for our GUI */
1215 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1217 return true; /* connection status changed */
1223 request_pressure_mode ();
1226 boost::shared_ptr<Port>
1227 Push2::output_port()
1232 boost::shared_ptr<Port>
1239 Push2::pad_note (int row, int col) const
1241 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1243 if (nni != nn_pad_map.end()) {
1244 return nni->second->filtered;
1251 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1253 MusicalMode m (mode);
1254 vector<float>::iterator interval;
1256 const int original_root = root;
1258 interval = m.steps.begin();
1259 root += (octave*12);
1262 const int root_start = root;
1264 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1265 vector<int> mode_vector; /* sorted in note order */
1267 mode_map.insert (note);
1268 mode_vector.push_back (note);
1270 /* build a map of all notes in the mode, from the root to 127 */
1272 while (note < 128) {
1274 if (interval == m.steps.end()) {
1276 /* last distance was the end of the scale,
1277 so wrap, adding the next note at one
1278 octave above the last root.
1281 interval = m.steps.begin();
1283 mode_map.insert (root);
1284 mode_vector.push_back (root);
1287 note = (int) floor (root + (2.0 * (*interval)));
1289 mode_map.insert (note);
1290 mode_vector.push_back (note);
1296 vector<int>::iterator notei;
1298 for (int row = 0; row < 8; ++row) {
1300 /* Ableton's grid layout wraps the available notes in the scale
1301 * by offsetting 3 notes per row (from the bottom)
1304 notei = mode_vector.begin();
1305 notei += row_offset;
1308 for (int col = 0; col < 8; ++col) {
1309 int index = 36 + (row*8) + col;
1310 Pad* pad = nn_pad_map[index];
1312 if (notei != mode_vector.end()) {
1315 pad->filtered = notenum;
1317 if ((notenum % 12) == original_root) {
1318 pad->set_color (LED::Green);
1319 pad->perma_color = LED::Green;
1321 pad->set_color (LED::White);
1322 pad->perma_color = LED::White;
1325 pad->do_when_pressed = Pad::FlashOff;
1330 pad->set_color (LED::Black);
1331 pad->do_when_pressed = Pad::Nothing;
1335 write (pad->state_msg());
1341 /* chromatic: all notes available, but highlight those in the scale */
1343 for (note = 36; note < 100; ++note) {
1345 Pad* pad = nn_pad_map[note];
1347 /* Chromatic: all pads play, half-tone steps. Light
1348 * those in the scale, and highlight root notes
1351 pad->filtered = root_start + (note - 36);
1353 if (mode_map.find (note) != mode_map.end()) {
1355 if ((note % 12) == original_root) {
1356 pad->set_color (LED::Green);
1357 pad->perma_color = LED::Green;
1359 pad->set_color (LED::White);
1360 pad->perma_color = LED::White;
1363 pad->do_when_pressed = Pad::FlashOff;
1367 /* note is not in mode, turn it off */
1369 pad->do_when_pressed = Pad::FlashOn;
1370 pad->set_color (LED::Black);
1374 write (pad->state_msg());
1380 _scale_root = original_root;
1381 _root_octave = octave;
1385 ScaleChange (); /* EMIT SIGNAL */
1389 Push2::set_percussive_mode (bool yn)
1392 cerr << "back to scale\n";
1393 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1400 for (int row = 0; row < 8; ++row) {
1402 for (int col = 0; col < 4; ++col) {
1404 int index = 36 + (row*8) + col;
1405 Pad* pad = nn_pad_map[index];
1407 pad->filtered = drum_note;
1412 for (int row = 0; row < 8; ++row) {
1414 for (int col = 4; col < 8; ++col) {
1416 int index = 36 + (row*8) + col;
1417 Pad* pad = nn_pad_map[index];
1419 pad->filtered = drum_note;
1428 Push2::current_layout () const
1430 Glib::Threads::Mutex::Lock lm (layout_lock);
1431 return _current_layout;
1435 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1437 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1438 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1439 boost::shared_ptr<MidiTrack> new_pad_target;
1441 /* See if there's a MIDI track selected */
1443 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1445 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1447 if (new_pad_target) {
1452 if (current_midi_track == new_pad_target) {
1457 if (!new_pad_target) {
1458 /* leave existing connection alone */
1462 /* disconnect from pad port, if appropriate */
1464 if (current_midi_track && pad_port) {
1465 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1468 /* now connect the pad port to this (newly) selected midi
1469 * track, if indeed there is one.
1472 if (new_pad_target && pad_port) {
1473 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1474 current_pad_target = new_pad_target;
1476 current_pad_target.reset ();
1481 Push2::button_by_id (ButtonID bid)
1483 return id_button_map[bid];
1487 Push2::get_color_index (uint32_t rgb)
1489 ColorMap::iterator i = color_map.find (rgb);
1491 if (i != color_map.end()) {
1496 UINT_TO_RGBA (rgb, &r, &g, &b, &a);
1497 int w = 204; /* not sure where/when we should get this value */
1499 /* get a free index */
1503 if (color_map_free_list.empty()) {
1504 /* random replacement of any entry above zero and below 122 (where the
1505 * Ableton standard colors live)
1507 index = 1 + (random() % 121);
1509 index = color_map_free_list.top();
1510 color_map_free_list.pop();
1513 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1514 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1516 palette_msg[7] = index;
1517 palette_msg[8] = r & 0x7f;
1518 palette_msg[9] = r & 0x1;
1519 palette_msg[10] = g & 0x7f;
1520 palette_msg[11] = g & 0x1;
1521 palette_msg[12] = b & 0x7f;
1522 palette_msg[13] = b & 0x1;
1523 palette_msg[14] = w & 0x7f;
1524 palette_msg[15] = w & 0x1;
1526 write (palette_msg);
1527 write (update_pallette_msg);
1529 color_map[index] = rgb;
1535 Push2::build_color_map ()
1537 /* These are "standard" colors that Ableton docs suggest will always be
1538 there. Put them in our color map so that when we look up these
1539 colors, we will use the Ableton indices for them.
1542 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1543 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1544 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1545 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1546 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1547 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1548 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1550 for (uint8_t n = 1; n < 122; ++n) {
1551 color_map_free_list.push (n);
1556 Push2::fill_color_table ()
1558 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1559 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1561 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1563 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1564 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1565 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1567 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1568 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1570 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1571 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1572 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1573 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1578 Push2::get_color (ColorName name)
1580 Colors::iterator c = colors.find (name);
1581 if (c != colors.end()) {
1589 Push2::set_current_layout (Push2Layout* layout)
1591 if (_current_layout) {
1592 _current_layout->hide ();
1593 _canvas->root()->remove (_current_layout);
1594 _previous_layout = _current_layout;
1597 /* turn off all strip buttons - let new layout set them if it
1601 strip_buttons_off ();
1603 _current_layout = layout;
1605 if (_current_layout) {
1606 _canvas->root()->add (_current_layout);
1607 _current_layout->show ();
1610 _canvas->request_redraw ();
1614 Push2::use_previous_layout ()
1616 if (_previous_layout) {
1617 set_current_layout (_previous_layout);
1622 Push2::request_pressure_mode ()
1624 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1629 Push2::set_pressure_mode (PressureMode pm)
1631 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1635 /* nothing to do, message is correct */
1645 cerr << "Sent PM message " << msg << endl;