Merge branch 'windows+cc' into cairocanvas
[ardour.git] / libs / surfaces / mackie / surface.cc
index 6fb94cba93549a9678e5655e8fa9150ad73ed8c3..694c73b1bcb638f49bb3772bceeb6a55159f33cb 100644 (file)
@@ -1,3 +1,22 @@
+/*
+    Copyright (C) 2012 Paul Davis 
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #include <sstream>
 #include <iomanip>
 #include <iostream>
@@ -5,7 +24,6 @@
 #include <cmath>
 
 #include "midi++/port.h"
-#include "midi++/manager.h"
 
 #include "ardour/automation_control.h"
 #include "ardour/debug.h"
@@ -63,20 +81,29 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
        , _active (false)
        , _connected (false)
        , _jog_wheel (0)
+       , _master_fader (0)
+       , _last_master_gain_written (-0.0f)
 {
-       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init\n");
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface init\n");
        
-       _port = new SurfacePort (*this);
+       try {
+               _port = new SurfacePort (*this);
+       } catch (...) {
+               throw failed_constructor ();
+       }
 
        /* only the first Surface object has global controls */
 
        if (_number == 0) {
+               DEBUG_TRACE (DEBUG::MackieControl, "Surface is first. Might have global controls.\n");
                if (_mcp.device_info().has_global_controls()) {
                        init_controls ();
+                       DEBUG_TRACE (DEBUG::MackieControl, "init_controls done\n");
                }
 
                if (_mcp.device_info().has_master_fader()) {
                        setup_master ();
+                       DEBUG_TRACE (DEBUG::MackieControl, "setup_master done\n");
                }
        }
 
@@ -84,16 +111,17 @@ Surface::Surface (MackieControlProtocol& mcp, const std::string& device_name, ui
        
        if (n) {
                init_strips (n);
+               DEBUG_TRACE (DEBUG::MackieControl, "init_strips done\n");
        }
        
        connect_to_signals ();
 
-       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init finish\n");
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::Surface done\n");
 }
 
 Surface::~Surface ()
 {
-       DEBUG_TRACE (DEBUG::MackieControl, "Surface: destructor\n");
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface init\n");
 
        zero_all ();
 
@@ -109,6 +137,41 @@ Surface::~Surface ()
        
        delete _jog_wheel;
        delete _port;
+
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::~Surface done\n");
+}
+
+XMLNode&
+Surface::get_state()
+{
+       char buf[64];
+       snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
+       XMLNode* node = new XMLNode (buf);
+
+       node->add_child_nocopy (_port->get_state());
+
+       return *node;
+}
+
+int
+Surface::set_state (const XMLNode& node, int version)
+{
+       char buf[64];
+       snprintf (buf, sizeof (buf), X_("surface-%u"), _number);
+       XMLNode* mynode = node.child (buf);
+
+       if (!mynode) {
+               return 0;
+       }
+
+       XMLNode* portnode = mynode->child (X_("Port"));
+       if (portnode) {
+               if (_port->set_state (*portnode, version)) {
+                       return -1;
+               }
+       }
+
+       return 0;
 }
 
 const MidiByteArray& 
