Add a GUI to set the number of active extenders for the Mackie control surface.
[ardour.git] / libs / surfaces / mackie / mackie_control_protocol.cc
index 3fa1686d9b568006042051a8a9232d1931a7e3e4..5dddb186139c7098af061b2441259f3087883f4c 100644 (file)
@@ -16,6 +16,7 @@
        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
+#include <fcntl.h>
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -23,7 +24,6 @@
 #include <vector>
 #include <iomanip>
 
-#define __STDC_FORMAT_MACROS
 #include <inttypes.h>
 #include <float.h>
 #include <sys/time.h>
 #include "ardour/location.h"
 #include "ardour/midi_ui.h"
 #include "ardour/panner.h"
+#include "ardour/panner_shell.h"
 #include "ardour/route.h"
 #include "ardour/session.h"
 #include "ardour/tempo.h"
 #include "ardour/types.h"
+#include "ardour/audioengine.h"
 
 #include "mackie_control_protocol.h"
 
@@ -66,8 +68,6 @@ using namespace std;
 using namespace Mackie;
 using namespace PBD;
 
-using boost::shared_ptr;
-
 #include "i18n.h"
 
 MackieMidiBuilder builder;
@@ -81,6 +81,9 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
        , _surface (0)
        , _jog_wheel (*this)
        , _timecode_type (ARDOUR::AnyTime::BBT)
+       , _input_bundle (new ARDOUR::Bundle (_("Mackie Control In"), true))
+       , _output_bundle (new ARDOUR::Bundle (_("Mackie Control Out"), false))
+       , _gui (0)
 {
        DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n");
 }
