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 #ifdef PLATFORM_WINDOWS
61 #define random() rand()
64 using namespace ARDOUR;
68 using namespace ArdourSurface;
70 #include "pbd/abstract_ui.cc" // instantiate template
72 #define ABLETON 0x2982
75 __attribute__((constructor)) static void
78 EnumWriter& enum_writer (EnumWriter::instance());
83 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
84 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
88 Push2::Push2 (ARDOUR::Session& s)
89 : ControlProtocol (s, string (X_("Ableton Push 2")))
90 , AbstractUI<Push2Request> (name())
92 , _modifier_state (None)
95 , _previous_layout (0)
96 , connection_state (ConnectionState (0))
98 , _mode (MusicalMode::IonianMajor)
104 , _pressure_mode (AfterTouch)
105 , selection_color (LED::Green)
106 , contrast_color (LED::Green)
107 , in_range_select (false)
114 /* master cannot be removed, so no need to connect to going-away signal */
115 master = session->master_out ();
118 throw failed_constructor ();
121 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
123 /* catch current selection, if any */
125 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
126 stripable_selection_change (sp);
129 /* catch arrival and departure of Push2 itself */
130 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
132 /* Catch port connections and disconnections */
133 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
135 /* ports might already be there */
136 port_registration_handler ();
143 delete track_mix_layout;
149 Push2::port_registration_handler ()
151 if (!_async_in && !_async_out) {
152 /* ports not registered yet */
156 if (_async_in->connected() && _async_out->connected()) {
157 /* don't waste cycles here */
161 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
162 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
166 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
167 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
169 if (!in.empty() && !out.empty()) {
170 cerr << "Push2: both ports found\n";
171 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
172 if (!_async_in->connected()) {
173 AudioEngine::instance()->connect (_async_in->name(), in.front());
175 if (!_async_out->connected()) {
176 AudioEngine::instance()->connect (_async_out->name(), out.front());
191 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
195 if ((err = libusb_claim_interface (handle, 0x00))) {
200 _canvas = new Push2Canvas (*this, 960, 160);
201 mix_layout = new MixLayout (*this, *session, "globalmix");
202 scale_layout = new ScaleLayout (*this, *session, "scale");
203 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
204 splash_layout = new SplashLayout (*this, *session, "splash");
206 error << _("Cannot construct Canvas for display") << endmsg;
207 libusb_release_interface (handle, 0x00);
208 libusb_close (handle);
215 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
216 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
218 if (_async_in == 0 || _async_out == 0) {
222 /* We do not add our ports to the input/output bundles because we don't
223 * want users wiring them by hand. They could use JACK tools if they
224 * really insist on that.
227 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
228 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
230 /* Create a shadow port where, depending on the state of the surface,
231 * we will make pad note on/off events appear. The surface code will
232 * automatically this port to the first selected MIDI track.
235 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));
236 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
240 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
242 _output_bundle->add_channel (
244 ARDOUR::DataType::MIDI,
245 session->engine().make_port_name_non_relative (shadow_port->name())
249 session->BundleAddedOrRemoved ();
251 connect_to_parser ();
256 list<boost::shared_ptr<ARDOUR::Bundle> >
259 list<boost::shared_ptr<ARDOUR::Bundle> > b;
261 if (_output_bundle) {
262 b.push_back (_output_bundle);
271 init_buttons (false);
272 strip_buttons_off ();
274 /* wait for button data to be flushed */
276 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
277 asp->drain (10000, 500000);
279 AudioEngine::instance()->unregister_port (_async_in);
280 AudioEngine::instance()->unregister_port (_async_out);
282 _async_in.reset ((ARDOUR::Port*) 0);
283 _async_out.reset ((ARDOUR::Port*) 0);
287 periodic_connection.disconnect ();
288 session_connections.drop_connections ();
290 if (_current_layout) {
291 _canvas->root()->remove (_current_layout);
299 delete splash_layout;
303 libusb_release_interface (handle, 0x00);
304 libusb_close (handle);
312 Push2::strip_buttons_off ()
314 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
315 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
317 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
318 Button* b = id_button_map[strip_buttons[n]];
320 b->set_color (LED::Black);
321 b->set_state (LED::OneShot24th);
322 write (b->state_msg());
328 Push2::init_buttons (bool startup)
330 /* This is a list of buttons that we want lit because they do something
331 in ardour related (loosely, sometimes) to their illuminated label.
334 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
335 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
336 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
339 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
340 Button* b = id_button_map[buttons[n]];
343 b->set_color (LED::White);
345 b->set_color (LED::Black);
347 b->set_state (LED::OneShot24th);
348 write (b->state_msg());
353 /* all other buttons are off (black) */
355 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
356 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
357 Accent, Note, Session, };
359 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
360 Button* b = id_button_map[off_buttons[n]];
362 b->set_color (LED::Black);
363 b->set_state (LED::OneShot24th);
364 write (b->state_msg());
369 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
370 Pad* pad = pi->second;
372 pad->set_color (LED::Black);
373 pad->set_state (LED::OneShot24th);
374 write (pad->state_msg());
382 libusb_device_handle *h;
385 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
386 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
391 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
396 Push2::request_factory (uint32_t num_requests)
398 /* AbstractUI<T>::request_buffer_factory() is a template method only
399 instantiated in this source module. To provide something visible for
400 use in the interface/descriptor, we have this static method that is
403 return request_buffer_factory (num_requests);
407 Push2::do_request (Push2Request * req)
409 if (req->type == CallSlot) {
411 call_slot (MISSING_INVALIDATOR, req->the_slot);
413 } else if (req->type == Quit) {
431 set_current_layout (splash_layout);
432 splash_start = get_microseconds ();
440 /* display splash for 2 seconds */
442 if (get_microseconds() - splash_start > 2000000) {
444 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
445 set_current_layout (mix_layout);
449 if (_current_layout) {
450 _current_layout->update_meters ();
451 _current_layout->update_clocks ();
460 Push2::set_active (bool yn)
462 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
464 if (yn == active()) {
470 /* start event loop */
475 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
480 /* Connect input port to event loop */
484 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
485 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
486 asp->xthread().attach (main_loop()->get_context());
488 connect_session_signals ();
490 /* set up periodic task used to push a frame buffer to the
491 * device (25fps). The device can handle 60fps, but we don't
492 * need that frame rate.
495 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
496 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
497 vblank_timeout->attach (main_loop()->get_context());
500 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
501 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
502 periodic_timeout->attach (main_loop()->get_context());
506 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
515 ControlProtocol::set_active (yn);
517 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
523 Push2::init_touch_strip ()
525 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
526 /* flags are the final byte (ignore end-of-sysex */
528 /* show bar, not point
532 msg[7] = (1<<4) | (1<<5) | (1<<6);
537 Push2::write (const MidiByteArray& data)
539 /* immediate delivery */
540 _output_port->write (&data[0], data.size(), 0);
544 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
547 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
553 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
555 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
560 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
561 framepos_t now = AudioEngine::instance()->sample_time();
575 Push2::connect_to_parser ()
577 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
579 MIDI::Parser* p = _input_port->parser();
582 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
583 /* V-Pot messages are Controller */
584 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
585 /* Button messages are NoteOn */
586 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
587 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
588 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
589 /* Fader messages are Pitchbend */
590 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
594 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
596 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
602 MidiByteArray msg (sz, raw_bytes);
603 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
605 if (!push2_sysex_header.compare_n (msg, 6)) {
610 case 0x1f: /* pressure mode */
612 _pressure_mode = AfterTouch;
613 PressureModeChange (AfterTouch);
614 cerr << "Pressure mode is after\n";
616 _pressure_mode = PolyPressure;
617 PressureModeChange (PolyPressure);
618 cerr << "Pressure mode is poly\n";
625 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
627 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
629 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
632 /* any press cancels any pending long press timeouts */
633 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
634 Button* bb = id_button_map[*x];
635 bb->timeout_connection.disconnect ();
639 if (b != cc_button_map.end()) {
641 Button* button = b->second;
644 buttons_down.insert (button->id);
645 start_press_timeout (*button, button->id);
647 buttons_down.erase (button->id);
648 button->timeout_connection.disconnect ();
652 set<ButtonID>::iterator c = consumed.find (button->id);
654 if (c == consumed.end()) {
655 if (ev->value == 0) {
656 (this->*button->release_method)();
658 (this->*button->press_method)();
661 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
669 int delta = ev->value;
672 delta = -(128 - delta);
675 switch (ev->controller_number) {
677 _current_layout->strip_vpot (0, delta);
680 _current_layout->strip_vpot (1, delta);
683 _current_layout->strip_vpot (2, delta);
686 _current_layout->strip_vpot (3, delta);
689 _current_layout->strip_vpot (4, delta);
692 _current_layout->strip_vpot (5, delta);
695 _current_layout->strip_vpot (6, delta);
698 _current_layout->strip_vpot (7, delta);
703 other_vpot (8, delta);
706 other_vpot (1, delta);
711 other_vpot (2, delta);
718 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
720 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
722 if (ev->velocity == 0) {
723 handle_midi_note_off_message (parser, ev);
727 switch (ev->note_number) {
729 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
732 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
735 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
738 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
741 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
744 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
747 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
750 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
755 other_vpot_touch (0, ev->velocity > 64);
758 other_vpot_touch (1, ev->velocity > 64);
763 other_vpot_touch (3, ev->velocity > 64);
768 if (ev->velocity < 64) {
774 if (ev->note_number < 11) {
778 /* Pad illuminations */
780 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
782 if (pm == nn_pad_map.end()) {
786 const Pad * const pad_pressed = pm->second;
788 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
790 if (pads_with_note.first == fn_pad_map.end()) {
794 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
795 Pad* pad = pi->second;
797 pad->set_color (contrast_color);
798 pad->set_state (LED::OneShot24th);
799 write (pad->state_msg());
804 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
806 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
808 if (ev->note_number < 11) {
809 /* theoretically related to encoder touch start/end, but
810 * actually they send note on with two different velocity
816 /* Pad illuminations */
818 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
820 if (pm == nn_pad_map.end()) {
824 const Pad * const pad_pressed = pm->second;
826 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
828 if (pads_with_note.first == fn_pad_map.end()) {
832 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
833 Pad* pad = pi->second;
835 if (pad->do_when_pressed == Pad::FlashOn) {
836 pad->set_color (LED::Black);
837 pad->set_state (LED::OneShot24th);
838 write (pad->state_msg());
839 } else if (pad->do_when_pressed == Pad::FlashOff) {
840 pad->set_color (pad->perma_color);
841 pad->set_state (LED::OneShot24th);
842 write (pad->state_msg());
848 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
853 Push2::thread_init ()
855 struct sched_param rtparam;
857 pthread_set_name (event_loop_name().c_str());
859 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
860 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
862 memset (&rtparam, 0, sizeof (rtparam));
863 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
865 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
866 // do we care? not particularly.
871 Push2::connect_session_signals()
873 // receive routes added
874 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
875 // receive VCAs added
876 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
878 // receive record state toggled
879 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
880 // receive transport state changed
881 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
882 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
883 // receive punch-in and punch-out
884 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
885 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
886 // receive rude solo changed
887 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
891 Push2::notify_record_state_changed ()
893 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
895 if (b == id_button_map.end()) {
899 switch (session->record_status ()) {
900 case Session::Disabled:
901 b->second->set_color (LED::White);
902 b->second->set_state (LED::NoTransition);
904 case Session::Enabled:
905 b->second->set_color (LED::Red);
906 b->second->set_state (LED::Blinking4th);
908 case Session::Recording:
909 b->second->set_color (LED::Red);
910 b->second->set_state (LED::OneShot24th);
914 write (b->second->state_msg());
918 Push2::notify_transport_state_changed ()
920 Button* b = id_button_map[Play];
922 if (session->transport_rolling()) {
923 b->set_state (LED::OneShot24th);
924 b->set_color (LED::Green);
927 /* disable any blink on FixedLength from pending edit range op */
928 Button* fl = id_button_map[FixedLength];
930 fl->set_color (LED::Black);
931 fl->set_state (LED::NoTransition);
932 write (fl->state_msg());
934 b->set_color (LED::White);
935 b->set_state (LED::NoTransition);
938 write (b->state_msg());
942 Push2::notify_loop_state_changed ()
947 Push2::notify_parameter_changed (std::string param)
949 IDButtonMap::iterator b;
951 if (param == "clicking") {
952 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
955 if (Config->get_clicking()) {
956 b->second->set_state (LED::Blinking4th);
957 b->second->set_color (LED::White);
959 b->second->set_color (LED::White);
960 b->second->set_state (LED::NoTransition);
962 write (b->second->state_msg ());
967 Push2::notify_solo_active_changed (bool yn)
969 IDButtonMap::iterator b = id_button_map.find (Solo);
971 if (b == id_button_map.end()) {
976 b->second->set_state (LED::Blinking4th);
977 b->second->set_color (LED::Red);
979 b->second->set_state (LED::NoTransition);
980 b->second->set_color (LED::White);
983 write (b->second->state_msg());
989 XMLNode& node (ControlProtocol::get_state());
992 child = new XMLNode (X_("Input"));
993 child->add_child_nocopy (_async_in->get_state());
994 node.add_child_nocopy (*child);
995 child = new XMLNode (X_("Output"));
996 child->add_child_nocopy (_async_out->get_state());
997 node.add_child_nocopy (*child);
999 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1000 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1001 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1002 node.add_property (X_("mode"), enum_2_string (_mode));
1008 Push2::set_state (const XMLNode & node, int version)
1010 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1014 if (ControlProtocol::set_state (node, version)) {
1020 if ((child = node.child (X_("Input"))) != 0) {
1021 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1023 _async_in->set_state (*portnode, version);
1027 if ((child = node.child (X_("Output"))) != 0) {
1028 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1030 _async_out->set_state (*portnode, version);
1034 XMLProperty const* prop;
1036 if ((prop = node.property (X_("root"))) != 0) {
1037 _scale_root = atoi (prop->value());
1040 if ((prop = node.property (X_("root_octave"))) != 0) {
1041 _root_octave = atoi (prop->value());
1044 if ((prop = node.property (X_("in_key"))) != 0) {
1045 _in_key = string_is_affirmative (prop->value());
1048 if ((prop = node.property (X_("mode"))) != 0) {
1049 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1056 Push2::other_vpot (int n, int delta)
1058 boost::shared_ptr<Amp> click_gain;
1064 /* metronome gain control */
1065 click_gain = session->click_gain();
1067 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1069 ac->set_value (ac->interface_to_internal (
1070 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1071 PBD::Controllable::UseGroup);
1076 /* master gain control */
1078 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1080 ac->set_value (ac->interface_to_internal (
1081 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1082 PBD::Controllable::UseGroup);
1090 Push2::other_vpot_touch (int n, bool touching)
1099 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1102 ac->start_touch (session->audible_frame());
1104 ac->stop_touch (true, session->audible_frame());
1112 Push2::start_shift ()
1114 cerr << "start shift\n";
1115 _modifier_state = ModifierState (_modifier_state | ModShift);
1116 Button* b = id_button_map[Shift];
1117 b->set_color (LED::White);
1118 b->set_state (LED::Blinking16th);
1119 write (b->state_msg());
1125 if (_modifier_state & ModShift) {
1126 cerr << "end shift\n";
1127 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1128 Button* b = id_button_map[Shift];
1129 b->timeout_connection.disconnect ();
1130 b->set_color (LED::White);
1131 b->set_state (LED::OneShot24th);
1132 write (b->state_msg());
1137 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1139 /* This filter is called asynchronously from a realtime process
1140 context. It must use atomics to check state, and must not block.
1143 bool matched = false;
1145 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1146 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1148 /* encoder touch start/touch end use note
1149 * 0-10. touchstrip uses note 12
1152 if ((*ev).note() > 10 && (*ev).note() != 12) {
1154 const int n = (*ev).note ();
1155 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1157 if (nni != nn_pad_map.end()) {
1158 Pad const * pad = nni->second;
1159 /* shift for output to the shadow port */
1160 if (pad->filtered >= 0) {
1161 (*ev).set_note (pad->filtered + (octave_shift*12));
1162 out.push_back (*ev);
1163 /* shift back so that the pads light correctly */
1166 /* no mapping, don't send event */
1169 out.push_back (*ev);
1174 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1175 out.push_back (*ev);
1183 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1185 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1186 if (!_input_port || !_output_port) {
1190 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1191 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1193 if (ni == name1 || ni == name2) {
1195 connection_state |= InputConnected;
1197 connection_state &= ~InputConnected;
1199 } else if (no == name1 || no == name2) {
1201 connection_state |= OutputConnected;
1203 connection_state &= ~OutputConnected;
1206 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1211 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1213 /* XXX this is a horrible hack. Without a short sleep here,
1214 something prevents the device wakeup messages from being
1215 sent and/or the responses from being received.
1219 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1223 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1226 ConnectionChange (); /* emit signal for our GUI */
1228 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1230 return true; /* connection status changed */
1236 request_pressure_mode ();
1239 boost::shared_ptr<Port>
1240 Push2::output_port()
1245 boost::shared_ptr<Port>
1252 Push2::pad_note (int row, int col) const
1254 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1256 if (nni != nn_pad_map.end()) {
1257 return nni->second->filtered;
1264 Push2::update_selection_color ()
1266 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1268 if (!current_midi_track) {
1272 selection_color = get_color_index (current_midi_track->presentation_info().color());
1273 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1275 reset_pad_colors ();
1279 Push2::reset_pad_colors ()
1281 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1285 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1287 MusicalMode m (mode);
1288 vector<float>::iterator interval;
1290 const int original_root = root;
1292 interval = m.steps.begin();
1293 root += (octave*12);
1296 const int root_start = root;
1298 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1299 vector<int> mode_vector; /* sorted in note order */
1301 mode_map.insert (note);
1302 mode_vector.push_back (note);
1304 /* build a map of all notes in the mode, from the root to 127 */
1306 while (note < 128) {
1308 if (interval == m.steps.end()) {
1310 /* last distance was the end of the scale,
1311 so wrap, adding the next note at one
1312 octave above the last root.
1315 interval = m.steps.begin();
1317 mode_map.insert (root);
1318 mode_vector.push_back (root);
1321 note = (int) floor (root + (2.0 * (*interval)));
1323 mode_map.insert (note);
1324 mode_vector.push_back (note);
1328 fn_pad_map.clear ();
1332 vector<int>::iterator notei;
1335 for (int row = 0; row < 8; ++row) {
1337 /* Ableton's grid layout wraps the available notes in the scale
1338 * by offsetting 3 notes per row (from the bottom)
1341 notei = mode_vector.begin();
1342 notei += row_offset;
1345 for (int col = 0; col < 8; ++col) {
1346 int index = 36 + (row*8) + col;
1347 Pad* pad = nn_pad_map[index];
1349 if (notei != mode_vector.end()) {
1352 pad->filtered = notenum;
1354 fn_pad_map.insert (make_pair (notenum, pad));
1356 if ((notenum % 12) == original_root) {
1357 pad->set_color (selection_color);
1358 pad->perma_color = selection_color;
1360 pad->set_color (LED::White);
1361 pad->perma_color = LED::White;
1364 pad->do_when_pressed = Pad::FlashOff;
1369 pad->set_color (LED::Black);
1370 pad->do_when_pressed = Pad::Nothing;
1374 pad->set_state (LED::OneShot24th);
1375 write (pad->state_msg());
1381 /* chromatic: all notes available, but highlight those in the scale */
1383 for (note = 36; note < 100; ++note) {
1385 Pad* pad = nn_pad_map[note];
1387 /* Chromatic: all pads play, half-tone steps. Light
1388 * those in the scale, and highlight root notes
1391 pad->filtered = root_start + (note - 36);
1393 fn_pad_map.insert (make_pair (pad->filtered, pad));
1395 if (mode_map.find (note) != mode_map.end()) {
1397 if ((note % 12) == original_root) {
1398 pad->set_color (selection_color);
1399 pad->perma_color = selection_color;
1401 pad->set_color (LED::White);
1402 pad->perma_color = LED::White;
1405 pad->do_when_pressed = Pad::FlashOff;
1409 /* note is not in mode, turn it off */
1411 pad->do_when_pressed = Pad::FlashOn;
1412 pad->set_color (LED::Black);
1416 pad->set_state (LED::OneShot24th);
1417 write (pad->state_msg());
1423 bool changed = false;
1425 if (_scale_root != original_root) {
1426 _scale_root = original_root;
1429 if (_root_octave != octave) {
1430 _root_octave = octave;
1433 if (_in_key != inkey) {
1437 if (_mode != mode) {
1443 ScaleChange (); /* EMIT SIGNAL */
1448 Push2::set_percussive_mode (bool yn)
1451 cerr << "back to scale\n";
1452 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1459 fn_pad_map.clear ();
1461 for (int row = 0; row < 8; ++row) {
1463 for (int col = 0; col < 4; ++col) {
1465 int index = 36 + (row*8) + col;
1466 Pad* pad = nn_pad_map[index];
1468 pad->filtered = drum_note;
1473 for (int row = 0; row < 8; ++row) {
1475 for (int col = 4; col < 8; ++col) {
1477 int index = 36 + (row*8) + col;
1478 Pad* pad = nn_pad_map[index];
1480 pad->filtered = drum_note;
1489 Push2::current_layout () const
1491 Glib::Threads::Mutex::Lock lm (layout_lock);
1492 return _current_layout;
1496 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1498 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1499 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1500 boost::shared_ptr<MidiTrack> new_pad_target;
1502 /* See if there's a MIDI track selected */
1504 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1506 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1508 if (new_pad_target) {
1513 if (current_midi_track == new_pad_target) {
1518 if (!new_pad_target) {
1519 /* leave existing connection alone */
1523 /* disconnect from pad port, if appropriate */
1525 if (current_midi_track && pad_port) {
1527 /* XXX this could possibly leave dangling MIDI notes.
1529 * A general libardour fix is required. It isn't obvious
1530 * how note resolution can be done unless disconnecting
1531 * becomes "slow" (i.e. deferred for as long as it takes
1532 * to resolve notes).
1534 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1537 /* now connect the pad port to this (newly) selected midi
1538 * track, if indeed there is one.
1541 if (new_pad_target && pad_port) {
1542 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1543 current_pad_target = new_pad_target;
1544 selection_color = get_color_index (new_pad_target->presentation_info().color());
1545 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1547 current_pad_target.reset ();
1548 selection_color = LED::Green;
1549 contrast_color = LED::Green;
1552 reset_pad_colors ();
1556 Push2::button_by_id (ButtonID bid)
1558 return id_button_map[bid];
1562 Push2::get_color_index (ArdourCanvas::Color rgba)
1564 ColorMap::iterator i = color_map.find (rgba);
1566 if (i != color_map.end()) {
1570 double dr, dg, db, da;
1572 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1573 int w = 126; /* not sure where/when we should get this value */
1576 r = (int) floor (255.0 * dr);
1577 g = (int) floor (255.0 * dg);
1578 b = (int) floor (255.0 * db);
1580 /* get a free index */
1584 if (color_map_free_list.empty()) {
1585 /* random replacement of any entry above zero and below 122 (where the
1586 * Ableton standard colors live)
1588 index = 1 + (random() % 121);
1590 index = color_map_free_list.top();
1591 color_map_free_list.pop();
1594 MidiByteArray palette_msg (17,
1596 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1597 0x00, /* index = 7 */
1598 0x00, 0x00, /* r = 8 & 9 */
1599 0x00, 0x00, /* g = 10 & 11 */
1600 0x00, 0x00, /* b = 12 & 13 */
1601 0x00, 0x00, /* w (a?) = 14 & 15*/
1603 palette_msg[7] = index;
1604 palette_msg[8] = r & 0x7f;
1605 palette_msg[9] = (r & 0x80) >> 7;
1606 palette_msg[10] = g & 0x7f;
1607 palette_msg[11] = (g & 0x80) >> 7;
1608 palette_msg[12] = b & 0x7f;
1609 palette_msg[13] = (b & 0x80) >> 7;
1610 palette_msg[14] = w & 0x7f;
1611 palette_msg[15] = w & 0x80;
1613 write (palette_msg);
1615 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1616 write (update_pallette_msg);
1618 color_map[rgba] = index;
1624 Push2::build_color_map ()
1626 /* These are "standard" colors that Ableton docs suggest will always be
1627 there. Put them in our color map so that when we look up these
1628 colors, we will use the Ableton indices for them.
1631 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1632 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1633 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1634 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1635 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1636 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1637 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1639 for (uint8_t n = 1; n < 122; ++n) {
1640 color_map_free_list.push (n);
1645 Push2::fill_color_table ()
1647 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1648 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1650 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1652 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1653 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1654 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1656 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1657 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1659 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1660 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1661 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1662 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1667 Push2::get_color (ColorName name)
1669 Colors::iterator c = colors.find (name);
1670 if (c != colors.end()) {
1678 Push2::set_current_layout (Push2Layout* layout)
1680 if (layout && layout == _current_layout) {
1681 _current_layout->show ();
1684 if (_current_layout) {
1685 _current_layout->hide ();
1686 _canvas->root()->remove (_current_layout);
1687 _previous_layout = _current_layout;
1690 _current_layout = layout;
1692 if (_current_layout) {
1693 _canvas->root()->add (_current_layout);
1694 _current_layout->show ();
1698 _canvas->request_redraw ();
1703 Push2::use_previous_layout ()
1705 if (_previous_layout) {
1706 set_current_layout (_previous_layout);
1711 Push2::request_pressure_mode ()
1713 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1718 Push2::set_pressure_mode (PressureMode pm)
1720 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1724 /* nothing to do, message is correct */
1734 cerr << "Sent PM message " << msg << endl;