FP8/16 subscribe to panner changes (add/remove channel)
[ardour.git] / libs / surfaces / faderport8 / faderport8.cc
index 6c54d841d343f815135468388080519d4749f793..bbb85faac505cf1f3da08d5460bfef51310b13f4 100644 (file)
@@ -37,6 +37,7 @@
 #include "ardour/debug.h"
 #include "ardour/midi_track.h"
 #include "ardour/midiport_manager.h"
+#include "ardour/panner_shell.h"
 #include "ardour/plugin.h"
 #include "ardour/plugin_insert.h"
 #include "ardour/processor.h"
 #include "faderport8.h"
 
 using namespace ARDOUR;
-using namespace ArdourSurface;
 using namespace PBD;
 using namespace Glib;
 using namespace std;
-using namespace ArdourSurface::FP8Types;
+using namespace ArdourSurface::FP_NAMESPACE;
+using namespace ArdourSurface::FP_NAMESPACE::FP8Types;
 
 #include "pbd/i18n.h"
 
@@ -86,16 +87,20 @@ debug_2byte_msg (std::string const& msg, int b0, int b1)
 }
 
 FaderPort8::FaderPort8 (Session& s)
+#ifdef FADERPORT16
+       : ControlProtocol (s, _("PreSonus FaderPort16"))
+#else
        : ControlProtocol (s, _("PreSonus FaderPort8"))
+#endif
        , AbstractUI<FaderPort8Request> (name())
        , _connection_state (ConnectionState (0))
        , _device_active (false)
        , _ctrls (*this)
-       , _channel_off (0)
        , _plugin_off (0)
        , _parameter_off (0)
        , _show_presets (false)
        , _showing_well_known (0)
+       , _timer_divider (0)
        , _blink_onoff (false)
        , _shift_lock (false)
        , _shift_pressed (0)
@@ -110,8 +115,13 @@ FaderPort8::FaderPort8 (Session& s)
        boost::shared_ptr<ARDOUR::Port> inp;
        boost::shared_ptr<ARDOUR::Port> outp;
 
+#ifdef FADERPORT16
+       inp  = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort16 Recv", true);
+       outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort16 Send", true);
+#else
        inp  = AudioEngine::instance()->register_input_port (DataType::MIDI, "FaderPort8 Recv", true);
        outp = AudioEngine::instance()->register_output_port (DataType::MIDI, "FaderPort8 Send", true);
+#endif
        _input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(inp);
        _output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(outp);
 
@@ -119,8 +129,13 @@ FaderPort8::FaderPort8 (Session& s)
                throw failed_constructor();
        }
 
+#ifdef FADERPORT16
+       _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort16 (Receive)"), true));
+       _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort16 (Send) "), false));
+#else
        _input_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Receive)"), true));
        _output_bundle.reset (new ARDOUR::Bundle (_("FaderPort8 (Send) "), false));
+#endif
 
        _input_bundle->add_channel (
                inp->name(),
@@ -142,7 +157,7 @@ FaderPort8::FaderPort8 (Session& s)
        setup_actions ();
 
        _ctrls.FaderModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::notify_fader_mode_changed, this));
-       _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this, true));
+       _ctrls.MixModeChanged.connect_same_thread (modechange_connections, boost::bind (&FaderPort8::assign_strips, this));
 }
 
 FaderPort8::~FaderPort8 ()
@@ -156,6 +171,7 @@ FaderPort8::~FaderPort8 ()
 
        if (_input_port) {
                DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
+               Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
                AudioEngine::instance()->unregister_port (_input_port);
                _input_port.reset ();
        }
@@ -165,6 +181,7 @@ FaderPort8::~FaderPort8 ()
        if (_output_port) {
                _output_port->drain (10000,  250000); /* check every 10 msecs, wait up to 1/4 second for the port to drain */
                DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering output port %1\n", boost::shared_ptr<ARDOUR::Port>(_output_port)->name()));
+               Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
                AudioEngine::instance()->unregister_port (_output_port);
                _output_port.reset ();
        }
