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)
113 /* master cannot be removed, so no need to connect to going-away signal */
114 master = session->master_out ();
117 throw failed_constructor ();
120 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
122 /* catch current selection, if any */
124 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
125 stripable_selection_change (sp);
128 /* catch arrival and departure of Push2 itself */
129 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
131 /* Catch port connections and disconnections */
132 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
134 /* ports might already be there */
135 port_registration_handler ();
142 delete track_mix_layout;
148 Push2::port_registration_handler ()
150 if (!_async_in && !_async_out) {
151 /* ports not registered yet */
155 if (_async_in->connected() && _async_out->connected()) {
156 /* don't waste cycles here */
160 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
161 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
165 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
166 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
168 if (!in.empty() && !out.empty()) {
169 cerr << "Push2: both ports found\n";
170 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
171 if (!_async_in->connected()) {
172 AudioEngine::instance()->connect (_async_in->name(), in.front());
174 if (!_async_out->connected()) {
175 AudioEngine::instance()->connect (_async_out->name(), out.front());
190 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
194 if ((err = libusb_claim_interface (handle, 0x00))) {
199 _canvas = new Push2Canvas (*this, 960, 160);
200 mix_layout = new MixLayout (*this, *session, "globalmix");
201 scale_layout = new ScaleLayout (*this, *session, "scale");
202 track_mix_layout = new TrackMixLayout (*this, *session, "trackmix");
203 splash_layout = new SplashLayout (*this, *session, "splash");
205 error << _("Cannot construct Canvas for display") << endmsg;
206 libusb_release_interface (handle, 0x00);
207 libusb_close (handle);
214 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
215 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
217 if (_async_in == 0 || _async_out == 0) {
221 /* We do not add our ports to the input/output bundles because we don't
222 * want users wiring them by hand. They could use JACK tools if they
223 * really insist on that.
226 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
227 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
229 /* Create a shadow port where, depending on the state of the surface,
230 * we will make pad note on/off events appear. The surface code will
231 * automatically this port to the first selected MIDI track.
234 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));
235 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
239 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
241 _output_bundle->add_channel (
243 ARDOUR::DataType::MIDI,
244 session->engine().make_port_name_non_relative (shadow_port->name())
248 session->BundleAddedOrRemoved ();
250 connect_to_parser ();
255 list<boost::shared_ptr<ARDOUR::Bundle> >
258 list<boost::shared_ptr<ARDOUR::Bundle> > b;
260 if (_output_bundle) {
261 b.push_back (_output_bundle);
270 init_buttons (false);
271 strip_buttons_off ();
273 /* wait for button data to be flushed */
275 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
276 asp->drain (10000, 500000);
278 AudioEngine::instance()->unregister_port (_async_in);
279 AudioEngine::instance()->unregister_port (_async_out);
281 _async_in.reset ((ARDOUR::Port*) 0);
282 _async_out.reset ((ARDOUR::Port*) 0);
286 periodic_connection.disconnect ();
287 session_connections.drop_connections ();
289 if (_current_layout) {
290 _canvas->root()->remove (_current_layout);
298 delete splash_layout;
302 libusb_release_interface (handle, 0x00);
303 libusb_close (handle);
311 Push2::strip_buttons_off ()
313 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
314 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
316 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
317 Button* b = id_button_map[strip_buttons[n]];
319 b->set_color (LED::Black);
320 b->set_state (LED::OneShot24th);
321 write (b->state_msg());
327 Push2::init_buttons (bool startup)
329 /* This is a list of buttons that we want lit because they do something
330 in ardour related (loosely, sometimes) to their illuminated label.
333 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
334 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session,
335 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
338 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
339 Button* b = id_button_map[buttons[n]];
342 b->set_color (LED::White);
344 b->set_color (LED::Black);
346 b->set_state (LED::OneShot24th);
347 write (b->state_msg());
352 /* all other buttons are off (black) */
354 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
355 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
356 Accent, Note, Session, };
358 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
359 Button* b = id_button_map[off_buttons[n]];
361 b->set_color (LED::Black);
362 b->set_state (LED::OneShot24th);
363 write (b->state_msg());
368 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
369 Pad* pad = pi->second;
371 pad->set_color (LED::Black);
372 pad->set_state (LED::OneShot24th);
373 write (pad->state_msg());
381 libusb_device_handle *h;
384 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
385 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
390 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
395 Push2::request_factory (uint32_t num_requests)
397 /* AbstractUI<T>::request_buffer_factory() is a template method only
398 instantiated in this source module. To provide something visible for
399 use in the interface/descriptor, we have this static method that is
402 return request_buffer_factory (num_requests);
406 Push2::do_request (Push2Request * req)
408 if (req->type == CallSlot) {
410 call_slot (MISSING_INVALIDATOR, req->the_slot);
412 } else if (req->type == Quit) {
430 set_current_layout (splash_layout);
431 splash_start = get_microseconds ();
439 /* display splash for 2 seconds */
441 if (get_microseconds() - splash_start > 2000000) {
443 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
444 set_current_layout (mix_layout);
448 if (_current_layout) {
449 _current_layout->update_meters ();
450 _current_layout->update_clocks ();
459 Push2::set_active (bool yn)
461 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
463 if (yn == active()) {
469 /* start event loop */
474 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
479 /* Connect input port to event loop */
483 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
484 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
485 asp->xthread().attach (main_loop()->get_context());
487 connect_session_signals ();
489 /* set up periodic task used to push a frame buffer to the
490 * device (25fps). The device can handle 60fps, but we don't
491 * need that frame rate.
494 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
495 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
496 vblank_timeout->attach (main_loop()->get_context());
499 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
500 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
501 periodic_timeout->attach (main_loop()->get_context());
505 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
514 ControlProtocol::set_active (yn);
516 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
522 Push2::init_touch_strip ()
524 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
525 /* flags are the final byte (ignore end-of-sysex */
527 /* show bar, not point
531 msg[7] = (1<<4) | (1<<5) | (1<<6);
536 Push2::write (const MidiByteArray& data)
538 /* immediate delivery */
539 _output_port->write (&data[0], data.size(), 0);
543 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
546 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
552 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
554 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
559 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
560 framepos_t now = AudioEngine::instance()->sample_time();
574 Push2::connect_to_parser ()
576 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
578 MIDI::Parser* p = _input_port->parser();
581 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
582 /* V-Pot messages are Controller */
583 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
584 /* Button messages are NoteOn */
585 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
586 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
587 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
588 /* Fader messages are Pitchbend */
589 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
593 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
595 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
601 MidiByteArray msg (sz, raw_bytes);
602 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
604 if (!push2_sysex_header.compare_n (msg, 6)) {
609 case 0x1f: /* pressure mode */
611 _pressure_mode = AfterTouch;
612 PressureModeChange (AfterTouch);
613 cerr << "Pressure mode is after\n";
615 _pressure_mode = PolyPressure;
616 PressureModeChange (PolyPressure);
617 cerr << "Pressure mode is poly\n";
624 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
626 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
628 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
631 /* any press cancels any pending long press timeouts */
632 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
633 Button* bb = id_button_map[*x];
634 bb->timeout_connection.disconnect ();
638 if (b != cc_button_map.end()) {
640 Button* button = b->second;
643 buttons_down.insert (button->id);
644 start_press_timeout (*button, button->id);
646 buttons_down.erase (button->id);
647 button->timeout_connection.disconnect ();
651 set<ButtonID>::iterator c = consumed.find (button->id);
653 if (c == consumed.end()) {
654 if (ev->value == 0) {
655 (this->*button->release_method)();
657 (this->*button->press_method)();
660 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
668 int delta = ev->value;
671 delta = -(128 - delta);
674 switch (ev->controller_number) {
676 _current_layout->strip_vpot (0, delta);
679 _current_layout->strip_vpot (1, delta);
682 _current_layout->strip_vpot (2, delta);
685 _current_layout->strip_vpot (3, delta);
688 _current_layout->strip_vpot (4, delta);
691 _current_layout->strip_vpot (5, delta);
694 _current_layout->strip_vpot (6, delta);
697 _current_layout->strip_vpot (7, delta);
702 other_vpot (8, delta);
705 other_vpot (1, delta);
710 other_vpot (2, delta);
717 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
719 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
721 if (ev->velocity == 0) {
722 handle_midi_note_off_message (parser, ev);
726 switch (ev->note_number) {
728 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
731 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
734 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
737 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
740 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
743 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
746 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
749 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
754 other_vpot_touch (0, ev->velocity > 64);
757 other_vpot_touch (1, ev->velocity > 64);
762 other_vpot_touch (3, ev->velocity > 64);
767 if (ev->velocity < 64) {
773 if (ev->note_number < 11) {
777 /* Pad illuminations */
779 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
781 if (pm == nn_pad_map.end()) {
785 const Pad * const pad_pressed = pm->second;
787 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
789 if (pads_with_note.first == fn_pad_map.end()) {
793 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
794 Pad* pad = pi->second;
796 pad->set_color (contrast_color);
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 /* Pad illuminations */
817 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
819 if (pm == nn_pad_map.end()) {
823 const Pad * const pad_pressed = pm->second;
825 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
827 if (pads_with_note.first == fn_pad_map.end()) {
831 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
832 Pad* pad = pi->second;
834 if (pad->do_when_pressed == Pad::FlashOn) {
835 pad->set_color (LED::Black);
836 pad->set_state (LED::OneShot24th);
837 write (pad->state_msg());
838 } else if (pad->do_when_pressed == Pad::FlashOff) {
839 pad->set_color (pad->perma_color);
840 pad->set_state (LED::OneShot24th);
841 write (pad->state_msg());
847 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
852 Push2::thread_init ()
854 struct sched_param rtparam;
856 pthread_set_name (event_loop_name().c_str());
858 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
859 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
861 memset (&rtparam, 0, sizeof (rtparam));
862 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
864 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
865 // do we care? not particularly.
870 Push2::connect_session_signals()
872 // receive routes added
873 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
874 // receive VCAs added
875 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
877 // receive record state toggled
878 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
879 // receive transport state changed
880 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
881 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
882 // receive punch-in and punch-out
883 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
884 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
885 // receive rude solo changed
886 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
890 Push2::notify_record_state_changed ()
892 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
894 if (b == id_button_map.end()) {
898 switch (session->record_status ()) {
899 case Session::Disabled:
900 b->second->set_color (LED::White);
901 b->second->set_state (LED::NoTransition);
903 case Session::Enabled:
904 b->second->set_color (LED::Red);
905 b->second->set_state (LED::Blinking4th);
907 case Session::Recording:
908 b->second->set_color (LED::Red);
909 b->second->set_state (LED::OneShot24th);
913 write (b->second->state_msg());
917 Push2::notify_transport_state_changed ()
919 Button* b = id_button_map[Play];
921 if (session->transport_rolling()) {
922 b->set_state (LED::OneShot24th);
923 b->set_color (LED::Green);
926 /* disable any blink on FixedLength from pending edit range op */
927 Button* fl = id_button_map[FixedLength];
929 fl->set_color (LED::Black);
930 fl->set_state (LED::NoTransition);
931 write (fl->state_msg());
933 b->set_color (LED::White);
934 b->set_state (LED::NoTransition);
937 write (b->state_msg());
941 Push2::notify_loop_state_changed ()
946 Push2::notify_parameter_changed (std::string param)
948 IDButtonMap::iterator b;
950 if (param == "clicking") {
951 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
954 if (Config->get_clicking()) {
955 b->second->set_state (LED::Blinking4th);
956 b->second->set_color (LED::White);
958 b->second->set_color (LED::White);
959 b->second->set_state (LED::NoTransition);
961 write (b->second->state_msg ());
966 Push2::notify_solo_active_changed (bool yn)
968 IDButtonMap::iterator b = id_button_map.find (Solo);
970 if (b == id_button_map.end()) {
975 b->second->set_state (LED::Blinking4th);
976 b->second->set_color (LED::Red);
978 b->second->set_state (LED::NoTransition);
979 b->second->set_color (LED::White);
982 write (b->second->state_msg());
988 XMLNode& node (ControlProtocol::get_state());
991 child = new XMLNode (X_("Input"));
992 child->add_child_nocopy (_async_in->get_state());
993 node.add_child_nocopy (*child);
994 child = new XMLNode (X_("Output"));
995 child->add_child_nocopy (_async_out->get_state());
996 node.add_child_nocopy (*child);
998 node.add_property (X_("root"), to_string (_scale_root, std::dec));
999 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1000 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1001 node.add_property (X_("mode"), enum_2_string (_mode));
1007 Push2::set_state (const XMLNode & node, int version)
1009 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1013 if (ControlProtocol::set_state (node, version)) {
1019 if ((child = node.child (X_("Input"))) != 0) {
1020 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1022 _async_in->set_state (*portnode, version);
1026 if ((child = node.child (X_("Output"))) != 0) {
1027 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1029 _async_out->set_state (*portnode, version);
1033 XMLProperty const* prop;
1035 if ((prop = node.property (X_("root"))) != 0) {
1036 _scale_root = atoi (prop->value());
1039 if ((prop = node.property (X_("root_octave"))) != 0) {
1040 _root_octave = atoi (prop->value());
1043 if ((prop = node.property (X_("in_key"))) != 0) {
1044 _in_key = string_is_affirmative (prop->value());
1047 if ((prop = node.property (X_("mode"))) != 0) {
1048 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1055 Push2::other_vpot (int n, int delta)
1057 boost::shared_ptr<Amp> click_gain;
1063 /* metronome gain control */
1064 click_gain = session->click_gain();
1066 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1068 ac->set_value (ac->interface_to_internal (
1069 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1070 PBD::Controllable::UseGroup);
1075 /* master gain control */
1077 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1079 ac->set_value (ac->interface_to_internal (
1080 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1081 PBD::Controllable::UseGroup);
1089 Push2::other_vpot_touch (int n, bool touching)
1098 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1101 ac->start_touch (session->audible_frame());
1103 ac->stop_touch (true, session->audible_frame());
1111 Push2::start_shift ()
1113 cerr << "start shift\n";
1114 _modifier_state = ModifierState (_modifier_state | ModShift);
1115 Button* b = id_button_map[Shift];
1116 b->set_color (LED::White);
1117 b->set_state (LED::Blinking16th);
1118 write (b->state_msg());
1124 if (_modifier_state & ModShift) {
1125 cerr << "end shift\n";
1126 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1127 Button* b = id_button_map[Shift];
1128 b->timeout_connection.disconnect ();
1129 b->set_color (LED::White);
1130 b->set_state (LED::OneShot24th);
1131 write (b->state_msg());
1136 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1138 /* This filter is called asynchronously from a realtime process
1139 context. It must use atomics to check state, and must not block.
1142 bool matched = false;
1144 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1145 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1147 /* encoder touch start/touch end use note
1148 * 0-10. touchstrip uses note 12
1151 if ((*ev).note() > 10 && (*ev).note() != 12) {
1153 const int n = (*ev).note ();
1154 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1156 if (nni != nn_pad_map.end()) {
1157 Pad const * pad = nni->second;
1158 /* shift for output to the shadow port */
1159 if (pad->filtered >= 0) {
1160 (*ev).set_note (pad->filtered + (octave_shift*12));
1161 out.push_back (*ev);
1162 /* shift back so that the pads light correctly */
1165 /* no mapping, don't send event */
1168 out.push_back (*ev);
1173 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1174 out.push_back (*ev);
1182 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1184 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1185 if (!_input_port || !_output_port) {
1189 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1190 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1192 if (ni == name1 || ni == name2) {
1194 connection_state |= InputConnected;
1196 connection_state &= ~InputConnected;
1198 } else if (no == name1 || no == name2) {
1200 connection_state |= OutputConnected;
1202 connection_state &= ~OutputConnected;
1205 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1210 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1212 /* XXX this is a horrible hack. Without a short sleep here,
1213 something prevents the device wakeup messages from being
1214 sent and/or the responses from being received.
1218 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1222 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1225 ConnectionChange (); /* emit signal for our GUI */
1227 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1229 return true; /* connection status changed */
1235 request_pressure_mode ();
1238 boost::shared_ptr<Port>
1239 Push2::output_port()
1244 boost::shared_ptr<Port>
1251 Push2::pad_note (int row, int col) const
1253 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1255 if (nni != nn_pad_map.end()) {
1256 return nni->second->filtered;
1263 Push2::update_selection_color ()
1265 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1267 if (!current_midi_track) {
1271 selection_color = get_color_index (current_midi_track->presentation_info().color());
1272 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1274 reset_pad_colors ();
1278 Push2::reset_pad_colors ()
1280 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1284 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1286 MusicalMode m (mode);
1287 vector<float>::iterator interval;
1289 const int original_root = root;
1291 interval = m.steps.begin();
1292 root += (octave*12);
1295 const int root_start = root;
1297 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1298 vector<int> mode_vector; /* sorted in note order */
1300 mode_map.insert (note);
1301 mode_vector.push_back (note);
1303 /* build a map of all notes in the mode, from the root to 127 */
1305 while (note < 128) {
1307 if (interval == m.steps.end()) {
1309 /* last distance was the end of the scale,
1310 so wrap, adding the next note at one
1311 octave above the last root.
1314 interval = m.steps.begin();
1316 mode_map.insert (root);
1317 mode_vector.push_back (root);
1320 note = (int) floor (root + (2.0 * (*interval)));
1322 mode_map.insert (note);
1323 mode_vector.push_back (note);
1327 fn_pad_map.clear ();
1331 vector<int>::iterator notei;
1334 for (int row = 0; row < 8; ++row) {
1336 /* Ableton's grid layout wraps the available notes in the scale
1337 * by offsetting 3 notes per row (from the bottom)
1340 notei = mode_vector.begin();
1341 notei += row_offset;
1344 for (int col = 0; col < 8; ++col) {
1345 int index = 36 + (row*8) + col;
1346 Pad* pad = nn_pad_map[index];
1348 if (notei != mode_vector.end()) {
1351 pad->filtered = notenum;
1353 fn_pad_map.insert (make_pair (notenum, pad));
1355 if ((notenum % 12) == original_root) {
1356 pad->set_color (selection_color);
1357 pad->perma_color = selection_color;
1359 pad->set_color (LED::White);
1360 pad->perma_color = LED::White;
1363 pad->do_when_pressed = Pad::FlashOff;
1368 pad->set_color (LED::Black);
1369 pad->do_when_pressed = Pad::Nothing;
1373 pad->set_state (LED::OneShot24th);
1374 write (pad->state_msg());
1380 /* chromatic: all notes available, but highlight those in the scale */
1382 for (note = 36; note < 100; ++note) {
1384 Pad* pad = nn_pad_map[note];
1386 /* Chromatic: all pads play, half-tone steps. Light
1387 * those in the scale, and highlight root notes
1390 pad->filtered = root_start + (note - 36);
1392 fn_pad_map.insert (make_pair (pad->filtered, pad));
1394 if (mode_map.find (note) != mode_map.end()) {
1396 if ((note % 12) == original_root) {
1397 pad->set_color (selection_color);
1398 pad->perma_color = selection_color;
1400 pad->set_color (LED::White);
1401 pad->perma_color = LED::White;
1404 pad->do_when_pressed = Pad::FlashOff;
1408 /* note is not in mode, turn it off */
1410 pad->do_when_pressed = Pad::FlashOn;
1411 pad->set_color (LED::Black);
1415 pad->set_state (LED::OneShot24th);
1416 write (pad->state_msg());
1422 bool changed = false;
1424 if (_scale_root != original_root) {
1425 _scale_root = original_root;
1428 if (_root_octave != octave) {
1429 _root_octave = octave;
1432 if (_in_key != inkey) {
1436 if (_mode != mode) {
1442 ScaleChange (); /* EMIT SIGNAL */
1447 Push2::set_percussive_mode (bool yn)
1450 cerr << "back to scale\n";
1451 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1458 fn_pad_map.clear ();
1460 for (int row = 0; row < 8; ++row) {
1462 for (int col = 0; col < 4; ++col) {
1464 int index = 36 + (row*8) + col;
1465 Pad* pad = nn_pad_map[index];
1467 pad->filtered = drum_note;
1472 for (int row = 0; row < 8; ++row) {
1474 for (int col = 4; col < 8; ++col) {
1476 int index = 36 + (row*8) + col;
1477 Pad* pad = nn_pad_map[index];
1479 pad->filtered = drum_note;
1488 Push2::current_layout () const
1490 Glib::Threads::Mutex::Lock lm (layout_lock);
1491 return _current_layout;
1495 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1497 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1498 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1499 boost::shared_ptr<MidiTrack> new_pad_target;
1501 /* See if there's a MIDI track selected */
1503 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1505 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1507 if (new_pad_target) {
1512 if (current_midi_track == new_pad_target) {
1517 if (!new_pad_target) {
1518 /* leave existing connection alone */
1522 /* disconnect from pad port, if appropriate */
1524 if (current_midi_track && pad_port) {
1526 /* XXX this could possibly leave dangling MIDI notes.
1528 * A general libardour fix is required. It isn't obvious
1529 * how note resolution can be done unless disconnecting
1530 * becomes "slow" (i.e. deferred for as long as it takes
1531 * to resolve notes).
1533 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1536 /* now connect the pad port to this (newly) selected midi
1537 * track, if indeed there is one.
1540 if (new_pad_target && pad_port) {
1541 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1542 current_pad_target = new_pad_target;
1543 selection_color = get_color_index (new_pad_target->presentation_info().color());
1544 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1546 current_pad_target.reset ();
1547 selection_color = LED::Green;
1548 contrast_color = LED::Green;
1551 reset_pad_colors ();
1555 Push2::button_by_id (ButtonID bid)
1557 return id_button_map[bid];
1561 Push2::get_color_index (ArdourCanvas::Color rgba)
1563 ColorMap::iterator i = color_map.find (rgba);
1565 if (i != color_map.end()) {
1569 double dr, dg, db, da;
1571 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1572 int w = 126; /* not sure where/when we should get this value */
1575 r = (int) floor (255.0 * dr);
1576 g = (int) floor (255.0 * dg);
1577 b = (int) floor (255.0 * db);
1579 /* get a free index */
1583 if (color_map_free_list.empty()) {
1584 /* random replacement of any entry above zero and below 122 (where the
1585 * Ableton standard colors live)
1587 index = 1 + (random() % 121);
1589 index = color_map_free_list.top();
1590 color_map_free_list.pop();
1593 MidiByteArray palette_msg (17,
1595 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1596 0x00, /* index = 7 */
1597 0x00, 0x00, /* r = 8 & 9 */
1598 0x00, 0x00, /* g = 10 & 11 */
1599 0x00, 0x00, /* b = 12 & 13 */
1600 0x00, 0x00, /* w (a?) = 14 & 15*/
1602 palette_msg[7] = index;
1603 palette_msg[8] = r & 0x7f;
1604 palette_msg[9] = (r & 0x80) >> 7;
1605 palette_msg[10] = g & 0x7f;
1606 palette_msg[11] = (g & 0x80) >> 7;
1607 palette_msg[12] = b & 0x7f;
1608 palette_msg[13] = (b & 0x80) >> 7;
1609 palette_msg[14] = w & 0x7f;
1610 palette_msg[15] = w & 0x80;
1612 write (palette_msg);
1614 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1615 write (update_pallette_msg);
1617 color_map[rgba] = index;
1623 Push2::build_color_map ()
1625 /* These are "standard" colors that Ableton docs suggest will always be
1626 there. Put them in our color map so that when we look up these
1627 colors, we will use the Ableton indices for them.
1630 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1631 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1632 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1633 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1634 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1635 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1636 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1638 for (uint8_t n = 1; n < 122; ++n) {
1639 color_map_free_list.push (n);
1644 Push2::fill_color_table ()
1646 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1647 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1649 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1651 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1652 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1653 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1655 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1656 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1658 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1659 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1660 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1661 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1666 Push2::get_color (ColorName name)
1668 Colors::iterator c = colors.find (name);
1669 if (c != colors.end()) {
1677 Push2::set_current_layout (Push2Layout* layout)
1679 if (layout && layout == _current_layout) {
1680 _current_layout->show ();
1683 if (_current_layout) {
1684 _current_layout->hide ();
1685 _canvas->root()->remove (_current_layout);
1686 _previous_layout = _current_layout;
1689 _current_layout = layout;
1691 if (_current_layout) {
1692 _canvas->root()->add (_current_layout);
1693 _current_layout->show ();
1697 _canvas->request_redraw ();
1702 Push2::use_previous_layout ()
1704 if (_previous_layout) {
1705 set_current_layout (_previous_layout);
1710 Push2::request_pressure_mode ()
1712 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1717 Push2::set_pressure_mode (PressureMode pm)
1719 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1723 /* nothing to do, message is correct */
1733 cerr << "Sent PM message " << msg << endl;