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);
304 /* wait for button data to be flushed */
306 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
307 asp->drain (10000, 500000);
309 AudioEngine::instance()->unregister_port (_async_in);
310 AudioEngine::instance()->unregister_port (_async_out);
312 _async_in.reset ((ARDOUR::Port*) 0);
313 _async_out.reset ((ARDOUR::Port*) 0);
317 periodic_connection.disconnect ();
318 session_connections.drop_connections ();
320 if (_current_layout) {
321 _canvas->root()->remove (_current_layout);
329 delete splash_layout;
333 libusb_release_interface (handle, 0x00);
334 libusb_close (handle);
342 Push2::strip_buttons_off ()
344 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
345 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
347 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
348 Button* b = id_button_map[strip_buttons[n]];
350 b->set_color (LED::Black);
351 b->set_state (LED::OneShot24th);
352 write (b->state_msg());
358 Push2::init_buttons (bool startup)
360 /* This is a list of buttons that we want lit because they do something
361 in ardour related (loosely, sometimes) to their illuminated label.
364 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
365 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
366 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
369 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
370 Button* b = id_button_map[buttons[n]];
373 b->set_color (LED::White);
375 b->set_color (LED::Black);
377 b->set_state (LED::OneShot24th);
378 write (b->state_msg());
381 /* Strip buttons should all be off (black) by default. They will change
382 * color to reflect various conditions
385 strip_buttons_off ();
389 /* all other buttons are off (black) */
391 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
392 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
393 Accent, Note, Session, };
395 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
396 Button* b = id_button_map[off_buttons[n]];
398 b->set_color (LED::Black);
399 b->set_state (LED::OneShot24th);
400 write (b->state_msg());
405 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
406 Pad* pad = pi->second;
408 pad->set_color (LED::Black);
409 pad->set_state (LED::OneShot24th);
410 write (pad->state_msg());
418 libusb_device_handle *h;
421 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
422 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
427 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
432 Push2::request_factory (uint32_t num_requests)
434 /* AbstractUI<T>::request_buffer_factory() is a template method only
435 instantiated in this source module. To provide something visible for
436 use in the interface/descriptor, we have this static method that is
439 return request_buffer_factory (num_requests);
443 Push2::do_request (Push2Request * req)
445 if (req->type == CallSlot) {
447 call_slot (MISSING_INVALIDATOR, req->the_slot);
449 } else if (req->type == Quit) {
467 set_current_layout (splash_layout);
468 splash_start = get_microseconds ();
476 /* display splash for 2 seconds */
478 if (get_microseconds() - splash_start > 2000000) {
480 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
481 set_current_layout (mix_layout);
491 Push2::set_active (bool yn)
493 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
495 if (yn == active()) {
501 /* start event loop */
506 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
511 /* Connect input port to event loop */
515 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
516 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
517 asp->xthread().attach (main_loop()->get_context());
519 connect_session_signals ();
521 /* set up periodic task used to push a frame buffer to the
522 * device (25fps). The device can handle 60fps, but we don't
523 * need that frame rate.
526 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
527 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
528 vblank_timeout->attach (main_loop()->get_context());
531 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
532 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
533 periodic_timeout->attach (main_loop()->get_context());
537 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
546 ControlProtocol::set_active (yn);
548 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
554 Push2::init_touch_strip ()
556 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
557 /* flags are the final byte (ignore end-of-sysex */
559 /* show bar, not point
563 msg[7] = (1<<4) | (1<<5) | (1<<6);
568 Push2::write (const MidiByteArray& data)
570 /* immediate delivery */
571 _output_port->write (&data[0], data.size(), 0);
575 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
578 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
584 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
586 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
591 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
592 framepos_t now = AudioEngine::instance()->sample_time();
606 Push2::connect_to_parser ()
608 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
610 MIDI::Parser* p = _input_port->parser();
613 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
614 /* V-Pot messages are Controller */
615 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
616 /* Button messages are NoteOn */
617 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
618 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
619 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
620 /* Fader messages are Pitchbend */
621 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
625 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
627 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
628 MidiByteArray msg (sz, raw_bytes);
629 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
630 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
632 if (msg == aftertouch_mode_response) {
633 _pressure_mode = AfterTouch;
634 PressureModeChange (AfterTouch);
635 cerr << "Pressure mod eis after\n";
636 } else if (msg == polypress_mode_response) {
637 _pressure_mode = PolyPressure;
638 PressureModeChange (PolyPressure);
639 cerr << "Pressure mod eis poly\n";
644 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
646 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
648 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
651 /* any press cancels any pending long press timeouts */
652 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
653 Button* bb = id_button_map[*x];
654 bb->timeout_connection.disconnect ();
658 if (b != cc_button_map.end()) {
660 Button* button = b->second;
663 buttons_down.insert (button->id);
664 start_press_timeout (*button, button->id);
666 buttons_down.erase (button->id);
667 button->timeout_connection.disconnect ();
671 set<ButtonID>::iterator c = consumed.find (button->id);
673 if (c == consumed.end()) {
674 if (ev->value == 0) {
675 (this->*button->release_method)();
677 (this->*button->press_method)();
680 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
688 int delta = ev->value;
691 delta = -(128 - delta);
694 switch (ev->controller_number) {
696 _current_layout->strip_vpot (0, delta);
699 _current_layout->strip_vpot (1, delta);
702 _current_layout->strip_vpot (2, delta);
705 _current_layout->strip_vpot (3, delta);
708 _current_layout->strip_vpot (4, delta);
711 _current_layout->strip_vpot (5, delta);
714 _current_layout->strip_vpot (6, delta);
717 _current_layout->strip_vpot (7, delta);
722 other_vpot (8, delta);
725 other_vpot (1, delta);
730 other_vpot (2, delta);
737 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
739 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
741 if (ev->velocity == 0) {
742 handle_midi_note_off_message (parser, ev);
746 switch (ev->note_number) {
748 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
751 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
754 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
757 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
760 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
763 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
766 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
769 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
774 other_vpot_touch (0, ev->velocity > 64);
777 other_vpot_touch (1, ev->velocity > 64);
782 other_vpot_touch (3, ev->velocity > 64);
787 if (ev->velocity < 64) {
793 if (ev->note_number < 11) {
797 /* Pad illuminations */
799 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
801 if (pm == nn_pad_map.end()) {
805 const Pad * const pad_pressed = pm->second;
807 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
809 if (pads_with_note.first == fn_pad_map.end()) {
813 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
814 Pad* pad = pi->second;
816 if (pad->do_when_pressed == Pad::FlashOn) {
817 pad->set_color (LED::White);
818 pad->set_state (LED::OneShot24th);
819 write (pad->state_msg());
820 } else if (pad->do_when_pressed == Pad::FlashOff) {
821 pad->set_color (contrast_color);
822 pad->set_state (LED::OneShot24th);
823 write (pad->state_msg());
829 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
831 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
833 if (ev->note_number < 11) {
834 /* theoretically related to encoder touch start/end, but
835 * actually they send note on with two different velocity
841 /* Pad illuminations */
843 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
845 if (pm == nn_pad_map.end()) {
849 const Pad * const pad_pressed = pm->second;
851 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
853 if (pads_with_note.first == fn_pad_map.end()) {
857 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
858 Pad* pad = pi->second;
860 if (pad->do_when_pressed == Pad::FlashOn) {
861 pad->set_color (LED::Black);
862 pad->set_state (LED::OneShot24th);
863 write (pad->state_msg());
864 } else if (pad->do_when_pressed == Pad::FlashOff) {
865 pad->set_color (pad->perma_color);
866 pad->set_state (LED::OneShot24th);
867 write (pad->state_msg());
873 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
878 Push2::thread_init ()
880 struct sched_param rtparam;
882 pthread_set_name (event_loop_name().c_str());
884 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
885 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
887 memset (&rtparam, 0, sizeof (rtparam));
888 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
890 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
891 // do we care? not particularly.
896 Push2::connect_session_signals()
898 // receive routes added
899 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
900 // receive VCAs added
901 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
903 // receive record state toggled
904 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
905 // receive transport state changed
906 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
907 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
908 // receive punch-in and punch-out
909 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
910 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
911 // receive rude solo changed
912 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
916 Push2::notify_record_state_changed ()
918 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
920 if (b == id_button_map.end()) {
924 switch (session->record_status ()) {
925 case Session::Disabled:
926 b->second->set_color (LED::White);
927 b->second->set_state (LED::NoTransition);
929 case Session::Enabled:
930 b->second->set_color (LED::Red);
931 b->second->set_state (LED::Blinking4th);
933 case Session::Recording:
934 b->second->set_color (LED::Red);
935 b->second->set_state (LED::OneShot24th);
939 write (b->second->state_msg());
943 Push2::notify_transport_state_changed ()
945 Button* b = id_button_map[Play];
947 if (session->transport_rolling()) {
948 b->set_state (LED::OneShot24th);
949 b->set_color (LED::Green);
952 /* disable any blink on FixedLength from pending edit range op */
953 Button* fl = id_button_map[FixedLength];
955 fl->set_color (LED::Black);
956 fl->set_state (LED::NoTransition);
957 write (fl->state_msg());
959 b->set_color (LED::White);
960 b->set_state (LED::NoTransition);
963 write (b->state_msg());
967 Push2::notify_loop_state_changed ()
972 Push2::notify_parameter_changed (std::string param)
974 IDButtonMap::iterator b;
976 if (param == "clicking") {
977 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
980 if (Config->get_clicking()) {
981 b->second->set_state (LED::Blinking4th);
982 b->second->set_color (LED::White);
984 b->second->set_color (LED::White);
985 b->second->set_state (LED::NoTransition);
987 write (b->second->state_msg ());
992 Push2::notify_solo_active_changed (bool yn)
994 IDButtonMap::iterator b = id_button_map.find (Solo);
996 if (b == id_button_map.end()) {
1001 b->second->set_state (LED::Blinking4th);
1002 b->second->set_color (LED::Red);
1004 b->second->set_state (LED::NoTransition);
1005 b->second->set_color (LED::White);
1008 write (b->second->state_msg());
1014 XMLNode& node (ControlProtocol::get_state());
1017 child = new XMLNode (X_("Input"));
1018 child->add_child_nocopy (_async_in->get_state());
1019 node.add_child_nocopy (*child);
1020 child = new XMLNode (X_("Output"));
1021 child->add_child_nocopy (_async_out->get_state());
1022 node.add_child_nocopy (*child);
1024 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1025 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1026 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1027 node.add_property (X_("mode"), enum_2_string (_mode));
1033 Push2::set_state (const XMLNode & node, int version)
1035 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1039 if (ControlProtocol::set_state (node, version)) {
1045 if ((child = node.child (X_("Input"))) != 0) {
1046 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1048 _async_in->set_state (*portnode, version);
1052 if ((child = node.child (X_("Output"))) != 0) {
1053 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1055 _async_out->set_state (*portnode, version);
1059 XMLProperty const* prop;
1061 if ((prop = node.property (X_("root"))) != 0) {
1062 _scale_root = atoi (prop->value());
1065 if ((prop = node.property (X_("root_octave"))) != 0) {
1066 _root_octave = atoi (prop->value());
1069 if ((prop = node.property (X_("in_key"))) != 0) {
1070 _in_key = string_is_affirmative (prop->value());
1073 if ((prop = node.property (X_("mode"))) != 0) {
1074 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1081 Push2::other_vpot (int n, int delta)
1089 /* master gain control */
1091 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1093 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1101 Push2::other_vpot_touch (int n, bool touching)
1110 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1113 ac->start_touch (session->audible_frame());
1115 ac->stop_touch (true, session->audible_frame());
1123 Push2::start_shift ()
1125 cerr << "start shift\n";
1126 _modifier_state = ModifierState (_modifier_state | ModShift);
1127 Button* b = id_button_map[Shift];
1128 b->set_color (LED::White);
1129 b->set_state (LED::Blinking16th);
1130 write (b->state_msg());
1136 if (_modifier_state & ModShift) {
1137 cerr << "end shift\n";
1138 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1139 Button* b = id_button_map[Shift];
1140 b->timeout_connection.disconnect ();
1141 b->set_color (LED::White);
1142 b->set_state (LED::OneShot24th);
1143 write (b->state_msg());
1148 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1150 /* This filter is called asynchronously from a realtime process
1151 context. It must use atomics to check state, and must not block.
1154 bool matched = false;
1156 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1157 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1159 /* encoder touch start/touch end use note
1160 * 0-10. touchstrip uses note 12
1163 if ((*ev).note() > 10 && (*ev).note() != 12) {
1165 const int n = (*ev).note ();
1166 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1168 if (nni != nn_pad_map.end()) {
1169 Pad const * pad = nni->second;
1170 /* shift for output to the shadow port */
1171 if (pad->filtered >= 0) {
1172 (*ev).set_note (pad->filtered + (octave_shift*12));
1173 out.push_back (*ev);
1174 /* shift back so that the pads light correctly */
1177 /* no mapping, don't send event */
1180 out.push_back (*ev);
1185 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1186 out.push_back (*ev);
1194 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1196 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1197 if (!_input_port || !_output_port) {
1201 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1202 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1204 if (ni == name1 || ni == name2) {
1206 connection_state |= InputConnected;
1208 connection_state &= ~InputConnected;
1210 } else if (no == name1 || no == name2) {
1212 connection_state |= OutputConnected;
1214 connection_state &= ~OutputConnected;
1217 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1222 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1224 /* XXX this is a horrible hack. Without a short sleep here,
1225 something prevents the device wakeup messages from being
1226 sent and/or the responses from being received.
1230 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1234 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1237 ConnectionChange (); /* emit signal for our GUI */
1239 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1241 return true; /* connection status changed */
1247 request_pressure_mode ();
1250 boost::shared_ptr<Port>
1251 Push2::output_port()
1256 boost::shared_ptr<Port>
1263 Push2::pad_note (int row, int col) const
1265 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1267 if (nni != nn_pad_map.end()) {
1268 return nni->second->filtered;
1275 Push2::reset_pad_colors ()
1277 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1281 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1283 MusicalMode m (mode);
1284 vector<float>::iterator interval;
1286 const int original_root = root;
1288 interval = m.steps.begin();
1289 root += (octave*12);
1292 const int root_start = root;
1294 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1295 vector<int> mode_vector; /* sorted in note order */
1297 mode_map.insert (note);
1298 mode_vector.push_back (note);
1300 /* build a map of all notes in the mode, from the root to 127 */
1302 while (note < 128) {
1304 if (interval == m.steps.end()) {
1306 /* last distance was the end of the scale,
1307 so wrap, adding the next note at one
1308 octave above the last root.
1311 interval = m.steps.begin();
1313 mode_map.insert (root);
1314 mode_vector.push_back (root);
1317 note = (int) floor (root + (2.0 * (*interval)));
1319 mode_map.insert (note);
1320 mode_vector.push_back (note);
1324 fn_pad_map.clear ();
1328 vector<int>::iterator notei;
1331 for (int row = 0; row < 8; ++row) {
1333 /* Ableton's grid layout wraps the available notes in the scale
1334 * by offsetting 3 notes per row (from the bottom)
1337 notei = mode_vector.begin();
1338 notei += row_offset;
1341 for (int col = 0; col < 8; ++col) {
1342 int index = 36 + (row*8) + col;
1343 Pad* pad = nn_pad_map[index];
1345 if (notei != mode_vector.end()) {
1348 pad->filtered = notenum;
1350 fn_pad_map.insert (make_pair (notenum, pad));
1352 if ((notenum % 12) == original_root) {
1353 pad->set_color (selection_color);
1354 pad->perma_color = selection_color;
1356 pad->set_color (LED::White);
1357 pad->perma_color = LED::White;
1360 pad->do_when_pressed = Pad::FlashOff;
1365 pad->set_color (LED::Black);
1366 pad->do_when_pressed = Pad::Nothing;
1370 pad->set_state (LED::OneShot24th);
1371 write (pad->state_msg());
1377 /* chromatic: all notes available, but highlight those in the scale */
1379 for (note = 36; note < 100; ++note) {
1381 Pad* pad = nn_pad_map[note];
1383 /* Chromatic: all pads play, half-tone steps. Light
1384 * those in the scale, and highlight root notes
1387 pad->filtered = root_start + (note - 36);
1389 fn_pad_map.insert (make_pair (pad->filtered, pad));
1391 if (mode_map.find (note) != mode_map.end()) {
1393 if ((note % 12) == original_root) {
1394 pad->set_color (selection_color);
1395 pad->perma_color = selection_color;
1397 pad->set_color (LED::White);
1398 pad->perma_color = LED::White;
1401 pad->do_when_pressed = Pad::FlashOff;
1405 /* note is not in mode, turn it off */
1407 pad->do_when_pressed = Pad::FlashOn;
1408 pad->set_color (LED::Black);
1412 pad->set_state (LED::OneShot24th);
1413 write (pad->state_msg());
1419 bool changed = false;
1421 if (_scale_root != original_root) {
1422 _scale_root = original_root;
1425 if (_root_octave != octave) {
1426 _root_octave = octave;
1429 if (_in_key != inkey) {
1433 if (_mode != mode) {
1439 ScaleChange (); /* EMIT SIGNAL */
1444 Push2::set_percussive_mode (bool yn)
1447 cerr << "back to scale\n";
1448 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1455 for (int row = 0; row < 8; ++row) {
1457 for (int col = 0; col < 4; ++col) {
1459 int index = 36 + (row*8) + col;
1460 Pad* pad = nn_pad_map[index];
1462 pad->filtered = drum_note;
1467 for (int row = 0; row < 8; ++row) {
1469 for (int col = 4; col < 8; ++col) {
1471 int index = 36 + (row*8) + col;
1472 Pad* pad = nn_pad_map[index];
1474 pad->filtered = drum_note;
1483 Push2::current_layout () const
1485 Glib::Threads::Mutex::Lock lm (layout_lock);
1486 return _current_layout;
1490 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1492 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1493 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1494 boost::shared_ptr<MidiTrack> new_pad_target;
1496 /* See if there's a MIDI track selected */
1498 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1500 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1502 if (new_pad_target) {
1507 if (current_midi_track == new_pad_target) {
1512 if (!new_pad_target) {
1513 /* leave existing connection alone */
1517 /* disconnect from pad port, if appropriate */
1519 if (current_midi_track && pad_port) {
1520 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1523 /* now connect the pad port to this (newly) selected midi
1524 * track, if indeed there is one.
1527 if (new_pad_target && pad_port) {
1528 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1529 current_pad_target = new_pad_target;
1530 selection_color = get_color_index (new_pad_target->presentation_info().color());
1531 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1533 current_pad_target.reset ();
1534 selection_color = LED::Green;
1535 contrast_color = LED::Green;
1538 reset_pad_colors ();
1542 Push2::button_by_id (ButtonID bid)
1544 return id_button_map[bid];
1548 Push2::get_color_index (ArdourCanvas::Color rgba)
1550 ColorMap::iterator i = color_map.find (rgba);
1552 if (i != color_map.end()) {
1557 UINT_TO_RGBA (rgba, &r, &g, &b, &a);
1558 int w = 204; /* not sure where/when we should get this value */
1560 /* get a free index */
1564 if (color_map_free_list.empty()) {
1565 /* random replacement of any entry above zero and below 122 (where the
1566 * Ableton standard colors live)
1568 index = 1 + (random() % 121);
1570 index = color_map_free_list.top();
1571 color_map_free_list.pop();
1574 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1575 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1577 palette_msg[7] = index;
1578 palette_msg[8] = r & 0x7f;
1579 palette_msg[9] = r & 0x1;
1580 palette_msg[10] = g & 0x7f;
1581 palette_msg[11] = g & 0x1;
1582 palette_msg[12] = b & 0x7f;
1583 palette_msg[13] = b & 0x1;
1584 palette_msg[14] = w & 0x7f;
1585 palette_msg[15] = w & 0x1;
1587 write (palette_msg);
1588 write (update_pallette_msg);
1590 color_map[rgba] = index;
1596 Push2::build_color_map ()
1598 /* These are "standard" colors that Ableton docs suggest will always be
1599 there. Put them in our color map so that when we look up these
1600 colors, we will use the Ableton indices for them.
1603 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1604 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1605 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1606 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1607 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1608 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1609 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1611 for (uint8_t n = 1; n < 122; ++n) {
1612 color_map_free_list.push (n);
1617 Push2::fill_color_table ()
1619 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1620 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1622 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1624 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1625 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1626 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1628 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1629 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1631 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1632 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1633 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1634 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1639 Push2::get_color (ColorName name)
1641 Colors::iterator c = colors.find (name);
1642 if (c != colors.end()) {
1650 Push2::set_current_layout (Push2Layout* layout)
1652 if (_current_layout) {
1653 _current_layout->hide ();
1654 _canvas->root()->remove (_current_layout);
1655 _previous_layout = _current_layout;
1658 /* turn off all strip buttons - let new layout set them if it
1662 strip_buttons_off ();
1664 _current_layout = layout;
1666 if (_current_layout) {
1667 _canvas->root()->add (_current_layout);
1668 _current_layout->show ();
1671 _canvas->request_redraw ();
1675 Push2::use_previous_layout ()
1677 if (_previous_layout) {
1678 set_current_layout (_previous_layout);
1683 Push2::request_pressure_mode ()
1685 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1690 Push2::set_pressure_mode (PressureMode pm)
1692 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1696 /* nothing to do, message is correct */
1706 cerr << "Sent PM message " << msg << endl;