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());
79 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
80 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
84 Push2::Push2 (ARDOUR::Session& s)
85 : ControlProtocol (s, string (X_("Ableton Push 2")))
86 , AbstractUI<Push2Request> (name())
88 , _modifier_state (None)
91 , _previous_layout (0)
92 , connection_state (ConnectionState (0))
94 , _mode (MusicalMode::IonianMajor)
100 , _pressure_mode (AfterTouch)
101 , selection_color (LED::Green)
102 , contrast_color (LED::Green)
109 /* master cannot be removed, so no need to connect to going-away signal */
110 master = session->master_out ();
113 throw failed_constructor ();
116 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
118 /* catch current selection, if any */
120 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
121 stripable_selection_change (sp);
124 /* catch arrival and departure of Push2 itself */
125 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
127 /* Catch port connections and disconnections */
128 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
130 /* ports might already be there */
131 port_registration_handler ();
138 delete track_mix_layout;
144 Push2::port_registration_handler ()
146 if (!_async_in && !_async_out) {
147 /* ports not registered yet */
151 if (_async_in->connected() && _async_out->connected()) {
152 /* don't waste cycles here */
156 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
157 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
161 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
162 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
164 if (!in.empty() && !out.empty()) {
165 cerr << "Push2: both ports found\n";
166 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
167 if (!_async_in->connected()) {
168 AudioEngine::instance()->connect (_async_in->name(), in.front());
170 if (!_async_out->connected()) {
171 AudioEngine::instance()->connect (_async_out->name(), out.front());
186 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
190 if ((err = libusb_claim_interface (handle, 0x00))) {
195 _canvas = new Push2Canvas (*this, 960, 160);
196 mix_layout = new MixLayout (*this, *session, "globalmix");
197 scale_layout = new ScaleLayout (*this, *session, "scale");
198 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
199 splash_layout = new SplashLayout (*this, *session, "splash");
201 error << _("Cannot construct Canvas for display") << endmsg;
202 libusb_release_interface (handle, 0x00);
203 libusb_close (handle);
210 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
211 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
213 if (_async_in == 0 || _async_out == 0) {
217 /* We do not add our ports to the input/output bundles because we don't
218 * want users wiring them by hand. They could use JACK tools if they
219 * really insist on that.
222 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
223 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
225 /* Create a shadow port where, depending on the state of the surface,
226 * we will make pad note on/off events appear. The surface code will
227 * automatically this port to the first selected MIDI track.
230 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));
231 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
235 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
237 _output_bundle->add_channel (
239 ARDOUR::DataType::MIDI,
240 session->engine().make_port_name_non_relative (shadow_port->name())
244 session->BundleAddedOrRemoved ();
246 connect_to_parser ();
251 list<boost::shared_ptr<ARDOUR::Bundle> >
254 list<boost::shared_ptr<ARDOUR::Bundle> > b;
256 if (_output_bundle) {
257 b.push_back (_output_bundle);
266 init_buttons (false);
267 strip_buttons_off ();
269 /* wait for button data to be flushed */
271 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
272 asp->drain (10000, 500000);
274 AudioEngine::instance()->unregister_port (_async_in);
275 AudioEngine::instance()->unregister_port (_async_out);
277 _async_in.reset ((ARDOUR::Port*) 0);
278 _async_out.reset ((ARDOUR::Port*) 0);
282 periodic_connection.disconnect ();
283 session_connections.drop_connections ();
285 if (_current_layout) {
286 _canvas->root()->remove (_current_layout);
294 delete splash_layout;
298 libusb_release_interface (handle, 0x00);
299 libusb_close (handle);
307 Push2::strip_buttons_off ()
309 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
310 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
312 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
313 Button* b = id_button_map[strip_buttons[n]];
315 b->set_color (LED::Black);
316 b->set_state (LED::OneShot24th);
317 write (b->state_msg());
323 Push2::init_buttons (bool startup)
325 /* This is a list of buttons that we want lit because they do something
326 in ardour related (loosely, sometimes) to their illuminated label.
329 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
330 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
331 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
334 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
335 Button* b = id_button_map[buttons[n]];
338 b->set_color (LED::White);
340 b->set_color (LED::Black);
342 b->set_state (LED::OneShot24th);
343 write (b->state_msg());
348 /* all other buttons are off (black) */
350 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
351 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
352 Accent, Note, Session, };
354 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
355 Button* b = id_button_map[off_buttons[n]];
357 b->set_color (LED::Black);
358 b->set_state (LED::OneShot24th);
359 write (b->state_msg());
364 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
365 Pad* pad = pi->second;
367 pad->set_color (LED::Black);
368 pad->set_state (LED::OneShot24th);
369 write (pad->state_msg());
377 libusb_device_handle *h;
380 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
381 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
386 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
391 Push2::request_factory (uint32_t num_requests)
393 /* AbstractUI<T>::request_buffer_factory() is a template method only
394 instantiated in this source module. To provide something visible for
395 use in the interface/descriptor, we have this static method that is
398 return request_buffer_factory (num_requests);
402 Push2::do_request (Push2Request * req)
404 if (req->type == CallSlot) {
406 call_slot (MISSING_INVALIDATOR, req->the_slot);
408 } else if (req->type == Quit) {
426 set_current_layout (splash_layout);
427 splash_start = get_microseconds ();
435 /* display splash for 2 seconds */
437 if (get_microseconds() - splash_start > 2000000) {
439 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
440 set_current_layout (mix_layout);
444 if (_current_layout) {
445 _current_layout->update_meters ();
446 _current_layout->update_clocks ();
455 Push2::set_active (bool yn)
457 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
459 if (yn == active()) {
465 /* start event loop */
470 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
475 /* Connect input port to event loop */
479 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
480 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
481 asp->xthread().attach (main_loop()->get_context());
483 connect_session_signals ();
485 /* set up periodic task used to push a frame buffer to the
486 * device (25fps). The device can handle 60fps, but we don't
487 * need that frame rate.
490 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
491 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
492 vblank_timeout->attach (main_loop()->get_context());
495 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
496 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
497 periodic_timeout->attach (main_loop()->get_context());
501 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
510 ControlProtocol::set_active (yn);
512 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
518 Push2::init_touch_strip ()
520 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
521 /* flags are the final byte (ignore end-of-sysex */
523 /* show bar, not point
527 msg[7] = (1<<4) | (1<<5) | (1<<6);
532 Push2::write (const MidiByteArray& data)
534 /* immediate delivery */
535 _output_port->write (&data[0], data.size(), 0);
539 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
542 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
548 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
550 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
555 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
556 framepos_t now = AudioEngine::instance()->sample_time();
570 Push2::connect_to_parser ()
572 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
574 MIDI::Parser* p = _input_port->parser();
577 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
578 /* V-Pot messages are Controller */
579 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
580 /* Button messages are NoteOn */
581 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
582 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
583 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
584 /* Fader messages are Pitchbend */
585 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
589 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
591 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
597 MidiByteArray msg (sz, raw_bytes);
598 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
600 if (!push2_sysex_header.compare_n (msg, 6)) {
605 case 0x1f: /* pressure mode */
607 _pressure_mode = AfterTouch;
608 PressureModeChange (AfterTouch);
609 cerr << "Pressure mode is after\n";
611 _pressure_mode = PolyPressure;
612 PressureModeChange (PolyPressure);
613 cerr << "Pressure mode is poly\n";
620 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
622 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
624 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
627 /* any press cancels any pending long press timeouts */
628 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
629 Button* bb = id_button_map[*x];
630 bb->timeout_connection.disconnect ();
634 if (b != cc_button_map.end()) {
636 Button* button = b->second;
639 buttons_down.insert (button->id);
640 start_press_timeout (*button, button->id);
642 buttons_down.erase (button->id);
643 button->timeout_connection.disconnect ();
647 set<ButtonID>::iterator c = consumed.find (button->id);
649 if (c == consumed.end()) {
650 if (ev->value == 0) {
651 (this->*button->release_method)();
653 (this->*button->press_method)();
656 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
664 int delta = ev->value;
667 delta = -(128 - delta);
670 switch (ev->controller_number) {
672 _current_layout->strip_vpot (0, delta);
675 _current_layout->strip_vpot (1, delta);
678 _current_layout->strip_vpot (2, delta);
681 _current_layout->strip_vpot (3, delta);
684 _current_layout->strip_vpot (4, delta);
687 _current_layout->strip_vpot (5, delta);
690 _current_layout->strip_vpot (6, delta);
693 _current_layout->strip_vpot (7, delta);
698 other_vpot (8, delta);
701 other_vpot (1, delta);
706 other_vpot (2, delta);
713 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
715 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
717 if (ev->velocity == 0) {
718 handle_midi_note_off_message (parser, ev);
722 switch (ev->note_number) {
724 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
727 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
730 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
733 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
736 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
739 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
742 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
745 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
750 other_vpot_touch (0, ev->velocity > 64);
753 other_vpot_touch (1, ev->velocity > 64);
758 other_vpot_touch (3, ev->velocity > 64);
763 if (ev->velocity < 64) {
769 if (ev->note_number < 11) {
773 /* Pad illuminations */
775 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
777 if (pm == nn_pad_map.end()) {
781 const Pad * const pad_pressed = pm->second;
783 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
785 if (pads_with_note.first == fn_pad_map.end()) {
789 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
790 Pad* pad = pi->second;
792 pad->set_color (contrast_color);
793 pad->set_state (LED::OneShot24th);
794 write (pad->state_msg());
799 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
801 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
803 if (ev->note_number < 11) {
804 /* theoretically related to encoder touch start/end, but
805 * actually they send note on with two different velocity
811 /* Pad illuminations */
813 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
815 if (pm == nn_pad_map.end()) {
819 const Pad * const pad_pressed = pm->second;
821 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
823 if (pads_with_note.first == fn_pad_map.end()) {
827 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
828 Pad* pad = pi->second;
830 if (pad->do_when_pressed == Pad::FlashOn) {
831 pad->set_color (LED::Black);
832 pad->set_state (LED::OneShot24th);
833 write (pad->state_msg());
834 } else if (pad->do_when_pressed == Pad::FlashOff) {
835 pad->set_color (pad->perma_color);
836 pad->set_state (LED::OneShot24th);
837 write (pad->state_msg());
843 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
848 Push2::thread_init ()
850 struct sched_param rtparam;
852 pthread_set_name (event_loop_name().c_str());
854 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
855 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
857 memset (&rtparam, 0, sizeof (rtparam));
858 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
860 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
861 // do we care? not particularly.
866 Push2::connect_session_signals()
868 // receive routes added
869 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
870 // receive VCAs added
871 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
873 // receive record state toggled
874 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
875 // receive transport state changed
876 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
877 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
878 // receive punch-in and punch-out
879 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
880 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
881 // receive rude solo changed
882 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
886 Push2::notify_record_state_changed ()
888 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
890 if (b == id_button_map.end()) {
894 switch (session->record_status ()) {
895 case Session::Disabled:
896 b->second->set_color (LED::White);
897 b->second->set_state (LED::NoTransition);
899 case Session::Enabled:
900 b->second->set_color (LED::Red);
901 b->second->set_state (LED::Blinking4th);
903 case Session::Recording:
904 b->second->set_color (LED::Red);
905 b->second->set_state (LED::OneShot24th);
909 write (b->second->state_msg());
913 Push2::notify_transport_state_changed ()
915 Button* b = id_button_map[Play];
917 if (session->transport_rolling()) {
918 b->set_state (LED::OneShot24th);
919 b->set_color (LED::Green);
922 /* disable any blink on FixedLength from pending edit range op */
923 Button* fl = id_button_map[FixedLength];
925 fl->set_color (LED::Black);
926 fl->set_state (LED::NoTransition);
927 write (fl->state_msg());
929 b->set_color (LED::White);
930 b->set_state (LED::NoTransition);
933 write (b->state_msg());
937 Push2::notify_loop_state_changed ()
942 Push2::notify_parameter_changed (std::string param)
944 IDButtonMap::iterator b;
946 if (param == "clicking") {
947 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
950 if (Config->get_clicking()) {
951 b->second->set_state (LED::Blinking4th);
952 b->second->set_color (LED::White);
954 b->second->set_color (LED::White);
955 b->second->set_state (LED::NoTransition);
957 write (b->second->state_msg ());
962 Push2::notify_solo_active_changed (bool yn)
964 IDButtonMap::iterator b = id_button_map.find (Solo);
966 if (b == id_button_map.end()) {
971 b->second->set_state (LED::Blinking4th);
972 b->second->set_color (LED::Red);
974 b->second->set_state (LED::NoTransition);
975 b->second->set_color (LED::White);
978 write (b->second->state_msg());
984 XMLNode& node (ControlProtocol::get_state());
987 child = new XMLNode (X_("Input"));
988 child->add_child_nocopy (_async_in->get_state());
989 node.add_child_nocopy (*child);
990 child = new XMLNode (X_("Output"));
991 child->add_child_nocopy (_async_out->get_state());
992 node.add_child_nocopy (*child);
994 node.add_property (X_("root"), to_string (_scale_root, std::dec));
995 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
996 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
997 node.add_property (X_("mode"), enum_2_string (_mode));
1003 Push2::set_state (const XMLNode & node, int version)
1005 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1009 if (ControlProtocol::set_state (node, version)) {
1015 if ((child = node.child (X_("Input"))) != 0) {
1016 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1018 _async_in->set_state (*portnode, version);
1022 if ((child = node.child (X_("Output"))) != 0) {
1023 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1025 _async_out->set_state (*portnode, version);
1029 XMLProperty const* prop;
1031 if ((prop = node.property (X_("root"))) != 0) {
1032 _scale_root = atoi (prop->value());
1035 if ((prop = node.property (X_("root_octave"))) != 0) {
1036 _root_octave = atoi (prop->value());
1039 if ((prop = node.property (X_("in_key"))) != 0) {
1040 _in_key = string_is_affirmative (prop->value());
1043 if ((prop = node.property (X_("mode"))) != 0) {
1044 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1051 Push2::other_vpot (int n, int delta)
1053 boost::shared_ptr<Amp> click_gain;
1059 /* metronome gain control */
1060 click_gain = session->click_gain();
1062 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1064 ac->set_value (ac->interface_to_internal (
1065 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1066 PBD::Controllable::UseGroup);
1071 /* master gain control */
1073 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1075 ac->set_value (ac->interface_to_internal (
1076 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1077 PBD::Controllable::UseGroup);
1085 Push2::other_vpot_touch (int n, bool touching)
1094 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1097 ac->start_touch (session->audible_frame());
1099 ac->stop_touch (true, session->audible_frame());
1107 Push2::start_shift ()
1109 cerr << "start shift\n";
1110 _modifier_state = ModifierState (_modifier_state | ModShift);
1111 Button* b = id_button_map[Shift];
1112 b->set_color (LED::White);
1113 b->set_state (LED::Blinking16th);
1114 write (b->state_msg());
1120 if (_modifier_state & ModShift) {
1121 cerr << "end shift\n";
1122 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1123 Button* b = id_button_map[Shift];
1124 b->timeout_connection.disconnect ();
1125 b->set_color (LED::White);
1126 b->set_state (LED::OneShot24th);
1127 write (b->state_msg());
1132 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1134 /* This filter is called asynchronously from a realtime process
1135 context. It must use atomics to check state, and must not block.
1138 bool matched = false;
1140 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1141 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1143 /* encoder touch start/touch end use note
1144 * 0-10. touchstrip uses note 12
1147 if ((*ev).note() > 10 && (*ev).note() != 12) {
1149 const int n = (*ev).note ();
1150 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1152 if (nni != nn_pad_map.end()) {
1153 Pad const * pad = nni->second;
1154 /* shift for output to the shadow port */
1155 if (pad->filtered >= 0) {
1156 (*ev).set_note (pad->filtered + (octave_shift*12));
1157 out.push_back (*ev);
1158 /* shift back so that the pads light correctly */
1161 /* no mapping, don't send event */
1164 out.push_back (*ev);
1169 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1170 out.push_back (*ev);
1178 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1180 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1181 if (!_input_port || !_output_port) {
1185 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1186 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1188 if (ni == name1 || ni == name2) {
1190 connection_state |= InputConnected;
1192 connection_state &= ~InputConnected;
1194 } else if (no == name1 || no == name2) {
1196 connection_state |= OutputConnected;
1198 connection_state &= ~OutputConnected;
1201 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1206 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1208 /* XXX this is a horrible hack. Without a short sleep here,
1209 something prevents the device wakeup messages from being
1210 sent and/or the responses from being received.
1214 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1218 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1221 ConnectionChange (); /* emit signal for our GUI */
1223 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1225 return true; /* connection status changed */
1231 request_pressure_mode ();
1234 boost::shared_ptr<Port>
1235 Push2::output_port()
1240 boost::shared_ptr<Port>
1247 Push2::pad_note (int row, int col) const
1249 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1251 if (nni != nn_pad_map.end()) {
1252 return nni->second->filtered;
1259 Push2::update_selection_color ()
1261 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1263 if (!current_midi_track) {
1267 selection_color = get_color_index (current_midi_track->presentation_info().color());
1268 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1270 reset_pad_colors ();
1274 Push2::reset_pad_colors ()
1276 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1280 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1282 MusicalMode m (mode);
1283 vector<float>::iterator interval;
1285 const int original_root = root;
1287 interval = m.steps.begin();
1288 root += (octave*12);
1291 const int root_start = root;
1293 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1294 vector<int> mode_vector; /* sorted in note order */
1296 mode_map.insert (note);
1297 mode_vector.push_back (note);
1299 /* build a map of all notes in the mode, from the root to 127 */
1301 while (note < 128) {
1303 if (interval == m.steps.end()) {
1305 /* last distance was the end of the scale,
1306 so wrap, adding the next note at one
1307 octave above the last root.
1310 interval = m.steps.begin();
1312 mode_map.insert (root);
1313 mode_vector.push_back (root);
1316 note = (int) floor (root + (2.0 * (*interval)));
1318 mode_map.insert (note);
1319 mode_vector.push_back (note);
1323 fn_pad_map.clear ();
1327 vector<int>::iterator notei;
1330 for (int row = 0; row < 8; ++row) {
1332 /* Ableton's grid layout wraps the available notes in the scale
1333 * by offsetting 3 notes per row (from the bottom)
1336 notei = mode_vector.begin();
1337 notei += row_offset;
1340 for (int col = 0; col < 8; ++col) {
1341 int index = 36 + (row*8) + col;
1342 Pad* pad = nn_pad_map[index];
1344 if (notei != mode_vector.end()) {
1347 pad->filtered = notenum;
1349 fn_pad_map.insert (make_pair (notenum, pad));
1351 if ((notenum % 12) == original_root) {
1352 pad->set_color (selection_color);
1353 pad->perma_color = selection_color;
1355 pad->set_color (LED::White);
1356 pad->perma_color = LED::White;
1359 pad->do_when_pressed = Pad::FlashOff;
1364 pad->set_color (LED::Black);
1365 pad->do_when_pressed = Pad::Nothing;
1369 pad->set_state (LED::OneShot24th);
1370 write (pad->state_msg());
1376 /* chromatic: all notes available, but highlight those in the scale */
1378 for (note = 36; note < 100; ++note) {
1380 Pad* pad = nn_pad_map[note];
1382 /* Chromatic: all pads play, half-tone steps. Light
1383 * those in the scale, and highlight root notes
1386 pad->filtered = root_start + (note - 36);
1388 fn_pad_map.insert (make_pair (pad->filtered, pad));
1390 if (mode_map.find (note) != mode_map.end()) {
1392 if ((note % 12) == original_root) {
1393 pad->set_color (selection_color);
1394 pad->perma_color = selection_color;
1396 pad->set_color (LED::White);
1397 pad->perma_color = LED::White;
1400 pad->do_when_pressed = Pad::FlashOff;
1404 /* note is not in mode, turn it off */
1406 pad->do_when_pressed = Pad::FlashOn;
1407 pad->set_color (LED::Black);
1411 pad->set_state (LED::OneShot24th);
1412 write (pad->state_msg());
1418 bool changed = false;
1420 if (_scale_root != original_root) {
1421 _scale_root = original_root;
1424 if (_root_octave != octave) {
1425 _root_octave = octave;
1428 if (_in_key != inkey) {
1432 if (_mode != mode) {
1438 ScaleChange (); /* EMIT SIGNAL */
1443 Push2::set_percussive_mode (bool yn)
1446 cerr << "back to scale\n";
1447 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1454 fn_pad_map.clear ();
1456 for (int row = 0; row < 8; ++row) {
1458 for (int col = 0; col < 4; ++col) {
1460 int index = 36 + (row*8) + col;
1461 Pad* pad = nn_pad_map[index];
1463 pad->filtered = drum_note;
1468 for (int row = 0; row < 8; ++row) {
1470 for (int col = 4; col < 8; ++col) {
1472 int index = 36 + (row*8) + col;
1473 Pad* pad = nn_pad_map[index];
1475 pad->filtered = drum_note;
1484 Push2::current_layout () const
1486 Glib::Threads::Mutex::Lock lm (layout_lock);
1487 return _current_layout;
1491 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1493 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1494 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1495 boost::shared_ptr<MidiTrack> new_pad_target;
1497 /* See if there's a MIDI track selected */
1499 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1501 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1503 if (new_pad_target) {
1508 if (current_midi_track == new_pad_target) {
1513 if (!new_pad_target) {
1514 /* leave existing connection alone */
1518 /* disconnect from pad port, if appropriate */
1520 if (current_midi_track && pad_port) {
1522 /* XXX this could possibly leave dangling MIDI notes.
1524 * A general libardour fix is required. It isn't obvious
1525 * how note resolution can be done unless disconnecting
1526 * becomes "slow" (i.e. deferred for as long as it takes
1527 * to resolve notes).
1529 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1532 /* now connect the pad port to this (newly) selected midi
1533 * track, if indeed there is one.
1536 if (new_pad_target && pad_port) {
1537 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1538 current_pad_target = new_pad_target;
1539 selection_color = get_color_index (new_pad_target->presentation_info().color());
1540 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1542 current_pad_target.reset ();
1543 selection_color = LED::Green;
1544 contrast_color = LED::Green;
1547 reset_pad_colors ();
1551 Push2::button_by_id (ButtonID bid)
1553 return id_button_map[bid];
1557 Push2::get_color_index (ArdourCanvas::Color rgba)
1559 ColorMap::iterator i = color_map.find (rgba);
1561 if (i != color_map.end()) {
1565 double dr, dg, db, da;
1567 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1568 int w = 126; /* not sure where/when we should get this value */
1571 r = (int) floor (255.0 * dr);
1572 g = (int) floor (255.0 * dg);
1573 b = (int) floor (255.0 * db);
1575 /* get a free index */
1579 if (color_map_free_list.empty()) {
1580 /* random replacement of any entry above zero and below 122 (where the
1581 * Ableton standard colors live)
1583 index = 1 + (random() % 121);
1585 index = color_map_free_list.top();
1586 color_map_free_list.pop();
1589 MidiByteArray palette_msg (17,
1591 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1592 0x00, /* index = 7 */
1593 0x00, 0x00, /* r = 8 & 9 */
1594 0x00, 0x00, /* g = 10 & 11 */
1595 0x00, 0x00, /* b = 12 & 13 */
1596 0x00, 0x00, /* w (a?) = 14 & 15*/
1598 palette_msg[7] = index;
1599 palette_msg[8] = r & 0x7f;
1600 palette_msg[9] = (r & 0x80) >> 7;
1601 palette_msg[10] = g & 0x7f;
1602 palette_msg[11] = (g & 0x80) >> 7;
1603 palette_msg[12] = b & 0x7f;
1604 palette_msg[13] = (b & 0x80) >> 7;
1605 palette_msg[14] = w & 0x7f;
1606 palette_msg[15] = w & 0x80;
1608 write (palette_msg);
1610 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1611 write (update_pallette_msg);
1613 color_map[rgba] = index;
1619 Push2::build_color_map ()
1621 /* These are "standard" colors that Ableton docs suggest will always be
1622 there. Put them in our color map so that when we look up these
1623 colors, we will use the Ableton indices for them.
1626 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1627 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1628 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1629 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1630 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1631 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1632 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1634 for (uint8_t n = 1; n < 122; ++n) {
1635 color_map_free_list.push (n);
1640 Push2::fill_color_table ()
1642 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1643 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1645 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1647 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1648 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1649 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1651 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1652 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1654 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1655 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1656 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1657 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1662 Push2::get_color (ColorName name)
1664 Colors::iterator c = colors.find (name);
1665 if (c != colors.end()) {
1673 Push2::set_current_layout (Push2Layout* layout)
1675 if (layout && layout == _current_layout) {
1676 _current_layout->show ();
1679 if (_current_layout) {
1680 _current_layout->hide ();
1681 _canvas->root()->remove (_current_layout);
1682 _previous_layout = _current_layout;
1685 _current_layout = layout;
1687 if (_current_layout) {
1688 _canvas->root()->add (_current_layout);
1689 _current_layout->show ();
1693 _canvas->request_redraw ();
1698 Push2::use_previous_layout ()
1700 if (_previous_layout) {
1701 set_current_layout (_previous_layout);
1706 Push2::request_pressure_mode ()
1708 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1713 Push2::set_pressure_mode (PressureMode pm)
1715 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1719 /* nothing to do, message is correct */
1729 cerr << "Sent PM message " << msg << endl;