@@ -209,19 +226,12 @@ FaderPort8::stop ()
 void
 FaderPort8::thread_init ()
 {
-       struct sched_param rtparam;
-
        pthread_set_name (event_loop_name().c_str());
 
        PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
        ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
 
-       memset (&rtparam, 0, sizeof (rtparam));
-       rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
-
-       if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
-               // do we care? not particularly.
-       }
+       set_thread_priority ();
 }
 
 bool
@@ -236,7 +246,7 @@ FaderPort8::periodic ()
                _timecode = Timecode::timecode_format_time(TC);
 
                char buf[16];
-               Timecode::BBT_Time BBT = session->tempo_map ().bbt_at_frame (session->transport_frame ());
+               Timecode::BBT_Time BBT = session->tempo_map ().bbt_at_sample (session->transport_sample ());
                snprintf (buf, sizeof (buf),
                                " %02" PRIu32 "|%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32,
                                BBT.bars % 100, BBT.beats %100,
@@ -247,6 +257,14 @@ FaderPort8::periodic ()
                _musical_time.clear ();
        }
 
+#ifdef FADERPORT16
+       /* every second, send "running" */
+       if (++_timer_divider == 10) {
+               _timer_divider = 0;
+               tx_midi3 (0xa0, 0x00, 0x00);
+       }
+#endif
+
        /* update stripables */
        Periodic ();
        return true;
@@ -323,10 +341,12 @@ FaderPort8::connected ()
        // ideally check firmware version >= 1.01 (USB bcdDevice 0x0101) (vendor 0x194f prod 0x0202)
        // but we don't have a handle to the underlying USB device here.
 
-       _channel_off = _plugin_off = _parameter_off = 0;
+       memset (_channel_off, 0, sizeof (_channel_off));
+       _plugin_off = _parameter_off = 0;
        _blink_onoff = false;
        _shift_lock = false;
        _shift_pressed = 0;
+       _timer_divider = 0;
 
        start_midi_handling ();
        _ctrls.initialize ();
@@ -341,7 +361,7 @@ FaderPort8::connected ()
        tx_midi3 (0x90, 0x46, 0x00);
 
        send_session_state ();
-       assign_strips (true);
+       assign_strips ();
 
        Glib::RefPtr<Glib::TimeoutSource> blink_timer =
                Glib::TimeoutSource::create (200);
