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 "control_group.h"
40 #include "midi_byte_array.h"
41 #include "mackie_port.h"
44 using namespace Mackie;
47 #define NUCLEUS_DEBUG 1
49 MIDI::byte MackieMidiBuilder::calculate_pot_value (midi_pot_mode mode, const ControlState & state)
51 // TODO do an exact calc for 0.50? To allow manually re-centering the port.
54 MIDI::byte retval = (state.pos > 0.45 && state.pos < 0.55 ? 1 : 0) << 6;
57 retval |= (mode << 4);
59 // value, but only if off hasn't explicitly been set
60 if (state.led_state != off)
61 retval += (int(state.pos * 10.0) + 1) & 0x0f; // 0b00001111
66 MidiByteArray MackieMidiBuilder::build_led_ring (const Pot & pot, const ControlState & state, midi_pot_mode mode )
68 return build_led_ring (pot.led_ring(), state, mode);
71 MidiByteArray MackieMidiBuilder::build_led_ring (const LedRing & led_ring, const ControlState & state, midi_pot_mode mode)
73 // The other way of doing this:
74 // 0x30 + pot/ring number (0-7)
75 //, 0x30 + led_ring.ordinal() - 1
76 return MidiByteArray (3
80 , 0x20 + led_ring.raw_id()
82 , calculate_pot_value (mode, state)
86 MidiByteArray MackieMidiBuilder::build_led (const Button & button, LedState ls)
88 return build_led (button.led(), ls);
91 MidiByteArray MackieMidiBuilder::build_led (const Led & led, LedState ls)
96 case LedState::on: state = 0x7f; break;
97 case LedState::off: state = 0x00; break;
98 case LedState::none: state = 0x00; break; // actually, this should never happen.
99 case LedState::flashing: state = 0x01; break;
102 return MidiByteArray (3
109 MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
111 int posi = int (0x3fff * pos);
113 return MidiByteArray (3
114 , midi_fader_id | fader.raw_id()
122 MidiByteArray MackieMidiBuilder::zero_strip (SurfacePort & port, const Strip & strip)
124 Group::Controls::const_iterator it = strip.controls().begin();
125 MidiByteArray retval;
127 for (; it != strip.controls().end(); ++it) {
128 Control & control = **it;
129 if (control.accepts_feedback())
130 retval << zero_control (control);
133 // These must have sysex headers
135 /* XXX: not sure about this check to only display stuff for strips of index < 8 */
136 if (strip.index() < 8) {
137 retval << strip_display_blank (port, strip, 0);
138 retval << strip_display_blank (port, strip, 1);
144 MidiByteArray MackieMidiBuilder::zero_control (const Control & control)
146 switch (control.type()) {
147 case Control::type_button:
148 return build_led ((Button&)control, off);
150 case Control::type_led:
151 return build_led ((Led&)control, off);
153 case Control::type_fader:
154 return build_fader ((Fader&)control, 0.0);
156 case Control::type_pot:
157 return build_led_ring (dynamic_cast<const Pot&> (control), off);
159 case Control::type_led_ring:
160 return build_led_ring (dynamic_cast<const LedRing&> (control), off);
162 case Control::type_meter:
163 return const_cast<Meter&>(dynamic_cast<const Meter&>(control)).update_message (0.0);
167 os << "Unknown control type " << control << " in Strip::zero_control";
168 throw MackieControlException (os.str());
172 char translate_seven_segment (char achar)
174 achar = toupper (achar);
175 if (achar >= 0x40 && achar <= 0x60)
177 else if (achar >= 0x21 && achar <= 0x3f)
183 MidiByteArray MackieMidiBuilder::two_char_display (const std::string & msg, const std::string & dots)
185 if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
186 if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
188 MidiByteArray bytes (5, 0xb0, 0x4a, 0x00, 0x4b, 0x00);
190 // chars are understood by the surface in right-to-left order
191 // could also exchange the 0x4a and 0x4b, above
192 bytes[4] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
193 bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
198 MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std::string & /*dots*/)
201 os << setfill('0') << setw(2) << value % 100;
202 return two_char_display (os.str());
205 MidiByteArray MackieMidiBuilder::strip_display_blank (SurfacePort & port, const Strip & strip, unsigned int line_number)
207 // 6 spaces, not 7 because strip_display adds a space where appropriate
208 return strip_display (port, strip, line_number, " ");
211 MidiByteArray MackieMidiBuilder::strip_display (SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line)
213 assert (line_number <= 1);
215 MidiByteArray retval;
216 uint32_t index = strip.index() % port.strips();
218 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display index: %1, line %2 = %3\n", strip.index(), line_number, line));
221 retval << port.sysex_hdr();
225 // offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
226 retval << (index * 7 + (line_number * 0x38));
228 // ascii data to display
230 // pad with " " out to 6 chars
231 for (int i = line.length(); i < 6; ++i) {
235 // column spacer, unless it's the right-hand column
236 if (strip.index() < 7) {
243 DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
248 MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std::vector<std::string> & /*lines1*/, std::vector<std::string> & /*lines2*/)
250 MidiByteArray retval;
252 // NOTE remember max 112 bytes per message, including sysex headers
253 retval << "Not working yet";
257 MidiByteArray MackieMidiBuilder::timecode_display (SurfacePort & port, const std::string & timecode, const std::string & last_timecode)
259 // if there's no change, send nothing, not even sysex header
260 if (timecode == last_timecode) return MidiByteArray();
262 // length sanity checking
263 string local_timecode = timecode;
265 // truncate to 10 characters
266 if (local_timecode.length() > 10) {
267 local_timecode = local_timecode.substr (0, 10);
270 // pad to 10 characters
271 while (local_timecode.length() < 10) {
272 local_timecode += " ";
275 // find the suffix of local_timecode that differs from last_timecode
276 std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
278 MidiByteArray retval;
281 retval << port.sysex_hdr();
283 // code for timecode display
286 // translate characters. These are sent in reverse order of display
287 // hence the reverse iterators
288 string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
289 for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
290 retval << translate_seven_segment (*it);