use constructor syntax
[ardour.git] / libs / surfaces / faderport8 / faderport8.cc
index bac6f13e03463ec9460649ab54b972f0e860aa24..c3e4a20a9a4192e4315d6533264d8c9ce9335d8b 100644 (file)
@@ -91,7 +91,6 @@ FaderPort8::FaderPort8 (Session& s)
        , _connection_state (ConnectionState (0))
        , _device_active (false)
        , _ctrls (*this)
-       , _channel_off (0)
        , _plugin_off (0)
        , _parameter_off (0)
        , _show_presets (false)
@@ -100,6 +99,8 @@ FaderPort8::FaderPort8 (Session& s)
        , _shift_lock (false)
        , _shift_pressed (0)
        , gui (0)
+       , _link_enabled (false)
+       , _link_locked (false)
        , _clock_mode (1)
        , _scribble_mode (2)
        , _two_line_text (false)
@@ -140,36 +141,36 @@ 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 ()
 {
-       cerr << "~FP8\n";
-       disconnected ();
-       close ();
+       /* this will be called from the main UI thread. during Session::destroy().
+        * There can be concurrent activity from BaseUI::main_thread -> AsyncMIDIPort
+        * -> MIDI::Parser::signal -> ... to any of the midi_connections
+        *
+        * stop event loop early and join thread */
+       stop ();
 
        if (_input_port) {
                DEBUG_TRACE (DEBUG::FaderPort8, string_compose ("unregistering input port %1\n", boost::shared_ptr<ARDOUR::Port>(_input_port)->name()));
-               _input_port->disconnect_all ();
-               _input_port->drain (5000, 50000);
-               _input_port->clear ();
+               Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
                AudioEngine::instance()->unregister_port (_input_port);
                _input_port.reset ();
        }
 
+       disconnected (); // zero faders, turn lights off, clear strips
+
        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 ();
        }
 
        tear_down_gui ();
-
-       /* stop event loop */
-       DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
-       BaseUI::quit ();
 }
 
 /* ****************************************************************************
@@ -194,32 +195,27 @@ FaderPort8::do_request (FaderPort8Request* req)
                call_slot (MISSING_INVALIDATOR, req->the_slot);
        } else if (req->type == Quit) {
                stop ();
+               disconnected ();
        }
 }
 
-int
+void
 FaderPort8::stop ()
 {
+       DEBUG_TRACE (DEBUG::FaderPort8, "BaseUI::quit ()\n");
        BaseUI::quit ();
-       return 0;
+       close (); // drop references, disconnect from session signals
 }
 
 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
@@ -234,7 +230,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,
@@ -275,8 +271,7 @@ FaderPort8::set_active (bool yn)
                BaseUI::run ();
                connect_session_signals ();
        } else {
-               BaseUI::quit ();
-               close ();
+               stop ();
        }
 
        ControlProtocol::set_active (yn);
@@ -322,7 +317,8 @@ 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;
@@ -340,7 +336,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);
@@ -483,7 +479,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);
        }
 
@@ -707,6 +703,7 @@ FaderPort8::get_state ()
 
        node.set_property (X_("clock-mode"), _clock_mode);
        node.set_property (X_("scribble-mode"), _scribble_mode);
+       node.set_property (X_("two-line-text"), _two_line_text);
 
        for (UserActionMap::const_iterator i = _user_action_map.begin (); i != _user_action_map.end (); ++i) {
                if (i->second.empty()) {
@@ -760,6 +757,7 @@ FaderPort8::set_state (const XMLNode& node, int version)
 
        node.get_property (X_("clock-mode"), _clock_mode);
        node.get_property (X_("scribble-mode"), _scribble_mode);
+       node.get_property (X_("two-line-text"), _two_line_text);
 
        _user_action_map.clear ();
        // TODO: When re-loading state w/o surface re-init becomes possible,
@@ -938,11 +936,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 - 8);
+       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;
@@ -982,6 +982,78 @@ FaderPort8::assign_stripables (bool select_only)
        }
 }
 
+/* ****************************************************************************
+ * Control Link/Lock
+ */
+
+void
+FaderPort8::unlock_link (bool drop)
+{
+       link_locked_connection.disconnect ();
+
+       if (drop) {
+               stop_link (); // calls back here with drop = false
+               return;
+       }
+
+       _link_locked = false;
+
+       if (_link_enabled) {
+               assert (_ctrls.button (FP8Controls::BtnLink).is_active ());
+               _link_control.reset ();
+               start_link (); // re-connect & update LED colors
+       } else {
+               _ctrls.button (FP8Controls::BtnLink).set_active (false);
+               _ctrls.button (FP8Controls::BtnLink).set_color (0x888888ff);
+               _ctrls.button (FP8Controls::BtnLock).set_active (false);
+               _ctrls.button (FP8Controls::BtnLock).set_color (0x888888ff);
+       }
+}
+
+void
+FaderPort8::lock_link ()
+{
+       boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl> (_link_control.lock ());
+       if (!ac) {
+               return;
+       }
+       ac->DropReferences.connect (link_locked_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::unlock_link, this, true), this);
+
+       // stop watching for focus events
+       link_connection.disconnect ();
+
+       _link_locked = true;
+
+       _ctrls.button (FP8Controls::BtnLock).set_color (0x00ff00ff);
+       _ctrls.button (FP8Controls::BtnLink).set_color (0x00ff00ff);
+}
+
+void
+FaderPort8::stop_link ()
+{
+       if (!_link_enabled) {
+               return;
+       }
+       link_connection.disconnect ();
+       _link_control.reset ();
+       _link_enabled = false;
+       unlock_link (); // also updates button colors
+}
+
+void
+FaderPort8::start_link ()
+{
+       assert (!_link_locked);
+
+       _link_enabled = true;
+       _ctrls.button (FP8Controls::BtnLink).set_active (true);
+       _ctrls.button (FP8Controls::BtnLock).set_active (true);
+       nofity_focus_control (_link_control); // update BtnLink, BtnLock colors
+
+       PBD::Controllable::GUIFocusChanged.connect (link_connection, MISSING_INVALIDATOR, boost::bind (&FaderPort8::nofity_focus_control, this, _1), this);
+}
+
+
 /* ****************************************************************************
  * Plugin selection and parameters
  */
@@ -1498,12 +1570,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 ();
 
@@ -1565,6 +1633,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 {
@@ -1617,12 +1697,13 @@ FaderPort8::notify_fader_mode_changed ()
                case ModeSend:
                        _plugin_off = 0;
                        _parameter_off = 0;
+                       stop_link ();
                        // force unset rec-arm button, see also FaderPort8::button_arm
                        _ctrls.button (FP8Controls::BtnArm).set_active (false);
                        ARMButtonChange (false);
                        break;
        }
-       assign_strips (false);
+       assign_strips ();
        notify_automation_mode_changed ();
 }
 
@@ -1636,7 +1717,7 @@ FaderPort8::notify_stripable_added_or_removed ()
         *    - Properties::hidden
         *    - Properties::order
         */
-       assign_strips (false);
+       assign_strips ();
 }
 
 /* called from static PresentationInfo::Change */
@@ -1772,16 +1853,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 + 8) {
                return;
        }
 
-       if (_channel_off > off) {
-               _channel_off = off;
+       if (channel_off > off) {
+               channel_off = off;
        } else {
-               _channel_off = off - 7;
+               channel_off = off - 7;
        }
-       assign_strips (false);
+       set_channel_off (_ctrls.mix_mode (), channel_off);
+       assign_strips ();
 }
 
 void
@@ -1834,8 +1917,8 @@ FaderPort8::bank (bool down, bool page)
        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