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));
630 MidiByteArray msg (sz, raw_bytes);
631 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
633 if (!push2_sysex_header.compare_n (msg, 6)) {
638 case 0x1f: /* pressure mode */
640 _pressure_mode = AfterTouch;
641 PressureModeChange (AfterTouch);
642 cerr << "Pressure mode is after\n";
644 _pressure_mode = PolyPressure;
645 PressureModeChange (PolyPressure);
646 cerr << "Pressure mode is poly\n";
653 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
655 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
657 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
660 /* any press cancels any pending long press timeouts */
661 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
662 Button* bb = id_button_map[*x];
663 bb->timeout_connection.disconnect ();
667 if (b != cc_button_map.end()) {
669 Button* button = b->second;
672 buttons_down.insert (button->id);
673 start_press_timeout (*button, button->id);
675 buttons_down.erase (button->id);
676 button->timeout_connection.disconnect ();
680 set<ButtonID>::iterator c = consumed.find (button->id);
682 if (c == consumed.end()) {
683 if (ev->value == 0) {
684 (this->*button->release_method)();
686 (this->*button->press_method)();
689 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
697 int delta = ev->value;
700 delta = -(128 - delta);
703 switch (ev->controller_number) {
705 _current_layout->strip_vpot (0, delta);
708 _current_layout->strip_vpot (1, delta);
711 _current_layout->strip_vpot (2, delta);
714 _current_layout->strip_vpot (3, delta);
717 _current_layout->strip_vpot (4, delta);
720 _current_layout->strip_vpot (5, delta);
723 _current_layout->strip_vpot (6, delta);
726 _current_layout->strip_vpot (7, delta);
731 other_vpot (8, delta);
734 other_vpot (1, delta);
739 other_vpot (2, delta);
746 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
748 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
750 if (ev->velocity == 0) {
751 handle_midi_note_off_message (parser, ev);
755 switch (ev->note_number) {
757 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
760 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
763 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
766 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
769 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
772 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
775 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
778 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
783 other_vpot_touch (0, ev->velocity > 64);
786 other_vpot_touch (1, ev->velocity > 64);
791 other_vpot_touch (3, ev->velocity > 64);
796 if (ev->velocity < 64) {
802 if (ev->note_number < 11) {
806 /* Pad illuminations */
808 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
810 if (pm == nn_pad_map.end()) {
814 const Pad * const pad_pressed = pm->second;
816 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
818 if (pads_with_note.first == fn_pad_map.end()) {
822 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
823 Pad* pad = pi->second;
825 pad->set_color (contrast_color);
826 pad->set_state (LED::OneShot24th);
827 write (pad->state_msg());
832 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
834 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
836 if (ev->note_number < 11) {
837 /* theoretically related to encoder touch start/end, but
838 * actually they send note on with two different velocity
844 /* Pad illuminations */
846 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
848 if (pm == nn_pad_map.end()) {
852 const Pad * const pad_pressed = pm->second;
854 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
856 if (pads_with_note.first == fn_pad_map.end()) {
860 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
861 Pad* pad = pi->second;
863 if (pad->do_when_pressed == Pad::FlashOn) {
864 pad->set_color (LED::Black);
865 pad->set_state (LED::OneShot24th);
866 write (pad->state_msg());
867 } else if (pad->do_when_pressed == Pad::FlashOff) {
868 pad->set_color (pad->perma_color);
869 pad->set_state (LED::OneShot24th);
870 write (pad->state_msg());
876 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
881 Push2::thread_init ()
883 struct sched_param rtparam;
885 pthread_set_name (event_loop_name().c_str());
887 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
888 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
890 memset (&rtparam, 0, sizeof (rtparam));
891 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
893 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
894 // do we care? not particularly.
899 Push2::connect_session_signals()
901 // receive routes added
902 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
903 // receive VCAs added
904 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
906 // receive record state toggled
907 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
908 // receive transport state changed
909 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
910 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
911 // receive punch-in and punch-out
912 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
913 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
914 // receive rude solo changed
915 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
919 Push2::notify_record_state_changed ()
921 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
923 if (b == id_button_map.end()) {
927 switch (session->record_status ()) {
928 case Session::Disabled:
929 b->second->set_color (LED::White);
930 b->second->set_state (LED::NoTransition);
932 case Session::Enabled:
933 b->second->set_color (LED::Red);
934 b->second->set_state (LED::Blinking4th);
936 case Session::Recording:
937 b->second->set_color (LED::Red);
938 b->second->set_state (LED::OneShot24th);
942 write (b->second->state_msg());
946 Push2::notify_transport_state_changed ()
948 Button* b = id_button_map[Play];
950 if (session->transport_rolling()) {
951 b->set_state (LED::OneShot24th);
952 b->set_color (LED::Green);
955 /* disable any blink on FixedLength from pending edit range op */
956 Button* fl = id_button_map[FixedLength];
958 fl->set_color (LED::Black);
959 fl->set_state (LED::NoTransition);
960 write (fl->state_msg());
962 b->set_color (LED::White);
963 b->set_state (LED::NoTransition);
966 write (b->state_msg());
970 Push2::notify_loop_state_changed ()
975 Push2::notify_parameter_changed (std::string param)
977 IDButtonMap::iterator b;
979 if (param == "clicking") {
980 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
983 if (Config->get_clicking()) {
984 b->second->set_state (LED::Blinking4th);
985 b->second->set_color (LED::White);
987 b->second->set_color (LED::White);
988 b->second->set_state (LED::NoTransition);
990 write (b->second->state_msg ());
995 Push2::notify_solo_active_changed (bool yn)
997 IDButtonMap::iterator b = id_button_map.find (Solo);
999 if (b == id_button_map.end()) {
1004 b->second->set_state (LED::Blinking4th);
1005 b->second->set_color (LED::Red);
1007 b->second->set_state (LED::NoTransition);
1008 b->second->set_color (LED::White);
1011 write (b->second->state_msg());
1017 XMLNode& node (ControlProtocol::get_state());
1020 child = new XMLNode (X_("Input"));
1021 child->add_child_nocopy (_async_in->get_state());
1022 node.add_child_nocopy (*child);
1023 child = new XMLNode (X_("Output"));
1024 child->add_child_nocopy (_async_out->get_state());
1025 node.add_child_nocopy (*child);
1027 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1028 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1029 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1030 node.add_property (X_("mode"), enum_2_string (_mode));
1036 Push2::set_state (const XMLNode & node, int version)
1038 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1042 if (ControlProtocol::set_state (node, version)) {
1048 if ((child = node.child (X_("Input"))) != 0) {
1049 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1051 _async_in->set_state (*portnode, version);
1055 if ((child = node.child (X_("Output"))) != 0) {
1056 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1058 _async_out->set_state (*portnode, version);
1062 XMLProperty const* prop;
1064 if ((prop = node.property (X_("root"))) != 0) {
1065 _scale_root = atoi (prop->value());
1068 if ((prop = node.property (X_("root_octave"))) != 0) {
1069 _root_octave = atoi (prop->value());
1072 if ((prop = node.property (X_("in_key"))) != 0) {
1073 _in_key = string_is_affirmative (prop->value());
1076 if ((prop = node.property (X_("mode"))) != 0) {
1077 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1084 Push2::other_vpot (int n, int delta)
1092 /* master gain control */
1094 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1096 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1104 Push2::other_vpot_touch (int n, bool touching)
1113 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1116 ac->start_touch (session->audible_frame());
1118 ac->stop_touch (true, session->audible_frame());
1126 Push2::start_shift ()
1128 cerr << "start shift\n";
1129 _modifier_state = ModifierState (_modifier_state | ModShift);
1130 Button* b = id_button_map[Shift];
1131 b->set_color (LED::White);
1132 b->set_state (LED::Blinking16th);
1133 write (b->state_msg());
1139 if (_modifier_state & ModShift) {
1140 cerr << "end shift\n";
1141 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1142 Button* b = id_button_map[Shift];
1143 b->timeout_connection.disconnect ();
1144 b->set_color (LED::White);
1145 b->set_state (LED::OneShot24th);
1146 write (b->state_msg());
1151 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1153 /* This filter is called asynchronously from a realtime process
1154 context. It must use atomics to check state, and must not block.
1157 bool matched = false;
1159 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1160 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1162 /* encoder touch start/touch end use note
1163 * 0-10. touchstrip uses note 12
1166 if ((*ev).note() > 10 && (*ev).note() != 12) {
1168 const int n = (*ev).note ();
1169 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1171 if (nni != nn_pad_map.end()) {
1172 Pad const * pad = nni->second;
1173 /* shift for output to the shadow port */
1174 if (pad->filtered >= 0) {
1175 (*ev).set_note (pad->filtered + (octave_shift*12));
1176 out.push_back (*ev);
1177 /* shift back so that the pads light correctly */
1180 /* no mapping, don't send event */
1183 out.push_back (*ev);
1188 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1189 out.push_back (*ev);
1197 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1199 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1200 if (!_input_port || !_output_port) {
1204 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1205 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1207 if (ni == name1 || ni == name2) {
1209 connection_state |= InputConnected;
1211 connection_state &= ~InputConnected;
1213 } else if (no == name1 || no == name2) {
1215 connection_state |= OutputConnected;
1217 connection_state &= ~OutputConnected;
1220 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1225 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1227 /* XXX this is a horrible hack. Without a short sleep here,
1228 something prevents the device wakeup messages from being
1229 sent and/or the responses from being received.
1233 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1237 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1240 ConnectionChange (); /* emit signal for our GUI */
1242 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1244 return true; /* connection status changed */
1250 request_pressure_mode ();
1253 boost::shared_ptr<Port>
1254 Push2::output_port()
1259 boost::shared_ptr<Port>
1266 Push2::pad_note (int row, int col) const
1268 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1270 if (nni != nn_pad_map.end()) {
1271 return nni->second->filtered;
1278 Push2::update_selection_color ()
1280 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1282 if (!current_midi_track) {
1286 selection_color = get_color_index (current_midi_track->presentation_info().color());
1287 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1289 reset_pad_colors ();
1293 Push2::reset_pad_colors ()
1295 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1299 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1301 MusicalMode m (mode);
1302 vector<float>::iterator interval;
1304 const int original_root = root;
1306 interval = m.steps.begin();
1307 root += (octave*12);
1310 const int root_start = root;
1312 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1313 vector<int> mode_vector; /* sorted in note order */
1315 mode_map.insert (note);
1316 mode_vector.push_back (note);
1318 /* build a map of all notes in the mode, from the root to 127 */
1320 while (note < 128) {
1322 if (interval == m.steps.end()) {
1324 /* last distance was the end of the scale,
1325 so wrap, adding the next note at one
1326 octave above the last root.
1329 interval = m.steps.begin();
1331 mode_map.insert (root);
1332 mode_vector.push_back (root);
1335 note = (int) floor (root + (2.0 * (*interval)));
1337 mode_map.insert (note);
1338 mode_vector.push_back (note);
1342 fn_pad_map.clear ();
1346 vector<int>::iterator notei;
1349 for (int row = 0; row < 8; ++row) {
1351 /* Ableton's grid layout wraps the available notes in the scale
1352 * by offsetting 3 notes per row (from the bottom)
1355 notei = mode_vector.begin();
1356 notei += row_offset;
1359 for (int col = 0; col < 8; ++col) {
1360 int index = 36 + (row*8) + col;
1361 Pad* pad = nn_pad_map[index];
1363 if (notei != mode_vector.end()) {
1366 pad->filtered = notenum;
1368 fn_pad_map.insert (make_pair (notenum, pad));
1370 if ((notenum % 12) == original_root) {
1371 pad->set_color (selection_color);
1372 pad->perma_color = selection_color;
1374 pad->set_color (LED::White);
1375 pad->perma_color = LED::White;
1378 pad->do_when_pressed = Pad::FlashOff;
1383 pad->set_color (LED::Black);
1384 pad->do_when_pressed = Pad::Nothing;
1388 pad->set_state (LED::OneShot24th);
1389 write (pad->state_msg());
1395 /* chromatic: all notes available, but highlight those in the scale */
1397 for (note = 36; note < 100; ++note) {
1399 Pad* pad = nn_pad_map[note];
1401 /* Chromatic: all pads play, half-tone steps. Light
1402 * those in the scale, and highlight root notes
1405 pad->filtered = root_start + (note - 36);
1407 fn_pad_map.insert (make_pair (pad->filtered, pad));
1409 if (mode_map.find (note) != mode_map.end()) {
1411 if ((note % 12) == original_root) {
1412 pad->set_color (selection_color);
1413 pad->perma_color = selection_color;
1415 pad->set_color (LED::White);
1416 pad->perma_color = LED::White;
1419 pad->do_when_pressed = Pad::FlashOff;
1423 /* note is not in mode, turn it off */
1425 pad->do_when_pressed = Pad::FlashOn;
1426 pad->set_color (LED::Black);
1430 pad->set_state (LED::OneShot24th);
1431 write (pad->state_msg());
1437 bool changed = false;
1439 if (_scale_root != original_root) {
1440 _scale_root = original_root;
1443 if (_root_octave != octave) {
1444 _root_octave = octave;
1447 if (_in_key != inkey) {
1451 if (_mode != mode) {
1457 ScaleChange (); /* EMIT SIGNAL */
1462 Push2::set_percussive_mode (bool yn)
1465 cerr << "back to scale\n";
1466 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1473 for (int row = 0; row < 8; ++row) {
1475 for (int col = 0; col < 4; ++col) {
1477 int index = 36 + (row*8) + col;
1478 Pad* pad = nn_pad_map[index];
1480 pad->filtered = drum_note;
1485 for (int row = 0; row < 8; ++row) {
1487 for (int col = 4; col < 8; ++col) {
1489 int index = 36 + (row*8) + col;
1490 Pad* pad = nn_pad_map[index];
1492 pad->filtered = drum_note;
1501 Push2::current_layout () const
1503 Glib::Threads::Mutex::Lock lm (layout_lock);
1504 return _current_layout;
1508 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1510 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1511 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1512 boost::shared_ptr<MidiTrack> new_pad_target;
1514 /* See if there's a MIDI track selected */
1516 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1518 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1520 if (new_pad_target) {
1525 if (current_midi_track == new_pad_target) {
1530 if (!new_pad_target) {
1531 /* leave existing connection alone */
1535 /* disconnect from pad port, if appropriate */
1537 if (current_midi_track && pad_port) {
1538 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1541 /* now connect the pad port to this (newly) selected midi
1542 * track, if indeed there is one.
1545 if (new_pad_target && pad_port) {
1546 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1547 current_pad_target = new_pad_target;
1548 selection_color = get_color_index (new_pad_target->presentation_info().color());
1549 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1551 current_pad_target.reset ();
1552 selection_color = LED::Green;
1553 contrast_color = LED::Green;
1556 reset_pad_colors ();
1560 Push2::button_by_id (ButtonID bid)
1562 return id_button_map[bid];
1566 Push2::get_color_index (ArdourCanvas::Color rgba)
1568 ColorMap::iterator i = color_map.find (rgba);
1570 if (i != color_map.end()) {
1574 double dr, dg, db, da;
1576 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1577 int w = 126; /* not sure where/when we should get this value */
1580 r = (int) floor (255.0 * dr);
1581 g = (int) floor (255.0 * dg);
1582 b = (int) floor (255.0 * db);
1584 /* get a free index */
1588 if (color_map_free_list.empty()) {
1589 /* random replacement of any entry above zero and below 122 (where the
1590 * Ableton standard colors live)
1592 index = 1 + (random() % 121);
1594 index = color_map_free_list.top();
1595 color_map_free_list.pop();
1598 MidiByteArray palette_msg (17,
1600 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1601 0x00, /* index = 7 */
1602 0x00, 0x00, /* r = 8 & 9 */
1603 0x00, 0x00, /* g = 10 & 11 */
1604 0x00, 0x00, /* b = 12 & 13 */
1605 0x00, 0x00, /* w (a?) = 14 & 15*/
1607 palette_msg[7] = index;
1608 palette_msg[8] = r & 0x7f;
1609 palette_msg[9] = (r & 0x80) >> 7;
1610 palette_msg[10] = g & 0x7f;
1611 palette_msg[11] = (g & 0x80) >> 7;
1612 palette_msg[12] = b & 0x7f;
1613 palette_msg[13] = (b & 0x80) >> 7;
1614 palette_msg[14] = w & 0x7f;
1615 palette_msg[15] = w & 0x80;
1617 write (palette_msg);
1619 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1620 write (update_pallette_msg);
1622 color_map[rgba] = index;
1628 Push2::build_color_map ()
1630 /* These are "standard" colors that Ableton docs suggest will always be
1631 there. Put them in our color map so that when we look up these
1632 colors, we will use the Ableton indices for them.
1635 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1636 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1637 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1638 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1639 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1640 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1641 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1643 for (uint8_t n = 1; n < 122; ++n) {
1644 color_map_free_list.push (n);
1649 Push2::fill_color_table ()
1651 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1652 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1654 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1656 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1657 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1658 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1660 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1661 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1663 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1664 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1665 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1666 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1671 Push2::get_color (ColorName name)
1673 Colors::iterator c = colors.find (name);
1674 if (c != colors.end()) {
1682 Push2::set_current_layout (Push2Layout* layout)
1684 if (_current_layout) {
1685 _current_layout->hide ();
1686 _canvas->root()->remove (_current_layout);
1687 _previous_layout = _current_layout;
1690 _current_layout = layout;
1692 if (_current_layout) {
1693 _canvas->root()->add (_current_layout);
1694 _current_layout->show ();
1697 _canvas->request_redraw ();
1701 Push2::use_previous_layout ()
1703 if (_previous_layout) {
1704 set_current_layout (_previous_layout);
1709 Push2::request_pressure_mode ()
1711 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1716 Push2::set_pressure_mode (PressureMode pm)
1718 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1722 /* nothing to do, message is correct */
1732 cerr << "Sent PM message " << msg << endl;