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"
54 #include "track_mix.h"
58 using namespace ARDOUR;
62 using namespace ArdourSurface;
64 #include "pbd/abstract_ui.cc" // instantiate template
66 #define ABLETON 0x2982
69 __attribute__((constructor)) static void
72 EnumWriter& enum_writer (EnumWriter::instance());
76 MusicalMode::Type mode;
78 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
79 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
81 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
82 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
83 REGISTER_CLASS_ENUM (MusicalMode, Minor);
84 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
85 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
86 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
87 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
88 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
89 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
90 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
91 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
92 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
93 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
94 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
95 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
96 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
97 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
98 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
99 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
100 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
101 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
102 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
103 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
104 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
105 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
106 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
107 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
108 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
109 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
110 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
111 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
112 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
113 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
114 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
115 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
116 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
117 REGISTER_CLASS_ENUM (MusicalMode, Persian);
118 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
122 Push2::Push2 (ARDOUR::Session& s)
123 : ControlProtocol (s, string (X_("Ableton Push 2")))
124 , AbstractUI<Push2Request> (name())
126 , _modifier_state (None)
128 , _current_layout (0)
130 , connection_state (ConnectionState (0))
132 , _mode (MusicalMode::IonianMajor)
138 , _pressure_mode (AfterTouch)
145 /* master cannot be removed, so no need to connect to going-away signal */
146 master = session->master_out ();
149 throw failed_constructor ();
152 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
154 /* catch current selection, if any */
156 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
157 stripable_selection_change (sp);
160 /* catch arrival and departure of Push2 itself */
161 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
163 /* Catch port connections and disconnections */
164 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
166 /* ports might already be there */
167 port_registration_handler ();
174 delete track_mix_layout;
180 Push2::port_registration_handler ()
182 if (!_async_in && !_async_out) {
183 /* ports not registered yet */
187 if (_async_in->connected() && _async_out->connected()) {
188 /* don't waste cycles here */
192 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
193 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
197 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
198 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
200 if (!in.empty() && !out.empty()) {
201 cerr << "Push2: both ports found\n";
202 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
203 if (!_async_in->connected()) {
204 AudioEngine::instance()->connect (_async_in->name(), in.front());
206 if (!_async_out->connected()) {
207 AudioEngine::instance()->connect (_async_out->name(), out.front());
222 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
226 if ((err = libusb_claim_interface (handle, 0x00))) {
231 _canvas = new Push2Canvas (*this, 160, 960);
232 mix_layout = new MixLayout (*this, *session);
233 scale_layout = new ScaleLayout (*this, *session);
234 track_mix_layout = new TrackMixLayout (*this, *session);
236 error << _("Cannot construct Canvas for display") << endmsg;
237 libusb_release_interface (handle, 0x00);
238 libusb_close (handle);
244 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
245 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
247 if (_async_in == 0 || _async_out == 0) {
251 /* We do not add our ports to the input/output bundles because we don't
252 * want users wiring them by hand. They could use JACK tools if they
253 * really insist on that.
256 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
257 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
259 /* Create a shadow port where, depending on the state of the surface,
260 * we will make pad note on/off events appear. The surface code will
261 * automatically this port to the first selected MIDI track.
264 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));
265 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
269 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
271 _output_bundle->add_channel (
273 ARDOUR::DataType::MIDI,
274 session->engine().make_port_name_non_relative (shadow_port->name())
278 session->BundleAddedOrRemoved ();
280 connect_to_parser ();
287 list<boost::shared_ptr<ARDOUR::Bundle> >
290 list<boost::shared_ptr<ARDOUR::Bundle> > b;
292 if (_output_bundle) {
293 b.push_back (_output_bundle);
302 init_buttons (false);
304 /* wait for button data to be flushed */
306 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
307 asp->drain (10000, 500000);
309 AudioEngine::instance()->unregister_port (_async_in);
310 AudioEngine::instance()->unregister_port (_async_out);
312 _async_in.reset ((ARDOUR::Port*) 0);
313 _async_out.reset ((ARDOUR::Port*) 0);
317 periodic_connection.disconnect ();
318 session_connections.drop_connections ();
328 libusb_release_interface (handle, 0x00);
329 libusb_close (handle);
337 Push2::init_buttons (bool startup)
339 /* This is a list of buttons that we want lit because they do something
340 in ardour related (loosely, sometimes) to their illuminated label.
343 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
344 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
345 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
348 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
349 Button* b = id_button_map[buttons[n]];
352 b->set_color (LED::White);
354 b->set_color (LED::Black);
356 b->set_state (LED::OneShot24th);
357 write (b->state_msg());
360 /* Strip buttons should all be off (black) by default. They will change
361 * color to reflect various conditions
364 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
365 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
367 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
368 Button* b = id_button_map[strip_buttons[n]];
370 b->set_color (LED::Black);
371 b->set_state (LED::OneShot24th);
372 write (b->state_msg());
377 /* all other buttons are off (black) */
379 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
380 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
381 Accent, Note, Session, };
383 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
384 Button* b = id_button_map[off_buttons[n]];
386 b->set_color (LED::Black);
387 b->set_state (LED::OneShot24th);
388 write (b->state_msg());
393 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
394 Pad* pad = pi->second;
396 pad->set_color (LED::Black);
397 pad->set_state (LED::OneShot24th);
398 write (pad->state_msg());
406 libusb_device_handle *h;
409 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
410 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
415 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
420 Push2::request_factory (uint32_t num_requests)
422 /* AbstractUI<T>::request_buffer_factory() is a template method only
423 instantiated in this source module. To provide something visible for
424 use in the interface/descriptor, we have this static method that is
427 return request_buffer_factory (num_requests);
431 Push2::do_request (Push2Request * req)
433 DEBUG_TRACE (DEBUG::Push2, string_compose ("doing request type %1\n", req->type));
434 if (req->type == CallSlot) {
436 call_slot (MISSING_INVALIDATOR, req->the_slot);
438 } else if (req->type == Quit) {
457 /* display splash for 3 seconds */
459 if (get_microseconds() - splash_start > 3000000) {
474 Push2::set_active (bool yn)
476 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
478 if (yn == active()) {
484 /* start event loop */
489 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
494 /* Connect input port to event loop */
498 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
499 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
500 asp->xthread().attach (main_loop()->get_context());
502 connect_session_signals ();
504 /* set up periodic task used to push a frame buffer to the
505 * device (25fps). The device can handle 60fps, but we don't
506 * need that frame rate.
509 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
510 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
511 vblank_timeout->attach (main_loop()->get_context());
514 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
515 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
516 periodic_timeout->attach (main_loop()->get_context());
520 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
522 set_current_layout (mix_layout);
530 ControlProtocol::set_active (yn);
532 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
538 Push2::init_touch_strip ()
540 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
541 /* flags are the final byte (ignore end-of-sysex */
543 /* show bar, not point
547 msg[7] = (1<<4) | (1<<5) | (1<<6);
552 Push2::write (const MidiByteArray& data)
554 /* immediate delivery */
555 _output_port->write (&data[0], data.size(), 0);
559 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
562 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
568 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
570 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
575 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
576 framepos_t now = AudioEngine::instance()->sample_time();
590 Push2::connect_to_parser ()
592 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
594 MIDI::Parser* p = _input_port->parser();
597 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
598 /* V-Pot messages are Controller */
599 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
600 /* Button messages are NoteOn */
601 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
602 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
603 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
604 /* Fader messages are Pitchbend */
605 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
609 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
611 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
612 MidiByteArray msg (sz, raw_bytes);
613 MidiByteArray aftertouch_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x0, 0xF7);
614 MidiByteArray polypress_mode_response (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0x1, 0xF7);
616 if (msg == aftertouch_mode_response) {
617 _pressure_mode = AfterTouch;
618 PressureModeChange (AfterTouch);
619 cerr << "Pressure mod eis after\n";
620 } else if (msg == polypress_mode_response) {
621 _pressure_mode = PolyPressure;
622 PressureModeChange (PolyPressure);
623 cerr << "Pressure mod eis poly\n";
628 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
630 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
632 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
635 /* any press cancels any pending long press timeouts */
636 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
637 Button* bb = id_button_map[*x];
638 bb->timeout_connection.disconnect ();
642 if (b != cc_button_map.end()) {
644 Button* button = b->second;
647 buttons_down.insert (button->id);
648 start_press_timeout (*button, button->id);
650 buttons_down.erase (button->id);
651 button->timeout_connection.disconnect ();
655 set<ButtonID>::iterator c = consumed.find (button->id);
657 if (c == consumed.end()) {
658 if (ev->value == 0) {
659 (this->*button->release_method)();
661 (this->*button->press_method)();
664 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
672 int delta = ev->value;
675 delta = -(128 - delta);
678 switch (ev->controller_number) {
680 _current_layout->strip_vpot (0, delta);
683 _current_layout->strip_vpot (1, delta);
686 _current_layout->strip_vpot (2, delta);
689 _current_layout->strip_vpot (3, delta);
692 _current_layout->strip_vpot (4, delta);
695 _current_layout->strip_vpot (5, delta);
698 _current_layout->strip_vpot (6, delta);
701 _current_layout->strip_vpot (7, delta);
706 other_vpot (8, delta);
709 other_vpot (1, delta);
714 other_vpot (2, delta);
721 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
723 DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
725 if (ev->velocity == 0) {
726 handle_midi_note_off_message (parser, ev);
730 switch (ev->note_number) {
732 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
735 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
738 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
741 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
744 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
747 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
750 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
753 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
758 other_vpot_touch (0, ev->velocity > 64);
761 other_vpot_touch (1, ev->velocity > 64);
766 other_vpot_touch (3, ev->velocity > 64);
771 if (ev->velocity < 64) {
777 if (ev->note_number < 11) {
783 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
785 if (pi == nn_pad_map.end()) {
789 Pad* pad = pi->second;
791 if (pad->do_when_pressed == Pad::FlashOn) {
792 pad->set_color (LED::White);
793 pad->set_state (LED::OneShot24th);
794 write (pad->state_msg());
795 } else if (pad->do_when_pressed == Pad::FlashOff) {
796 pad->set_color (LED::Black);
797 pad->set_state (LED::OneShot24th);
798 write (pad->state_msg());
803 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
805 DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
807 if (ev->note_number < 11) {
808 /* theoretically related to encoder touch start/end, but
809 * actually they send note on with two different velocity
815 NNPadMap::iterator pi = nn_pad_map.find (ev->note_number);
817 if (pi == nn_pad_map.end()) {
821 Pad* pad = pi->second;
823 if (pad->do_when_pressed == Pad::FlashOn) {
824 pad->set_color (LED::Black);
825 pad->set_state (LED::OneShot24th);
826 write (pad->state_msg());
827 } else if (pad->do_when_pressed == Pad::FlashOff) {
828 pad->set_color (pad->perma_color);
829 pad->set_state (LED::OneShot24th);
830 write (pad->state_msg());
835 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
840 Push2::thread_init ()
842 struct sched_param rtparam;
844 pthread_set_name (event_loop_name().c_str());
846 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
847 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
849 memset (&rtparam, 0, sizeof (rtparam));
850 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
852 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
853 // do we care? not particularly.
858 Push2::connect_session_signals()
860 // receive routes added
861 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
862 // receive VCAs added
863 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
865 // receive record state toggled
866 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
867 // receive transport state changed
868 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
869 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
870 // receive punch-in and punch-out
871 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
872 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
873 // receive rude solo changed
874 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
878 Push2::notify_record_state_changed ()
880 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
882 if (b == id_button_map.end()) {
886 switch (session->record_status ()) {
887 case Session::Disabled:
888 b->second->set_color (LED::White);
889 b->second->set_state (LED::NoTransition);
891 case Session::Enabled:
892 b->second->set_color (LED::Red);
893 b->second->set_state (LED::Blinking4th);
895 case Session::Recording:
896 b->second->set_color (LED::Red);
897 b->second->set_state (LED::OneShot24th);
901 write (b->second->state_msg());
905 Push2::notify_transport_state_changed ()
907 Button* b = id_button_map[Play];
909 if (session->transport_rolling()) {
910 b->set_state (LED::OneShot24th);
911 b->set_color (LED::Green);
914 /* disable any blink on FixedLength from pending edit range op */
915 Button* fl = id_button_map[FixedLength];
917 fl->set_color (LED::Black);
918 fl->set_state (LED::NoTransition);
919 write (fl->state_msg());
921 b->set_color (LED::White);
922 b->set_state (LED::NoTransition);
925 write (b->state_msg());
929 Push2::notify_loop_state_changed ()
934 Push2::notify_parameter_changed (std::string param)
936 IDButtonMap::iterator b;
938 if (param == "clicking") {
939 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
942 if (Config->get_clicking()) {
943 b->second->set_state (LED::Blinking4th);
944 b->second->set_color (LED::White);
946 b->second->set_color (LED::White);
947 b->second->set_state (LED::NoTransition);
949 write (b->second->state_msg ());
954 Push2::notify_solo_active_changed (bool yn)
956 IDButtonMap::iterator b = id_button_map.find (Solo);
958 if (b == id_button_map.end()) {
963 b->second->set_state (LED::Blinking4th);
964 b->second->set_color (LED::Red);
966 b->second->set_state (LED::NoTransition);
967 b->second->set_color (LED::White);
970 write (b->second->state_msg());
976 XMLNode& node (ControlProtocol::get_state());
979 child = new XMLNode (X_("Input"));
980 child->add_child_nocopy (_async_in->get_state());
981 node.add_child_nocopy (*child);
982 child = new XMLNode (X_("Output"));
983 child->add_child_nocopy (_async_out->get_state());
984 node.add_child_nocopy (*child);
986 node.add_property (X_("root"), to_string (_scale_root, std::dec));
987 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
988 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
989 node.add_property (X_("mode"), enum_2_string (_mode));
995 Push2::set_state (const XMLNode & node, int version)
997 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1001 if (ControlProtocol::set_state (node, version)) {
1007 if ((child = node.child (X_("Input"))) != 0) {
1008 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1010 _async_in->set_state (*portnode, version);
1014 if ((child = node.child (X_("Output"))) != 0) {
1015 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1017 _async_out->set_state (*portnode, version);
1021 XMLProperty const* prop;
1023 if ((prop = node.property (X_("root"))) != 0) {
1024 _scale_root = atoi (prop->value());
1027 if ((prop = node.property (X_("root_octave"))) != 0) {
1028 _root_octave = atoi (prop->value());
1031 if ((prop = node.property (X_("in_key"))) != 0) {
1032 _in_key = string_is_affirmative (prop->value());
1035 if ((prop = node.property (X_("mode"))) != 0) {
1036 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1043 Push2::other_vpot (int n, int delta)
1051 /* master gain control */
1053 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1055 ac->set_value (ac->get_value() + ((2.0/64.0) * delta), PBD::Controllable::UseGroup);
1063 Push2::other_vpot_touch (int n, bool touching)
1072 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1075 ac->start_touch (session->audible_frame());
1077 ac->stop_touch (true, session->audible_frame());
1085 Push2::start_shift ()
1087 cerr << "start shift\n";
1088 _modifier_state = ModifierState (_modifier_state | ModShift);
1089 Button* b = id_button_map[Shift];
1090 b->set_color (LED::White);
1091 b->set_state (LED::Blinking16th);
1092 write (b->state_msg());
1098 if (_modifier_state & ModShift) {
1099 cerr << "end shift\n";
1100 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1101 Button* b = id_button_map[Shift];
1102 b->timeout_connection.disconnect ();
1103 b->set_color (LED::White);
1104 b->set_state (LED::OneShot24th);
1105 write (b->state_msg());
1115 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1117 /* This filter is called asynchronously from a realtime process
1118 context. It must use atomics to check state, and must not block.
1121 bool matched = false;
1123 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1124 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1126 /* encoder touch start/touch end use note
1127 * 0-10. touchstrip uses note 12
1130 if ((*ev).note() > 10 && (*ev).note() != 12) {
1132 const int n = (*ev).note ();
1133 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1135 if (nni != nn_pad_map.end()) {
1136 Pad const * pad = nni->second;
1137 /* shift for output to the shadow port */
1138 if (pad->filtered >= 0) {
1139 (*ev).set_note (pad->filtered + (octave_shift*12));
1140 out.push_back (*ev);
1141 /* shift back so that the pads light correctly */
1144 /* no mapping, don't send event */
1147 out.push_back (*ev);
1152 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1153 out.push_back (*ev);
1161 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1163 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1164 if (!_input_port || !_output_port) {
1168 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1169 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1171 if (ni == name1 || ni == name2) {
1173 connection_state |= InputConnected;
1175 connection_state &= ~InputConnected;
1177 } else if (no == name1 || no == name2) {
1179 connection_state |= OutputConnected;
1181 connection_state &= ~OutputConnected;
1184 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1189 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1191 /* XXX this is a horrible hack. Without a short sleep here,
1192 something prevents the device wakeup messages from being
1193 sent and/or the responses from being received.
1197 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1201 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1204 ConnectionChange (); /* emit signal for our GUI */
1206 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1208 return true; /* connection status changed */
1214 request_pressure_mode ();
1217 boost::shared_ptr<Port>
1218 Push2::output_port()
1223 boost::shared_ptr<Port>
1230 Push2::pad_note (int row, int col) const
1232 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1234 if (nni != nn_pad_map.end()) {
1235 return nni->second->filtered;
1242 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1244 MusicalMode m (mode);
1245 vector<float>::iterator interval;
1247 const int original_root = root;
1249 interval = m.steps.begin();
1250 root += (octave*12);
1253 const int root_start = root;
1255 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1256 vector<int> mode_vector; /* sorted in note order */
1258 mode_map.insert (note);
1259 mode_vector.push_back (note);
1261 /* build a map of all notes in the mode, from the root to 127 */
1263 while (note < 128) {
1265 if (interval == m.steps.end()) {
1267 /* last distance was the end of the scale,
1268 so wrap, adding the next note at one
1269 octave above the last root.
1272 interval = m.steps.begin();
1274 mode_map.insert (root);
1275 mode_vector.push_back (root);
1278 note = (int) floor (root + (2.0 * (*interval)));
1280 mode_map.insert (note);
1281 mode_vector.push_back (note);
1287 vector<int>::iterator notei;
1289 for (int row = 0; row < 8; ++row) {
1291 /* Ableton's grid layout wraps the available notes in the scale
1292 * by offsetting 3 notes per row (from the bottom)
1295 notei = mode_vector.begin();
1296 notei += row_offset;
1299 for (int col = 0; col < 8; ++col) {
1300 int index = 36 + (row*8) + col;
1301 Pad* pad = nn_pad_map[index];
1303 if (notei != mode_vector.end()) {
1306 pad->filtered = notenum;
1308 if ((notenum % 12) == original_root) {
1309 pad->set_color (LED::Green);
1310 pad->perma_color = LED::Green;
1312 pad->set_color (LED::White);
1313 pad->perma_color = LED::White;
1316 pad->do_when_pressed = Pad::FlashOff;
1321 pad->set_color (LED::Black);
1322 pad->do_when_pressed = Pad::Nothing;
1326 write (pad->state_msg());
1332 /* chromatic: all notes available, but highlight those in the scale */
1334 for (note = 36; note < 100; ++note) {
1336 Pad* pad = nn_pad_map[note];
1338 /* Chromatic: all pads play, half-tone steps. Light
1339 * those in the scale, and highlight root notes
1342 pad->filtered = root_start + (note - 36);
1344 if (mode_map.find (note) != mode_map.end()) {
1346 if ((note % 12) == original_root) {
1347 pad->set_color (LED::Green);
1348 pad->perma_color = LED::Green;
1350 pad->set_color (LED::White);
1351 pad->perma_color = LED::White;
1354 pad->do_when_pressed = Pad::FlashOff;
1358 /* note is not in mode, turn it off */
1360 pad->do_when_pressed = Pad::FlashOn;
1361 pad->set_color (LED::Black);
1365 write (pad->state_msg());
1369 PadChange (); /* EMIT SIGNAL */
1373 _scale_root = original_root;
1374 _root_octave = octave;
1380 Push2::set_percussive_mode (bool yn)
1383 cerr << "back to scale\n";
1384 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1391 for (int row = 0; row < 8; ++row) {
1393 for (int col = 0; col < 4; ++col) {
1395 int index = 36 + (row*8) + col;
1396 Pad* pad = nn_pad_map[index];
1398 pad->filtered = drum_note;
1403 for (int row = 0; row < 8; ++row) {
1405 for (int col = 4; col < 8; ++col) {
1407 int index = 36 + (row*8) + col;
1408 Pad* pad = nn_pad_map[index];
1410 pad->filtered = drum_note;
1417 PadChange (); /* EMIT SIGNAL */
1421 Push2::current_layout () const
1423 Glib::Threads::Mutex::Lock lm (layout_lock);
1424 return _current_layout;
1428 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1430 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1431 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1432 boost::shared_ptr<MidiTrack> new_pad_target;
1434 /* See if there's a MIDI track selected */
1436 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1438 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1440 if (new_pad_target) {
1445 if (new_pad_target) {
1446 cerr << "new midi pad target " << new_pad_target->name() << endl;
1448 cerr << "no midi pad target\n";
1451 if (current_midi_track == new_pad_target) {
1456 if (!new_pad_target) {
1457 /* leave existing connection alone */
1461 /* disconnect from pad port, if appropriate */
1463 if (current_midi_track && pad_port) {
1464 cerr << "Disconnect pads from " << current_midi_track->name() << endl;
1465 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1468 /* now connect the pad port to this (newly) selected midi
1469 * track, if indeed there is one.
1472 if (new_pad_target && pad_port) {
1473 cerr << "Reconnect pads to " << new_pad_target->name() << endl;
1474 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1475 current_pad_target = new_pad_target;
1477 current_pad_target.reset ();
1482 Push2::button_by_id (ButtonID bid)
1484 return id_button_map[bid];
1488 Push2::get_color_index (uint32_t rgb)
1490 ColorMap::iterator i = color_map.find (rgb);
1492 if (i != color_map.end()) {
1497 UINT_TO_RGBA (rgb, &r, &g, &b, &a);
1498 int w = 204; /* not sure where/when we should get this value */
1500 /* get a free index */
1504 if (color_map_free_list.empty()) {
1505 /* random replacement of any entry above zero and below 122 (where the
1506 * Ableton standard colors live)
1508 index = 1 + (random() % 121);
1510 index = color_map_free_list.top();
1511 color_map_free_list.pop();
1514 MidiByteArray palette_msg (17, 0xf0, 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x7E, 0x00, 0xF7);
1515 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1517 palette_msg[7] = index;
1518 palette_msg[8] = r & 0x7f;
1519 palette_msg[9] = r & 0x1;
1520 palette_msg[10] = g & 0x7f;
1521 palette_msg[11] = g & 0x1;
1522 palette_msg[12] = b & 0x7f;
1523 palette_msg[13] = b & 0x1;
1524 palette_msg[14] = w & 0x7f;
1525 palette_msg[15] = w & 0x1;
1527 write (palette_msg);
1528 write (update_pallette_msg);
1530 color_map[index] = rgb;
1536 Push2::build_color_map ()
1538 /* These are "standard" colors that Ableton docs suggest will always be
1539 there. Put them in our color map so that when we look up these
1540 colors, we will use the Ableton indices for them.
1543 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1544 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1545 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1546 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1547 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1548 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1549 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1551 for (uint8_t n = 1; n < 122; ++n) {
1552 color_map_free_list.push (n);
1557 Push2::fill_color_table ()
1559 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1560 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1562 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1564 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1565 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1566 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1568 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1569 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1571 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1572 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1573 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1574 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1579 Push2::get_color (ColorName name)
1581 Colors::iterator c = colors.find (name);
1582 if (c != colors.end()) {
1590 Push2::set_current_layout (Push2Layout* layout)
1592 if (_current_layout) {
1593 _current_layout->hide ();
1594 _canvas->root()->remove (_current_layout);
1597 _current_layout = layout;
1599 if (_current_layout) {
1600 _current_layout->show ();
1601 _canvas->root()->add (_current_layout);
1606 Push2::request_pressure_mode ()
1608 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1613 Push2::set_pressure_mode (PressureMode pm)
1615 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1619 /* nothing to do, message is correct */
1629 cerr << "Sent PM message " << msg << endl;