@@ -136,24 +199,28 @@ void
 Surface::init_controls()
 {
        Group* group;
-
+       
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating groups\n");
        groups["assignment"] = new Group  ("assignment");
        groups["automation"] = new Group  ("automation");
        groups["bank"] = new Group  ("bank");
        groups["cursor"] = new Group  ("cursor");
        groups["display"] = new Group  ("display");
-       groups["functions"] = new Group  ("functions");
+       groups["function select"] = new Group  ("function select");
+       groups["global view"] = new Group ("global view");
+       groups["master"] = new Group ("master");
        groups["modifiers"] = new Group  ("modifiers");
        groups["none"] = new Group  ("none");
        groups["transport"] = new Group  ("transport");
        groups["user"] = new Group  ("user");
-       groups["master"] = new Group ("master");
-       groups["view"] = new Group ("view");
+       groups["utilities"] = new Group  ("utilities");
                
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating jog wheel\n");
        if (_mcp.device_info().has_jog_wheel()) {
                _jog_wheel = new Mackie::JogWheel (_mcp);
        }
 
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: creating global controls\n");
        for (uint32_t n = 0; mackie_global_controls[n].name[0]; ++n) {
                group = groups[mackie_global_controls[n].group_name];
                Control* control = mackie_global_controls[n].factory (*this, mackie_global_controls[n].id, mackie_global_controls[n].name, *group);
@@ -161,7 +228,7 @@ Surface::init_controls()
        }
 
        /* add global buttons */
-
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::init_controls: adding global buttons\n");
        const map<Button::ID,GlobalButtonInfo>& global_buttons (_mcp.device_info().global_buttons());
 
        for (map<Button::ID,GlobalButtonInfo>::const_iterator b = global_buttons.begin(); b != global_buttons.end(); ++b){
@@ -191,8 +258,6 @@ Surface::init_strips (uint32_t n)
 void
 Surface::setup_master ()
 {
-       _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, 8, "master", *groups["master"]));
-       
        boost::shared_ptr<Route> m;
        
        if ((m = _mcp.get_session().monitor_out()) == 0) {
@@ -202,17 +267,49 @@ Surface::setup_master ()
        if (!m) {
                return;
        }
+
+       _master_fader = dynamic_cast<Fader*> (Fader::factory (*this, _mcp.device_info().strip_cnt(), "master", *groups["master"]));
        
        _master_fader->set_control (m->gain_control());
        m->gain_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&Surface::master_gain_changed, this), ui_context());
+
+       Groups::iterator group_it;
+       group_it = groups.find("master");
+
+       DeviceInfo device_info = _mcp.device_info();
+       GlobalButtonInfo master_button = device_info.get_global_button(Button::MasterFaderTouch);
+       Button* bb = dynamic_cast<Button*> (Button::factory (
+               *this,
+               Button::MasterFaderTouch,
+               master_button.id,
+               master_button.label,
+               *(group_it->second)
+));
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("surface %1 Master Fader new button BID %2 id %3\n",
+               number(), Button::MasterFaderTouch, bb->id()));
 }
 
 void
 Surface::master_gain_changed ()
 {
+       if (!_master_fader) {
+               return;
+       }
+
        boost::shared_ptr<AutomationControl> ac = _master_fader->control();
-       float pos = ac->internal_to_interface (ac->get_value());
-       _port->write (_master_fader->set_position (pos));
+       if (!ac) {
+               return;
+       }
+
+       float normalized_position = ac->internal_to_interface (ac->get_value());
+       if (normalized_position == _last_master_gain_written) {
+               return;
+       }
+
+       DEBUG_TRACE (DEBUG::MackieControl, "Surface::master_gain_changed: updating surface master fader\n");
+
+       _port->write (_master_fader->set_position (normalized_position));
+       _last_master_gain_written = normalized_position;
 }
 
 float 
@@ -275,14 +372,12 @@ Surface::connect_to_signals ()
                /* Button messages are NoteOn. libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
                p->note_off.connect_same_thread (*this, boost::bind (&Surface::handle_midi_note_on_message, this, _1, _2));
                /* Fader messages are Pitchbend */
-               p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 0U));
-               p->channel_pitchbend[1].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 1U));
-               p->channel_pitchbend[2].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 2U));
-               p->channel_pitchbend[3].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 3U));
-               p->channel_pitchbend[4].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 4U));
-               p->channel_pitchbend[5].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 5U));
-               p->channel_pitchbend[6].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 6U));
-               p->channel_pitchbend[7].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, 7U));
+               uint32_t i;
+               for (i = 0; i < _mcp.device_info().strip_cnt(); i++) {
+                       p->channel_pitchbend[i].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, i));
+               }
+               // Master fader
+               p->channel_pitchbend[_mcp.device_info().strip_cnt()].connect_same_thread (*this, boost::bind (&Surface::handle_midi_pitchbend_message, this, _1, _2, _mcp.device_info().strip_cnt()));
                
                _connected = true;
        }
