summaryrefslogtreecommitdiff
path: root/libs/surfaces
diff options
context:
space:
mode:
authorJohn Anderson <ardour@semiosix.com>2007-12-30 08:31:10 +0000
committerJohn Anderson <ardour@semiosix.com>2007-12-30 08:31:10 +0000
commit19bf004a79d57a9763dcda51f38132f69b050002 (patch)
treeac665dff9cca410fe39f40cce7e8bf414564da18 /libs/surfaces
parent9c751e3c1c3a33859fc6cc58f741307a749e6141 (diff)
Make Mackie timecode display work. Not fully tested because I don't have a Mackie.
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2818 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/surfaces')
-rw-r--r--libs/surfaces/mackie/TODO3
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.cc156
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol.h32
-rw-r--r--libs/surfaces/mackie/mackie_control_protocol_poll.cc4
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.cc38
-rw-r--r--libs/surfaces/mackie/mackie_midi_builder.h7
6 files changed, 226 insertions, 14 deletions
diff --git a/libs/surfaces/mackie/TODO b/libs/surfaces/mackie/TODO
index 71b36c27ed..84b75d1a9b 100644
--- a/libs/surfaces/mackie/TODO
+++ b/libs/surfaces/mackie/TODO
@@ -1,5 +1,5 @@
* two bcf doesn't work
-* remappable buttons
+* remappable buttons (OSC or Surfax?)
* 7/1 configurable to 8
* need an object that can encapsulate different port types, ie BCF vs MCU. Not at the surface level.
* finish button implementations.
@@ -11,7 +11,6 @@
MCU
---
* if mackie wheel moves too fast, it's ignored.
-* timecode displays
* gain/panner display in second line
* metering on second line
* per-strip signal led
diff --git a/libs/surfaces/mackie/mackie_control_protocol.cc b/libs/surfaces/mackie/mackie_control_protocol.cc
index 7d6bb75fc3..aac2a4a71e 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol.cc
@@ -14,7 +14,6 @@
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 <iostream>
@@ -22,6 +21,7 @@
#include <cmath>
#include <sstream>
#include <vector>
+#include <iomanip>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
@@ -45,6 +45,8 @@
#include <ardour/location.h>
#include <ardour/dB.h>
#include <ardour/panner.h>
+#include <ardour/tempo.h>
+#include <ardour/types.h>
#include "mackie_control_protocol.h"
@@ -79,6 +81,7 @@ MackieControlProtocol::MackieControlProtocol (Session& session)
, pfd( 0 )
, nfds( 0 )
, _jog_wheel( *this )
+ , _timecode_type( ARDOUR::AnyTime::BBT )
{
#ifdef DEBUG
cout << "MackieControlProtocol::MackieControlProtocol" << endl;
@@ -489,9 +492,28 @@ void MackieControlProtocol::update_led( Mackie::Button & button, Mackie::LedStat
}
}
+void MackieControlProtocol::update_smpte_beats_led()
+{
+ switch ( _timecode_type )
+ {
+ case ARDOUR::AnyTime::BBT:
+ update_global_led( "beats", on );
+ update_global_led( "smpte", off );
+ break;
+ case ARDOUR::AnyTime::SMPTE:
+ update_global_led( "smpte", on );
+ update_global_led( "beats", off );
+ break;
+ default:
+ ostringstream os;
+ os << "Unknown Anytime::Type " << _timecode_type;
+ throw runtime_error( os.str() );
+ }
+}
+
void MackieControlProtocol::update_global_button( const string & name, LedState ls )
{
- if ( surface().controls_by_name.find( name ) !=surface().controls_by_name.end() )
+ if ( surface().controls_by_name.find( name ) != surface().controls_by_name.end() )
{
Button * button = dynamic_cast<Button*>( surface().controls_by_name[name] );
mcu_port().write( builder.build_led( button->led(), ls ) );
@@ -504,6 +526,21 @@ void MackieControlProtocol::update_global_button( const string & name, LedState
}
}
+void MackieControlProtocol::update_global_led( const string & name, LedState ls )
+{
+ if ( surface().controls_by_name.find( name ) != surface().controls_by_name.end() )
+ {
+ Led * led = dynamic_cast<Led*>( surface().controls_by_name[name] );
+ mcu_port().write( builder.build_led( *led, ls ) );
+ }
+ else
+ {
+#ifdef DEBUG
+ cout << "Led " << name << " not found" << endl;
+#endif
+ }
+}
+
// send messages to surface to set controls to correct values
void MackieControlProtocol::update_surface()
{
@@ -529,6 +566,7 @@ void MackieControlProtocol::update_surface()
// update global buttons and displays
notify_record_state_changed();
notify_transport_state_changed();
+ update_smpte_beats_led();
}
}
@@ -1071,7 +1109,82 @@ void MackieControlProtocol::update_automation( RouteSignal & rs )
_automation_last.start();
}
-void MackieControlProtocol::poll_automation()
+string MackieControlProtocol::format_bbt_timecode( nframes_t now_frame )
+{
+ BBT_Time bbt_time;
+ session->bbt_time( now_frame, bbt_time );
+
+ // According to the Logic docs
+ // digits: 888/88/88/888
+ // BBT mode: Bars/Beats/Subdivisions/Ticks
+ ostringstream os;
+ os << setw(3) << setfill('0') << bbt_time.bars;
+ os << setw(2) << setfill('0') << bbt_time.beats;
+
+ // figure out subdivisions per beat
+ const Meter & meter = session->tempo_map().meter_at( now_frame );
+ int subdiv = 2;
+ if ( meter.note_divisor() == 8 && meter.beats_per_bar() == 12.0 || meter.beats_per_bar() == 9.0 || meter.beats_per_bar() == 6.0 )
+ {
+ 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 );
+
+ os << setw(2) << setfill('0') << subdivisions + 1;
+ os << setw(3) << setfill('0') << ticks;
+
+ return os.str();
+}
+
+string MackieControlProtocol::format_smpte_timecode( nframes_t now_frame )
+{
+ SMPTE::Time smpte;
+ session->smpte_time( now_frame, smpte );
+
+ // According to the Logic docs
+ // digits: 888/88/88/888
+ // SMPTE mode: Hours/Minutes/Seconds/Frames
+ ostringstream os;
+ os << setw(3) << setfill('0') << smpte.hours;
+ os << setw(2) << setfill('0') << smpte.minutes;
+ os << setw(2) << setfill('0') << smpte.seconds;
+ os << setw(3) << setfill('0') << smpte.frames;
+
+ return os.str();
+}
+
+void MackieControlProtocol::update_timecode_display()
+{
+ // do assignment here so current_frame is fixed
+ nframes_t current_frame = session->transport_frame();
+ string timecode;
+
+ switch ( _timecode_type )
+ {
+ case ARDOUR::AnyTime::BBT:
+ timecode = format_bbt_timecode( current_frame );
+ break;
+ case ARDOUR::AnyTime::SMPTE:
+ timecode = format_smpte_timecode( current_frame );
+ break;
+ default:
+ ostringstream os;
+ os << "Unknown timecode: " << _timecode_type;
+ throw runtime_error( os.str() );
+ }
+
+ // only write the timecode string to the MCU if it's changed
+ // since last time. This is to reduce midi bandwidth used.
+ if ( timecode != _timecode_last )
+ {
+ mcu_port().write( builder.timecode_display( mcu_port(), timecode, _timecode_last ) );
+ _timecode_last = timecode;
+ }
+}
+
+void MackieControlProtocol::poll_session_data()
{
if ( _active && _automation_last.elapsed() >= 20 )
{
@@ -1082,8 +1195,17 @@ void MackieControlProtocol::poll_automation()
}
// and the master strip
- if ( master_route_signal != 0 ) update_automation( *master_route_signal );
-
+ if ( master_route_signal != 0 )
+ {
+ update_automation( *master_route_signal );
+ }
+
+ // and timecode display if it's a real mackie
+ if ( mcu_port().emulation() == MackiePort::mackie )
+ {
+ update_timecode_display();
+ }
+
_automation_last.start();
}
}
@@ -1583,3 +1705,27 @@ LedState MackieControlProtocol::save_release( Button & button )
{
return off;
}
+
+LedState MackieControlProtocol::smpte_beats_press( Button & )
+{
+ switch ( _timecode_type )
+ {
+ case ARDOUR::AnyTime::BBT:
+ _timecode_type = ARDOUR::AnyTime::SMPTE;
+ break;
+ case ARDOUR::AnyTime::SMPTE:
+ _timecode_type = ARDOUR::AnyTime::BBT;
+ break;
+ default:
+ ostringstream os;
+ os << "Unknown Anytime::Type " << _timecode_type;
+ throw runtime_error( os.str() );
+ }
+ update_smpte_beats_led();
+ return on;
+}
+
+LedState MackieControlProtocol::smpte_beats_release( Button & )
+{
+ return off;
+}
diff --git a/libs/surfaces/mackie/mackie_control_protocol.h b/libs/surfaces/mackie/mackie_control_protocol.h
index c3e79b846f..564a554a4b 100644
--- a/libs/surfaces/mackie/mackie_control_protocol.h
+++ b/libs/surfaces/mackie/mackie_control_protocol.h
@@ -120,12 +120,15 @@ class MackieControlProtocol
void notify_parameter_changed( const char * );
void notify_solo_active_changed( bool );
- // this is called to generate the midi to send in response to
- // a button press.
+ /// Turn smpte on and beats off, or vice versa, depending
+ /// on state of _timecode_type
+ void update_smpte_beats_led();
+
+ /// this is called to generate the midi to send in response to a button press.
void update_led( Mackie::Button & button, Mackie::LedState );
- // calls update_led, but looks up the button by name
void update_global_button( const std::string & name, Mackie::LedState );
+ void update_global_led( const std::string & name, Mackie::LedState );
// transport button handler methods from MackieButtonHandler
virtual Mackie::LedState frm_left_press( Mackie::Button & );
@@ -193,6 +196,9 @@ class MackieControlProtocol
virtual Mackie::LedState save_press( Mackie::Button & );
virtual Mackie::LedState save_release( Mackie::Button & );
+ virtual Mackie::LedState smpte_beats_press( Mackie::Button & );
+ virtual Mackie::LedState smpte_beats_release( Mackie::Button & );
+
// jog wheel states
virtual Mackie::LedState zoom_press( Mackie::Button & );
virtual Mackie::LedState zoom_release( Mackie::Button & );
@@ -274,12 +280,22 @@ class MackieControlProtocol
void add_port( MIDI::Port &, int number );
- /// read automation data from the currently active routes and send to surface
- void poll_automation();
+ /**
+ Read session data and send to surface. Includes
+ automation from the currently active routes and
+ timecode displays.
+ */
+ void poll_session_data();
// called from poll_automation to figure out which automations need to be sent
void update_automation( Mackie::RouteSignal & );
+
+ // also called from poll_automation to update timecode display
+ void update_timecode_display();
+ std::string format_bbt_timecode( nframes_t now_frame );
+ std::string format_smpte_timecode( nframes_t now_frame );
+
/**
notification that the port is about to start it's init sequence.
We must make sure that before this exits, the port is being polled
@@ -341,6 +357,12 @@ class MackieControlProtocol
// Timer for controlling midi bandwidth used by automation polls
Mackie::Timer _automation_last;
+
+ // last written timecode string
+ std::string _timecode_last;
+
+ // Which timecode are we displaying? BBT or SMPTE
+ ARDOUR::AnyTime::Type _timecode_type;
};
#endif // ardour_mackie_control_protocol_h
diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
index 8b6d332e71..b760b05aee 100644
--- a/libs/surfaces/mackie/mackie_control_protocol_poll.cc
+++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc
@@ -61,8 +61,8 @@ void * MackieControlProtocol::monitor_work()
update_ports();
}
}
- // poll for automation data from the routes
- poll_automation();
+ // poll for session data that needs to go to the unit
+ poll_session_data();
}
catch ( exception & e )
{
diff --git a/libs/surfaces/mackie/mackie_midi_builder.cc b/libs/surfaces/mackie/mackie_midi_builder.cc
index a1040ae111..ce4eb1fda0 100644
--- a/libs/surfaces/mackie/mackie_midi_builder.cc
+++ b/libs/surfaces/mackie/mackie_midi_builder.cc
@@ -20,6 +20,7 @@
#include <typeinfo>
#include <sstream>
#include <iomanip>
+#include <algorithm>
#include "controls.h"
#include "midi_byte_array.h"
@@ -234,3 +235,40 @@ MidiByteArray MackieMidiBuilder::all_strips_display( MackiePort & port, std::vec
retval << "Not working yet";
return retval;
}
+
+MidiByteArray MackieMidiBuilder::timecode_display( MackiePort & port, const std::string & timecode, const std::string & last_timecode )
+{
+ // if there's no change, send nothing, not even sysex header
+ if ( timecode == last_timecode ) return MidiByteArray();
+
+ // length sanity checking
+ string local_timecode = timecode;
+ // truncate to 10 characters
+ if ( local_timecode.length() > 10 ) local_timecode = local_timecode.substr( 0, 10 );
+ // pad to 10 characters
+ 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() );
+
+ MidiByteArray retval;
+
+ // sysex header
+ retval << port.sysex_hdr();
+
+ // code for timecode display
+ retval << 0x10;
+
+ // 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 )
+ {
+ retval << translate_seven_segment( *it );
+ }
+
+ // sysex trailer
+ retval << MIDI::eox;
+
+ return retval;
+}
diff --git a/libs/surfaces/mackie/mackie_midi_builder.h b/libs/surfaces/mackie/mackie_midi_builder.h
index 2a0ece6e85..78bf4e5243 100644
--- a/libs/surfaces/mackie/mackie_midi_builder.h
+++ b/libs/surfaces/mackie/mackie_midi_builder.h
@@ -77,6 +77,13 @@ public:
MidiByteArray two_char_display( unsigned int value, const std::string & dots = " " );
/**
+ Timecode display. Only the difference between timecode and last_timecode will
+ be encoded, to save midi bandwidth. If they're the same, an empty array will
+ be returned
+ */
+ MidiByteArray timecode_display( MackiePort &, const std::string & timecode, const std::string & last_timecode = "" );
+
+ /**
for displaying characters on the strip LCD
pass MackiePort so we know which sysex header to use
*/