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);
237 scale_layout = new ScaleLayout (*this, *session);
238 track_mix_layout = new TrackMixLayout (*this, *session);
239 splash_layout = new SplashLayout (*this, *session);
241 error << _("Cannot construct Canvas for display") << endmsg;
242 libusb_release_interface (handle, 0x00);
243 libusb_close (handle);
249 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
250 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
252 if (_async_in == 0 || _async_out == 0) {
256 /* We do not add our ports to the input/output bundles because we don't
257 * want users wiring them by hand. They could use JACK tools if they
258 * really insist on that.
261 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
262 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
264 /* Create a shadow port where, depending on the state of the surface,
265 * we will make pad note on/off events appear. The surface code will
266 * automatically this port to the first selected MIDI track.
269 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));
270 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
274 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
276 _output_bundle->add_channel (
278 ARDOUR::DataType::MIDI,
279 session->engine().make_port_name_non_relative (shadow_port->name())
283 session->BundleAddedOrRemoved ();
285 connect_to_parser ();
290 list<boost::shared_ptr<ARDOUR::Bundle> >
293 list<boost::shared_ptr<ARDOUR::Bundle> > b;
295 if (_output_bundle) {
296 b.push_back (_output_bundle);
305 init_buttons (false);
306 strip_buttons_off ();
308 /* wait for button data to be flushed */
310 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
311 asp->drain (10000, 500000);
313 AudioEngine::instance()->unregister_port (_async_in);
314 AudioEngine::instance()->unregister_port (_async_out);
316 _async_in.reset ((ARDOUR::Port*) 0);
317 _async_out.reset ((ARDOUR::Port*) 0);
321 periodic_connection.disconnect ();
322 session_connections.drop_connections ();
324 if (_current_layout) {
325 _canvas->root()->remove (_current_layout);
333 delete splash_layout;
337 libusb_release_interface (handle, 0x00);
338 libusb_close (handle);
346 Push2::strip_buttons_off ()
348 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
349 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
351 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
352 Button* b = id_button_map[strip_buttons[n]];
354 b->set_color (LED::Black);
355 b->set_state (LED::OneShot24th);
356 write (b->state_msg());
362 Push2::init_buttons (bool startup)
364 /* This is a list of buttons that we want lit because they do something
365 in ardour related (loosely, sometimes) to their illuminated label.
368 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
369 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
370 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
373 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
374 Button* b = id_button_map[buttons[n]];
377 b->set_color (LED::White);
379 b->set_color (LED::Black);
381 b->set_state (LED::OneShot24th);
382 write (b->state_msg());
387 /* all other buttons are off (black) */
389 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
390 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
391 Accent, Note, Session, };
393 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
394 Button* b = id_button_map[off_buttons[n]];
396 b->set_color (LED::Black);
397 b->set_state (LED::OneShot24th);
398 write (b->state_msg());
403 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
404 Pad* pad = pi->second;
406 pad->set_color (LED::Black);
407 pad->set_state (LED::OneShot24th);
408 write (pad->state_msg());
416 libusb_device_handle *h;
419 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
420 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
425 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
430 Push2::request_factory (uint32_t num_requests)
432 /* AbstractUI<T>::request_buffer_factory() is a template method only
433 instantiated in this source module. To provide something visible for
434 use in the interface/descriptor, we have this static method that is
437 return request_buffer_factory (num_requests);
441 Push2::do_request (Push2Request * req)
443 if (req->type == CallSlot) {
445 call_slot (MISSING_INVALIDATOR, req->the_slot);
447 } else if (req->type == Quit) {
465 set_current_layout (splash_layout);
466 splash_start = get_microseconds ();
474 /* display splash for 2 seconds */
476 if (get_microseconds() - splash_start > 2000000) {
478 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
479 set_current_layout (mix_layout);
489 Push2::set_active (bool yn)
491 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
493 if (yn == active()) {
499 /* start event loop */
504 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
509 /* Connect input port to event loop */
513 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
514 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
515 asp->xthread().attach (main_loop()->get_context());
517 connect_session_signals ();
519 /* set up periodic task used to push a frame buffer to the
520 * device (25fps). The device can handle 60fps, but we don't
521 * need that frame rate.
524 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
525 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
526 vblank_timeout->attach (main_loop()->get_context());
529 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
530 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
531 periodic_timeout->attach (main_loop()->get_context());
535 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
544 ControlProtocol::set_active (yn);
546 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
552 Push2::init_touch_strip ()
554 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
555 /* flags are the final byte (ignore end-of-sysex */
557 /* show bar, not point
561 msg[7] = (1<<4) | (1<<5) | (1<<6);
566 Push2::write (const MidiByteArray& data)
568 /* immediate delivery */
569 _output_port->write (&data[0], data.size(), 0);
573 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
576 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
582 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
584 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
589 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
590 framepos_t now = AudioEngine::instance()->sample_time();
604 Push2::connect_to_parser ()
606 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
608 MIDI::Parser* p = _input_port->parser();
611 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
612 /* V-Pot messages are Controller */
613 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
614 /* Button messages are NoteOn */
615 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
616 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
617 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
618 /* Fader messages are Pitchbend */
619 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
623 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
625 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
631 MidiByteArray msg (sz, raw_bytes);
632 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
634 if (!push2_sysex_header.compare_n (msg, 6)) {
639 case 0x1f: /* pressure mode */
641 _pressure_mode = AfterTouch;
642 PressureModeChange (AfterTouch);
643 cerr << "Pressure mode is after\n";
645 _pressure_mode = PolyPressure;
646 PressureModeChange (PolyPressure);
647 cerr << "Pressure mode is poly\n";
654 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
656 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
658 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
661 /* any press cancels any pending long press timeouts */
662 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
663 Button* bb = id_button_map[*x];
664 bb->timeout_connection.disconnect ();
668 if (b != cc_button_map.end()) {
670 Button* button = b->second;
673 buttons_down.insert (button->id);
674 start_press_timeout (*button, button->id);
676 buttons_down.erase (button->id);
677 button->timeout_connection.disconnect ();
681 set<ButtonID>::iterator c = consumed.find (button->id);
683 if (c == consumed.end()) {
684 if (ev->value == 0) {
685 (this->*button->release_method)();
687 (this->*button->press_method)();
690 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
698 int delta = ev->value;
701 delta = -(128 - delta);
704 switch (ev->controller_number) {
706 _current_layout->strip_vpot (0, delta);
709 _current_layout->strip_vpot (1, delta);
712 _current_layout->strip_vpot (2, delta);
715 _current_layout->strip_vpot (3, delta);
718 _current_layout->strip_vpot (4, delta);
721 _current_layout->strip_vpot (5, delta);
724 _current_layout->strip_vpot (6, delta);
727 _current_layout->strip_vpot (7, delta);
732 other_vpot (8, delta);
735 other_vpot (1, delta);
740 other_vpot (2, delta);
747 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
749 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
751 if (ev->velocity == 0) {
752 handle_midi_note_off_message (parser, ev);
756 switch (ev->note_number) {
758 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
761 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
764 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
767 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
770 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
773 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
776 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
779 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
784 other_vpot_touch (0, ev->velocity > 64);
787 other_vpot_touch (1, ev->velocity > 64);
792 other_vpot_touch (3, ev->velocity > 64);
797 if (ev->velocity < 64) {
803 if (ev->note_number < 11) {
807 /* Pad illuminations */
809 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
811 if (pm == nn_pad_map.end()) {
815 const Pad * const pad_pressed = pm->second;
817 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
819 if (pads_with_note.first == fn_pad_map.end()) {
823 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
824 Pad* pad = pi->second;
826 pad->set_color (contrast_color);
827 pad->set_state (LED::OneShot24th);
828 write (pad->state_msg());
833 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
835 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
837 if (ev->note_number < 11) {
838 /* theoretically related to encoder touch start/end, but
839 * actually they send note on with two different velocity
845 /* Pad illuminations */
847 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
849 if (pm == nn_pad_map.end()) {
853 const Pad * const pad_pressed = pm->second;
855 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
857 if (pads_with_note.first == fn_pad_map.end()) {
861 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
862 Pad* pad = pi->second;
864 if (pad->do_when_pressed == Pad::FlashOn) {
865 pad->set_color (LED::Black);
866 pad->set_state (LED::OneShot24th);
867 write (pad->state_msg());
868 } else if (pad->do_when_pressed == Pad::FlashOff) {
869 pad->set_color (pad->perma_color);
870 pad->set_state (LED::OneShot24th);
871 write (pad->state_msg());
877 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
882 Push2::thread_init ()
884 struct sched_param rtparam;
886 pthread_set_name (event_loop_name().c_str());
888 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
889 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
891 memset (&rtparam, 0, sizeof (rtparam));
892 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
894 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
895 // do we care? not particularly.
900 Push2::connect_session_signals()
902 // receive routes added
903 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
904 // receive VCAs added
905 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
907 // receive record state toggled
908 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
909 // receive transport state changed
910 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
911 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
912 // receive punch-in and punch-out
913 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
914 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
915 // receive rude solo changed
916 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
920 Push2::notify_record_state_changed ()
922 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
924 if (b == id_button_map.end()) {
928 switch (session->record_status ()) {
929 case Session::Disabled:
930 b->second->set_color (LED::White);
931 b->second->set_state (LED::NoTransition);
933 case Session::Enabled:
934 b->second->set_color (LED::Red);
935 b->second->set_state (LED::Blinking4th);
937 case Session::Recording:
938 b->second->set_color (LED::Red);
939 b->second->set_state (LED::OneShot24th);
943 write (b->second->state_msg());
947 Push2::notify_transport_state_changed ()
949 Button* b = id_button_map[Play];
951 if (session->transport_rolling()) {
952 b->set_state (LED::OneShot24th);
953 b->set_color (LED::Green);
956 /* disable any blink on FixedLength from pending edit range op */
957 Button* fl = id_button_map[FixedLength];
959 fl->set_color (LED::Black);
960 fl->set_state (LED::NoTransition);
961 write (fl->state_msg());
963 b->set_color (LED::White);
964 b->set_state (LED::NoTransition);
967 write (b->state_msg());
971 Push2::notify_loop_state_changed ()
976 Push2::notify_parameter_changed (std::string param)
978 IDButtonMap::iterator b;
980 if (param == "clicking") {
981 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
984 if (Config->get_clicking()) {
985 b->second->set_state (LED::Blinking4th);
986 b->second->set_color (LED::White);
988 b->second->set_color (LED::White);
989 b->second->set_state (LED::NoTransition);
991 write (b->second->state_msg ());
996 Push2::notify_solo_active_changed (bool yn)
998 IDButtonMap::iterator b = id_button_map.find (Solo);
1000 if (b == id_button_map.end()) {
1005 b->second->set_state (LED::Blinking4th);
1006 b->second->set_color (LED::Red);
1008 b->second->set_state (LED::NoTransition);
1009 b->second->set_color (LED::White);
1012 write (b->second->state_msg());
1018 XMLNode& node (ControlProtocol::get_state());
1021 child = new XMLNode (X_("Input"));
1022 child->add_child_nocopy (_async_in->get_state());
1023 node.add_child_nocopy (*child);
1024 child = new XMLNode (X_("Output"));
1025 child->add_child_nocopy (_async_out->get_state());
1026 node.add_child_nocopy (*child);
1028 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1029 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1030 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1031 node.add_property (X_("mode"), enum_2_string (_mode));
1037 Push2::set_state (const XMLNode & node, int version)
1039 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1043 if (ControlProtocol::set_state (node, version)) {
1049 if ((child = node.child (X_("Input"))) != 0) {
1050 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1052 _async_in->set_state (*portnode, version);
1056 if ((child = node.child (X_("Output"))) != 0) {
1057 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1059 _async_out->set_state (*portnode, version);
1063 XMLProperty const* prop;
1065 if ((prop = node.property (X_("root"))) != 0) {
1066 _scale_root = atoi (prop->value());
1069 if ((prop = node.property (X_("root_octave"))) != 0) {
1070 _root_octave = atoi (prop->value());
1073 if ((prop = node.property (X_("in_key"))) != 0) {
1074 _in_key = string_is_affirmative (prop->value());
1077 if ((prop = node.property (X_("mode"))) != 0) {
1078 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1085 Push2::other_vpot (int n, int delta)
1087 boost::shared_ptr<Amp> click_gain;
1093 /* metronome gain control */
1094 click_gain = session->click_gain();
1096 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1098 ac->set_value (ac->interface_to_internal (
1099 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1100 PBD::Controllable::UseGroup);
1105 /* master gain control */
1107 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1109 ac->set_value (ac->interface_to_internal (
1110 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1111 PBD::Controllable::UseGroup);
1119 Push2::other_vpot_touch (int n, bool touching)
1128 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1131 ac->start_touch (session->audible_frame());
1133 ac->stop_touch (true, session->audible_frame());
1141 Push2::start_shift ()
1143 cerr << "start shift\n";
1144 _modifier_state = ModifierState (_modifier_state | ModShift);
1145 Button* b = id_button_map[Shift];
1146 b->set_color (LED::White);
1147 b->set_state (LED::Blinking16th);
1148 write (b->state_msg());
1154 if (_modifier_state & ModShift) {
1155 cerr << "end shift\n";
1156 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1157 Button* b = id_button_map[Shift];
1158 b->timeout_connection.disconnect ();
1159 b->set_color (LED::White);
1160 b->set_state (LED::OneShot24th);
1161 write (b->state_msg());
1166 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1168 /* This filter is called asynchronously from a realtime process
1169 context. It must use atomics to check state, and must not block.
1172 bool matched = false;
1174 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1175 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1177 /* encoder touch start/touch end use note
1178 * 0-10. touchstrip uses note 12
1181 if ((*ev).note() > 10 && (*ev).note() != 12) {
1183 const int n = (*ev).note ();
1184 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1186 if (nni != nn_pad_map.end()) {
1187 Pad const * pad = nni->second;
1188 /* shift for output to the shadow port */
1189 if (pad->filtered >= 0) {
1190 (*ev).set_note (pad->filtered + (octave_shift*12));
1191 out.push_back (*ev);
1192 /* shift back so that the pads light correctly */
1195 /* no mapping, don't send event */
1198 out.push_back (*ev);
1203 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1204 out.push_back (*ev);
1212 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1214 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1215 if (!_input_port || !_output_port) {
1219 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1220 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1222 if (ni == name1 || ni == name2) {
1224 connection_state |= InputConnected;
1226 connection_state &= ~InputConnected;
1228 } else if (no == name1 || no == name2) {
1230 connection_state |= OutputConnected;
1232 connection_state &= ~OutputConnected;
1235 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1240 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1242 /* XXX this is a horrible hack. Without a short sleep here,
1243 something prevents the device wakeup messages from being
1244 sent and/or the responses from being received.
1248 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1252 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1255 ConnectionChange (); /* emit signal for our GUI */
1257 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1259 return true; /* connection status changed */
1265 request_pressure_mode ();
1268 boost::shared_ptr<Port>
1269 Push2::output_port()
1274 boost::shared_ptr<Port>
1281 Push2::pad_note (int row, int col) const
1283 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1285 if (nni != nn_pad_map.end()) {
1286 return nni->second->filtered;
1293 Push2::update_selection_color ()
1295 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1297 if (!current_midi_track) {
1301 selection_color = get_color_index (current_midi_track->presentation_info().color());
1302 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1304 reset_pad_colors ();
1308 Push2::reset_pad_colors ()
1310 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1314 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1316 MusicalMode m (mode);
1317 vector<float>::iterator interval;
1319 const int original_root = root;
1321 interval = m.steps.begin();
1322 root += (octave*12);
1325 const int root_start = root;
1327 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1328 vector<int> mode_vector; /* sorted in note order */
1330 mode_map.insert (note);
1331 mode_vector.push_back (note);
1333 /* build a map of all notes in the mode, from the root to 127 */
1335 while (note < 128) {
1337 if (interval == m.steps.end()) {
1339 /* last distance was the end of the scale,
1340 so wrap, adding the next note at one
1341 octave above the last root.
1344 interval = m.steps.begin();
1346 mode_map.insert (root);
1347 mode_vector.push_back (root);
1350 note = (int) floor (root + (2.0 * (*interval)));
1352 mode_map.insert (note);
1353 mode_vector.push_back (note);
1357 fn_pad_map.clear ();
1361 vector<int>::iterator notei;
1364 for (int row = 0; row < 8; ++row) {
1366 /* Ableton's grid layout wraps the available notes in the scale
1367 * by offsetting 3 notes per row (from the bottom)
1370 notei = mode_vector.begin();
1371 notei += row_offset;
1374 for (int col = 0; col < 8; ++col) {
1375 int index = 36 + (row*8) + col;
1376 Pad* pad = nn_pad_map[index];
1378 if (notei != mode_vector.end()) {
1381 pad->filtered = notenum;
1383 fn_pad_map.insert (make_pair (notenum, pad));
1385 if ((notenum % 12) == original_root) {
1386 pad->set_color (selection_color);
1387 pad->perma_color = selection_color;
1389 pad->set_color (LED::White);
1390 pad->perma_color = LED::White;
1393 pad->do_when_pressed = Pad::FlashOff;
1398 pad->set_color (LED::Black);
1399 pad->do_when_pressed = Pad::Nothing;
1403 pad->set_state (LED::OneShot24th);
1404 write (pad->state_msg());
1410 /* chromatic: all notes available, but highlight those in the scale */
1412 for (note = 36; note < 100; ++note) {
1414 Pad* pad = nn_pad_map[note];
1416 /* Chromatic: all pads play, half-tone steps. Light
1417 * those in the scale, and highlight root notes
1420 pad->filtered = root_start + (note - 36);
1422 fn_pad_map.insert (make_pair (pad->filtered, pad));
1424 if (mode_map.find (note) != mode_map.end()) {
1426 if ((note % 12) == original_root) {
1427 pad->set_color (selection_color);
1428 pad->perma_color = selection_color;
1430 pad->set_color (LED::White);
1431 pad->perma_color = LED::White;
1434 pad->do_when_pressed = Pad::FlashOff;
1438 /* note is not in mode, turn it off */
1440 pad->do_when_pressed = Pad::FlashOn;
1441 pad->set_color (LED::Black);
1445 pad->set_state (LED::OneShot24th);
1446 write (pad->state_msg());
1452 bool changed = false;
1454 if (_scale_root != original_root) {
1455 _scale_root = original_root;
1458 if (_root_octave != octave) {
1459 _root_octave = octave;
1462 if (_in_key != inkey) {
1466 if (_mode != mode) {
1472 ScaleChange (); /* EMIT SIGNAL */
1477 Push2::set_percussive_mode (bool yn)
1480 cerr << "back to scale\n";
1481 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1488 fn_pad_map.clear ();
1490 for (int row = 0; row < 8; ++row) {
1492 for (int col = 0; col < 4; ++col) {
1494 int index = 36 + (row*8) + col;
1495 Pad* pad = nn_pad_map[index];
1497 pad->filtered = drum_note;
1502 for (int row = 0; row < 8; ++row) {
1504 for (int col = 4; col < 8; ++col) {
1506 int index = 36 + (row*8) + col;
1507 Pad* pad = nn_pad_map[index];
1509 pad->filtered = drum_note;
1518 Push2::current_layout () const
1520 Glib::Threads::Mutex::Lock lm (layout_lock);
1521 return _current_layout;
1525 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1527 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1528 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1529 boost::shared_ptr<MidiTrack> new_pad_target;
1531 /* See if there's a MIDI track selected */
1533 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1535 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1537 if (new_pad_target) {
1542 if (current_midi_track == new_pad_target) {
1547 if (!new_pad_target) {
1548 /* leave existing connection alone */
1552 /* disconnect from pad port, if appropriate */
1554 if (current_midi_track && pad_port) {
1556 /* XXX this could possibly leave dangling MIDI notes.
1558 * A general libardour fix is required. It isn't obvious
1559 * how note resolution can be done unless disconnecting
1560 * becomes "slow" (i.e. deferred for as long as it takes
1561 * to resolve notes).
1563 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1566 /* now connect the pad port to this (newly) selected midi
1567 * track, if indeed there is one.
1570 if (new_pad_target && pad_port) {
1571 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1572 current_pad_target = new_pad_target;
1573 selection_color = get_color_index (new_pad_target->presentation_info().color());
1574 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1576 current_pad_target.reset ();
1577 selection_color = LED::Green;
1578 contrast_color = LED::Green;
1581 reset_pad_colors ();
1585 Push2::button_by_id (ButtonID bid)
1587 return id_button_map[bid];
1591 Push2::get_color_index (ArdourCanvas::Color rgba)
1593 ColorMap::iterator i = color_map.find (rgba);
1595 if (i != color_map.end()) {
1599 double dr, dg, db, da;
1601 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1602 int w = 126; /* not sure where/when we should get this value */
1605 r = (int) floor (255.0 * dr);
1606 g = (int) floor (255.0 * dg);
1607 b = (int) floor (255.0 * db);
1609 /* get a free index */
1613 if (color_map_free_list.empty()) {
1614 /* random replacement of any entry above zero and below 122 (where the
1615 * Ableton standard colors live)
1617 index = 1 + (random() % 121);
1619 index = color_map_free_list.top();
1620 color_map_free_list.pop();
1623 MidiByteArray palette_msg (17,
1625 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1626 0x00, /* index = 7 */
1627 0x00, 0x00, /* r = 8 & 9 */
1628 0x00, 0x00, /* g = 10 & 11 */
1629 0x00, 0x00, /* b = 12 & 13 */
1630 0x00, 0x00, /* w (a?) = 14 & 15*/
1632 palette_msg[7] = index;
1633 palette_msg[8] = r & 0x7f;
1634 palette_msg[9] = (r & 0x80) >> 7;
1635 palette_msg[10] = g & 0x7f;
1636 palette_msg[11] = (g & 0x80) >> 7;
1637 palette_msg[12] = b & 0x7f;
1638 palette_msg[13] = (b & 0x80) >> 7;
1639 palette_msg[14] = w & 0x7f;
1640 palette_msg[15] = w & 0x80;
1642 write (palette_msg);
1644 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1645 write (update_pallette_msg);
1647 color_map[rgba] = index;
1653 Push2::build_color_map ()
1655 /* These are "standard" colors that Ableton docs suggest will always be
1656 there. Put them in our color map so that when we look up these
1657 colors, we will use the Ableton indices for them.
1660 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1661 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1662 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1663 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1664 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1665 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1666 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1668 for (uint8_t n = 1; n < 122; ++n) {
1669 color_map_free_list.push (n);
1674 Push2::fill_color_table ()
1676 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1677 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1679 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1681 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1682 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1683 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1685 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1686 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1688 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1689 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1690 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1691 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1696 Push2::get_color (ColorName name)
1698 Colors::iterator c = colors.find (name);
1699 if (c != colors.end()) {
1707 Push2::set_current_layout (Push2Layout* layout)
1709 if (_current_layout) {
1710 _current_layout->hide ();
1711 _canvas->root()->remove (_current_layout);
1712 _previous_layout = _current_layout;
1715 _current_layout = layout;
1717 if (_current_layout) {
1718 _canvas->root()->add (_current_layout);
1719 _current_layout->show ();
1722 _canvas->request_redraw ();
1726 Push2::use_previous_layout ()
1728 if (_previous_layout) {
1729 set_current_layout (_previous_layout);
1734 Push2::request_pressure_mode ()
1736 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1741 Push2::set_pressure_mode (PressureMode pm)
1743 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1747 /* nothing to do, message is correct */
1757 cerr << "Sent PM message " << msg << endl;