2 Copyright (C) 2006,2007 John Anderson
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 #include "mackie_midi_builder.h"
26 #include "pbd/compose.h"
28 #include "ardour/debug.h"
30 #include "midi_byte_array.h"
31 #include "mackie_port.h"
34 using namespace Mackie;
37 #define NUCLEUS_DEBUG 1
39 MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state )
41 // TODO do an exact calc for 0.50? To allow manually re-centering the port.
44 MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6;
47 retval |= ( mode << 4 );
49 // value, but only if off hasn't explicitly been set
50 if ( state.led_state != off )
51 retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111
56 MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state, midi_pot_mode mode )
58 return build_led_ring( pot.led_ring(), state, mode );
61 MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state, midi_pot_mode mode )
63 // The other way of doing this:
64 // 0x30 + pot/ring number (0-7)
65 //, 0x30 + led_ring.ordinal() - 1
66 return MidiByteArray ( 3
70 , 0x20 + led_ring.raw_id()
72 , calculate_pot_value( mode, state )
76 MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls )
78 return build_led( button.led(), ls );
81 MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
86 case LedState::on: state = 0x7f; break;
87 case LedState::off: state = 0x00; break;
88 case LedState::none: state = 0x00; break; // actually, this should never happen.
89 case LedState::flashing: state = 0x01; break;
92 return MidiByteArray ( 3
99 MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
101 int posi = int( 0x3fff * pos );
103 return MidiByteArray ( 3
104 , midi_fader_id | fader.raw_id()
112 MidiByteArray MackieMidiBuilder::build_meter (const Meter & meter, float val)
114 MIDI::byte segment = lrintf (val*16.0);
116 return MidiByteArray (2,
118 (meter.raw_id()<<3) | segment);
121 MidiByteArray MackieMidiBuilder::zero_strip( SurfacePort & port, const Strip & strip )
123 Group::Controls::const_iterator it = strip.controls().begin();
124 MidiByteArray retval;
125 for (; it != strip.controls().end(); ++it )
127 Control & control = **it;
128 if ( control.accepts_feedback() )
129 retval << zero_control( control );
132 // These must have sysex headers
134 /* XXX: not sure about this check to only display stuff for strips of index < 8 */
135 if (strip.index() < 8) {
136 retval << strip_display_blank( port, strip, 0 );
137 retval << strip_display_blank( port, strip, 1 );
143 MidiByteArray MackieMidiBuilder::zero_control( const Control & control )
145 switch( control.type() ) {
146 case Control::type_button:
147 return build_led( (Button&)control, off );
149 case Control::type_led:
150 return build_led( (Led&)control, off );
152 case Control::type_fader:
153 return build_fader( (Fader&)control, 0.0 );
155 case Control::type_pot:
156 return build_led_ring( dynamic_cast<const Pot&>( control ), off );
158 case Control::type_led_ring:
159 return build_led_ring( dynamic_cast<const LedRing&>( control ), off );
161 case Control::type_meter:
162 return build_meter (dynamic_cast<const Meter&>(control), 0.0);
166 os << "Unknown control type " << control << " in Strip::zero_control";
167 throw MackieControlException( os.str() );
171 char translate_seven_segment( char achar )
173 achar = toupper( achar );
174 if ( achar >= 0x40 && achar <= 0x60 )
176 else if ( achar >= 0x21 && achar <= 0x3f )
182 MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots )
184 if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" );
185 if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" );
187 MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 );
189 // chars are understood by the surface in right-to-left order
190 // could also exchange the 0x4a and 0x4b, above
191 bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 );
192 bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 );
197 MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std::string & /*dots*/)
200 os << setfill('0') << setw(2) << value % 100;
201 return two_char_display( os.str() );
204 MidiByteArray MackieMidiBuilder::strip_display_blank( SurfacePort & port, const Strip & strip, unsigned int line_number )
206 // 6 spaces, not 7 because strip_display adds a space where appropriate
207 return strip_display( port, strip, line_number, " " );
210 MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line )
212 assert (line_number <= 1);
214 MidiByteArray retval;
215 uint32_t index = strip.index() % port.strips();
217 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display index: %1, line %2 = %3\n", strip.index(), line_number, line));
220 retval << port.sysex_hdr();
224 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line )
225 retval << (index * 7 + (line_number * 0x38));
227 // ascii data to display
229 // pad with " " out to 6 chars
230 for (int i = line.length(); i < 6; ++i) {
234 // column spacer, unless it's the right-hand column
235 if (strip.index() < 7) {
242 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
247 MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std::vector<std::string> & /*lines1*/, std::vector<std::string> & /*lines2*/)
249 MidiByteArray retval;
251 // NOTE remember max 112 bytes per message, including sysex headers
252 retval << "Not working yet";
256 MidiByteArray MackieMidiBuilder::timecode_display( SurfacePort & port, const std::string & timecode, const std::string & last_timecode )
258 // if there's no change, send nothing, not even sysex header
259 if ( timecode == last_timecode ) return MidiByteArray();
261 // length sanity checking
262 string local_timecode = timecode;
263 // truncate to 10 characters
264 if ( local_timecode.length() > 10 ) local_timecode = local_timecode.substr( 0, 10 );
265 // pad to 10 characters
266 while ( local_timecode.length() < 10 ) local_timecode += " ";
268 // find the suffix of local_timecode that differs from last_timecode
269 std::pair<string::const_iterator,string::iterator> pp = mismatch( last_timecode.begin(), last_timecode.end(), local_timecode.begin() );
271 MidiByteArray retval;
274 retval << port.sysex_hdr();
276 // code for timecode display
279 // translate characters. These are sent in reverse order of display
280 // hence the reverse iterators
281 string::reverse_iterator rend = reverse_iterator<string::iterator>( pp.second );
282 for ( string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it )
284 retval << translate_seven_segment( *it );