@@ -359,7 +379,7 @@ FaderPort8::disconnected ()
 {
        stop_midi_handling ();
        if (_device_active) {
-               for (uint8_t id = 0; id < 8; ++id) {
+               for (uint8_t id = 0; id < N_STRIPS; ++id) {
                        _ctrls.strip(id).unset_controllables ();
                }
                _ctrls.all_lights_off ();
@@ -484,7 +504,7 @@ FaderPort8::midi_input_handler (Glib::IOCondition ioc, boost::weak_ptr<ARDOUR::A
 #ifdef VERBOSE_DEBUG
                DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("data available on %1\n", boost::shared_ptr<MIDI::Port>(port)->name()));
 #endif
-               framepos_t now = session->engine().sample_time();
+               samplepos_t now = session->engine().sample_time();
                port->parse (now);
        }
 
@@ -558,13 +578,17 @@ void
 FaderPort8::controller_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
 {
        debug_2byte_msg ("CC", tb->controller_number, tb->value);
-       // encoder
-       // val Bit 7 = direction, Bits 0-6 = number of steps
+       /* encoder
+        *  val Bit 6 = direction, Bits 0-5 = number of steps
+        */
+       static const uint8_t dir_mask = 0x40;
+       static const uint8_t step_mask = 0x3f;
+
        if (tb->controller_number == 0x3c) {
-               encoder_navigate (tb->value & 0x40 ? true : false, tb->value & 0x3f);
+               encoder_navigate (tb->value & dir_mask ? true : false, tb->value & step_mask);
        }
        if (tb->controller_number == 0x10) {
-               encoder_parameter (tb->value & 0x40 ? true : false, tb->value & 0x3f);
+               encoder_parameter (tb->value & dir_mask ? true : false, tb->value & step_mask);
        }
 }
 
@@ -574,7 +598,12 @@ FaderPort8::note_on_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
        debug_2byte_msg ("ON", tb->note_number, tb->velocity);
 
        /* fader touch */
-       if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
+#ifdef FADERPORT16
+       static const uint8_t touch_id_uppper = 0x77;
+#else
+       static const uint8_t touch_id_uppper = 0x6f;
+#endif
+       if (tb->note_number >= 0x68 && tb->note_number <= touch_id_uppper) {
                _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
                return;
        }
@@ -613,7 +642,12 @@ FaderPort8::note_off_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb)
 {
        debug_2byte_msg ("OF", tb->note_number, tb->velocity);
 
-       if (tb->note_number >= 0x68 && tb->note_number <= 0x6f) {
+#ifdef FADERPORT16
+       static const uint8_t touch_id_uppper = 0x77;
+#else
+       static const uint8_t touch_id_uppper = 0x6f;
+#endif
+       if (tb->note_number >= 0x68 && tb->note_number <= touch_id_uppper) {
                // fader touch
                _ctrls.midi_touch (tb->note_number - 0x68, tb->velocity);
                return;
@@ -941,11 +975,13 @@ FaderPort8::assign_stripables (bool select_only)
        }
 
        int n_strips = strips.size();
-       _channel_off = std::min (_channel_off, n_strips - 8);
-       _channel_off = std::max (0, _channel_off);
+       int channel_off = get_channel_off (_ctrls.mix_mode ());
+       channel_off = std::min (channel_off, n_strips - N_STRIPS);
+       channel_off = std::max (0, channel_off);
+       set_channel_off (_ctrls.mix_mode (), channel_off);
 
        uint8_t id = 0;
-       int skip = _channel_off;
+       int skip = channel_off;
        for (StripableList::const_iterator s = strips.begin(); s != strips.end(); ++s) {
                if (skip > 0) {
                        --skip;
@@ -961,6 +997,13 @@ FaderPort8::assign_stripables (bool select_only)
                (*s)->presentation_info ().PropertyChanged.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
                                boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), _1), this);
 
+               if (boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*s)) {
+                       if (r->panner_shell()) {
+                               r->panner_shell()->Changed.connect (assigned_stripable_connections, MISSING_INVALIDATOR,
+                               boost::bind (&FaderPort8::notify_stripable_property_changed, this, boost::weak_ptr<Stripable> (*s), PropertyChange()), this);
+                       }
+               }
+
                if (select_only) {
                        /* used in send mode */
                        _ctrls.strip(id).set_text_line (3, (*s)->name (), true);
@@ -975,11 +1018,11 @@ FaderPort8::assign_stripables (bool select_only)
                 boost::function<void ()> cb (boost::bind (&FaderPort8::select_strip, this, boost::weak_ptr<Stripable> (*s)));
                 _ctrls.strip(id).set_select_cb (cb);
 
-               if (++id == 8) {
+               if (++id == N_STRIPS) {
                        break;
                }
        }
-       for (; id < 8; ++id) {
+       for (; id < N_STRIPS; ++id) {
                _ctrls.strip(id).unset_controllables (select_only ? (FP8Strip::CTRL_SELECT | FP8Strip::CTRL_TEXT3) : FP8Strip::CTRL_ALL);
                _ctrls.strip(id).set_periodic_display_mode (FP8Strip::Stripables);
        }
@@ -1109,7 +1152,7 @@ FaderPort8::assign_processor_ctrls ()
 
        int n_parameters = std::max (toggle_params.size(), slider_params.size());
 
-       _parameter_off = std::min (_parameter_off, n_parameters - 8);
+       _parameter_off = std::min (_parameter_off, n_parameters - N_STRIPS);
        _parameter_off = std::max (0, _parameter_off);
 
        uint8_t id = 0;
@@ -1133,13 +1176,13 @@ FaderPort8::assign_processor_ctrls ()
                        _ctrls.strip(id).set_select_controllable (toggle_params[i]->ac);
                        _ctrls.strip(id).set_text_line (3, toggle_params[i]->name, true);
                }
-               if (++id == 8) {
+               if (++id == N_STRIPS) {
                        break;
                }
        }
 
        // clear remaining
-       for (; id < 8; ++id) {
+       for (; id < N_STRIPS; ++id) {
                _ctrls.strip(id).unset_controllables ();
        }
 }
@@ -1159,7 +1202,7 @@ FaderPort8::assign_plugin_presets (boost::shared_ptr<PluginInsert> pi)
 
        int n_parameters = presets.size ();
 
-       _parameter_off = std::min (_parameter_off, n_parameters - 7);
+       _parameter_off = std::min (_parameter_off, n_parameters - (N_STRIPS - 1));
        _parameter_off = std::max (0, _parameter_off);
        Plugin::PresetRecord active = plugin->last_preset ();
 
@@ -1180,18 +1223,18 @@ FaderPort8::assign_plugin_presets (boost::shared_ptr<PluginInsert> pi)
                _ctrls.strip(id).set_text_line (0, label.substr (0, 9));
                _ctrls.strip(id).set_text_line (1, label.length () > 9 ? label.substr (9) : "");
                _ctrls.strip(id).set_text_line (3, "PRESET", true);
-               if (++id == 7) {
+               if (++id == (N_STRIPS - 1)) {
                        break;
                }
        }
 
        // clear remaining
-       for (; id < 7; ++id) {
+       for (; id < (N_STRIPS - 1); ++id) {
                _ctrls.strip(id).unset_controllables ();
        }
 
        // pin clear-preset to the last slot
-       assert (id == 7);
+       assert (id == (N_STRIPS - 1));
        _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT0 & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
         boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin_preset, this, SIZE_MAX));
        _ctrls.strip(id).set_select_cb (cb);
@@ -1416,7 +1459,7 @@ FaderPort8::spill_plugins ()
        }
 
        int n_plugins = procs.size();
