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)
138 , selection_color (LED::Green)
139 , contrast_color (LED::Green)
146 /* master cannot be removed, so no need to connect to going-away signal */
147 master = session->master_out ();
150 throw failed_constructor ();
153 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
155 /* catch current selection, if any */
157 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
158 stripable_selection_change (sp);
161 /* catch arrival and departure of Push2 itself */
162 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
164 /* Catch port connections and disconnections */
165 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
167 /* ports might already be there */
168 port_registration_handler ();
175 delete track_mix_layout;
181 Push2::port_registration_handler ()
183 if (!_async_in && !_async_out) {
184 /* ports not registered yet */
188 if (_async_in->connected() && _async_out->connected()) {
189 /* don't waste cycles here */
193 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
194 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
198 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
199 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
201 if (!in.empty() && !out.empty()) {
202 cerr << "Push2: both ports found\n";
203 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
204 if (!_async_in->connected()) {
205 AudioEngine::instance()->connect (_async_in->name(), in.front());
207 if (!_async_out->connected()) {
208 AudioEngine::instance()->connect (_async_out->name(), out.front());
223 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
227 if ((err = libusb_claim_interface (handle, 0x00))) {
232 _canvas = new Push2Canvas (*this, 960, 160);
233 mix_layout = new MixLayout (*this, *session);
234 scale_layout = new ScaleLayout (*this, *session);
235 track_mix_layout = new TrackMixLayout (*this, *session);
236 splash_layout = new SplashLayout (*this, *session);
238 error << _("Cannot construct Canvas for display") << endmsg;
239 libusb_release_interface (handle, 0x00);
240 libusb_close (handle);
246 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
247 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
249 if (_async_in == 0 || _async_out == 0) {
253 /* We do not add our ports to the input/output bundles because we don't
254 * want users wiring them by hand. They could use JACK tools if they
255 * really insist on that.
258 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
259 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
261 /* Create a shadow port where, depending on the state of the surface,
262 * we will make pad note on/off events appear. The surface code will
263 * automatically this port to the first selected MIDI track.
266 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));
267 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
271 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
273 _output_bundle->add_channel (
275 ARDOUR::DataType::MIDI,
276 session->engine().make_port_name_non_relative (shadow_port->name())
280 session->BundleAddedOrRemoved ();
282 connect_to_parser ();
287 list<boost::shared_ptr<ARDOUR::Bundle> >
290 list<boost::shared_ptr<ARDOUR::Bundle> > b;
292 if (_output_bundle) {
293 b.push_back (_output_bundle);
302 init_buttons (false);
303 strip_buttons_off ();
305 /* wait for button data to be flushed */
307 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
308 asp->drain (10000, 500000);
310 AudioEngine::instance()->unregister_port (_async_in);
311 AudioEngine::instance()->unregister_port (_async_out);
313 _async_in.reset ((ARDOUR::Port*) 0);
314 _async_out.reset ((ARDOUR::Port*) 0);
318 periodic_connection.disconnect ();
319 session_connections.drop_connections ();
321 if (_current_layout) {
322 _canvas->root()->remove (_current_layout);
330 delete splash_layout;
334 libusb_release_interface (handle, 0x00);
335 libusb_close (handle);
343 Push2::strip_buttons_off ()
345 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
346 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
348 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
349 Button* b = id_button_map[strip_buttons[n]];
351 b->set_color (LED::Black);
352 b->set_state (LED::OneShot24th);
353 write (b->state_msg());
359 Push2::init_buttons (bool startup)
361 /* This is a list of buttons that we want lit because they do something
362 in ardour related (loosely, sometimes) to their illuminated label.
365 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
366 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
367 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
370 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
371 Button* b = id_button_map[buttons[n]];
374 b->set_color (LED::White);
376 b->set_color (LED::Black);
378 b->set_state (LED::OneShot24th);
379 write (b->state_msg());
384 /* all other buttons are off (black) */
386 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
387 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
388 Accent, Note, Session, };
390 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
391 Button* b = id_button_map[off_buttons[n]];
393 b->set_color (LED::Black);
394 b->set_state (LED::OneShot24th);
395 write (b->state_msg());
400 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
401 Pad* pad = pi->second;
403 pad->set_color (LED::Black);
404 pad->set_state (LED::OneShot24th);
405 write (pad->state_msg());
413 libusb_device_handle *h;
416 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
417 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
422 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
427 Push2::request_factory (uint32_t num_requests)
429 /* AbstractUI<T>::request_buffer_factory() is a template method only
430 instantiated in this source module. To provide something visible for
431 use in the interface/descriptor, we have this static method that is
434 return request_buffer_factory (num_requests);
438 Push2::do_request (Push2Request * req)
440 if (req->type == CallSlot) {
442 call_slot (MISSING_INVALIDATOR, req->the_slot);
444 } else if (req->type == Quit) {
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);
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 /* start event loop */
501 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
506 /* Connect input port to event loop */
510 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
511 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
512 asp->xthread().attach (main_loop()->get_context());
514 connect_session_signals ();
516 /* set up periodic task used to push a frame buffer to the
517 * device (25fps). The device can handle 60fps, but we don't
518 * need that frame rate.
521 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
522 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
523 vblank_timeout->attach (main_loop()->get_context());
526 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
527 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
528 periodic_timeout->attach (main_loop()->get_context());
532 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
541 ControlProtocol::set_active (yn);
543 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
549 Push2::init_touch_strip ()
551 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
552 /* flags are the final byte (ignore end-of-sysex */
554 /* show bar, not point
558 msg[7] = (1<<4) | (1<<5) | (1<<6);
563 Push2::write (const MidiByteArray& data)
565 /* immediate delivery */
566 _output_port->write (&data[0], data.size(), 0);
570 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
573 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
579 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
581 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
586 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
587 framepos_t now = AudioEngine::instance()->sample_time();
601 Push2::connect_to_parser ()
603 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
605 MIDI::Parser* p = _input_port->parser();
608 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
609 /* V-Pot messages are Controller */
610 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
611 /* Button messages are NoteOn */
612 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
613 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
614 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
615 /* Fader messages are Pitchbend */
616 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
620 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
622 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
623 MidiByteArray msg (sz, raw_bytes);
624 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
625 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
627 if (msg == aftertouch_mode_response) {
628 _pressure_mode = AfterTouch;
629 PressureModeChange (AfterTouch);
630 cerr << "Pressure mod eis after\n";
631 } else if (msg == polypress_mode_response) {
632 _pressure_mode = PolyPressure;
633 PressureModeChange (PolyPressure);
634 cerr << "Pressure mod eis poly\n";
639 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
641 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
643 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
646 /* any press cancels any pending long press timeouts */
647 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
648 Button* bb = id_button_map[*x];
649 bb->timeout_connection.disconnect ();
653 if (b != cc_button_map.end()) {
655 Button* button = b->second;
658 buttons_down.insert (button->id);
659 start_press_timeout (*button, button->id);
661 buttons_down.erase (button->id);
662 button->timeout_connection.disconnect ();
666 set<ButtonID>::iterator c = consumed.find (button->id);
668 if (c == consumed.end()) {
669 if (ev->value == 0) {
670 (this->*button->release_method)();
672 (this->*button->press_method)();
675 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
683 int delta = ev->value;
686 delta = -(128 - delta);
689 switch (ev->controller_number) {
691 _current_layout->strip_vpot (0, delta);
694 _current_layout->strip_vpot (1, delta);
697 _current_layout->strip_vpot (2, delta);
700 _current_layout->strip_vpot (3, delta);
703 _current_layout->strip_vpot (4, delta);
706 _current_layout->strip_vpot (5, delta);
709 _current_layout->strip_vpot (6, delta);
712 _current_layout->strip_vpot (7, delta);
717 other_vpot (8, delta);
720 other_vpot (1, delta);
725 other_vpot (2, delta);
732 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
734 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
736 if (ev->velocity == 0) {
737 handle_midi_note_off_message (parser, ev);
741 switch (ev->note_number) {
743 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
746 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
749 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
752 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
755 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
758 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
761 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
764 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
769 other_vpot_touch (0, ev->velocity > 64);
772 other_vpot_touch (1, ev->velocity > 64);
777 other_vpot_touch (3, ev->velocity > 64);
782 if (ev->velocity < 64) {
788 if (ev->note_number < 11) {
792 /* Pad illuminations */
794 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
796 if (pm == nn_pad_map.end()) {
800 const Pad * const pad_pressed = pm->second;
802 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
804 if (pads_with_note.first == fn_pad_map.end()) {
808 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
809 Pad* pad = pi->second;
811 pad->set_color (contrast_color);
812 pad->set_state (LED::OneShot24th);
813 write (pad->state_msg());
818 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
820 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
822 if (ev->note_number < 11) {
823 /* theoretically related to encoder touch start/end, but
824 * actually they send note on with two different velocity
830 /* Pad illuminations */
832 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
834 if (pm == nn_pad_map.end()) {
838 const Pad * const pad_pressed = pm->second;
840 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
842 if (pads_with_note.first == fn_pad_map.end()) {
846 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
847 Pad* pad = pi->second;
849 if (pad->do_when_pressed == Pad::FlashOn) {
850 pad->set_color (LED::Black);
851 pad->set_state (LED::OneShot24th);
852 write (pad->state_msg());
853 } else if (pad->do_when_pressed == Pad::FlashOff) {
854 pad->set_color (pad->perma_color);
855 pad->set_state (LED::OneShot24th);
856 write (pad->state_msg());
862 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
867 Push2::thread_init ()
869 struct sched_param rtparam;
871 pthread_set_name (event_loop_name().c_str());
873 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
874 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
876 memset (&rtparam, 0, sizeof (rtparam));
877 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
879 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
880 // do we care? not particularly.
885 Push2::connect_session_signals()
887 // receive routes added
888 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
889 // receive VCAs added
890 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
892 // receive record state toggled
893 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
894 // receive transport state changed
895 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
896 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
897 // receive punch-in and punch-out
898 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
899 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
900 // receive rude solo changed
901 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
905 Push2::notify_record_state_changed ()
907 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
909 if (b == id_button_map.end()) {
913 switch (session->record_status ()) {
914 case Session::Disabled:
915 b->second->set_color (LED::White);
916 b->second->set_state (LED::NoTransition);
918 case Session::Enabled:
919 b->second->set_color (LED::Red);
920 b->second->set_state (LED::Blinking4th);
922 case Session::Recording:
923 b->second->set_color (LED::Red);
924 b->second->set_state (LED::OneShot24th);
928 write (b->second->state_msg());
932 Push2::notify_transport_state_changed ()
934 Button* b = id_button_map[Play];
936 if (session->transport_rolling()) {
937 b->set_state (LED::OneShot24th);
938 b->set_color (LED::Green);
941 /* disable any blink on FixedLength from pending edit range op */
942 Button* fl = id_button_map[FixedLength];
944 fl->set_color (LED::Black);
945 fl->set_state (LED::NoTransition);
946 write (fl->state_msg());
948 b->set_color (LED::White);
949 b->set_state (LED::NoTransition);
952 write (b->state_msg());
956 Push2::notify_loop_state_changed ()
961 Push2::notify_parameter_changed (std::string param)
963 IDButtonMap::iterator b;
965 if (param == "clicking") {
966 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
969 if (Config->get_clicking()) {
970 b->second->set_state (LED::Blinking4th);
971 b->second->set_color (LED::White);
973 b->second->set_color (LED::White);
974 b->second->set_state (LED::NoTransition);
976 write (b->second->state_msg ());
981 Push2::notify_solo_active_changed (bool yn)
983 IDButtonMap::iterator b = id_button_map.find (Solo);
985 if (b == id_button_map.end()) {
990 b->second->set_state (LED::Blinking4th);
991 b->second->set_color (LED::Red);
993 b->second->set_state (LED::NoTransition);
994 b->second->set_color (LED::White);
997 write (b->second->state_msg());
1003 XMLNode& node (ControlProtocol::get_state());
1006 child = new XMLNode (X_("Input"));
1007 child->add_child_nocopy (_async_in->get_state());
1008 node.add_child_nocopy (*child);
1009 child = new XMLNode (X_("Output"));
1010 child->add_child_nocopy (_async_out->get_state());
1011 node.add_child_nocopy (*child);
1013 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1014 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1015 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1016 node.add_property (X_("mode"), enum_2_string (_mode));
1022 Push2::set_state (const XMLNode & node, int version)
1024 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1028 if (ControlProtocol::set_state (node, version)) {
1034 if ((child = node.child (X_("Input"))) != 0) {
1035 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1037 _async_in->set_state (*portnode, version);
1041 if ((child = node.child (X_("Output"))) != 0) {
1042 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1044 _async_out->set_state (*portnode, version);
1048 XMLProperty const* prop;
1050 if ((prop = node.property (X_("root"))) != 0) {
1051 _scale_root = atoi (prop->value());
1054 if ((prop = node.property (X_("root_octave"))) != 0) {
1055 _root_octave = atoi (prop->value());
1058 if ((prop = node.property (X_("in_key"))) != 0) {
1059 _in_key = string_is_affirmative (prop->value());
1062 if ((prop = node.property (X_("mode"))) != 0) {
1063 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1070 Push2::other_vpot (int n, int delta)
1078 /* master gain control */
1080 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1082 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1090 Push2::other_vpot_touch (int n, bool touching)
1099 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1102 ac->start_touch (session->audible_frame());
1104 ac->stop_touch (true, session->audible_frame());
1112 Push2::start_shift ()
1114 cerr << "start shift\n";
1115 _modifier_state = ModifierState (_modifier_state | ModShift);
1116 Button* b = id_button_map[Shift];
1117 b->set_color (LED::White);
1118 b->set_state (LED::Blinking16th);
1119 write (b->state_msg());
1125 if (_modifier_state & ModShift) {
1126 cerr << "end shift\n";
1127 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1128 Button* b = id_button_map[Shift];
1129 b->timeout_connection.disconnect ();
1130 b->set_color (LED::White);
1131 b->set_state (LED::OneShot24th);
1132 write (b->state_msg());
1137 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1139 /* This filter is called asynchronously from a realtime process
1140 context. It must use atomics to check state, and must not block.
1143 bool matched = false;
1145 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1146 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1148 /* encoder touch start/touch end use note
1149 * 0-10. touchstrip uses note 12
1152 if ((*ev).note() > 10 && (*ev).note() != 12) {
1154 const int n = (*ev).note ();
1155 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1157 if (nni != nn_pad_map.end()) {
1158 Pad const * pad = nni->second;
1159 /* shift for output to the shadow port */
1160 if (pad->filtered >= 0) {
1161 (*ev).set_note (pad->filtered + (octave_shift*12));
1162 out.push_back (*ev);
1163 /* shift back so that the pads light correctly */
1166 /* no mapping, don't send event */
1169 out.push_back (*ev);
1174 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1175 out.push_back (*ev);
1183 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1185 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1186 if (!_input_port || !_output_port) {
1190 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1191 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1193 if (ni == name1 || ni == name2) {
1195 connection_state |= InputConnected;
1197 connection_state &= ~InputConnected;
1199 } else if (no == name1 || no == name2) {
1201 connection_state |= OutputConnected;
1203 connection_state &= ~OutputConnected;
1206 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1211 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1213 /* XXX this is a horrible hack. Without a short sleep here,
1214 something prevents the device wakeup messages from being
1215 sent and/or the responses from being received.
1219 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1223 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1226 ConnectionChange (); /* emit signal for our GUI */
1228 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1230 return true; /* connection status changed */
1236 request_pressure_mode ();
1239 boost::shared_ptr<Port>
1240 Push2::output_port()
1245 boost::shared_ptr<Port>
1252 Push2::pad_note (int row, int col) const
1254 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1256 if (nni != nn_pad_map.end()) {
1257 return nni->second->filtered;
1264 Push2::reset_pad_colors ()
1266 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1270 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1272 MusicalMode m (mode);
1273 vector<float>::iterator interval;
1275 const int original_root = root;
1277 interval = m.steps.begin();
1278 root += (octave*12);
1281 const int root_start = root;
1283 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1284 vector<int> mode_vector; /* sorted in note order */
1286 mode_map.insert (note);
1287 mode_vector.push_back (note);
1289 /* build a map of all notes in the mode, from the root to 127 */
1291 while (note < 128) {
1293 if (interval == m.steps.end()) {
1295 /* last distance was the end of the scale,
1296 so wrap, adding the next note at one
1297 octave above the last root.
1300 interval = m.steps.begin();
1302 mode_map.insert (root);
1303 mode_vector.push_back (root);
1306 note = (int) floor (root + (2.0 * (*interval)));
1308 mode_map.insert (note);
1309 mode_vector.push_back (note);
1313 fn_pad_map.clear ();
1317 vector<int>::iterator notei;
1320 for (int row = 0; row < 8; ++row) {
1322 /* Ableton's grid layout wraps the available notes in the scale
1323 * by offsetting 3 notes per row (from the bottom)
1326 notei = mode_vector.begin();
1327 notei += row_offset;
1330 for (int col = 0; col < 8; ++col) {
1331 int index = 36 + (row*8) + col;
1332 Pad* pad = nn_pad_map[index];
1334 if (notei != mode_vector.end()) {
1337 pad->filtered = notenum;
1339 fn_pad_map.insert (make_pair (notenum, pad));
1341 if ((notenum % 12) == original_root) {
1342 pad->set_color (selection_color);
1343 pad->perma_color = selection_color;
1345 pad->set_color (LED::White);
1346 pad->perma_color = LED::White;
1349 pad->do_when_pressed = Pad::FlashOff;
1354 pad->set_color (LED::Black);
1355 pad->do_when_pressed = Pad::Nothing;
1359 pad->set_state (LED::OneShot24th);
1360 write (pad->state_msg());
1366 /* chromatic: all notes available, but highlight those in the scale */
1368 for (note = 36; note < 100; ++note) {
1370 Pad* pad = nn_pad_map[note];
1372 /* Chromatic: all pads play, half-tone steps. Light
1373 * those in the scale, and highlight root notes
1376 pad->filtered = root_start + (note - 36);
1378 fn_pad_map.insert (make_pair (pad->filtered, pad));
1380 if (mode_map.find (note) != mode_map.end()) {
1382 if ((note % 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;
1394 /* note is not in mode, turn it off */
1396 pad->do_when_pressed = Pad::FlashOn;
1397 pad->set_color (LED::Black);
1401 pad->set_state (LED::OneShot24th);
1402 write (pad->state_msg());
1408 bool changed = false;
1410 if (_scale_root != original_root) {
1411 _scale_root = original_root;
1414 if (_root_octave != octave) {
1415 _root_octave = octave;
1418 if (_in_key != inkey) {
1422 if (_mode != mode) {
1428 ScaleChange (); /* EMIT SIGNAL */
1433 Push2::set_percussive_mode (bool yn)
1436 cerr << "back to scale\n";
1437 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1444 for (int row = 0; row < 8; ++row) {
1446 for (int col = 0; col < 4; ++col) {
1448 int index = 36 + (row*8) + col;
1449 Pad* pad = nn_pad_map[index];
1451 pad->filtered = drum_note;
1456 for (int row = 0; row < 8; ++row) {
1458 for (int col = 4; col < 8; ++col) {
1460 int index = 36 + (row*8) + col;
1461 Pad* pad = nn_pad_map[index];
1463 pad->filtered = drum_note;
1472 Push2::current_layout () const
1474 Glib::Threads::Mutex::Lock lm (layout_lock);
1475 return _current_layout;
1479 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1481 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1482 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1483 boost::shared_ptr<MidiTrack> new_pad_target;
1485 /* See if there's a MIDI track selected */
1487 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1489 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1491 if (new_pad_target) {
1496 if (current_midi_track == new_pad_target) {
1501 if (!new_pad_target) {
1502 /* leave existing connection alone */
1506 /* disconnect from pad port, if appropriate */
1508 if (current_midi_track && pad_port) {
1509 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1512 /* now connect the pad port to this (newly) selected midi
1513 * track, if indeed there is one.
1516 if (new_pad_target && pad_port) {
1517 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1518 current_pad_target = new_pad_target;
1519 selection_color = get_color_index (new_pad_target->presentation_info().color());
1520 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1522 current_pad_target.reset ();
1523 selection_color = LED::Green;
1524 contrast_color = LED::Green;
1527 reset_pad_colors ();
1531 Push2::button_by_id (ButtonID bid)
1533 return id_button_map[bid];
1537 Push2::get_color_index (ArdourCanvas::Color rgba)
1539 ColorMap::iterator i = color_map.find (rgba);
1541 if (i != color_map.end()) {
1546 UINT_TO_RGBA (rgba, &r, &g, &b, &a);
1547 int w = 204; /* not sure where/when we should get this value */
1549 /* get a free index */
1553 if (color_map_free_list.empty()) {
1554 /* random replacement of any entry above zero and below 122 (where the
1555 * Ableton standard colors live)
1557 index = 1 + (random() % 121);
1559 index = color_map_free_list.top();
1560 color_map_free_list.pop();
1563 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1564 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1566 palette_msg[7] = index;
1567 palette_msg[8] = r & 0x7f;
1568 palette_msg[9] = r & 0x1;
1569 palette_msg[10] = g & 0x7f;
1570 palette_msg[11] = g & 0x1;
1571 palette_msg[12] = b & 0x7f;
1572 palette_msg[13] = b & 0x1;
1573 palette_msg[14] = w & 0x7f;
1574 palette_msg[15] = w & 0x1;
1576 write (palette_msg);
1577 write (update_pallette_msg);
1579 color_map[rgba] = index;
1585 Push2::build_color_map ()
1587 /* These are "standard" colors that Ableton docs suggest will always be
1588 there. Put them in our color map so that when we look up these
1589 colors, we will use the Ableton indices for them.
1592 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1593 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1594 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1595 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1596 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1597 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1598 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1600 for (uint8_t n = 1; n < 122; ++n) {
1601 color_map_free_list.push (n);
1606 Push2::fill_color_table ()
1608 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1609 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1611 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1613 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1614 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1615 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1617 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1618 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1620 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1621 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1622 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1623 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1628 Push2::get_color (ColorName name)
1630 Colors::iterator c = colors.find (name);
1631 if (c != colors.end()) {
1639 Push2::set_current_layout (Push2Layout* layout)
1641 if (_current_layout) {
1642 _current_layout->hide ();
1643 _canvas->root()->remove (_current_layout);
1644 _previous_layout = _current_layout;
1647 _current_layout = layout;
1649 if (_current_layout) {
1650 _canvas->root()->add (_current_layout);
1651 _current_layout->show ();
1654 _canvas->request_redraw ();
1658 Push2::use_previous_layout ()
1660 if (_previous_layout) {
1661 set_current_layout (_previous_layout);
1666 Push2::request_pressure_mode ()
1668 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1673 Push2::set_pressure_mode (PressureMode pm)
1675 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1679 /* nothing to do, message is correct */
1689 cerr << "Sent PM message " << msg << endl;