Merge with trunk R2978.
[ardour.git] / libs / surfaces / mackie / mackie_midi_builder.cc
1 /*
2         Copyright (C) 2006,2007 John Anderson
3
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.
8
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.
13
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.
17 */
18 #include "mackie_midi_builder.h"
19
20 #include <typeinfo>
21 #include <sstream>
22 #include <iomanip>
23
24 #include "controls.h"
25 #include "midi_byte_array.h"
26
27 using namespace Mackie;
28 using namespace std;
29
30 MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const ControlState & state )
31 {
32         // TODO do an exact calc for 0.50? To allow manually re-centering the port.
33         
34         // center on or off
35         MIDI::byte retval = ( state.pos > 0.45 && state.pos < 0.55 ? 1 : 0 ) << 6;
36         
37         // mode
38         retval |= ( mode << 4 );
39         
40         // value, but only if off hasn't explicitly been set
41         if ( state.led_state != off )
42                 retval += ( int(state.pos * 10.0) + 1 ) & 0x0f; // 0b00001111
43         
44         return retval;
45 }
46
47 MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state )
48 {
49         return build_led_ring( pot.led_ring(), state );
50 }
51
52 MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state )
53 {
54         // The other way of doing this:
55         // 0x30 + pot/ring number (0-7)
56         //, 0x30 + led_ring.ordinal() - 1
57         return MidiByteArray ( 3
58                 // the control type
59                 , midi_pot_id
60                 // the id
61                 , 0x20 + led_ring.id()
62                 // the value
63                 , calculate_pot_value( midi_pot_mode_dot, state )
64         );
65 }
66
67 MidiByteArray MackieMidiBuilder::build_led( const Button & button, LedState ls )
68 {
69         return build_led( button.led(), ls );
70 }
71
72 MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
73 {
74         MIDI::byte state = 0;
75         switch ( ls.state() )
76         {
77                 case LedState::on:                      state = 0x7f; break;
78                 case LedState::off:                     state = 0x00; break;
79                 case LedState::none:                    state = 0x00; break; // actually, this should never happen.
80                 case LedState::flashing:        state = 0x01; break;
81         }
82         
83         return MidiByteArray ( 3
84                 , midi_button_id
85                 , led.id()
86                 , state
87         );
88 }
89
90 MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
91 {
92         int posi = int( 0x3fff * pos );
93         
94         return MidiByteArray ( 3
95                 , midi_fader_id | fader.id()
96                 // lower-order bits
97                 , posi & 0x7f
98                 // higher-order bits
99                 , ( posi >> 7 )
100         );
101 }
102
103 MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
104 {
105         Group::Controls::const_iterator it = strip.controls().begin();
106         MidiByteArray retval;
107         for (; it != strip.controls().end(); ++it )
108         {
109                 Control & control = **it;
110                 if ( control.accepts_feedback() )
111                         retval << zero_control( control );
112         }
113         return retval;
114 }
115
116 MidiByteArray MackieMidiBuilder::zero_control( const Control & control )
117 {
118         switch( control.type() )
119         {
120                 case Control::type_button:
121                         return build_led( (Button&)control, off );
122                 
123                 case Control::type_led:
124                         return build_led( (Led&)control, off );
125                 
126                 case Control::type_fader:
127                         return build_fader( (Fader&)control, 0.0 );
128                 
129                 case Control::type_pot:
130                         return build_led_ring( dynamic_cast<const Pot&>( control ), off );
131                 
132                 case Control::type_led_ring:
133                         return build_led_ring( dynamic_cast<const LedRing&>( control ), off );
134                 
135                 default:
136                         ostringstream os;
137                         os << "Unknown control type " << control << " in Strip::zero_control";
138                         throw MackieControlException( os.str() );
139         }
140 }
141
142 char translate_seven_segment( char achar )
143 {
144         achar = toupper( achar );
145         if ( achar >= 0x40 && achar <= 0x60 )
146                 return achar - 0x40;
147         else if ( achar >= 0x21 && achar <= 0x3f )
148       return achar;
149         else
150       return 0x00;
151 }
152
153 MidiByteArray MackieMidiBuilder::two_char_display( const std::string & msg, const std::string & dots )
154 {
155         if ( msg.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: msg must be exactly 2 characters" );
156         if ( dots.length() != 2 ) throw MackieControlException( "MackieMidiBuilder::two_char_display: dots must be exactly 2 characters" );
157         
158         MidiByteArray bytes( 5, 0xb0, 0x4a, 0x00, 0x4b, 0x00 );
159         
160         // chars are understood by the surface in right-to-left order
161         // could also exchange the 0x4a and 0x4b, above
162         bytes[4] = translate_seven_segment( msg[0] ) + ( dots[0] == '.' ? 0x40 : 0x00 );
163         bytes[2] = translate_seven_segment( msg[1] ) + ( dots[1] == '.' ? 0x40 : 0x00 );
164         
165         return bytes;
166 }
167
168 MidiByteArray MackieMidiBuilder::two_char_display( unsigned int value, const std::string & dots )
169 {
170         ostringstream os;
171         os << setfill('0') << setw(2) << value % 100;
172         return two_char_display( os.str() );
173 }