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)
145 /* master cannot be removed, so no need to connect to going-away signal */
146 master = session->master_out ();
149 throw failed_constructor ();
152 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
154 /* catch current selection, if any */
156 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
157 stripable_selection_change (sp);
160 /* catch arrival and departure of Push2 itself */
161 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
163 /* Catch port connections and disconnections */
164 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
166 /* ports might already be there */
167 port_registration_handler ();
174 delete track_mix_layout;
180 Push2::port_registration_handler ()
182 if (!_async_in && !_async_out) {
183 /* ports not registered yet */
187 if (_async_in->connected() && _async_out->connected()) {
188 /* don't waste cycles here */
192 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
193 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
197 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
198 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
200 if (!in.empty() && !out.empty()) {
201 cerr << "Push2: both ports found\n";
202 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
203 if (!_async_in->connected()) {
204 AudioEngine::instance()->connect (_async_in->name(), in.front());
206 if (!_async_out->connected()) {
207 AudioEngine::instance()->connect (_async_out->name(), out.front());
222 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
226 if ((err = libusb_claim_interface (handle, 0x00))) {
231 _canvas = new Push2Canvas (*this, 960, 160);
232 mix_layout = new MixLayout (*this, *session);
233 scale_layout = new ScaleLayout (*this, *session);
234 track_mix_layout = new TrackMixLayout (*this, *session);
235 splash_layout = new SplashLayout (*this, *session);
237 error << _("Cannot construct Canvas for display") << endmsg;
238 libusb_release_interface (handle, 0x00);
239 libusb_close (handle);
245 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
246 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
248 if (_async_in == 0 || _async_out == 0) {
252 /* We do not add our ports to the input/output bundles because we don't
253 * want users wiring them by hand. They could use JACK tools if they
254 * really insist on that.
257 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
258 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
260 /* Create a shadow port where, depending on the state of the surface,
261 * we will make pad note on/off events appear. The surface code will
262 * automatically this port to the first selected MIDI track.
265 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));
266 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
270 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
272 _output_bundle->add_channel (
274 ARDOUR::DataType::MIDI,
275 session->engine().make_port_name_non_relative (shadow_port->name())
279 session->BundleAddedOrRemoved ();
281 connect_to_parser ();
286 list<boost::shared_ptr<ARDOUR::Bundle> >
289 list<boost::shared_ptr<ARDOUR::Bundle> > b;
291 if (_output_bundle) {
292 b.push_back (_output_bundle);
301 init_buttons (false);
303 /* wait for button data to be flushed */
305 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
306 asp->drain (10000, 500000);
308 AudioEngine::instance()->unregister_port (_async_in);
309 AudioEngine::instance()->unregister_port (_async_out);
311 _async_in.reset ((ARDOUR::Port*) 0);
312 _async_out.reset ((ARDOUR::Port*) 0);
316 periodic_connection.disconnect ();
317 session_connections.drop_connections ();
319 if (_current_layout) {
320 _canvas->root()->remove (_current_layout);
328 delete splash_layout;
332 libusb_release_interface (handle, 0x00);
333 libusb_close (handle);
341 Push2::strip_buttons_off ()
343 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
344 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
346 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
347 Button* b = id_button_map[strip_buttons[n]];
349 b->set_color (LED::Black);
350 b->set_state (LED::OneShot24th);
351 write (b->state_msg());
357 Push2::init_buttons (bool startup)
359 /* This is a list of buttons that we want lit because they do something
360 in ardour related (loosely, sometimes) to their illuminated label.
363 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
364 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
365 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
368 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
369 Button* b = id_button_map[buttons[n]];
372 b->set_color (LED::White);
374 b->set_color (LED::Black);
376 b->set_state (LED::OneShot24th);
377 write (b->state_msg());
380 /* Strip buttons should all be off (black) by default. They will change
381 * color to reflect various conditions
384 strip_buttons_off ();
388 /* all other buttons are off (black) */
390 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
391 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
392 Accent, Note, Session, };
394 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
395 Button* b = id_button_map[off_buttons[n]];
397 b->set_color (LED::Black);
398 b->set_state (LED::OneShot24th);
399 write (b->state_msg());
404 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
405 Pad* pad = pi->second;
407 pad->set_color (LED::Black);
408 pad->set_state (LED::OneShot24th);
409 write (pad->state_msg());
417 libusb_device_handle *h;
420 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
421 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
426 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
431 Push2::request_factory (uint32_t num_requests)
433 /* AbstractUI<T>::request_buffer_factory() is a template method only
434 instantiated in this source module. To provide something visible for
435 use in the interface/descriptor, we have this static method that is
438 return request_buffer_factory (num_requests);
442 Push2::do_request (Push2Request * req)
444 if (req->type == CallSlot) {
446 call_slot (MISSING_INVALIDATOR, req->the_slot);
448 } else if (req->type == Quit) {
466 set_current_layout (splash_layout);
467 splash_start = get_microseconds ();
475 /* display splash for 2 seconds */
477 if (get_microseconds() - splash_start > 2000000) {
479 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
480 set_current_layout (mix_layout);
490 Push2::set_active (bool yn)
492 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
494 if (yn == active()) {
500 /* start event loop */
505 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
510 /* Connect input port to event loop */
514 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
515 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
516 asp->xthread().attach (main_loop()->get_context());
518 connect_session_signals ();
520 /* set up periodic task used to push a frame buffer to the
521 * device (25fps). The device can handle 60fps, but we don't
522 * need that frame rate.
525 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
526 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
527 vblank_timeout->attach (main_loop()->get_context());
530 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
531 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
532 periodic_timeout->attach (main_loop()->get_context());
536 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
545 ControlProtocol::set_active (yn);
547 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
553 Push2::init_touch_strip ()
555 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
556 /* flags are the final byte (ignore end-of-sysex */
558 /* show bar, not point
562 msg[7] = (1<<4) | (1<<5) | (1<<6);
567 Push2::write (const MidiByteArray& data)
569 /* immediate delivery */
570 _output_port->write (&data[0], data.size(), 0);
574 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
577 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
583 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
585 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
590 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
591 framepos_t now = AudioEngine::instance()->sample_time();
605 Push2::connect_to_parser ()
607 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
609 MIDI::Parser* p = _input_port->parser();
612 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
613 /* V-Pot messages are Controller */
614 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
615 /* Button messages are NoteOn */
616 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
617 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
618 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
619 /* Fader messages are Pitchbend */
620 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
624 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
626 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
627 MidiByteArray msg (sz, raw_bytes);
628 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
629 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
631 if (msg == aftertouch_mode_response) {
632 _pressure_mode = AfterTouch;
633 PressureModeChange (AfterTouch);
634 cerr << "Pressure mod eis after\n";
635 } else if (msg == polypress_mode_response) {
636 _pressure_mode = PolyPressure;
637 PressureModeChange (PolyPressure);
638 cerr << "Pressure mod eis poly\n";
643 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
645 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
647 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
650 /* any press cancels any pending long press timeouts */
651 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
652 Button* bb = id_button_map[*x];
653 bb->timeout_connection.disconnect ();
657 if (b != cc_button_map.end()) {
659 Button* button = b->second;
662 buttons_down.insert (button->id);
663 start_press_timeout (*button, button->id);
665 buttons_down.erase (button->id);
666 button->timeout_connection.disconnect ();
670 set<ButtonID>::iterator c = consumed.find (button->id);
672 if (c == consumed.end()) {
673 if (ev->value == 0) {
674 (this->*button->release_method)();
676 (this->*button->press_method)();
679 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
687 int delta = ev->value;
690 delta = -(128 - delta);
693 switch (ev->controller_number) {
695 _current_layout->strip_vpot (0, delta);
698 _current_layout->strip_vpot (1, delta);
701 _current_layout->strip_vpot (2, delta);
704 _current_layout->strip_vpot (3, delta);
707 _current_layout->strip_vpot (4, delta);
710 _current_layout->strip_vpot (5, delta);
713 _current_layout->strip_vpot (6, delta);
716 _current_layout->strip_vpot (7, delta);
721 other_vpot (8, delta);
724 other_vpot (1, delta);
729 other_vpot (2, delta);
736 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
738 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
740 if (ev->velocity == 0) {
741 handle_midi_note_off_message (parser, ev);
745 switch (ev->note_number) {
747 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
750 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
753 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
756 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
759 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
762 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
765 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
768 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
773 other_vpot_touch (0, ev->velocity > 64);
776 other_vpot_touch (1, ev->velocity > 64);
781 other_vpot_touch (3, ev->velocity > 64);
786 if (ev->velocity < 64) {
792 if (ev->note_number < 11) {
798 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
800 if (pi == nn_pad_map.end()) {
804 Pad* pad = pi->second;
806 if (pad->do_when_pressed == Pad::FlashOn) {
807 pad->set_color (LED::White);
808 pad->set_state (LED::OneShot24th);
809 write (pad->state_msg());
810 } else if (pad->do_when_pressed == Pad::FlashOff) {
811 pad->set_color (LED::Black);
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 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
832 if (pi == nn_pad_map.end()) {
836 Pad* pad = pi->second;
838 if (pad->do_when_pressed == Pad::FlashOn) {
839 pad->set_color (LED::Black);
840 pad->set_state (LED::OneShot24th);
841 write (pad->state_msg());
842 } else if (pad->do_when_pressed == Pad::FlashOff) {
843 pad->set_color (pad->perma_color);
844 pad->set_state (LED::OneShot24th);
845 write (pad->state_msg());
850 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
855 Push2::thread_init ()
857 struct sched_param rtparam;
859 pthread_set_name (event_loop_name().c_str());
861 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
862 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
864 memset (&rtparam, 0, sizeof (rtparam));
865 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
867 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
868 // do we care? not particularly.
873 Push2::connect_session_signals()
875 // receive routes added
876 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
877 // receive VCAs added
878 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
880 // receive record state toggled
881 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
882 // receive transport state changed
883 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
884 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
885 // receive punch-in and punch-out
886 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
887 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
888 // receive rude solo changed
889 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
893 Push2::notify_record_state_changed ()
895 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
897 if (b == id_button_map.end()) {
901 switch (session->record_status ()) {
902 case Session::Disabled:
903 b->second->set_color (LED::White);
904 b->second->set_state (LED::NoTransition);
906 case Session::Enabled:
907 b->second->set_color (LED::Red);
908 b->second->set_state (LED::Blinking4th);
910 case Session::Recording:
911 b->second->set_color (LED::Red);
912 b->second->set_state (LED::OneShot24th);
916 write (b->second->state_msg());
920 Push2::notify_transport_state_changed ()
922 Button* b = id_button_map[Play];
924 if (session->transport_rolling()) {
925 b->set_state (LED::OneShot24th);
926 b->set_color (LED::Green);
929 /* disable any blink on FixedLength from pending edit range op */
930 Button* fl = id_button_map[FixedLength];
932 fl->set_color (LED::Black);
933 fl->set_state (LED::NoTransition);
934 write (fl->state_msg());
936 b->set_color (LED::White);
937 b->set_state (LED::NoTransition);
940 write (b->state_msg());
944 Push2::notify_loop_state_changed ()
949 Push2::notify_parameter_changed (std::string param)
951 IDButtonMap::iterator b;
953 if (param == "clicking") {
954 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
957 if (Config->get_clicking()) {
958 b->second->set_state (LED::Blinking4th);
959 b->second->set_color (LED::White);
961 b->second->set_color (LED::White);
962 b->second->set_state (LED::NoTransition);
964 write (b->second->state_msg ());
969 Push2::notify_solo_active_changed (bool yn)
971 IDButtonMap::iterator b = id_button_map.find (Solo);
973 if (b == id_button_map.end()) {
978 b->second->set_state (LED::Blinking4th);
979 b->second->set_color (LED::Red);
981 b->second->set_state (LED::NoTransition);
982 b->second->set_color (LED::White);
985 write (b->second->state_msg());
991 XMLNode& node (ControlProtocol::get_state());
994 child = new XMLNode (X_("Input"));
995 child->add_child_nocopy (_async_in->get_state());
996 node.add_child_nocopy (*child);
997 child = new XMLNode (X_("Output"));
998 child->add_child_nocopy (_async_out->get_state());
999 node.add_child_nocopy (*child);
1001 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1002 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1003 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1004 node.add_property (X_("mode"), enum_2_string (_mode));
1010 Push2::set_state (const XMLNode & node, int version)
1012 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1016 if (ControlProtocol::set_state (node, version)) {
1022 if ((child = node.child (X_("Input"))) != 0) {
1023 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1025 _async_in->set_state (*portnode, version);
1029 if ((child = node.child (X_("Output"))) != 0) {
1030 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1032 _async_out->set_state (*portnode, version);
1036 XMLProperty const* prop;
1038 if ((prop = node.property (X_("root"))) != 0) {
1039 _scale_root = atoi (prop->value());
1042 if ((prop = node.property (X_("root_octave"))) != 0) {
1043 _root_octave = atoi (prop->value());
1046 if ((prop = node.property (X_("in_key"))) != 0) {
1047 _in_key = string_is_affirmative (prop->value());
1050 if ((prop = node.property (X_("mode"))) != 0) {
1051 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1058 Push2::other_vpot (int n, int delta)
1066 /* master gain control */
1068 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1070 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1078 Push2::other_vpot_touch (int n, bool touching)
1087 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1090 ac->start_touch (session->audible_frame());
1092 ac->stop_touch (true, session->audible_frame());
1100 Push2::start_shift ()
1102 cerr << "start shift\n";
1103 _modifier_state = ModifierState (_modifier_state | ModShift);
1104 Button* b = id_button_map[Shift];
1105 b->set_color (LED::White);
1106 b->set_state (LED::Blinking16th);
1107 write (b->state_msg());
1113 if (_modifier_state & ModShift) {
1114 cerr << "end shift\n";
1115 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1116 Button* b = id_button_map[Shift];
1117 b->timeout_connection.disconnect ();
1118 b->set_color (LED::White);
1119 b->set_state (LED::OneShot24th);
1120 write (b->state_msg());
1125 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1127 /* This filter is called asynchronously from a realtime process
1128 context. It must use atomics to check state, and must not block.
1131 bool matched = false;
1133 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1134 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1136 /* encoder touch start/touch end use note
1137 * 0-10. touchstrip uses note 12
1140 if ((*ev).note() > 10 && (*ev).note() != 12) {
1142 const int n = (*ev).note ();
1143 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1145 if (nni != nn_pad_map.end()) {
1146 Pad const * pad = nni->second;
1147 /* shift for output to the shadow port */
1148 if (pad->filtered >= 0) {
1149 (*ev).set_note (pad->filtered + (octave_shift*12));
1150 out.push_back (*ev);
1151 /* shift back so that the pads light correctly */
1154 /* no mapping, don't send event */
1157 out.push_back (*ev);
1162 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1163 out.push_back (*ev);
1171 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1173 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1174 if (!_input_port || !_output_port) {
1178 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1179 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1181 if (ni == name1 || ni == name2) {
1183 connection_state |= InputConnected;
1185 connection_state &= ~InputConnected;
1187 } else if (no == name1 || no == name2) {
1189 connection_state |= OutputConnected;
1191 connection_state &= ~OutputConnected;
1194 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1199 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1201 /* XXX this is a horrible hack. Without a short sleep here,
1202 something prevents the device wakeup messages from being
1203 sent and/or the responses from being received.
1207 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1211 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1214 ConnectionChange (); /* emit signal for our GUI */
1216 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1218 return true; /* connection status changed */
1224 request_pressure_mode ();
1227 boost::shared_ptr<Port>
1228 Push2::output_port()
1233 boost::shared_ptr<Port>
1240 Push2::pad_note (int row, int col) const
1242 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1244 if (nni != nn_pad_map.end()) {
1245 return nni->second->filtered;
1252 Push2::reset_pad_colors ()
1254 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1258 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1260 MusicalMode m (mode);
1261 vector<float>::iterator interval;
1263 const int original_root = root;
1265 interval = m.steps.begin();
1266 root += (octave*12);
1269 const int root_start = root;
1271 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1272 vector<int> mode_vector; /* sorted in note order */
1274 mode_map.insert (note);
1275 mode_vector.push_back (note);
1277 /* build a map of all notes in the mode, from the root to 127 */
1279 while (note < 128) {
1281 if (interval == m.steps.end()) {
1283 /* last distance was the end of the scale,
1284 so wrap, adding the next note at one
1285 octave above the last root.
1288 interval = m.steps.begin();
1290 mode_map.insert (root);
1291 mode_vector.push_back (root);
1294 note = (int) floor (root + (2.0 * (*interval)));
1296 mode_map.insert (note);
1297 mode_vector.push_back (note);
1303 vector<int>::iterator notei;
1305 for (int row = 0; row < 8; ++row) {
1307 /* Ableton's grid layout wraps the available notes in the scale
1308 * by offsetting 3 notes per row (from the bottom)
1311 notei = mode_vector.begin();
1312 notei += row_offset;
1315 for (int col = 0; col < 8; ++col) {
1316 int index = 36 + (row*8) + col;
1317 Pad* pad = nn_pad_map[index];
1319 if (notei != mode_vector.end()) {
1322 pad->filtered = notenum;
1324 if ((notenum % 12) == original_root) {
1325 pad->set_color (selection_color);
1326 pad->perma_color = selection_color;
1328 pad->set_color (LED::White);
1329 pad->perma_color = LED::White;
1332 pad->do_when_pressed = Pad::FlashOff;
1337 pad->set_color (LED::Black);
1338 pad->do_when_pressed = Pad::Nothing;
1342 pad->set_state (LED::OneShot24th);
1343 write (pad->state_msg());
1349 /* chromatic: all notes available, but highlight those in the scale */
1351 for (note = 36; note < 100; ++note) {
1353 Pad* pad = nn_pad_map[note];
1355 /* Chromatic: all pads play, half-tone steps. Light
1356 * those in the scale, and highlight root notes
1359 pad->filtered = root_start + (note - 36);
1361 if (mode_map.find (note) != mode_map.end()) {
1363 if ((note % 12) == original_root) {
1364 pad->set_color (selection_color);
1365 pad->perma_color = selection_color;
1367 pad->set_color (LED::White);
1368 pad->perma_color = LED::White;
1371 pad->do_when_pressed = Pad::FlashOff;
1375 /* note is not in mode, turn it off */
1377 pad->do_when_pressed = Pad::FlashOn;
1378 pad->set_color (LED::Black);
1382 pad->set_state (LED::OneShot24th);
1383 write (pad->state_msg());
1389 bool changed = false;
1391 if (_scale_root != original_root) {
1392 _scale_root = original_root;
1395 if (_root_octave != octave) {
1396 _root_octave = octave;
1399 if (_in_key != inkey) {
1403 if (_mode != mode) {
1409 ScaleChange (); /* EMIT SIGNAL */
1414 Push2::set_percussive_mode (bool yn)
1417 cerr << "back to scale\n";
1418 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1425 for (int row = 0; row < 8; ++row) {
1427 for (int col = 0; col < 4; ++col) {
1429 int index = 36 + (row*8) + col;
1430 Pad* pad = nn_pad_map[index];
1432 pad->filtered = drum_note;
1437 for (int row = 0; row < 8; ++row) {
1439 for (int col = 4; col < 8; ++col) {
1441 int index = 36 + (row*8) + col;
1442 Pad* pad = nn_pad_map[index];
1444 pad->filtered = drum_note;
1453 Push2::current_layout () const
1455 Glib::Threads::Mutex::Lock lm (layout_lock);
1456 return _current_layout;
1460 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1462 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1463 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1464 boost::shared_ptr<MidiTrack> new_pad_target;
1466 /* See if there's a MIDI track selected */
1468 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1470 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1472 if (new_pad_target) {
1477 if (current_midi_track == new_pad_target) {
1482 if (!new_pad_target) {
1483 /* leave existing connection alone */
1487 /* disconnect from pad port, if appropriate */
1489 if (current_midi_track && pad_port) {
1490 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1493 /* now connect the pad port to this (newly) selected midi
1494 * track, if indeed there is one.
1497 if (new_pad_target && pad_port) {
1498 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1499 current_pad_target = new_pad_target;
1500 selection_color = get_color_index (new_pad_target->presentation_info().color());
1502 current_pad_target.reset ();
1503 selection_color = LED::Green;
1506 reset_pad_colors ();
1510 Push2::button_by_id (ButtonID bid)
1512 return id_button_map[bid];
1516 Push2::get_color_index (uint32_t rgb)
1518 ColorMap::iterator i = color_map.find (rgb);
1520 if (i != color_map.end()) {
1525 UINT_TO_RGBA (rgb, &r, &g, &b, &a);
1526 int w = 204; /* not sure where/when we should get this value */
1528 /* get a free index */
1532 if (color_map_free_list.empty()) {
1533 /* random replacement of any entry above zero and below 122 (where the
1534 * Ableton standard colors live)
1536 index = 1 + (random() % 121);
1538 index = color_map_free_list.top();
1539 color_map_free_list.pop();
1542 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1543 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1545 palette_msg[7] = index;
1546 palette_msg[8] = r & 0x7f;
1547 palette_msg[9] = r & 0x1;
1548 palette_msg[10] = g & 0x7f;
1549 palette_msg[11] = g & 0x1;
1550 palette_msg[12] = b & 0x7f;
1551 palette_msg[13] = b & 0x1;
1552 palette_msg[14] = w & 0x7f;
1553 palette_msg[15] = w & 0x1;
1555 write (palette_msg);
1556 write (update_pallette_msg);
1558 color_map[index] = rgb;
1564 Push2::build_color_map ()
1566 /* These are "standard" colors that Ableton docs suggest will always be
1567 there. Put them in our color map so that when we look up these
1568 colors, we will use the Ableton indices for them.
1571 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1572 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1573 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1574 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1575 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1576 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1577 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1579 for (uint8_t n = 1; n < 122; ++n) {
1580 color_map_free_list.push (n);
1585 Push2::fill_color_table ()
1587 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1588 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1590 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1592 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1593 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1594 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1596 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1597 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1599 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1600 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1601 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1602 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1607 Push2::get_color (ColorName name)
1609 Colors::iterator c = colors.find (name);
1610 if (c != colors.end()) {
1618 Push2::set_current_layout (Push2Layout* layout)
1620 if (_current_layout) {
1621 _current_layout->hide ();
1622 _canvas->root()->remove (_current_layout);
1623 _previous_layout = _current_layout;
1626 /* turn off all strip buttons - let new layout set them if it
1630 strip_buttons_off ();
1632 _current_layout = layout;
1634 if (_current_layout) {
1635 _canvas->root()->add (_current_layout);
1636 _current_layout->show ();
1639 _canvas->request_redraw ();
1643 Push2::use_previous_layout ()
1645 if (_previous_layout) {
1646 set_current_layout (_previous_layout);
1651 Push2::request_pressure_mode ()
1653 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1658 Push2::set_pressure_mode (PressureMode pm)
1660 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1664 /* nothing to do, message is correct */
1674 cerr << "Sent PM message " << msg << endl;