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/amp.h"
34 #include "ardour/async_midi_port.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/debug.h"
37 #include "ardour/midiport_manager.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/midi_port.h"
40 #include "ardour/session.h"
41 #include "ardour/tempo.h"
43 #include "gtkmm2ext/gui_thread.h"
44 #include "gtkmm2ext/rgb_macros.h"
46 #include "canvas/colors.h"
56 #include "track_mix.h"
60 using namespace ARDOUR;
64 using namespace ArdourSurface;
66 #include "pbd/abstract_ui.cc" // instantiate template
68 #define ABLETON 0x2982
71 __attribute__((constructor)) static void
74 EnumWriter& enum_writer (EnumWriter::instance());
78 MusicalMode::Type mode;
80 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
81 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
83 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
84 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
85 REGISTER_CLASS_ENUM (MusicalMode, Minor);
86 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
87 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
88 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
89 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
90 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
91 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
92 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
93 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
94 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
95 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
96 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
97 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
98 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
99 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
100 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
101 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
102 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
103 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
104 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
105 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
106 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
107 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
108 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
109 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
110 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
111 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
112 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
113 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
114 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
115 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
116 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
117 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
118 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
119 REGISTER_CLASS_ENUM (MusicalMode, Persian);
120 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
124 Push2::Push2 (ARDOUR::Session& s)
125 : ControlProtocol (s, string (X_("Ableton Push 2")))
126 , AbstractUI<Push2Request> (name())
128 , _modifier_state (None)
130 , _current_layout (0)
131 , _previous_layout (0)
132 , connection_state (ConnectionState (0))
134 , _mode (MusicalMode::IonianMajor)
140 , _pressure_mode (AfterTouch)
141 , selection_color (LED::Green)
142 , contrast_color (LED::Green)
149 /* master cannot be removed, so no need to connect to going-away signal */
150 master = session->master_out ();
153 throw failed_constructor ();
156 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
158 /* catch current selection, if any */
160 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
161 stripable_selection_change (sp);
164 /* catch arrival and departure of Push2 itself */
165 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
167 /* Catch port connections and disconnections */
168 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
170 /* ports might already be there */
171 port_registration_handler ();
178 delete track_mix_layout;
184 Push2::port_registration_handler ()
186 if (!_async_in && !_async_out) {
187 /* ports not registered yet */
191 if (_async_in->connected() && _async_out->connected()) {
192 /* don't waste cycles here */
196 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
197 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
201 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
202 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
204 if (!in.empty() && !out.empty()) {
205 cerr << "Push2: both ports found\n";
206 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
207 if (!_async_in->connected()) {
208 AudioEngine::instance()->connect (_async_in->name(), in.front());
210 if (!_async_out->connected()) {
211 AudioEngine::instance()->connect (_async_out->name(), out.front());
226 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
230 if ((err = libusb_claim_interface (handle, 0x00))) {
235 _canvas = new Push2Canvas (*this, 960, 160);
236 mix_layout = new MixLayout (*this, *session, "globalmix");
237 scale_layout = new ScaleLayout (*this, *session, "scale");
238 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
239 splash_layout = new SplashLayout (*this, *session, "splash");
241 error << _("Cannot construct Canvas for display") << endmsg;
242 libusb_release_interface (handle, 0x00);
243 libusb_close (handle);
250 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
251 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
253 if (_async_in == 0 || _async_out == 0) {
257 /* We do not add our ports to the input/output bundles because we don't
258 * want users wiring them by hand. They could use JACK tools if they
259 * really insist on that.
262 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
263 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
265 /* Create a shadow port where, depending on the state of the surface,
266 * we will make pad note on/off events appear. The surface code will
267 * automatically this port to the first selected MIDI track.
270 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));
271 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
275 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
277 _output_bundle->add_channel (
279 ARDOUR::DataType::MIDI,
280 session->engine().make_port_name_non_relative (shadow_port->name())
284 session->BundleAddedOrRemoved ();
286 connect_to_parser ();
291 list<boost::shared_ptr<ARDOUR::Bundle> >
294 list<boost::shared_ptr<ARDOUR::Bundle> > b;
296 if (_output_bundle) {
297 b.push_back (_output_bundle);
306 init_buttons (false);
307 strip_buttons_off ();
309 /* wait for button data to be flushed */
311 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
312 asp->drain (10000, 500000);
314 AudioEngine::instance()->unregister_port (_async_in);
315 AudioEngine::instance()->unregister_port (_async_out);
317 _async_in.reset ((ARDOUR::Port*) 0);
318 _async_out.reset ((ARDOUR::Port*) 0);
322 periodic_connection.disconnect ();
323 session_connections.drop_connections ();
325 if (_current_layout) {
326 _canvas->root()->remove (_current_layout);
334 delete splash_layout;
338 libusb_release_interface (handle, 0x00);
339 libusb_close (handle);
347 Push2::strip_buttons_off ()
349 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
350 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
352 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
353 Button* b = id_button_map[strip_buttons[n]];
355 b->set_color (LED::Black);
356 b->set_state (LED::OneShot24th);
357 write (b->state_msg());
363 Push2::init_buttons (bool startup)
365 /* This is a list of buttons that we want lit because they do something
366 in ardour related (loosely, sometimes) to their illuminated label.
369 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
370 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
371 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
374 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
375 Button* b = id_button_map[buttons[n]];
378 b->set_color (LED::White);
380 b->set_color (LED::Black);
382 b->set_state (LED::OneShot24th);
383 write (b->state_msg());
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);
484 if (_current_layout) {
485 _current_layout->update_meters ();
486 _current_layout->update_clocks ();
495 Push2::set_active (bool yn)
497 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
499 if (yn == active()) {
505 /* start event loop */
510 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
515 /* Connect input port to event loop */
519 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
520 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
521 asp->xthread().attach (main_loop()->get_context());
523 connect_session_signals ();
525 /* set up periodic task used to push a frame buffer to the
526 * device (25fps). The device can handle 60fps, but we don't
527 * need that frame rate.
530 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
531 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
532 vblank_timeout->attach (main_loop()->get_context());
535 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
536 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
537 periodic_timeout->attach (main_loop()->get_context());
541 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
550 ControlProtocol::set_active (yn);
552 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
558 Push2::init_touch_strip ()
560 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
561 /* flags are the final byte (ignore end-of-sysex */
563 /* show bar, not point
567 msg[7] = (1<<4) | (1<<5) | (1<<6);
572 Push2::write (const MidiByteArray& data)
574 /* immediate delivery */
575 _output_port->write (&data[0], data.size(), 0);
579 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
582 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
588 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
590 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
595 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
596 framepos_t now = AudioEngine::instance()->sample_time();
610 Push2::connect_to_parser ()
612 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
614 MIDI::Parser* p = _input_port->parser();
617 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
618 /* V-Pot messages are Controller */
619 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
620 /* Button messages are NoteOn */
621 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
622 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
623 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
624 /* Fader messages are Pitchbend */
625 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
629 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
631 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
637 MidiByteArray msg (sz, raw_bytes);
638 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
640 if (!push2_sysex_header.compare_n (msg, 6)) {
645 case 0x1f: /* pressure mode */
647 _pressure_mode = AfterTouch;
648 PressureModeChange (AfterTouch);
649 cerr << "Pressure mode is after\n";
651 _pressure_mode = PolyPressure;
652 PressureModeChange (PolyPressure);
653 cerr << "Pressure mode is poly\n";
660 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
662 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
664 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
667 /* any press cancels any pending long press timeouts */
668 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
669 Button* bb = id_button_map[*x];
670 bb->timeout_connection.disconnect ();
674 if (b != cc_button_map.end()) {
676 Button* button = b->second;
679 buttons_down.insert (button->id);
680 start_press_timeout (*button, button->id);
682 buttons_down.erase (button->id);
683 button->timeout_connection.disconnect ();
687 set<ButtonID>::iterator c = consumed.find (button->id);
689 if (c == consumed.end()) {
690 if (ev->value == 0) {
691 (this->*button->release_method)();
693 (this->*button->press_method)();
696 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
704 int delta = ev->value;
707 delta = -(128 - delta);
710 switch (ev->controller_number) {
712 _current_layout->strip_vpot (0, delta);
715 _current_layout->strip_vpot (1, delta);
718 _current_layout->strip_vpot (2, delta);
721 _current_layout->strip_vpot (3, delta);
724 _current_layout->strip_vpot (4, delta);
727 _current_layout->strip_vpot (5, delta);
730 _current_layout->strip_vpot (6, delta);
733 _current_layout->strip_vpot (7, delta);
738 other_vpot (8, delta);
741 other_vpot (1, delta);
746 other_vpot (2, delta);
753 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
755 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
757 if (ev->velocity == 0) {
758 handle_midi_note_off_message (parser, ev);
762 switch (ev->note_number) {
764 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
767 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
770 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
773 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
776 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
779 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
782 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
785 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
790 other_vpot_touch (0, ev->velocity > 64);
793 other_vpot_touch (1, ev->velocity > 64);
798 other_vpot_touch (3, ev->velocity > 64);
803 if (ev->velocity < 64) {
809 if (ev->note_number < 11) {
813 /* Pad illuminations */
815 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
817 if (pm == nn_pad_map.end()) {
821 const Pad * const pad_pressed = pm->second;
823 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
825 if (pads_with_note.first == fn_pad_map.end()) {
829 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
830 Pad* pad = pi->second;
832 pad->set_color (contrast_color);
833 pad->set_state (LED::OneShot24th);
834 write (pad->state_msg());
839 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
841 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
843 if (ev->note_number < 11) {
844 /* theoretically related to encoder touch start/end, but
845 * actually they send note on with two different velocity
851 /* Pad illuminations */
853 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
855 if (pm == nn_pad_map.end()) {
859 const Pad * const pad_pressed = pm->second;
861 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
863 if (pads_with_note.first == fn_pad_map.end()) {
867 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
868 Pad* pad = pi->second;
870 if (pad->do_when_pressed == Pad::FlashOn) {
871 pad->set_color (LED::Black);
872 pad->set_state (LED::OneShot24th);
873 write (pad->state_msg());
874 } else if (pad->do_when_pressed == Pad::FlashOff) {
875 pad->set_color (pad->perma_color);
876 pad->set_state (LED::OneShot24th);
877 write (pad->state_msg());
883 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
888 Push2::thread_init ()
890 struct sched_param rtparam;
892 pthread_set_name (event_loop_name().c_str());
894 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
895 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
897 memset (&rtparam, 0, sizeof (rtparam));
898 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
900 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
901 // do we care? not particularly.
906 Push2::connect_session_signals()
908 // receive routes added
909 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
910 // receive VCAs added
911 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
913 // receive record state toggled
914 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
915 // receive transport state changed
916 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
917 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
918 // receive punch-in and punch-out
919 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
920 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
921 // receive rude solo changed
922 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
926 Push2::notify_record_state_changed ()
928 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
930 if (b == id_button_map.end()) {
934 switch (session->record_status ()) {
935 case Session::Disabled:
936 b->second->set_color (LED::White);
937 b->second->set_state (LED::NoTransition);
939 case Session::Enabled:
940 b->second->set_color (LED::Red);
941 b->second->set_state (LED::Blinking4th);
943 case Session::Recording:
944 b->second->set_color (LED::Red);
945 b->second->set_state (LED::OneShot24th);
949 write (b->second->state_msg());
953 Push2::notify_transport_state_changed ()
955 Button* b = id_button_map[Play];
957 if (session->transport_rolling()) {
958 b->set_state (LED::OneShot24th);
959 b->set_color (LED::Green);
962 /* disable any blink on FixedLength from pending edit range op */
963 Button* fl = id_button_map[FixedLength];
965 fl->set_color (LED::Black);
966 fl->set_state (LED::NoTransition);
967 write (fl->state_msg());
969 b->set_color (LED::White);
970 b->set_state (LED::NoTransition);
973 write (b->state_msg());
977 Push2::notify_loop_state_changed ()
982 Push2::notify_parameter_changed (std::string param)
984 IDButtonMap::iterator b;
986 if (param == "clicking") {
987 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
990 if (Config->get_clicking()) {
991 b->second->set_state (LED::Blinking4th);
992 b->second->set_color (LED::White);
994 b->second->set_color (LED::White);
995 b->second->set_state (LED::NoTransition);
997 write (b->second->state_msg ());
1002 Push2::notify_solo_active_changed (bool yn)
1004 IDButtonMap::iterator b = id_button_map.find (Solo);
1006 if (b == id_button_map.end()) {
1011 b->second->set_state (LED::Blinking4th);
1012 b->second->set_color (LED::Red);
1014 b->second->set_state (LED::NoTransition);
1015 b->second->set_color (LED::White);
1018 write (b->second->state_msg());
1024 XMLNode& node (ControlProtocol::get_state());
1027 child = new XMLNode (X_("Input"));
1028 child->add_child_nocopy (_async_in->get_state());
1029 node.add_child_nocopy (*child);
1030 child = new XMLNode (X_("Output"));
1031 child->add_child_nocopy (_async_out->get_state());
1032 node.add_child_nocopy (*child);
1034 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1035 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1036 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1037 node.add_property (X_("mode"), enum_2_string (_mode));
1043 Push2::set_state (const XMLNode & node, int version)
1045 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1049 if (ControlProtocol::set_state (node, version)) {
1055 if ((child = node.child (X_("Input"))) != 0) {
1056 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1058 _async_in->set_state (*portnode, version);
1062 if ((child = node.child (X_("Output"))) != 0) {
1063 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1065 _async_out->set_state (*portnode, version);
1069 XMLProperty const* prop;
1071 if ((prop = node.property (X_("root"))) != 0) {
1072 _scale_root = atoi (prop->value());
1075 if ((prop = node.property (X_("root_octave"))) != 0) {
1076 _root_octave = atoi (prop->value());
1079 if ((prop = node.property (X_("in_key"))) != 0) {
1080 _in_key = string_is_affirmative (prop->value());
1083 if ((prop = node.property (X_("mode"))) != 0) {
1084 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1091 Push2::other_vpot (int n, int delta)
1093 boost::shared_ptr<Amp> click_gain;
1099 /* metronome gain control */
1100 click_gain = session->click_gain();
1102 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1104 ac->set_value (ac->interface_to_internal (
1105 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1106 PBD::Controllable::UseGroup);
1111 /* master gain control */
1113 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1115 ac->set_value (ac->interface_to_internal (
1116 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1117 PBD::Controllable::UseGroup);
1125 Push2::other_vpot_touch (int n, bool touching)
1134 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1137 ac->start_touch (session->audible_frame());
1139 ac->stop_touch (true, session->audible_frame());
1147 Push2::start_shift ()
1149 cerr << "start shift\n";
1150 _modifier_state = ModifierState (_modifier_state | ModShift);
1151 Button* b = id_button_map[Shift];
1152 b->set_color (LED::White);
1153 b->set_state (LED::Blinking16th);
1154 write (b->state_msg());
1160 if (_modifier_state & ModShift) {
1161 cerr << "end shift\n";
1162 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1163 Button* b = id_button_map[Shift];
1164 b->timeout_connection.disconnect ();
1165 b->set_color (LED::White);
1166 b->set_state (LED::OneShot24th);
1167 write (b->state_msg());
1172 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1174 /* This filter is called asynchronously from a realtime process
1175 context. It must use atomics to check state, and must not block.
1178 bool matched = false;
1180 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1181 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1183 /* encoder touch start/touch end use note
1184 * 0-10. touchstrip uses note 12
1187 if ((*ev).note() > 10 && (*ev).note() != 12) {
1189 const int n = (*ev).note ();
1190 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1192 if (nni != nn_pad_map.end()) {
1193 Pad const * pad = nni->second;
1194 /* shift for output to the shadow port */
1195 if (pad->filtered >= 0) {
1196 (*ev).set_note (pad->filtered + (octave_shift*12));
1197 out.push_back (*ev);
1198 /* shift back so that the pads light correctly */
1201 /* no mapping, don't send event */
1204 out.push_back (*ev);
1209 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1210 out.push_back (*ev);
1218 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1220 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1221 if (!_input_port || !_output_port) {
1225 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1226 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1228 if (ni == name1 || ni == name2) {
1230 connection_state |= InputConnected;
1232 connection_state &= ~InputConnected;
1234 } else if (no == name1 || no == name2) {
1236 connection_state |= OutputConnected;
1238 connection_state &= ~OutputConnected;
1241 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1246 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1248 /* XXX this is a horrible hack. Without a short sleep here,
1249 something prevents the device wakeup messages from being
1250 sent and/or the responses from being received.
1254 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1258 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1261 ConnectionChange (); /* emit signal for our GUI */
1263 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1265 return true; /* connection status changed */
1271 request_pressure_mode ();
1274 boost::shared_ptr<Port>
1275 Push2::output_port()
1280 boost::shared_ptr<Port>
1287 Push2::pad_note (int row, int col) const
1289 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1291 if (nni != nn_pad_map.end()) {
1292 return nni->second->filtered;
1299 Push2::update_selection_color ()
1301 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1303 if (!current_midi_track) {
1307 selection_color = get_color_index (current_midi_track->presentation_info().color());
1308 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1310 reset_pad_colors ();
1314 Push2::reset_pad_colors ()
1316 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1320 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1322 MusicalMode m (mode);
1323 vector<float>::iterator interval;
1325 const int original_root = root;
1327 interval = m.steps.begin();
1328 root += (octave*12);
1331 const int root_start = root;
1333 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1334 vector<int> mode_vector; /* sorted in note order */
1336 mode_map.insert (note);
1337 mode_vector.push_back (note);
1339 /* build a map of all notes in the mode, from the root to 127 */
1341 while (note < 128) {
1343 if (interval == m.steps.end()) {
1345 /* last distance was the end of the scale,
1346 so wrap, adding the next note at one
1347 octave above the last root.
1350 interval = m.steps.begin();
1352 mode_map.insert (root);
1353 mode_vector.push_back (root);
1356 note = (int) floor (root + (2.0 * (*interval)));
1358 mode_map.insert (note);
1359 mode_vector.push_back (note);
1363 fn_pad_map.clear ();
1367 vector<int>::iterator notei;
1370 for (int row = 0; row < 8; ++row) {
1372 /* Ableton's grid layout wraps the available notes in the scale
1373 * by offsetting 3 notes per row (from the bottom)
1376 notei = mode_vector.begin();
1377 notei += row_offset;
1380 for (int col = 0; col < 8; ++col) {
1381 int index = 36 + (row*8) + col;
1382 Pad* pad = nn_pad_map[index];
1384 if (notei != mode_vector.end()) {
1387 pad->filtered = notenum;
1389 fn_pad_map.insert (make_pair (notenum, pad));
1391 if ((notenum % 12) == original_root) {
1392 pad->set_color (selection_color);
1393 pad->perma_color = selection_color;
1395 pad->set_color (LED::White);
1396 pad->perma_color = LED::White;
1399 pad->do_when_pressed = Pad::FlashOff;
1404 pad->set_color (LED::Black);
1405 pad->do_when_pressed = Pad::Nothing;
1409 pad->set_state (LED::OneShot24th);
1410 write (pad->state_msg());
1416 /* chromatic: all notes available, but highlight those in the scale */
1418 for (note = 36; note < 100; ++note) {
1420 Pad* pad = nn_pad_map[note];
1422 /* Chromatic: all pads play, half-tone steps. Light
1423 * those in the scale, and highlight root notes
1426 pad->filtered = root_start + (note - 36);
1428 fn_pad_map.insert (make_pair (pad->filtered, pad));
1430 if (mode_map.find (note) != mode_map.end()) {
1432 if ((note % 12) == original_root) {
1433 pad->set_color (selection_color);
1434 pad->perma_color = selection_color;
1436 pad->set_color (LED::White);
1437 pad->perma_color = LED::White;
1440 pad->do_when_pressed = Pad::FlashOff;
1444 /* note is not in mode, turn it off */
1446 pad->do_when_pressed = Pad::FlashOn;
1447 pad->set_color (LED::Black);
1451 pad->set_state (LED::OneShot24th);
1452 write (pad->state_msg());
1458 bool changed = false;
1460 if (_scale_root != original_root) {
1461 _scale_root = original_root;
1464 if (_root_octave != octave) {
1465 _root_octave = octave;
1468 if (_in_key != inkey) {
1472 if (_mode != mode) {
1478 ScaleChange (); /* EMIT SIGNAL */
1483 Push2::set_percussive_mode (bool yn)
1486 cerr << "back to scale\n";
1487 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1494 fn_pad_map.clear ();
1496 for (int row = 0; row < 8; ++row) {
1498 for (int col = 0; col < 4; ++col) {
1500 int index = 36 + (row*8) + col;
1501 Pad* pad = nn_pad_map[index];
1503 pad->filtered = drum_note;
1508 for (int row = 0; row < 8; ++row) {
1510 for (int col = 4; col < 8; ++col) {
1512 int index = 36 + (row*8) + col;
1513 Pad* pad = nn_pad_map[index];
1515 pad->filtered = drum_note;
1524 Push2::current_layout () const
1526 Glib::Threads::Mutex::Lock lm (layout_lock);
1527 return _current_layout;
1531 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1533 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1534 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1535 boost::shared_ptr<MidiTrack> new_pad_target;
1537 /* See if there's a MIDI track selected */
1539 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1541 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1543 if (new_pad_target) {
1548 if (current_midi_track == new_pad_target) {
1553 if (!new_pad_target) {
1554 /* leave existing connection alone */
1558 /* disconnect from pad port, if appropriate */
1560 if (current_midi_track && pad_port) {
1562 /* XXX this could possibly leave dangling MIDI notes.
1564 * A general libardour fix is required. It isn't obvious
1565 * how note resolution can be done unless disconnecting
1566 * becomes "slow" (i.e. deferred for as long as it takes
1567 * to resolve notes).
1569 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1572 /* now connect the pad port to this (newly) selected midi
1573 * track, if indeed there is one.
1576 if (new_pad_target && pad_port) {
1577 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1578 current_pad_target = new_pad_target;
1579 selection_color = get_color_index (new_pad_target->presentation_info().color());
1580 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1582 current_pad_target.reset ();
1583 selection_color = LED::Green;
1584 contrast_color = LED::Green;
1587 reset_pad_colors ();
1591 Push2::button_by_id (ButtonID bid)
1593 return id_button_map[bid];
1597 Push2::get_color_index (ArdourCanvas::Color rgba)
1599 ColorMap::iterator i = color_map.find (rgba);
1601 if (i != color_map.end()) {
1605 double dr, dg, db, da;
1607 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1608 int w = 126; /* not sure where/when we should get this value */
1611 r = (int) floor (255.0 * dr);
1612 g = (int) floor (255.0 * dg);
1613 b = (int) floor (255.0 * db);
1615 /* get a free index */
1619 if (color_map_free_list.empty()) {
1620 /* random replacement of any entry above zero and below 122 (where the
1621 * Ableton standard colors live)
1623 index = 1 + (random() % 121);
1625 index = color_map_free_list.top();
1626 color_map_free_list.pop();
1629 MidiByteArray palette_msg (17,
1631 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1632 0x00, /* index = 7 */
1633 0x00, 0x00, /* r = 8 & 9 */
1634 0x00, 0x00, /* g = 10 & 11 */
1635 0x00, 0x00, /* b = 12 & 13 */
1636 0x00, 0x00, /* w (a?) = 14 & 15*/
1638 palette_msg[7] = index;
1639 palette_msg[8] = r & 0x7f;
1640 palette_msg[9] = (r & 0x80) >> 7;
1641 palette_msg[10] = g & 0x7f;
1642 palette_msg[11] = (g & 0x80) >> 7;
1643 palette_msg[12] = b & 0x7f;
1644 palette_msg[13] = (b & 0x80) >> 7;
1645 palette_msg[14] = w & 0x7f;
1646 palette_msg[15] = w & 0x80;
1648 write (palette_msg);
1650 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1651 write (update_pallette_msg);
1653 color_map[rgba] = index;
1659 Push2::build_color_map ()
1661 /* These are "standard" colors that Ableton docs suggest will always be
1662 there. Put them in our color map so that when we look up these
1663 colors, we will use the Ableton indices for them.
1666 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1667 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1668 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1669 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1670 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1671 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1672 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1674 for (uint8_t n = 1; n < 122; ++n) {
1675 color_map_free_list.push (n);
1680 Push2::fill_color_table ()
1682 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1683 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1685 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1687 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1688 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1689 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1691 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1692 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1694 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1695 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1696 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1697 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1702 Push2::get_color (ColorName name)
1704 Colors::iterator c = colors.find (name);
1705 if (c != colors.end()) {
1713 Push2::set_current_layout (Push2Layout* layout)
1715 if (layout && layout == _current_layout) {
1716 _current_layout->show ();
1719 if (_current_layout) {
1720 _current_layout->hide ();
1721 _canvas->root()->remove (_current_layout);
1722 _previous_layout = _current_layout;
1725 _current_layout = layout;
1727 if (_current_layout) {
1728 _canvas->root()->add (_current_layout);
1729 _current_layout->show ();
1733 _canvas->request_redraw ();
1738 Push2::use_previous_layout ()
1740 if (_previous_layout) {
1741 set_current_layout (_previous_layout);
1746 Push2::request_pressure_mode ()
1748 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1753 Push2::set_pressure_mode (PressureMode pm)
1755 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1759 /* nothing to do, message is correct */
1769 cerr << "Sent PM message " << msg << endl;