@@ -184,7 +187,7 @@ MackieControlProtocol::port_for_id (uint32_t index)
 // predicate for sort call in get_sorted_routes
 struct RouteByRemoteId
 {
-       bool operator () (const shared_ptr<Route> & a, const shared_ptr<Route> & b) const
+       bool operator () (const boost::shared_ptr<Route> & a, const boost::shared_ptr<Route> & b) const
        {
                return a->remote_control_id() < b->remote_control_id();
        }
@@ -275,6 +278,8 @@ MackieControlProtocol::switch_banks (int initial)
                for (; it != end && it != sorted.end(); ++it, ++i)
                {
                        boost::shared_ptr<Route> route = *it;
+
+                       assert (surface().strips[i]);
                        Strip & strip = *surface().strips[i];
 
                        DEBUG_TRACE (DEBUG::MackieControl, string_compose ("remote id %1 connecting %2 to %3 with port %4\n", 
@@ -392,7 +397,7 @@ MackieControlProtocol::set_active (bool yn)
 }
 
 bool 
-MackieControlProtocol::handle_strip_button (Control & control, ButtonState bs, boost::shared_ptr<Route> route)
+MackieControlProtocol::handle_strip_button (SurfacePort & port, Control & control, ButtonState bs, boost::shared_ptr<Route> route)
 {
        bool state = false;
 
@@ -401,7 +406,7 @@ MackieControlProtocol::handle_strip_button (Control & control, ButtonState bs, b
                if (control.name() == "recenable")
                {
                        state = !route->record_enabled();
-                       route->set_record_enable (state, this);
+                       route->set_record_enabled (state, this);
                }
                else if (control.name() == "mute")
                {
@@ -428,7 +433,14 @@ MackieControlProtocol::handle_strip_button (Control & control, ButtonState bs, b
        if (control.name() == "fader_touch")
        {
                state = bs == press;
-               control.strip().gain().in_use (state);
+               control.strip().gain().set_in_use (state);
+
+               if (ARDOUR::Config->get_mackie_emulation() == "bcf" && state) {
+                       /* BCF faders don't support touch, so add a timeout to reset
+                          their `in_use' state.
+                       */
+                       port.add_in_use_timeout (control.strip().gain(), &control.strip().fader_touch());
+               }
        }
 
        return state;
@@ -521,7 +533,7 @@ MackieControlProtocol::update_surface()
 
  boost::shared_ptr<Route> mr = master_route ();
  if (mr) {
- master_route_signal = shared_ptr<RouteSignal> (new RouteSignal (mr, *this, master_strip(), mcu_port()));
+ master_route_signal = boost::shared_ptr<RouteSignal> (new RouteSignal (mr, *this, master_strip(), mcu_port()));
  // update strip from route
  master_route_signal->notify_all();
  }
@@ -561,57 +573,68 @@ MackieControlProtocol::connect_session_signals()
 }
 
 void 
-MackieControlProtocol::add_port (MIDI::Port & midi_port, int number)
-{
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1,%2,%3\n", midi_port.name(), midi_port.device(), midi_port.type()));
-
-       if (string (midi_port.device()) == string ("ardour") && midi_port.type() == MIDI::Port::ALSA_Sequencer) {
-               throw MackieControlException ("The Mackie MCU driver will not use a port with device=ardour");
-       } else if (midi_port.type() == MIDI::Port::ALSA_Sequencer) {
-               throw MackieControlException ("alsa/sequencer ports don't work with the Mackie MCU driver right now");
-       } else {
-               MackiePort * sport = new MackiePort (*this, midi_port, number);
-               _ports.push_back (sport);
-               
-               sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
-               sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
-               sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
-       }
+MackieControlProtocol::add_port (MIDI::Port & midi_input_port, MIDI::Port & midi_output_port, int number)
+{
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1 %2\n", midi_input_port.name(), midi_output_port.name()));
+
+       MackiePort * sport = new MackiePort (*this, midi_input_port, midi_output_port, number);
+       _ports.push_back (sport);
+       
+       sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
+       sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
+       sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
+
+       _input_bundle->add_channel (
+               midi_input_port.name(),
+               ARDOUR::DataType::MIDI,
+               session->engine().make_port_name_non_relative (midi_input_port.name())
+               );
+       
+       _output_bundle->add_channel (
+               midi_output_port.name(),
+               ARDOUR::DataType::MIDI,
+               session->engine().make_port_name_non_relative (midi_output_port.name())
+               );
 }
 
 void 
 MackieControlProtocol::create_ports()
 {
        MIDI::Manager * mm = MIDI::Manager::instance();
-       MIDI::Port * midi_port = mm->port (default_port_name);
+       MIDI::Port * midi_input_port = mm->add_port (
+               new MIDI::Port (string_compose (_("%1 in"), default_port_name), MIDI::Port::IsInput, session->engine().jack())
+               );
+       MIDI::Port * midi_output_port = mm->add_port (
+               new MIDI::Port (string_compose (_("%1 out"), default_port_name), MIDI::Port::IsOutput, session->engine().jack())
+               );
 
-       // open main port               
+       /* Create main port */
 
-       if (midi_port == 0) {
+       if (!midi_input_port->ok() || !midi_output_port->ok()) {
                ostringstream os;
-               os << string_compose (_("no MIDI port named \"%1\" exists - Mackie control disabled"), default_port_name);
+               os << _("Mackie control MIDI ports could not be created; Mackie control disabled");
                error << os.str() << endmsg;
                throw MackieControlException (os.str());
        }
 
-       add_port (*midi_port, 0);
+       add_port (*midi_input_port, *midi_output_port, 0);
 
-       // open extender ports. Up to 9. Should be enough.
-       // could also use mm->get_midi_ports()
+       /* Create extender ports */
 
-       string ext_port_base = "mcu_xt_";
-
-       for (int index = 1; index <= 9; ++index) {
-               ostringstream os;
-               os << ext_port_base << index;
-               MIDI::Port * midi_port = mm->port (os.str());
-               if (midi_port != 0) {
-                       add_port (*midi_port, index);
+       for (int index = 1; index <= Config->get_mackie_extenders(); ++index) {
+               MIDI::Port * midi_input_port = mm->add_port (
+                       new MIDI::Port (string_compose (_("mcu_xt_%1 in"), index), MIDI::Port::IsInput, session->engine().jack())
+                       );
+               MIDI::Port * midi_output_port = mm->add_port (
+                       new MIDI::Port (string_compose (_("mcu_xt_%1 out"), index), MIDI::Port::IsOutput, session->engine().jack())
+                       );
+               if (midi_input_port->ok() && midi_output_port->ok()) {
+                       add_port (*midi_input_port, *midi_output_port, index);
                }
        }
 }
 
-shared_ptr<Route> 
+boost::shared_ptr<Route> 
 MackieControlProtocol::master_route()
 {
        return session->master_out ();
@@ -784,7 +807,12 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
                        // at which point the fader should just reset itself
                        if (route != 0)
                        {
-                               route->gain_control()->set_value (state.pos);
+                               route->gain_control()->set_value (slider_position_to_gain (state.pos));
+
+                               if (ARDOUR::Config->get_mackie_emulation() == "bcf") {
+                                       /* reset the timeout while we're still moving the fader */
+                                       port.add_in_use_timeout (control, control.in_use_touch_control);
+                               }
 
                                // must echo bytes back to slider now, because
                                // the notifier only works if the fader is not being
@@ -797,7 +825,7 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
                        if (control.group().is_strip()) {
                                // strips
                                if (route != 0) {
-                                       handle_strip_button (control, state.button_state, route);
+                                       handle_strip_button (port, control, state.button_state, route);
                                } else {
                                        // no route so always switch the light off
                                        // because no signals will be emitted by a non-route
@@ -806,7 +834,7 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
                        } else if (control.group().is_master()) {
                                // master fader touch
                                if (route != 0) {
-                                       handle_strip_button (control, state.button_state, route);
+                                       handle_strip_button (port, control, state.button_state, route);
                                }
                        } else {
                                // handle all non-strip buttons
@@ -817,23 +845,17 @@ MackieControlProtocol::handle_control_event (SurfacePort & port, Control & contr
                // pot (jog wheel, external control)
                case Control::type_pot:
                        if (control.group().is_strip()) {
-                               if (route != 0 && route->panner())
-                               {
+                               if (route) {
+                                        boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
                                        // pan for mono input routes, or stereo linked panners
-                                       if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
-                                       {
-                                               // assume pan for now
-                                               float xpos;
-                                               route->panner()->streampanner (0).get_effective_position (xpos);
-
-                                               // calculate new value, and trim
-                                               xpos += state.delta * state.sign;
-                                               if (xpos > 1.0)
-                                                       xpos = 1.0;
-                                               else if (xpos < 0.0)
-                                                       xpos = 0.0;
-
-                                               route->panner()->streampanner (0).set_position (xpos);
+                                        if (panner) {
+                                               double p = panner->position ();
+                                                
+                                               // calculate new value, and adjust
+                                               p += state.delta * state.sign;
+                                               p = min (1.0, p);
+                                               p = max (0.0, p);
+                                               panner->set_position (p);
                                        }
                                }
                                else
@@ -930,7 +952,7 @@ MackieControlProtocol::notify_gain_changed (RouteSignal * route_signal, bool for
                Fader & fader = route_signal->strip().gain();
                if (!fader.in_use())
                {
-                       float gain_value = route_signal->route()->gain_control()->get_value();
+                       float gain_value = gain_to_slider_position (route_signal->route()->gain_control()->get_value());
                        // check that something has actually changed
                        if (force_update || gain_value != route_signal->last_gain_written())
                        {
@@ -955,7 +977,9 @@ MackieControlProtocol::notify_property_changed (const PropertyChange& what_chang
        try
        {
                Strip & strip = route_signal->strip();
-               if (!strip.is_master())
+               
+               /* XXX: not sure about this check to only display stuff for strips of index < 8 */
+               if (!strip.is_master() && strip.index() < 8)
                {
                        string line1;
                        string fullname = route_signal->route()->name();
@@ -987,10 +1011,8 @@ MackieControlProtocol::notify_panner_changed (RouteSignal * route_signal, bool f
        {
                Pot & pot = route_signal->strip().vpot();
                boost::shared_ptr<Panner> panner = route_signal->route()->panner();
-               if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
-               {
-                       float pos;
-                       route_signal->route()->panner()->streampanner(0).get_effective_position (pos);
+               if (panner) {
+                       double pos = panner->position ();
 
                        // cache the MidiByteArray here, because the mackie led control is much lower
                        // resolution than the panner control. So we save lots of byte
@@ -1035,9 +1057,9 @@ MackieControlProtocol::update_automation (RouteSignal & rs)
 }
 
 string 
-MackieControlProtocol::format_bbt_timecode (nframes_t now_frame)
+MackieControlProtocol::format_bbt_timecode (framepos_t now_frame)
 {
-       BBT_Time bbt_time;
+       Timecode::BBT_Time bbt_time;
        session->bbt_time (now_frame, bbt_time);
 
        // According to the Logic docs
@@ -1055,8 +1077,8 @@ MackieControlProtocol::format_bbt_timecode (nframes_t now_frame)
                subdiv = 3;
        }
 
-       uint32_t subdivisions = bbt_time.ticks / uint32_t (Meter::ticks_per_beat / subdiv);
-       uint32_t ticks = bbt_time.ticks % uint32_t (Meter::ticks_per_beat / subdiv);
+       uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
+       uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
 
        os << setw(2) << setfill('0') << subdivisions + 1;
        os << setw(3) << setfill('0') << ticks;
@@ -1065,7 +1087,7 @@ MackieControlProtocol::format_bbt_timecode (nframes_t now_frame)
 }
 
 string 
-MackieControlProtocol::format_timecode_timecode (nframes_t now_frame)
+MackieControlProtocol::format_timecode_timecode (framepos_t now_frame)
 {
        Timecode::Time timecode;
        session->timecode_time (now_frame, timecode);
@@ -1088,7 +1110,7 @@ MackieControlProtocol::update_timecode_display()
        if (surface().has_timecode_display())
        {
                // do assignment here so current_frame is fixed
-               nframes_t current_frame = session->transport_frame();
+               framepos_t current_frame = session->transport_frame();
                string timecode;
 
                switch (_timecode_type)
@@ -1484,9 +1506,11 @@ MackieControlProtocol::left_press (Button &)
        if (sorted.size() > route_table.size())
        {
                int new_initial = _current_initial_bank - route_table.size();
-               if (new_initial < 0) new_initial = 0;
-               if (new_initial != int (_current_initial_bank))
-               {
+               if (new_initial < 0) {
+                       new_initial = 0;
+               }
+               
+               if (new_initial != int (_current_initial_bank)) {
                        session->set_dirty();
                        switch_banks (new_initial);
                }
@@ -1511,7 +1535,11 @@ MackieControlProtocol::right_press (Button &)
        Sorted sorted = get_sorted_routes();
        if (sorted.size() > route_table.size()) {
                uint32_t delta = sorted.size() - (route_table.size() + _current_initial_bank);
-               if (delta > route_table.size()) delta = route_table.size();
+
+               if (delta > route_table.size()) {
+                       delta = route_table.size();
+               }
+               
                if (delta > 0) {
                        session->set_dirty();
                        switch_banks (_current_initial_bank + delta);
@@ -1579,9 +1607,9 @@ MackieControlProtocol::marker_press (Button &)
 {
        // cut'n'paste from LocationUI::add_new_location()
        string markername;
-       nframes_t where = session->audible_frame();
+       framepos_t where = session->audible_frame();
        session->locations()->next_available_name(markername,"mcu");
-       Location *location = new Location (where, where, markername, Location::IsMark);
+       Location *location = new Location (*session, where, where, markername, Location::IsMark);
        session->begin_reversible_command (_("add marker"));
        XMLNode &before = session->locations()->get_state();
        session->locations()->add (location, true);
@@ -1701,3 +1729,11 @@ MackieControlProtocol::timecode_beats_release (Button &)
        return off;
 }
 
+list<boost::shared_ptr<ARDOUR::Bundle> >
+MackieControlProtocol::bundles ()
+{
+       list<boost::shared_ptr<ARDOUR::Bundle> > b;
+       b.push_back (_input_bundle);
+       b.push_back (_output_bundle);
+       return b;
+}