-       int spillwidth = 8;
+       int spillwidth = N_STRIPS;
        bool have_well_known_eq = false;
        bool have_well_known_comp = false;
 
@@ -1474,7 +1517,7 @@ FaderPort8::spill_plugins ()
        }
 
        if (have_well_known_comp) {
-                       assert (id < 8);
+                       assert (id < N_STRIPS);
                 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -2));
                 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
                 _ctrls.strip(id).set_select_cb (cb);
@@ -1488,7 +1531,7 @@ FaderPort8::spill_plugins ()
                 ++id;
        }
        if (have_well_known_eq) {
-                       assert (id < 8);
+                       assert (id < N_STRIPS);
                 boost::function<void ()> cb (boost::bind (&FaderPort8::select_plugin, this, -1));
                 _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT & ~FP8Strip::CTRL_SELECT);
                 _ctrls.strip(id).set_select_cb (cb);
@@ -1501,7 +1544,7 @@ FaderPort8::spill_plugins ()
                 _ctrls.strip(id).set_text_line (3, "");
                 ++id;
        }
-       assert (id == 8);
+       assert (id == N_STRIPS);
 }
 
 /* ****************************************************************************
@@ -1531,7 +1574,7 @@ FaderPort8::assign_sends ()
 
        set_periodic_display_mode (FP8Strip::SendDisplay);
 
-       _plugin_off = std::min (_plugin_off, n_sends - 8);
+       _plugin_off = std::min (_plugin_off, n_sends - N_STRIPS);
        _plugin_off = std::max (0, _plugin_off);
 
        uint8_t id = 0;
@@ -1551,16 +1594,16 @@ FaderPort8::assign_sends ()
                _ctrls.strip(id).set_text_line (0, s->send_name (i));
                _ctrls.strip(id).set_mute_controllable (s->send_enable_controllable (i));
 
-               if (++id == 8) {
+               if (++id == N_STRIPS) {
                        break;
                }
        }
        // clear remaining
-       for (; id < 8; ++id) {
+       for (; id < N_STRIPS; ++id) {
                _ctrls.strip(id).unset_controllables (FP8Strip::CTRL_ALL & ~FP8Strip::CTRL_TEXT3 & ~FP8Strip::CTRL_SELECT);
        }
 #ifdef MIXBUS // master-assign on last solo
-       _ctrls.strip(7).set_solo_controllable (s->master_send_enable_controllable ());
+       _ctrls.strip(N_STRIPS - 1).set_solo_controllable (s->master_send_enable_controllable ());
 #endif
        /* set select buttons */
        assigned_stripable_connections.drop_connections ();
