2 Copyright (C) 2016 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "pbd/compose.h"
22 #include "pbd/convert.h"
23 #include "pbd/debug.h"
24 #include "pbd/failed_constructor.h"
25 #include "pbd/file_utils.h"
26 #include "pbd/search_path.h"
27 #include "pbd/enumwriter.h"
29 #include "midi++/parser.h"
30 #include "timecode/time.h"
31 #include "timecode/bbt_time.h"
33 #include "ardour/amp.h"
34 #include "ardour/async_midi_port.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/debug.h"
37 #include "ardour/midiport_manager.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/midi_port.h"
40 #include "ardour/session.h"
41 #include "ardour/tempo.h"
43 #include "gtkmm2ext/gui_thread.h"
44 #include "gtkmm2ext/rgb_macros.h"
46 #include "canvas/colors.h"
56 #include "track_mix.h"
60 using namespace ARDOUR;
64 using namespace ArdourSurface;
66 #include "pbd/abstract_ui.cc" // instantiate template
68 #define ABLETON 0x2982
71 __attribute__((constructor)) static void
74 EnumWriter& enum_writer (EnumWriter::instance());
78 MusicalMode::Type mode;
80 #define REGISTER(e) enum_writer.register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
81 #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
83 REGISTER_CLASS_ENUM (MusicalMode,Dorian);
84 REGISTER_CLASS_ENUM (MusicalMode, IonianMajor);
85 REGISTER_CLASS_ENUM (MusicalMode, Minor);
86 REGISTER_CLASS_ENUM (MusicalMode, HarmonicMinor);
87 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorAscending);
88 REGISTER_CLASS_ENUM (MusicalMode, MelodicMinorDescending);
89 REGISTER_CLASS_ENUM (MusicalMode, Phrygian);
90 REGISTER_CLASS_ENUM (MusicalMode, Lydian);
91 REGISTER_CLASS_ENUM (MusicalMode, Mixolydian);
92 REGISTER_CLASS_ENUM (MusicalMode, Aeolian);
93 REGISTER_CLASS_ENUM (MusicalMode, Locrian);
94 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMajor);
95 REGISTER_CLASS_ENUM (MusicalMode, PentatonicMinor);
96 REGISTER_CLASS_ENUM (MusicalMode, Chromatic);
97 REGISTER_CLASS_ENUM (MusicalMode, BluesScale);
98 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMinor);
99 REGISTER_CLASS_ENUM (MusicalMode, NeapolitanMajor);
100 REGISTER_CLASS_ENUM (MusicalMode, Oriental);
101 REGISTER_CLASS_ENUM (MusicalMode, DoubleHarmonic);
102 REGISTER_CLASS_ENUM (MusicalMode, Enigmatic);
103 REGISTER_CLASS_ENUM (MusicalMode, Hirajoshi);
104 REGISTER_CLASS_ENUM (MusicalMode, HungarianMinor);
105 REGISTER_CLASS_ENUM (MusicalMode, HungarianMajor);
106 REGISTER_CLASS_ENUM (MusicalMode, Kumoi);
107 REGISTER_CLASS_ENUM (MusicalMode, Iwato);
108 REGISTER_CLASS_ENUM (MusicalMode, Hindu);
109 REGISTER_CLASS_ENUM (MusicalMode, Spanish8Tone);
110 REGISTER_CLASS_ENUM (MusicalMode, Pelog);
111 REGISTER_CLASS_ENUM (MusicalMode, HungarianGypsy);
112 REGISTER_CLASS_ENUM (MusicalMode, Overtone);
113 REGISTER_CLASS_ENUM (MusicalMode, LeadingWholeTone);
114 REGISTER_CLASS_ENUM (MusicalMode, Arabian);
115 REGISTER_CLASS_ENUM (MusicalMode, Balinese);
116 REGISTER_CLASS_ENUM (MusicalMode, Gypsy);
117 REGISTER_CLASS_ENUM (MusicalMode, Mohammedan);
118 REGISTER_CLASS_ENUM (MusicalMode, Javanese);
119 REGISTER_CLASS_ENUM (MusicalMode, Persian);
120 REGISTER_CLASS_ENUM (MusicalMode, Algerian);
124 Push2::Push2 (ARDOUR::Session& s)
125 : ControlProtocol (s, string (X_("Ableton Push 2")))
126 , AbstractUI<Push2Request> (name())
128 , _modifier_state (None)
130 , _current_layout (0)
131 , _previous_layout (0)
132 , connection_state (ConnectionState (0))
134 , _mode (MusicalMode::IonianMajor)
140 , _pressure_mode (AfterTouch)
141 , selection_color (LED::Green)
142 , contrast_color (LED::Green)
149 /* master cannot be removed, so no need to connect to going-away signal */
150 master = session->master_out ();
153 throw failed_constructor ();
156 ControlProtocol::StripableSelectionChanged.connect (selection_connection, MISSING_INVALIDATOR, boost::bind (&Push2::stripable_selection_change, this, _1), this);
158 /* catch current selection, if any */
160 StripableNotificationListPtr sp (new StripableNotificationList (ControlProtocol::last_selected()));
161 stripable_selection_change (sp);
164 /* catch arrival and departure of Push2 itself */
165 ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_reg_connection, MISSING_INVALIDATOR, boost::bind (&Push2::port_registration_handler, this), this);
167 /* Catch port connections and disconnections */
168 ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connection, MISSING_INVALIDATOR, boost::bind (&Push2::connection_handler, this, _1, _2, _3, _4, _5), this);
170 /* ports might already be there */
171 port_registration_handler ();
178 delete track_mix_layout;
184 Push2::port_registration_handler ()
186 if (!_async_in && !_async_out) {
187 /* ports not registered yet */
191 if (_async_in->connected() && _async_out->connected()) {
192 /* don't waste cycles here */
196 string input_port_name = X_("Ableton Push 2 MIDI 1 in");
197 string output_port_name = X_("Ableton Push 2 MIDI 1 out");
201 AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
202 AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
204 if (!in.empty() && !out.empty()) {
205 cerr << "Push2: both ports found\n";
206 cerr << "\tconnecting to " << in.front() << " + " << out.front() << endl;
207 if (!_async_in->connected()) {
208 AudioEngine::instance()->connect (_async_in->name(), in.front());
210 if (!_async_out->connected()) {
211 AudioEngine::instance()->connect (_async_out->name(), out.front());
226 if ((handle = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
230 if ((err = libusb_claim_interface (handle, 0x00))) {
235 _canvas = new Push2Canvas (*this, 960, 160);
236 mix_layout = new MixLayout (*this, *session);
237 scale_layout = new ScaleLayout (*this, *session);
238 track_mix_layout = new TrackMixLayout (*this, *session);
239 splash_layout = new SplashLayout (*this, *session);
241 error << _("Cannot construct Canvas for display") << endmsg;
242 libusb_release_interface (handle, 0x00);
243 libusb_close (handle);
249 _async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, X_("Push 2 in"), true);
250 _async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, X_("Push 2 out"), true);
252 if (_async_in == 0 || _async_out == 0) {
256 /* We do not add our ports to the input/output bundles because we don't
257 * want users wiring them by hand. They could use JACK tools if they
258 * really insist on that.
261 _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
262 _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
264 /* Create a shadow port where, depending on the state of the surface,
265 * we will make pad note on/off events appear. The surface code will
266 * automatically this port to the first selected MIDI track.
269 boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), X_("Push 2")), boost::bind (&Push2::pad_filter, this, _1, _2));
270 boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
274 _output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
276 _output_bundle->add_channel (
278 ARDOUR::DataType::MIDI,
279 session->engine().make_port_name_non_relative (shadow_port->name())
283 session->BundleAddedOrRemoved ();
285 connect_to_parser ();
290 list<boost::shared_ptr<ARDOUR::Bundle> >
293 list<boost::shared_ptr<ARDOUR::Bundle> > b;
295 if (_output_bundle) {
296 b.push_back (_output_bundle);
305 init_buttons (false);
306 strip_buttons_off ();
308 /* wait for button data to be flushed */
310 asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
311 asp->drain (10000, 500000);
313 AudioEngine::instance()->unregister_port (_async_in);
314 AudioEngine::instance()->unregister_port (_async_out);
316 _async_in.reset ((ARDOUR::Port*) 0);
317 _async_out.reset ((ARDOUR::Port*) 0);
321 periodic_connection.disconnect ();
322 session_connections.drop_connections ();
324 if (_current_layout) {
325 _canvas->root()->remove (_current_layout);
333 delete splash_layout;
337 libusb_release_interface (handle, 0x00);
338 libusb_close (handle);
346 Push2::strip_buttons_off ()
348 ButtonID strip_buttons[] = { Upper1, Upper2, Upper3, Upper4, Upper5, Upper6, Upper7, Upper8,
349 Lower1, Lower2, Lower3, Lower4, Lower5, Lower6, Lower7, Lower8, };
351 for (size_t n = 0; n < sizeof (strip_buttons) / sizeof (strip_buttons[0]); ++n) {
352 Button* b = id_button_map[strip_buttons[n]];
354 b->set_color (LED::Black);
355 b->set_state (LED::OneShot24th);
356 write (b->state_msg());
362 Push2::init_buttons (bool startup)
364 /* This is a list of buttons that we want lit because they do something
365 in ardour related (loosely, sometimes) to their illuminated label.
368 ButtonID buttons[] = { Mute, Solo, Master, Up, Right, Left, Down, Note, Session, Mix, AddTrack, Delete, Undo,
369 Metronome, Shift, Select, Play, RecordEnable, Automate, Repeat, Note, Session, DoubleLoop,
370 Quantize, Duplicate, Browse, PageRight, PageLeft, OctaveUp, OctaveDown, Layout, Scale
373 for (size_t n = 0; n < sizeof (buttons) / sizeof (buttons[0]); ++n) {
374 Button* b = id_button_map[buttons[n]];
377 b->set_color (LED::White);
379 b->set_color (LED::Black);
381 b->set_state (LED::OneShot24th);
382 write (b->state_msg());
387 /* all other buttons are off (black) */
389 ButtonID off_buttons[] = { TapTempo, Setup, User, Stop, Convert, New, FixedLength,
390 Fwd32ndT, Fwd32nd, Fwd16thT, Fwd16th, Fwd8thT, Fwd8th, Fwd4trT, Fwd4tr,
391 Accent, Note, Session, };
393 for (size_t n = 0; n < sizeof (off_buttons) / sizeof (off_buttons[0]); ++n) {
394 Button* b = id_button_map[off_buttons[n]];
396 b->set_color (LED::Black);
397 b->set_state (LED::OneShot24th);
398 write (b->state_msg());
403 for (NNPadMap::iterator pi = nn_pad_map.begin(); pi != nn_pad_map.end(); ++pi) {
404 Pad* pad = pi->second;
406 pad->set_color (LED::Black);
407 pad->set_state (LED::OneShot24th);
408 write (pad->state_msg());
416 libusb_device_handle *h;
419 if ((h = libusb_open_device_with_vid_pid (NULL, ABLETON, PUSH2)) == 0) {
420 DEBUG_TRACE (DEBUG::Push2, "no Push2 device found\n");
425 DEBUG_TRACE (DEBUG::Push2, "Push2 device located\n");
430 Push2::request_factory (uint32_t num_requests)
432 /* AbstractUI<T>::request_buffer_factory() is a template method only
433 instantiated in this source module. To provide something visible for
434 use in the interface/descriptor, we have this static method that is
437 return request_buffer_factory (num_requests);
441 Push2::do_request (Push2Request * req)
443 if (req->type == CallSlot) {
445 call_slot (MISSING_INVALIDATOR, req->the_slot);
447 } else if (req->type == Quit) {
465 set_current_layout (splash_layout);
466 splash_start = get_microseconds ();
474 /* display splash for 2 seconds */
476 if (get_microseconds() - splash_start > 2000000) {
478 DEBUG_TRACE (DEBUG::Push2, "splash interval ended, switch to mix layout\n");
479 set_current_layout (mix_layout);
483 if (_current_layout) {
484 _current_layout->update_meters ();
485 _current_layout->update_clocks ();
494 Push2::set_active (bool yn)
496 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active init with yn: '%1'\n", yn));
498 if (yn == active()) {
504 /* start event loop */
509 DEBUG_TRACE (DEBUG::Push2, "device open failed\n");
514 /* Connect input port to event loop */
518 asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
519 asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &Push2::midi_input_handler), _input_port));
520 asp->xthread().attach (main_loop()->get_context());
522 connect_session_signals ();
524 /* set up periodic task used to push a frame buffer to the
525 * device (25fps). The device can handle 60fps, but we don't
526 * need that frame rate.
529 Glib::RefPtr<Glib::TimeoutSource> vblank_timeout = Glib::TimeoutSource::create (40); // milliseconds
530 vblank_connection = vblank_timeout->connect (sigc::mem_fun (*this, &Push2::vblank));
531 vblank_timeout->attach (main_loop()->get_context());
534 Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (1000); // milliseconds
535 periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &Push2::periodic));
536 periodic_timeout->attach (main_loop()->get_context());
540 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
549 ControlProtocol::set_active (yn);
551 DEBUG_TRACE (DEBUG::Push2, string_compose("Push2Protocol::set_active done with yn: '%1'\n", yn));
557 Push2::init_touch_strip ()
559 MidiByteArray msg (9, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x17, 0x00, 0xf7);
560 /* flags are the final byte (ignore end-of-sysex */
562 /* show bar, not point
566 msg[7] = (1<<4) | (1<<5) | (1<<6);
571 Push2::write (const MidiByteArray& data)
573 /* immediate delivery */
574 _output_port->write (&data[0], data.size(), 0);
578 Push2::midi_input_handler (IOCondition ioc, MIDI::Port* port)
581 DEBUG_TRACE (DEBUG::Push2, "MIDI port closed\n");
587 // DEBUG_TRACE (DEBUG::Push2, string_compose ("something happend on %1\n", port->name()));
589 AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
594 //DEBUG_TRACE (DEBUG::Push2, string_compose ("data available on %1\n", port->name()));
595 framepos_t now = AudioEngine::instance()->sample_time();
609 Push2::connect_to_parser ()
611 DEBUG_TRACE (DEBUG::Push2, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
613 MIDI::Parser* p = _input_port->parser();
616 p->sysex.connect_same_thread (*this, boost::bind (&Push2::handle_midi_sysex, this, _1, _2, _3));
617 /* V-Pot messages are Controller */
618 p->controller.connect_same_thread (*this, boost::bind (&Push2::handle_midi_controller_message, this, _1, _2));
619 /* Button messages are NoteOn */
620 p->note_on.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
621 /* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
622 p->note_off.connect_same_thread (*this, boost::bind (&Push2::handle_midi_note_on_message, this, _1, _2));
623 /* Fader messages are Pitchbend */
624 p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Push2::handle_midi_pitchbend_message, this, _1, _2));
628 Push2::handle_midi_sysex (MIDI::Parser&, MIDI::byte* raw_bytes, size_t sz)
630 DEBUG_TRACE (DEBUG::Push2, string_compose ("Sysex, %1 bytes\n", sz));
636 MidiByteArray msg (sz, raw_bytes);
637 MidiByteArray push2_sysex_header (6, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01);
639 if (!push2_sysex_header.compare_n (msg, 6)) {
644 case 0x1f: /* pressure mode */
646 _pressure_mode = AfterTouch;
647 PressureModeChange (AfterTouch);
648 cerr << "Pressure mode is after\n";
650 _pressure_mode = PolyPressure;
651 PressureModeChange (PolyPressure);
652 cerr << "Pressure mode is poly\n";
659 Push2::handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
661 DEBUG_TRACE (DEBUG::Push2, string_compose ("CC %1 (value %2)\n", (int) ev->controller_number, (int) ev->value));
663 CCButtonMap::iterator b = cc_button_map.find (ev->controller_number);
666 /* any press cancels any pending long press timeouts */
667 for (set<ButtonID>::iterator x = buttons_down.begin(); x != buttons_down.end(); ++x) {
668 Button* bb = id_button_map[*x];
669 bb->timeout_connection.disconnect ();
673 if (b != cc_button_map.end()) {
675 Button* button = b->second;
678 buttons_down.insert (button->id);
679 start_press_timeout (*button, button->id);
681 buttons_down.erase (button->id);
682 button->timeout_connection.disconnect ();
686 set<ButtonID>::iterator c = consumed.find (button->id);
688 if (c == consumed.end()) {
689 if (ev->value == 0) {
690 (this->*button->release_method)();
692 (this->*button->press_method)();
695 DEBUG_TRACE (DEBUG::Push2, "button was consumed, ignored\n");
703 int delta = ev->value;
706 delta = -(128 - delta);
709 switch (ev->controller_number) {
711 _current_layout->strip_vpot (0, delta);
714 _current_layout->strip_vpot (1, delta);
717 _current_layout->strip_vpot (2, delta);
720 _current_layout->strip_vpot (3, delta);
723 _current_layout->strip_vpot (4, delta);
726 _current_layout->strip_vpot (5, delta);
729 _current_layout->strip_vpot (6, delta);
732 _current_layout->strip_vpot (7, delta);
737 other_vpot (8, delta);
740 other_vpot (1, delta);
745 other_vpot (2, delta);
752 Push2::handle_midi_note_on_message (MIDI::Parser& parser, MIDI::EventTwoBytes* ev)
754 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note On %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
756 if (ev->velocity == 0) {
757 handle_midi_note_off_message (parser, ev);
761 switch (ev->note_number) {
763 _current_layout->strip_vpot_touch (0, ev->velocity > 64);
766 _current_layout->strip_vpot_touch (1, ev->velocity > 64);
769 _current_layout->strip_vpot_touch (2, ev->velocity > 64);
772 _current_layout->strip_vpot_touch (3, ev->velocity > 64);
775 _current_layout->strip_vpot_touch (4, ev->velocity > 64);
778 _current_layout->strip_vpot_touch (5, ev->velocity > 64);
781 _current_layout->strip_vpot_touch (6, ev->velocity > 64);
784 _current_layout->strip_vpot_touch (7, ev->velocity > 64);
789 other_vpot_touch (0, ev->velocity > 64);
792 other_vpot_touch (1, ev->velocity > 64);
797 other_vpot_touch (3, ev->velocity > 64);
802 if (ev->velocity < 64) {
808 if (ev->note_number < 11) {
812 /* Pad illuminations */
814 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
816 if (pm == nn_pad_map.end()) {
820 const Pad * const pad_pressed = pm->second;
822 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
824 if (pads_with_note.first == fn_pad_map.end()) {
828 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
829 Pad* pad = pi->second;
831 pad->set_color (contrast_color);
832 pad->set_state (LED::OneShot24th);
833 write (pad->state_msg());
838 Push2::handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes* ev)
840 // DEBUG_TRACE (DEBUG::Push2, string_compose ("Note Off %1 (velocity %2)\n", (int) ev->note_number, (int) ev->velocity));
842 if (ev->note_number < 11) {
843 /* theoretically related to encoder touch start/end, but
844 * actually they send note on with two different velocity
850 /* Pad illuminations */
852 NNPadMap::const_iterator pm = nn_pad_map.find (ev->note_number);
854 if (pm == nn_pad_map.end()) {
858 const Pad * const pad_pressed = pm->second;
860 pair<FNPadMap::iterator,FNPadMap::iterator> pads_with_note = fn_pad_map.equal_range (pad_pressed->filtered);
862 if (pads_with_note.first == fn_pad_map.end()) {
866 for (FNPadMap::iterator pi = pads_with_note.first; pi != pads_with_note.second; ++pi) {
867 Pad* pad = pi->second;
869 if (pad->do_when_pressed == Pad::FlashOn) {
870 pad->set_color (LED::Black);
871 pad->set_state (LED::OneShot24th);
872 write (pad->state_msg());
873 } else if (pad->do_when_pressed == Pad::FlashOff) {
874 pad->set_color (pad->perma_color);
875 pad->set_state (LED::OneShot24th);
876 write (pad->state_msg());
882 Push2::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb)
887 Push2::thread_init ()
889 struct sched_param rtparam;
891 pthread_set_name (event_loop_name().c_str());
893 PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
894 ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
896 memset (&rtparam, 0, sizeof (rtparam));
897 rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
899 if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
900 // do we care? not particularly.
905 Push2::connect_session_signals()
907 // receive routes added
908 //session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
909 // receive VCAs added
910 //session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_vca_added, this, _1), this);
912 // receive record state toggled
913 session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_record_state_changed, this), this);
914 // receive transport state changed
915 session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_transport_state_changed, this), this);
916 session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_loop_state_changed, this), this);
917 // receive punch-in and punch-out
918 Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
919 session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_parameter_changed, this, _1), this);
920 // receive rude solo changed
921 session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&Push2::notify_solo_active_changed, this, _1), this);
925 Push2::notify_record_state_changed ()
927 IDButtonMap::iterator b = id_button_map.find (RecordEnable);
929 if (b == id_button_map.end()) {
933 switch (session->record_status ()) {
934 case Session::Disabled:
935 b->second->set_color (LED::White);
936 b->second->set_state (LED::NoTransition);
938 case Session::Enabled:
939 b->second->set_color (LED::Red);
940 b->second->set_state (LED::Blinking4th);
942 case Session::Recording:
943 b->second->set_color (LED::Red);
944 b->second->set_state (LED::OneShot24th);
948 write (b->second->state_msg());
952 Push2::notify_transport_state_changed ()
954 Button* b = id_button_map[Play];
956 if (session->transport_rolling()) {
957 b->set_state (LED::OneShot24th);
958 b->set_color (LED::Green);
961 /* disable any blink on FixedLength from pending edit range op */
962 Button* fl = id_button_map[FixedLength];
964 fl->set_color (LED::Black);
965 fl->set_state (LED::NoTransition);
966 write (fl->state_msg());
968 b->set_color (LED::White);
969 b->set_state (LED::NoTransition);
972 write (b->state_msg());
976 Push2::notify_loop_state_changed ()
981 Push2::notify_parameter_changed (std::string param)
983 IDButtonMap::iterator b;
985 if (param == "clicking") {
986 if ((b = id_button_map.find (Metronome)) == id_button_map.end()) {
989 if (Config->get_clicking()) {
990 b->second->set_state (LED::Blinking4th);
991 b->second->set_color (LED::White);
993 b->second->set_color (LED::White);
994 b->second->set_state (LED::NoTransition);
996 write (b->second->state_msg ());
1001 Push2::notify_solo_active_changed (bool yn)
1003 IDButtonMap::iterator b = id_button_map.find (Solo);
1005 if (b == id_button_map.end()) {
1010 b->second->set_state (LED::Blinking4th);
1011 b->second->set_color (LED::Red);
1013 b->second->set_state (LED::NoTransition);
1014 b->second->set_color (LED::White);
1017 write (b->second->state_msg());
1023 XMLNode& node (ControlProtocol::get_state());
1026 child = new XMLNode (X_("Input"));
1027 child->add_child_nocopy (_async_in->get_state());
1028 node.add_child_nocopy (*child);
1029 child = new XMLNode (X_("Output"));
1030 child->add_child_nocopy (_async_out->get_state());
1031 node.add_child_nocopy (*child);
1033 node.add_property (X_("root"), to_string (_scale_root, std::dec));
1034 node.add_property (X_("root_octave"), to_string (_root_octave, std::dec));
1035 node.add_property (X_("in_key"), _in_key ? X_("yes") : X_("no"));
1036 node.add_property (X_("mode"), enum_2_string (_mode));
1042 Push2::set_state (const XMLNode & node, int version)
1044 DEBUG_TRACE (DEBUG::Push2, string_compose ("Push2::set_state: active %1\n", active()));
1048 if (ControlProtocol::set_state (node, version)) {
1054 if ((child = node.child (X_("Input"))) != 0) {
1055 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1057 _async_in->set_state (*portnode, version);
1061 if ((child = node.child (X_("Output"))) != 0) {
1062 XMLNode* portnode = child->child (Port::state_node_name.c_str());
1064 _async_out->set_state (*portnode, version);
1068 XMLProperty const* prop;
1070 if ((prop = node.property (X_("root"))) != 0) {
1071 _scale_root = atoi (prop->value());
1074 if ((prop = node.property (X_("root_octave"))) != 0) {
1075 _root_octave = atoi (prop->value());
1078 if ((prop = node.property (X_("in_key"))) != 0) {
1079 _in_key = string_is_affirmative (prop->value());
1082 if ((prop = node.property (X_("mode"))) != 0) {
1083 _mode = (MusicalMode::Type) string_2_enum (prop->value(), _mode);
1090 Push2::other_vpot (int n, int delta)
1092 boost::shared_ptr<Amp> click_gain;
1098 /* metronome gain control */
1099 click_gain = session->click_gain();
1101 boost::shared_ptr<AutomationControl> ac = click_gain->gain_control();
1103 ac->set_value (ac->interface_to_internal (
1104 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1105 PBD::Controllable::UseGroup);
1110 /* master gain control */
1112 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1114 ac->set_value (ac->interface_to_internal (
1115 min (ac->upper(), max (ac->lower(), ac->internal_to_interface (ac->get_value()) + (delta/256.0)))),
1116 PBD::Controllable::UseGroup);
1124 Push2::other_vpot_touch (int n, bool touching)
1133 boost::shared_ptr<AutomationControl> ac = master->gain_control();
1136 ac->start_touch (session->audible_frame());
1138 ac->stop_touch (true, session->audible_frame());
1146 Push2::start_shift ()
1148 cerr << "start shift\n";
1149 _modifier_state = ModifierState (_modifier_state | ModShift);
1150 Button* b = id_button_map[Shift];
1151 b->set_color (LED::White);
1152 b->set_state (LED::Blinking16th);
1153 write (b->state_msg());
1159 if (_modifier_state & ModShift) {
1160 cerr << "end shift\n";
1161 _modifier_state = ModifierState (_modifier_state & ~(ModShift));
1162 Button* b = id_button_map[Shift];
1163 b->timeout_connection.disconnect ();
1164 b->set_color (LED::White);
1165 b->set_state (LED::OneShot24th);
1166 write (b->state_msg());
1171 Push2::pad_filter (MidiBuffer& in, MidiBuffer& out) const
1173 /* This filter is called asynchronously from a realtime process
1174 context. It must use atomics to check state, and must not block.
1177 bool matched = false;
1179 for (MidiBuffer::iterator ev = in.begin(); ev != in.end(); ++ev) {
1180 if ((*ev).is_note_on() || (*ev).is_note_off()) {
1182 /* encoder touch start/touch end use note
1183 * 0-10. touchstrip uses note 12
1186 if ((*ev).note() > 10 && (*ev).note() != 12) {
1188 const int n = (*ev).note ();
1189 NNPadMap::const_iterator nni = nn_pad_map.find (n);
1191 if (nni != nn_pad_map.end()) {
1192 Pad const * pad = nni->second;
1193 /* shift for output to the shadow port */
1194 if (pad->filtered >= 0) {
1195 (*ev).set_note (pad->filtered + (octave_shift*12));
1196 out.push_back (*ev);
1197 /* shift back so that the pads light correctly */
1200 /* no mapping, don't send event */
1203 out.push_back (*ev);
1208 } else if ((*ev).is_pitch_bender() || (*ev).is_poly_pressure() || (*ev).is_channel_pressure()) {
1209 out.push_back (*ev);
1217 Push2::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
1219 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler start\n");
1220 if (!_input_port || !_output_port) {
1224 string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
1225 string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
1227 if (ni == name1 || ni == name2) {
1229 connection_state |= InputConnected;
1231 connection_state &= ~InputConnected;
1233 } else if (no == name1 || no == name2) {
1235 connection_state |= OutputConnected;
1237 connection_state &= ~OutputConnected;
1240 DEBUG_TRACE (DEBUG::FaderPort, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
1245 if ((connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
1247 /* XXX this is a horrible hack. Without a short sleep here,
1248 something prevents the device wakeup messages from being
1249 sent and/or the responses from being received.
1253 DEBUG_TRACE (DEBUG::FaderPort, "device now connected for both input and output\n");
1257 DEBUG_TRACE (DEBUG::FaderPort, "Device disconnected (input or output or both) or not yet fully connected\n");
1260 ConnectionChange (); /* emit signal for our GUI */
1262 DEBUG_TRACE (DEBUG::FaderPort, "FaderPort::connection_handler end\n");
1264 return true; /* connection status changed */
1270 request_pressure_mode ();
1273 boost::shared_ptr<Port>
1274 Push2::output_port()
1279 boost::shared_ptr<Port>
1286 Push2::pad_note (int row, int col) const
1288 NNPadMap::const_iterator nni = nn_pad_map.find (36+(row*8)+col);
1290 if (nni != nn_pad_map.end()) {
1291 return nni->second->filtered;
1298 Push2::update_selection_color ()
1300 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1302 if (!current_midi_track) {
1306 selection_color = get_color_index (current_midi_track->presentation_info().color());
1307 contrast_color = get_color_index (ArdourCanvas::HSV (current_midi_track->presentation_info().color()).opposite().color());
1309 reset_pad_colors ();
1313 Push2::reset_pad_colors ()
1315 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1319 Push2::set_pad_scale (int root, int octave, MusicalMode::Type mode, bool inkey)
1321 MusicalMode m (mode);
1322 vector<float>::iterator interval;
1324 const int original_root = root;
1326 interval = m.steps.begin();
1327 root += (octave*12);
1330 const int root_start = root;
1332 set<int> mode_map; /* contains only notes in mode, O(logN) lookup */
1333 vector<int> mode_vector; /* sorted in note order */
1335 mode_map.insert (note);
1336 mode_vector.push_back (note);
1338 /* build a map of all notes in the mode, from the root to 127 */
1340 while (note < 128) {
1342 if (interval == m.steps.end()) {
1344 /* last distance was the end of the scale,
1345 so wrap, adding the next note at one
1346 octave above the last root.
1349 interval = m.steps.begin();
1351 mode_map.insert (root);
1352 mode_vector.push_back (root);
1355 note = (int) floor (root + (2.0 * (*interval)));
1357 mode_map.insert (note);
1358 mode_vector.push_back (note);
1362 fn_pad_map.clear ();
1366 vector<int>::iterator notei;
1369 for (int row = 0; row < 8; ++row) {
1371 /* Ableton's grid layout wraps the available notes in the scale
1372 * by offsetting 3 notes per row (from the bottom)
1375 notei = mode_vector.begin();
1376 notei += row_offset;
1379 for (int col = 0; col < 8; ++col) {
1380 int index = 36 + (row*8) + col;
1381 Pad* pad = nn_pad_map[index];
1383 if (notei != mode_vector.end()) {
1386 pad->filtered = notenum;
1388 fn_pad_map.insert (make_pair (notenum, pad));
1390 if ((notenum % 12) == original_root) {
1391 pad->set_color (selection_color);
1392 pad->perma_color = selection_color;
1394 pad->set_color (LED::White);
1395 pad->perma_color = LED::White;
1398 pad->do_when_pressed = Pad::FlashOff;
1403 pad->set_color (LED::Black);
1404 pad->do_when_pressed = Pad::Nothing;
1408 pad->set_state (LED::OneShot24th);
1409 write (pad->state_msg());
1415 /* chromatic: all notes available, but highlight those in the scale */
1417 for (note = 36; note < 100; ++note) {
1419 Pad* pad = nn_pad_map[note];
1421 /* Chromatic: all pads play, half-tone steps. Light
1422 * those in the scale, and highlight root notes
1425 pad->filtered = root_start + (note - 36);
1427 fn_pad_map.insert (make_pair (pad->filtered, pad));
1429 if (mode_map.find (note) != mode_map.end()) {
1431 if ((note % 12) == original_root) {
1432 pad->set_color (selection_color);
1433 pad->perma_color = selection_color;
1435 pad->set_color (LED::White);
1436 pad->perma_color = LED::White;
1439 pad->do_when_pressed = Pad::FlashOff;
1443 /* note is not in mode, turn it off */
1445 pad->do_when_pressed = Pad::FlashOn;
1446 pad->set_color (LED::Black);
1450 pad->set_state (LED::OneShot24th);
1451 write (pad->state_msg());
1457 bool changed = false;
1459 if (_scale_root != original_root) {
1460 _scale_root = original_root;
1463 if (_root_octave != octave) {
1464 _root_octave = octave;
1467 if (_in_key != inkey) {
1471 if (_mode != mode) {
1477 ScaleChange (); /* EMIT SIGNAL */
1482 Push2::set_percussive_mode (bool yn)
1485 cerr << "back to scale\n";
1486 set_pad_scale (_scale_root, _root_octave, _mode, _in_key);
1493 fn_pad_map.clear ();
1495 for (int row = 0; row < 8; ++row) {
1497 for (int col = 0; col < 4; ++col) {
1499 int index = 36 + (row*8) + col;
1500 Pad* pad = nn_pad_map[index];
1502 pad->filtered = drum_note;
1507 for (int row = 0; row < 8; ++row) {
1509 for (int col = 4; col < 8; ++col) {
1511 int index = 36 + (row*8) + col;
1512 Pad* pad = nn_pad_map[index];
1514 pad->filtered = drum_note;
1523 Push2::current_layout () const
1525 Glib::Threads::Mutex::Lock lm (layout_lock);
1526 return _current_layout;
1530 Push2::stripable_selection_change (StripableNotificationListPtr selected)
1532 boost::shared_ptr<MidiPort> pad_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
1533 boost::shared_ptr<MidiTrack> current_midi_track = current_pad_target.lock();
1534 boost::shared_ptr<MidiTrack> new_pad_target;
1536 /* See if there's a MIDI track selected */
1538 for (StripableNotificationList::iterator si = selected->begin(); si != selected->end(); ++si) {
1540 new_pad_target = boost::dynamic_pointer_cast<MidiTrack> ((*si).lock());
1542 if (new_pad_target) {
1547 if (current_midi_track == new_pad_target) {
1552 if (!new_pad_target) {
1553 /* leave existing connection alone */
1557 /* disconnect from pad port, if appropriate */
1559 if (current_midi_track && pad_port) {
1561 /* XXX this could possibly leave dangling MIDI notes.
1563 * A general libardour fix is required. It isn't obvious
1564 * how note resolution can be done unless disconnecting
1565 * becomes "slow" (i.e. deferred for as long as it takes
1566 * to resolve notes).
1568 current_midi_track->input()->disconnect (current_midi_track->input()->nth(0), pad_port->name(), this);
1571 /* now connect the pad port to this (newly) selected midi
1572 * track, if indeed there is one.
1575 if (new_pad_target && pad_port) {
1576 new_pad_target->input()->connect (new_pad_target->input()->nth (0), pad_port->name(), this);
1577 current_pad_target = new_pad_target;
1578 selection_color = get_color_index (new_pad_target->presentation_info().color());
1579 contrast_color = get_color_index (ArdourCanvas::HSV (new_pad_target->presentation_info().color()).opposite().color());
1581 current_pad_target.reset ();
1582 selection_color = LED::Green;
1583 contrast_color = LED::Green;
1586 reset_pad_colors ();
1590 Push2::button_by_id (ButtonID bid)
1592 return id_button_map[bid];
1596 Push2::get_color_index (ArdourCanvas::Color rgba)
1598 ColorMap::iterator i = color_map.find (rgba);
1600 if (i != color_map.end()) {
1604 double dr, dg, db, da;
1606 ArdourCanvas::color_to_rgba (rgba, dr, dg, db, da);
1607 int w = 126; /* not sure where/when we should get this value */
1610 r = (int) floor (255.0 * dr);
1611 g = (int) floor (255.0 * dg);
1612 b = (int) floor (255.0 * db);
1614 /* get a free index */
1618 if (color_map_free_list.empty()) {
1619 /* random replacement of any entry above zero and below 122 (where the
1620 * Ableton standard colors live)
1622 index = 1 + (random() % 121);
1624 index = color_map_free_list.top();
1625 color_map_free_list.pop();
1628 MidiByteArray palette_msg (17,
1630 0x00 , 0x21, 0x1d, 0x01, 0x01, 0x03, /* reset palette header */
1631 0x00, /* index = 7 */
1632 0x00, 0x00, /* r = 8 & 9 */
1633 0x00, 0x00, /* g = 10 & 11 */
1634 0x00, 0x00, /* b = 12 & 13 */
1635 0x00, 0x00, /* w (a?) = 14 & 15*/
1637 palette_msg[7] = index;
1638 palette_msg[8] = r & 0x7f;
1639 palette_msg[9] = (r & 0x80) >> 7;
1640 palette_msg[10] = g & 0x7f;
1641 palette_msg[11] = (g & 0x80) >> 7;
1642 palette_msg[12] = b & 0x7f;
1643 palette_msg[13] = (b & 0x80) >> 7;
1644 palette_msg[14] = w & 0x7f;
1645 palette_msg[15] = w & 0x80;
1647 write (palette_msg);
1649 MidiByteArray update_pallette_msg (8, 0xf0, 0x00, 0x21, 0x1d, 0x01, 0x01, 0x05, 0xF7);
1650 write (update_pallette_msg);
1652 color_map[rgba] = index;
1658 Push2::build_color_map ()
1660 /* These are "standard" colors that Ableton docs suggest will always be
1661 there. Put them in our color map so that when we look up these
1662 colors, we will use the Ableton indices for them.
1665 color_map.insert (make_pair (RGB_TO_UINT (0,0,0), 0));
1666 color_map.insert (make_pair (RGB_TO_UINT (204,204,204), 122));
1667 color_map.insert (make_pair (RGB_TO_UINT (64,64,64), 123));
1668 color_map.insert (make_pair (RGB_TO_UINT (20,20,20), 124));
1669 color_map.insert (make_pair (RGB_TO_UINT (0,0,255), 125));
1670 color_map.insert (make_pair (RGB_TO_UINT (0,255,0), 126));
1671 color_map.insert (make_pair (RGB_TO_UINT (255,0,0), 127));
1673 for (uint8_t n = 1; n < 122; ++n) {
1674 color_map_free_list.push (n);
1679 Push2::fill_color_table ()
1681 colors.insert (make_pair (DarkBackground, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1682 colors.insert (make_pair (LightBackground, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1684 colors.insert (make_pair (ParameterName, ArdourCanvas::rgba_to_color (0.98, 0.98, 0.98, 1)));
1686 colors.insert (make_pair (KnobArcBackground, ArdourCanvas::rgba_to_color (0.3, 0.3, 0.3, 1.0)));
1687 colors.insert (make_pair (KnobArcStart, ArdourCanvas::rgba_to_color (1.0, 0.0, 0.0, 1.0)));
1688 colors.insert (make_pair (KnobArcEnd, ArdourCanvas::rgba_to_color (0.0, 1.0, 0.0, 1.0)));
1690 colors.insert (make_pair (KnobLineShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.3)));
1691 colors.insert (make_pair (KnobLine, ArdourCanvas::rgba_to_color (1, 1, 1, 1)));
1693 colors.insert (make_pair (KnobForeground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1694 colors.insert (make_pair (KnobBackground, ArdourCanvas::rgba_to_color (0.2, 0.2, 0.2, 1)));
1695 colors.insert (make_pair (KnobShadow, ArdourCanvas::rgba_to_color (0, 0, 0, 0.1)));
1696 colors.insert (make_pair (KnobBorder, ArdourCanvas::rgba_to_color (0, 0, 0, 1)));
1701 Push2::get_color (ColorName name)
1703 Colors::iterator c = colors.find (name);
1704 if (c != colors.end()) {
1712 Push2::set_current_layout (Push2Layout* layout)
1714 if (_current_layout) {
1715 _current_layout->hide ();
1716 _canvas->root()->remove (_current_layout);
1717 _previous_layout = _current_layout;
1720 _current_layout = layout;
1722 if (_current_layout) {
1723 _canvas->root()->add (_current_layout);
1724 _current_layout->show ();
1727 _canvas->request_redraw ();
1731 Push2::use_previous_layout ()
1733 if (_previous_layout) {
1734 set_current_layout (_previous_layout);
1739 Push2::request_pressure_mode ()
1741 MidiByteArray msg (8, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1F, 0xF7);
1746 Push2::set_pressure_mode (PressureMode pm)
1748 MidiByteArray msg (9, 0xF0, 0x00, 0x21, 0x1D, 0x01, 0x01, 0x1E, 0x0, 0xF7);
1752 /* nothing to do, message is correct */
1762 cerr << "Sent PM message " << msg << endl;