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"
45 #include "canvas/colors.h"
55 #include "track_mix.h"
59 using namespace ARDOUR;
63 using namespace ArdourSurface;
65 #include "pbd/abstract_ui.cc" // instantiate template
67 #define ABLETON 0x2982
70 __attribute__((constructor)) static void
73 EnumWriter& enum_writer (EnumWriter::instance());
77 MusicalMode::Type mode;
79 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
80 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
82 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
83 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
84 REGISTER_CLASS_ENUM (MusicalMode, Minor);
85 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
86 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
87 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
88 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
89 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
90 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
91 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
92 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
93 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
94 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
95 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
96 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
97 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
98 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
99 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
100 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
101 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
102 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
103 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
104 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
105 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
106 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
107 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
108 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
109 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
110 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
111 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
112 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
113 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
114 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
115 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
116 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
117 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
118 REGISTER_CLASS_ENUM (MusicalMode, Persian);
119 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
123 Push2::Push2 (ARDOUR::Session& s)
124 : ControlProtocol (s, string (X_("Ableton Push 2")))
125 , AbstractUI<Push2Request> (name())
127 , _modifier_state (None)
129 , _current_layout (0)
130 , _previous_layout (0)
131 , connection_state (ConnectionState (0))
133 , _mode (MusicalMode::IonianMajor)
139 , _pressure_mode (AfterTouch)
140 , selection_color (LED::Green)
141 , contrast_color (LED::Green)
148 /* master cannot be removed, so no need to connect to going-away signal */
149 master = session->master_out ();
152 throw failed_constructor ();
155 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
157 /* catch current selection, if any */
159 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
160 stripable_selection_change (sp);
163 /* catch arrival and departure of Push2 itself */
164 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
166 /* Catch port connections and disconnections */
167 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
169 /* ports might already be there */
170 port_registration_handler ();
177 delete track_mix_layout;
183 Push2::port_registration_handler ()
185 if (!_async_in && !_async_out) {
186 /* ports not registered yet */
190 if (_async_in->connected() && _async_out->connected()) {
191 /* don't waste cycles here */
195 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
196 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
200 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
201 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
203 if (!in.empty() && !out.empty()) {
204 cerr << "Push2: both ports found\n";
205 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
206 if (!_async_in->connected()) {
207 AudioEngine::instance()->connect (_async_in->name(), in.front());
209 if (!_async_out->connected()) {
210 AudioEngine::instance()->connect (_async_out->name(), out.front());
225 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
229 if ((err = libusb_claim_interface (handle, 0x00))) {
234 _canvas = new Push2Canvas (*this, 960, 160);
235 mix_layout = new MixLayout (*this, *session);
236 scale_layout = new ScaleLayout (*this, *session);
237 track_mix_layout = new TrackMixLayout (*this, *session);
238 splash_layout = new SplashLayout (*this, *session);
240 error << _("Cannot construct Canvas for display") << endmsg;
241 libusb_release_interface (handle, 0x00);
242 libusb_close (handle);
248 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
249 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
251 if (_async_in == 0 || _async_out == 0) {
255 /* We do not add our ports to the input/output bundles because we don't
256 * want users wiring them by hand. They could use JACK tools if they
257 * really insist on that.
260 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
261 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
263 /* Create a shadow port where, depending on the state of the surface,
264 * we will make pad note on/off events appear. The surface code will
265 * automatically this port to the first selected MIDI track.
268 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));
269 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
273 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
275 _output_bundle->add_channel (
277 ARDOUR::DataType::MIDI,
278 session->engine().make_port_name_non_relative (shadow_port->name())
282 session->BundleAddedOrRemoved ();
284 connect_to_parser ();
289 list<boost::shared_ptr<ARDOUR::Bundle> >
292 list<boost::shared_ptr<ARDOUR::Bundle> > b;
294 if (_output_bundle) {
295 b.push_back (_output_bundle);
304 init_buttons (false);
305 strip_buttons_off ();
307 /* wait for button data to be flushed */
309 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
310 asp->drain (10000, 500000);
312 AudioEngine::instance()->unregister_port (_async_in);
313 AudioEngine::instance()->unregister_port (_async_out);
315 _async_in.reset ((ARDOUR::Port*) 0);
316 _async_out.reset ((ARDOUR::Port*) 0);
320 periodic_connection.disconnect ();
321 session_connections.drop_connections ();
323 if (_current_layout) {
324 _canvas->root()->remove (_current_layout);
332 delete splash_layout;
336 libusb_release_interface (handle, 0x00);
337 libusb_close (handle);
345 Push2::strip_buttons_off ()
347 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
348 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
350 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
351 Button* b = id_button_map[strip_buttons[n]];
353 b->set_color (LED::Black);
354 b->set_state (LED::OneShot24th);
355 write (b->state_msg());
361 Push2::init_buttons (bool startup)
363 /* This is a list of buttons that we want lit because they do something
364 in ardour related (loosely, sometimes) to their illuminated label.
367 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
368 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
369 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
372 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
373 Button* b = id_button_map[buttons[n]];
376 b->set_color (LED::White);
378 b->set_color (LED::Black);
380 b->set_state (LED::OneShot24th);
381 write (b->state_msg());
386 /* all other buttons are off (black) */
388 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
389 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
390 Accent, Note, Session, };
392 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
393 Button* b = id_button_map[off_buttons[n]];
395 b->set_color (LED::Black);
396 b->set_state (LED::OneShot24th);
397 write (b->state_msg());
402 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
403 Pad* pad = pi->second;
405 pad->set_color (LED::Black);
406 pad->set_state (LED::OneShot24th);
407 write (pad->state_msg());
415 libusb_device_handle *h;
418 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
419 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
424 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
429 Push2::request_factory (uint32_t num_requests)
431 /* AbstractUI<T>::request_buffer_factory() is a template method only
432 instantiated in this source module. To provide something visible for
433 use in the interface/descriptor, we have this static method that is
436 return request_buffer_factory (num_requests);
440 Push2::do_request (Push2Request * req)
442 if (req->type == CallSlot) {
444 call_slot (MISSING_INVALIDATOR, req->the_slot);
446 } else if (req->type == Quit) {
464 set_current_layout (splash_layout);
465 splash_start = get_microseconds ();
473 /* display splash for 2 seconds */
475 if (get_microseconds() - splash_start > 2000000) {
477 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
478 set_current_layout (mix_layout);
488 Push2::set_active (bool yn)
490 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
492 if (yn == active()) {
498 /* start event loop */
503 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
508 /* Connect input port to event loop */
512 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
513 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
514 asp->xthread().attach (main_loop()->get_context());
516 connect_session_signals ();
518 /* set up periodic task used to push a frame buffer to the
519 * device (25fps). The device can handle 60fps, but we don't
520 * need that frame rate.
523 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
524 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
525 vblank_timeout->attach (main_loop()->get_context());
528 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
529 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
530 periodic_timeout->attach (main_loop()->get_context());
534 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
543 ControlProtocol::set_active (yn);
545 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
551 Push2::init_touch_strip ()
553 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
554 /* flags are the final byte (ignore end-of-sysex */
556 /* show bar, not point
560 msg[7] = (1<<4) | (1<<5) | (1<<6);
565 Push2::write (const MidiByteArray& data)
567 /* immediate delivery */
568 _output_port->write (&data[0], data.size(), 0);
572 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
575 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
581 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
583 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
588 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
589 framepos_t now = AudioEngine::instance()->sample_time();
603 Push2::connect_to_parser ()
605 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
607 MIDI::Parser* p = _input_port->parser();
610 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
611 /* V-Pot messages are Controller */
612 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
613 /* Button messages are NoteOn */
614 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
615 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
616 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
617 /* Fader messages are Pitchbend */
618 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
622 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
624 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
625 MidiByteArray msg (sz, raw_bytes);
626 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
627 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
629 if (msg == aftertouch_mode_response) {
630 _pressure_mode = AfterTouch;
631 PressureModeChange (AfterTouch);
632 cerr << "Pressure mod eis after\n";
633 } else if (msg == polypress_mode_response) {
634 _pressure_mode = PolyPressure;
635 PressureModeChange (PolyPressure);
636 cerr << "Pressure mod eis poly\n";
641 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
643 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
645 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
648 /* any press cancels any pending long press timeouts */
649 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
650 Button* bb = id_button_map[*x];
651 bb->timeout_connection.disconnect ();
655 if (b != cc_button_map.end()) {
657 Button* button = b->second;
660 buttons_down.insert (button->id);
661 start_press_timeout (*button, button->id);
663 buttons_down.erase (button->id);
664 button->timeout_connection.disconnect ();
668 set<ButtonID>::iterator c = consumed.find (button->id);
670 if (c == consumed.end()) {
671 if (ev->value == 0) {
672 (this->*button->release_method)();
674 (this->*button->press_method)();
677 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
685 int delta = ev->value;
688 delta = -(128 - delta);
691 switch (ev->controller_number) {
693 _current_layout->strip_vpot (0, delta);
696 _current_layout->strip_vpot (1, delta);
699 _current_layout->strip_vpot (2, delta);
702 _current_layout->strip_vpot (3, delta);
705 _current_layout->strip_vpot (4, delta);
708 _current_layout->strip_vpot (5, delta);
711 _current_layout->strip_vpot (6, delta);
714 _current_layout->strip_vpot (7, delta);
719 other_vpot (8, delta);
722 other_vpot (1, delta);
727 other_vpot (2, delta);
734 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
736 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
738 if (ev->velocity == 0) {
739 handle_midi_note_off_message (parser, ev);
743 switch (ev->note_number) {
745 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
748 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
751 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
754 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
757 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
760 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
763 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
766 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
771 other_vpot_touch (0, ev->velocity > 64);
774 other_vpot_touch (1, ev->velocity > 64);
779 other_vpot_touch (3, ev->velocity > 64);
784 if (ev->velocity < 64) {
790 if (ev->note_number < 11) {
794 /* Pad illuminations */
796 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
798 if (pm == nn_pad_map.end()) {
802 const Pad * const pad_pressed = pm->second;
804 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
806 if (pads_with_note.first == fn_pad_map.end()) {
810 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
811 Pad* pad = pi->second;
813 pad->set_color (contrast_color);
814 pad->set_state (LED::OneShot24th);
815 write (pad->state_msg());
820 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
822 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
824 if (ev->note_number < 11) {
825 /* theoretically related to encoder touch start/end, but
826 * actually they send note on with two different velocity
832 /* Pad illuminations */
834 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
836 if (pm == nn_pad_map.end()) {
840 const Pad * const pad_pressed = pm->second;
842 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
844 if (pads_with_note.first == fn_pad_map.end()) {
848 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
849 Pad* pad = pi->second;
851 if (pad->do_when_pressed == Pad::FlashOn) {
852 pad->set_color (LED::Black);
853 pad->set_state (LED::OneShot24th);
854 write (pad->state_msg());
855 } else if (pad->do_when_pressed == Pad::FlashOff) {
856 pad->set_color (pad->perma_color);
857 pad->set_state (LED::OneShot24th);
858 write (pad->state_msg());
864 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
869 Push2::thread_init ()
871 struct sched_param rtparam;
873 pthread_set_name (event_loop_name().c_str());
875 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
876 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
878 memset (&rtparam, 0, sizeof (rtparam));
879 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
881 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
882 // do we care? not particularly.
887 Push2::connect_session_signals()
889 // receive routes added
890 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
891 // receive VCAs added
892 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
894 // receive record state toggled
895 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
896 // receive transport state changed
897 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
898 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
899 // receive punch-in and punch-out
900 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
901 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
902 // receive rude solo changed
903 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
907 Push2::notify_record_state_changed ()
909 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
911 if (b == id_button_map.end()) {
915 switch (session->record_status ()) {
916 case Session::Disabled:
917 b->second->set_color (LED::White);
918 b->second->set_state (LED::NoTransition);
920 case Session::Enabled:
921 b->second->set_color (LED::Red);
922 b->second->set_state (LED::Blinking4th);
924 case Session::Recording:
925 b->second->set_color (LED::Red);
926 b->second->set_state (LED::OneShot24th);
930 write (b->second->state_msg());
934 Push2::notify_transport_state_changed ()
936 Button* b = id_button_map[Play];
938 if (session->transport_rolling()) {
939 b->set_state (LED::OneShot24th);
940 b->set_color (LED::Green);
943 /* disable any blink on FixedLength from pending edit range op */
944 Button* fl = id_button_map[FixedLength];
946 fl->set_color (LED::Black);
947 fl->set_state (LED::NoTransition);
948 write (fl->state_msg());
950 b->set_color (LED::White);
951 b->set_state (LED::NoTransition);
954 write (b->state_msg());
958 Push2::notify_loop_state_changed ()
963 Push2::notify_parameter_changed (std::string param)
965 IDButtonMap::iterator b;
967 if (param == "clicking") {
968 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
971 if (Config->get_clicking()) {
972 b->second->set_state (LED::Blinking4th);
973 b->second->set_color (LED::White);
975 b->second->set_color (LED::White);
976 b->second->set_state (LED::NoTransition);
978 write (b->second->state_msg ());
983 Push2::notify_solo_active_changed (bool yn)
985 IDButtonMap::iterator b = id_button_map.find (Solo);
987 if (b == id_button_map.end()) {
992 b->second->set_state (LED::Blinking4th);
993 b->second->set_color (LED::Red);
995 b->second->set_state (LED::NoTransition);
996 b->second->set_color (LED::White);
999 write (b->second->state_msg());
1005 XMLNode& node (ControlProtocol::get_state());
1008 child = new XMLNode (X_("Input"));
1009 child->add_child_nocopy (_async_in->get_state());
1010 node.add_child_nocopy (*child);
1011 child = new XMLNode (X_("Output"));
1012 child->add_child_nocopy (_async_out->get_state());
1013 node.add_child_nocopy (*child);
1015 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1016 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1017 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1018 node.add_property (X_("mode"), enum_2_string (_mode));
1024 Push2::set_state (const XMLNode & node, int version)
1026 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1030 if (ControlProtocol::set_state (node, version)) {
1036 if ((child = node.child (X_("Input"))) != 0) {
1037 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1039 _async_in->set_state (*portnode, version);
1043 if ((child = node.child (X_("Output"))) != 0) {
1044 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1046 _async_out->set_state (*portnode, version);
1050 XMLProperty const* prop;
1052 if ((prop = node.property (X_("root"))) != 0) {
1053 _scale_root = atoi (prop->value());
1056 if ((prop = node.property (X_("root_octave"))) != 0) {
1057 _root_octave = atoi (prop->value());
1060 if ((prop = node.property (X_("in_key"))) != 0) {
1061 _in_key = string_is_affirmative (prop->value());
1064 if ((prop = node.property (X_("mode"))) != 0) {
1065 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1072 Push2::other_vpot (int n, int delta)
1080 /* master gain control */
1082 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1084 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1092 Push2::other_vpot_touch (int n, bool touching)
1101 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1104 ac->start_touch (session->audible_frame());
1106 ac->stop_touch (true, session->audible_frame());
1114 Push2::start_shift ()
1116 cerr << "start shift\n";
1117 _modifier_state = ModifierState (_modifier_state | ModShift);
1118 Button* b = id_button_map[Shift];
1119 b->set_color (LED::White);
1120 b->set_state (LED::Blinking16th);
1121 write (b->state_msg());
1127 if (_modifier_state & ModShift) {
1128 cerr << "end shift\n";
1129 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1130 Button* b = id_button_map[Shift];
1131 b->timeout_connection.disconnect ();
1132 b->set_color (LED::White);
1133 b->set_state (LED::OneShot24th);
1134 write (b->state_msg());
1139 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1141 /* This filter is called asynchronously from a realtime process
1142 context. It must use atomics to check state, and must not block.
1145 bool matched = false;
1147 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1148 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1150 /* encoder touch start/touch end use note
1151 * 0-10. touchstrip uses note 12
1154 if ((*ev).note() > 10 && (*ev).note() != 12) {
1156 const int n = (*ev).note ();
1157 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1159 if (nni != nn_pad_map.end()) {
1160 Pad const * pad = nni->second;
1161 /* shift for output to the shadow port */
1162 if (pad->filtered >= 0) {
1163 (*ev).set_note (pad->filtered + (octave_shift*12));
1164 out.push_back (*ev);
1165 /* shift back so that the pads light correctly */
1168 /* no mapping, don't send event */
1171 out.push_back (*ev);
1176 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1177 out.push_back (*ev);
1185 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1187 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1188 if (!_input_port || !_output_port) {
1192 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1193 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1195 if (ni == name1 || ni == name2) {
1197 connection_state |= InputConnected;
1199 connection_state &= ~InputConnected;
1201 } else if (no == name1 || no == name2) {
1203 connection_state |= OutputConnected;
1205 connection_state &= ~OutputConnected;
1208 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1213 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1215 /* XXX this is a horrible hack. Without a short sleep here,
1216 something prevents the device wakeup messages from being
1217 sent and/or the responses from being received.
1221 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1225 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1228 ConnectionChange (); /* emit signal for our GUI */
1230 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1232 return true; /* connection status changed */
1238 request_pressure_mode ();
1241 boost::shared_ptr<Port>
1242 Push2::output_port()
1247 boost::shared_ptr<Port>
1254 Push2::pad_note (int row, int col) const
1256 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1258 if (nni != nn_pad_map.end()) {
1259 return nni->second->filtered;
1266 Push2::reset_pad_colors ()
1268 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1272 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1274 MusicalMode m (mode);
1275 vector<float>::iterator interval;
1277 const int original_root = root;
1279 interval = m.steps.begin();
1280 root += (octave*12);
1283 const int root_start = root;
1285 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1286 vector<int> mode_vector; /* sorted in note order */
1288 mode_map.insert (note);
1289 mode_vector.push_back (note);
1291 /* build a map of all notes in the mode, from the root to 127 */
1293 while (note < 128) {
1295 if (interval == m.steps.end()) {
1297 /* last distance was the end of the scale,
1298 so wrap, adding the next note at one
1299 octave above the last root.
1302 interval = m.steps.begin();
1304 mode_map.insert (root);
1305 mode_vector.push_back (root);
1308 note = (int) floor (root + (2.0 * (*interval)));
1310 mode_map.insert (note);
1311 mode_vector.push_back (note);
1315 fn_pad_map.clear ();
1319 vector<int>::iterator notei;
1322 for (int row = 0; row < 8; ++row) {
1324 /* Ableton's grid layout wraps the available notes in the scale
1325 * by offsetting 3 notes per row (from the bottom)
1328 notei = mode_vector.begin();
1329 notei += row_offset;
1332 for (int col = 0; col < 8; ++col) {
1333 int index = 36 + (row*8) + col;
1334 Pad* pad = nn_pad_map[index];
1336 if (notei != mode_vector.end()) {
1339 pad->filtered = notenum;
1341 fn_pad_map.insert (make_pair (notenum, pad));
1343 if ((notenum % 12) == original_root) {
1344 pad->set_color (selection_color);
1345 pad->perma_color = selection_color;
1347 pad->set_color (LED::White);
1348 pad->perma_color = LED::White;
1351 pad->do_when_pressed = Pad::FlashOff;
1356 pad->set_color (LED::Black);
1357 pad->do_when_pressed = Pad::Nothing;
1361 pad->set_state (LED::OneShot24th);
1362 write (pad->state_msg());
1368 /* chromatic: all notes available, but highlight those in the scale */
1370 for (note = 36; note < 100; ++note) {
1372 Pad* pad = nn_pad_map[note];
1374 /* Chromatic: all pads play, half-tone steps. Light
1375 * those in the scale, and highlight root notes
1378 pad->filtered = root_start + (note - 36);
1380 fn_pad_map.insert (make_pair (pad->filtered, pad));
1382 if (mode_map.find (note) != mode_map.end()) {
1384 if ((note % 12) == original_root) {
1385 pad->set_color (selection_color);
1386 pad->perma_color = selection_color;
1388 pad->set_color (LED::White);
1389 pad->perma_color = LED::White;
1392 pad->do_when_pressed = Pad::FlashOff;
1396 /* note is not in mode, turn it off */
1398 pad->do_when_pressed = Pad::FlashOn;
1399 pad->set_color (LED::Black);
1403 pad->set_state (LED::OneShot24th);
1404 write (pad->state_msg());
1410 bool changed = false;
1412 if (_scale_root != original_root) {
1413 _scale_root = original_root;
1416 if (_root_octave != octave) {
1417 _root_octave = octave;
1420 if (_in_key != inkey) {
1424 if (_mode != mode) {
1430 ScaleChange (); /* EMIT SIGNAL */
1435 Push2::set_percussive_mode (bool yn)
1438 cerr << "back to scale\n";
1439 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1446 for (int row = 0; row < 8; ++row) {
1448 for (int col = 0; col < 4; ++col) {
1450 int index = 36 + (row*8) + col;
1451 Pad* pad = nn_pad_map[index];
1453 pad->filtered = drum_note;
1458 for (int row = 0; row < 8; ++row) {
1460 for (int col = 4; col < 8; ++col) {
1462 int index = 36 + (row*8) + col;
1463 Pad* pad = nn_pad_map[index];
1465 pad->filtered = drum_note;
1474 Push2::current_layout () const
1476 Glib::Threads::Mutex::Lock lm (layout_lock);
1477 return _current_layout;
1481 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1483 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1484 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1485 boost::shared_ptr<MidiTrack> new_pad_target;
1487 /* See if there's a MIDI track selected */
1489 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1491 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1493 if (new_pad_target) {
1498 if (current_midi_track == new_pad_target) {
1503 if (!new_pad_target) {
1504 /* leave existing connection alone */
1508 /* disconnect from pad port, if appropriate */
1510 if (current_midi_track && pad_port) {
1511 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1514 /* now connect the pad port to this (newly) selected midi
1515 * track, if indeed there is one.
1518 if (new_pad_target && pad_port) {
1519 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1520 current_pad_target = new_pad_target;
1521 selection_color = get_color_index (new_pad_target->presentation_info().color());
1522 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1524 current_pad_target.reset ();
1525 selection_color = LED::Green;
1526 contrast_color = LED::Green;
1529 reset_pad_colors ();
1533 Push2::button_by_id (ButtonID bid)
1535 return id_button_map[bid];
1539 Push2::get_color_index (ArdourCanvas::Color rgba)
1541 ColorMap::iterator i = color_map.find (rgba);
1543 if (i != color_map.end()) {
1547 double dr, dg, db, da;
1549 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1550 int w = 126; /* not sure where/when we should get this value */
1553 r = (int) floor (255.0 * dr);
1554 g = (int) floor (255.0 * dg);
1555 b = (int) floor (255.0 * db);
1557 /* get a free index */
1561 if (color_map_free_list.empty()) {
1562 /* random replacement of any entry above zero and below 122 (where the
1563 * Ableton standard colors live)
1565 index = 1 + (random() % 121);
1567 index = color_map_free_list.top();
1568 color_map_free_list.pop();
1571 MidiByteArray palette_msg (17,
1573 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1574 0x00, /* index = 7 */
1575 0x00, 0x00, /* r = 8 & 9 */
1576 0x00, 0x00, /* g = 10 & 11 */
1577 0x00, 0x00, /* b = 12 & 13 */
1578 0x00, 0x00, /* w (a?) = 14 & 15*/
1580 palette_msg[7] = index;
1581 palette_msg[8] = r & 0x7f;
1582 palette_msg[9] = (r & 0x80) >> 7;
1583 palette_msg[10] = g & 0x7f;
1584 palette_msg[11] = (g & 0x80) >> 7;
1585 palette_msg[12] = b & 0x7f;
1586 palette_msg[13] = (b & 0x80) >> 7;
1587 palette_msg[14] = w & 0x7f;
1588 palette_msg[15] = w & 0x80;
1590 write (palette_msg);
1592 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1593 write (update_pallette_msg);
1595 color_map[rgba] = index;
1601 Push2::build_color_map ()
1603 /* These are "standard" colors that Ableton docs suggest will always be
1604 there. Put them in our color map so that when we look up these
1605 colors, we will use the Ableton indices for them.
1608 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1609 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1610 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1611 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1612 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1613 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1614 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1616 for (uint8_t n = 1; n < 122; ++n) {
1617 color_map_free_list.push (n);
1622 Push2::fill_color_table ()
1624 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1625 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1627 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1629 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1630 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1631 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1633 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1634 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1636 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1637 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1638 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1639 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1644 Push2::get_color (ColorName name)
1646 Colors::iterator c = colors.find (name);
1647 if (c != colors.end()) {
1655 Push2::set_current_layout (Push2Layout* layout)
1657 if (_current_layout) {
1658 _current_layout->hide ();
1659 _canvas->root()->remove (_current_layout);
1660 _previous_layout = _current_layout;
1663 _current_layout = layout;
1665 if (_current_layout) {
1666 _canvas->root()->add (_current_layout);
1667 _current_layout->show ();
1670 _canvas->request_redraw ();
1674 Push2::use_previous_layout ()
1676 if (_previous_layout) {
1677 set_current_layout (_previous_layout);
1682 Push2::request_pressure_mode ()
1684 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1689 Push2::set_pressure_mode (PressureMode pm)
1691 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1695 /* nothing to do, message is correct */
1705 cerr << "Sent PM message " << msg << endl;