diff options
author | John Anderson <ardour@semiosix.com> | 2007-12-30 08:31:10 +0000 |
---|---|---|
committer | John Anderson <ardour@semiosix.com> | 2007-12-30 08:31:10 +0000 |
commit | 19bf004a79d57a9763dcda51f38132f69b050002 (patch) | |
tree | ac665dff9cca410fe39f40cce7e8bf414564da18 /libs/surfaces | |
parent | 9c751e3c1c3a33859fc6cc58f741307a749e6141 (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/TODO | 3 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_control_protocol.cc | 156 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_control_protocol.h | 32 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_control_protocol_poll.cc | 4 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_midi_builder.cc | 38 | ||||
-rw-r--r-- | libs/surfaces/mackie/mackie_midi_builder.h | 7 |
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 */ |