@@ -1573,12 +1616,8 @@ FaderPort8::assign_sends ()
  */
 
 void
-FaderPort8::assign_strips (bool reset_bank)
+FaderPort8::assign_strips ()
 {
-       if (reset_bank) {
-               _channel_off = 0;
-       }
-
        assigned_stripable_connections.drop_connections ();
        _assigned_strips.clear ();
 
@@ -1609,7 +1648,7 @@ FaderPort8::assign_strips (bool reset_bank)
 void
 FaderPort8::set_periodic_display_mode (FP8Strip::DisplayMode m)
 {
-       for (uint8_t id = 0; id < 8; ++id) {
+       for (uint8_t id = 0; id < N_STRIPS; ++id) {
                _ctrls.strip(id).set_periodic_display_mode (m);
        }
 }
@@ -1640,6 +1679,18 @@ FaderPort8::select_strip (boost::weak_ptr<Stripable> ws)
                return;
        }
 #if 1 /* single exclusive selection by default, toggle via shift */
+
+# if 1 /* selecting a selected strip -> move fader to unity */
+       if (s == first_selected_stripable () && !shift_mod ()) {
+               if (_ctrls.fader_mode () == ModeTrack) {
+                       boost::shared_ptr<AutomationControl> ac = s->gain_control ();
+                       ac->start_touch (ac->session().transport_sample());
+                       ac->set_value (ac->normal (), PBD::Controllable::UseGroup);
+               }
+               return;
+       }
+# endif
+
        if (shift_mod ()) {
                ToggleStripableSelection (s);
        } else {
@@ -1698,7 +1749,7 @@ FaderPort8::notify_fader_mode_changed ()
                        ARMButtonChange (false);
                        break;
        }
-       assign_strips (false);
+       assign_strips ();
        notify_automation_mode_changed ();
 }
 
@@ -1712,7 +1763,7 @@ FaderPort8::notify_stripable_added_or_removed ()
         *    - Properties::hidden
         *    - Properties::order
         */
-       assign_strips (false);
+       assign_strips ();
 }
 
 /* called from static PresentationInfo::Change */
@@ -1755,6 +1806,10 @@ FaderPort8::notify_stripable_property_changed (boost::weak_ptr<Stripable> ws, co
                _ctrls.strip(id).select_button ().set_color (s->presentation_info ().color());
        }
 
+       if (what_changed.empty ()) {
+               _ctrls.strip(id).set_stripable (s, _ctrls.fader_mode() == ModePan);
+       }
+
        if (what_changed.contains (Properties::name)) {
                switch (_ctrls.fader_mode ()) {
                        case ModeSend:
@@ -1848,16 +1903,18 @@ FaderPort8::move_selected_into_view ()
        }
        int off = std::distance (strips.begin(), it);
 
-       if (_channel_off <= off && off < _channel_off + 8) {
+       int channel_off = get_channel_off (_ctrls.mix_mode ());
+       if (channel_off <= off && off < channel_off + N_STRIPS) {
                return;
        }
 
-       if (_channel_off > off) {
-               _channel_off = off;
+       if (channel_off > off) {
+               channel_off = off;
        } else {
-               _channel_off = off - 7;
+               channel_off = off - (N_STRIPS - 1);
        }
-       assign_strips (false);
+       set_channel_off (_ctrls.mix_mode (), channel_off);
+       assign_strips ();
 }
 
 void
@@ -1906,18 +1963,18 @@ FaderPort8::select_prev_next (bool next)
 void
 FaderPort8::bank (bool down, bool page)
 {
-       int dt = page ? 8 : 1;
+       int dt = page ? N_STRIPS : 1;
        if (down) {
                dt *= -1;
        }
-       _channel_off += dt;
-       assign_strips (false);
+       set_channel_off (_ctrls.mix_mode (), get_channel_off (_ctrls.mix_mode ()) + dt);
+       assign_strips ();
 }
 
 void
 FaderPort8::bank_param (bool down, bool page)
 {
-       int dt = page ? 8 : 1;
+       int dt = page ? N_STRIPS : 1;
        if (down) {
                dt *= -1;
        }