@@ -300,7 +395,7 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin
         */
 
 
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi pitchbend on port %3, fader = %1 value = %2\n", 
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_pitchbend_message on port %3, fader = %1 value = %2\n",
                                                           fader_id, pb, _number));
        
        if (_mcp.device_info().no_handshake()) {
@@ -315,6 +410,7 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin
                if (strip) {
                        strip->handle_fader (*fader, pos);
                } else {
+                       DEBUG_TRACE (DEBUG::MackieControl, "Handling master fader\n");
                        /* master fader */
                        fader->set_value (pos); // alter master gain
                        _port->write (fader->set_position (pos)); // write back value (required for servo)
@@ -327,7 +423,7 @@ Surface::handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t pb, uin
 void 
 Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
 {
-       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("SurfacePort::handle_note_on %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
+       DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Surface::handle_midi_note_on_message %1 = %2\n", (int) ev->note_number, (int) ev->velocity));
        
        if (_mcp.device_info().no_handshake()) {
                turn_it_on ();
@@ -348,7 +444,7 @@ Surface::handle_midi_note_on_message (MIDI::Parser &, MIDI::EventTwoBytes* ev)
                        _mcp.handle_button_event (*this, *button, ev->velocity > 64 ? press : release);
                }
        } else {
-               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", ev->note_number));
+               DEBUG_TRACE (DEBUG::MackieControl, string_compose ("no button found for %1\n", (int) ev->note_number));
        }
 }
 
@@ -508,20 +604,21 @@ Surface::host_connection_confirmation (const MidiByteArray & bytes)
 void
 Surface::turn_it_on ()
 {
-       if (!_active) {
-               _active = true;
-               zero_controls ();
-               for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
-                       (*s)->notify_all ();
-               }
-               update_view_mode_display ();
+       if (_active) {
+               return;
        }
-}
 
-void 
-Surface::handle_port_inactive (SurfacePort*)
-{
-       _active = false;
+       _active = true;
+
+       for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
+               (*s)->notify_all ();
+       }
+
+       update_view_mode_display ();
+
+       if (_mcp.device_info ().has_global_controls ()) {
+               _mcp.update_global_button (Button::Read, _mcp.metering_active ());
+       }
 }
 
 void 
@@ -578,13 +675,13 @@ Surface::zero_all ()
        }
        
        if (_mcp.device_info().has_two_character_display()) {
-               show_two_char_display (string (2, ' '), string (2, '.'));
+               show_two_char_display (string (2, '0'), string (2, ' '));
        }
 
-       if (_mcp.device_info().has_master_fader ()) {
+       if (_mcp.device_info().has_master_fader () && _master_fader) {
                _port->write (_master_fader->zero ());
        }
-       
+
        // zero all strips
        for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
                (*it)->zero();
@@ -596,7 +693,7 @@ Surface::zero_all ()
 void
 Surface::zero_controls ()
 {
-       if (_stype != mcu || !_mcp.device_info().has_global_controls()) {
+       if (!_mcp.device_info().has_global_controls()) {
                return;
        }
 
@@ -613,11 +710,14 @@ Surface::zero_controls ()
 
        // and the led ring for the master strip
        blank_jog_ring ();
+
+       _last_master_gain_written = 0.0f;
 }
 
 void
 Surface::periodic (uint64_t now_usecs)
 {
+       master_gain_changed();
        for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
                (*s)->periodic (now_usecs);
        }
@@ -628,6 +728,8 @@ Surface::write (const MidiByteArray& data)
 {
        if (_active) {
                _port->write (data);
+       } else {
+               DEBUG_TRACE (DEBUG::MackieControl, "surface not active, write ignored\n");
        }
 }
 
@@ -717,18 +819,18 @@ Surface::display_timecode (const std::string & timecode, const std::string & las
        while  (local_timecode.length() < 10) { 
                local_timecode += " ";
        }
-               
-       // find the suffix of local_timecode that differs from last_timecode
-       std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
        
-       int position = 0x40;
-
-       // translate characters. These are sent in reverse order of display
-       // hence the reverse iterators
-       string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
-       for  (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
-               MidiByteArray retval (2, 0xb0, position++);
-               retval << translate_seven_segment (*it);
+       // translate characters.
+       // Only the characters that actually changed are sent.
+       int position = 0x3f;
+       int i;
+       for (i = local_timecode.length () - 1; i >= 0; i--) {
+               position++;
+               if (local_timecode[i] == last_timecode[i]) {
+                       continue;
+               }
+               MidiByteArray retval (2, 0xb0, position);
+               retval << translate_seven_segment (local_timecode[i]);
                _port->write (retval);
        }
 }