substantial overhaul of MCU code - no more separate thread, just connect to signals...
[ardour.git] / libs / surfaces / mackie / mackie_port.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_port.h"
19
20 #include "mackie_control_exception.h"
21 #include "mackie_control_protocol.h"
22 #include "mackie_midi_builder.h"
23 #include "controls.h"
24 #include "surface.h"
25
26 #include <glibmm/main.h>
27
28 #include <boost/shared_array.hpp>
29
30 #include "midi++/types.h"
31 #include "midi++/port.h"
32
33 #include "ardour/debug.h"
34 #include "ardour/rc_configuration.h"
35
36 #include "i18n.h"
37
38 #include <sstream>
39
40 using namespace std;
41 using namespace Mackie;
42 using namespace ARDOUR;
43
44 // The MCU sysex header
45 MidiByteArray mackie_sysex_hdr ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x10 );
46
47 // The MCU extender sysex header
48 MidiByteArray mackie_sysex_hdr_xt ( 5, MIDI::sysex, 0x0, 0x0, 0x66, 0x11 );
49
50 MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int number, port_type_t port_type )
51   : SurfacePort( port, number )
52   , _mcp( mcp )
53   , _port_type( port_type )
54   , _emulation( none )
55   , _initialising( true )
56 {
57         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::MackiePort\n");
58 }
59
60 MackiePort::~MackiePort()
61 {
62         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::~MackiePort\n");
63         close();
64         DEBUG_TRACE (DEBUG::MackieControl, "~MackiePort finished\n");
65 }
66
67 int MackiePort::strips() const
68 {
69         if ( _port_type == mcu )
70         {
71                 switch ( _emulation )
72                 {
73                         // BCF2000 only has 8 faders, so reserve one for master
74                         case bcf2000: return 7;
75                         case mackie: return 8;
76                         case none:
77                         default:
78                                 throw MackieControlException( "MackiePort::strips: don't know what emulation we're using" );
79                 }
80         }
81         else
82         {
83                 // must be an extender, ie no master fader
84                 return 8;
85         }
86 }
87
88 // should really be in MackiePort
89 void MackiePort::open()
90 {
91         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::open %1\n", *this));
92         
93         port().input()->sysex.connect (sysex_connection, boost::bind (&MackiePort::handle_midi_sysex, this, _1, _2, _3));
94                      
95         // make sure the device is connected
96         init();
97 }
98
99 void MackiePort::close()
100 {
101         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::close\n");
102         
103         // disconnect signals
104         any_connection.disconnect();
105         sysex_connection.disconnect();
106         
107         // TODO emit a "closing" signal?
108 }
109
110 const MidiByteArray & MackiePort::sysex_hdr() const
111 {
112         switch ( _port_type )
113         {
114                 case mcu: return mackie_sysex_hdr;
115                 case ext: return mackie_sysex_hdr_xt;
116         }
117         cout << "MackiePort::sysex_hdr _port_type not known" << endl;
118         return mackie_sysex_hdr;
119 }
120
121 MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiByteArray::iterator end )
122 {
123         MidiByteArray l;
124         back_insert_iterator<MidiByteArray> back ( l );
125         copy( begin, end, back );
126         
127         MidiByteArray retval;
128         
129         // this is how to calculate the response to the challenge.
130         // from the Logic docs.
131         retval << ( 0x7f & ( l[0] + ( l[1] ^ 0xa ) - l[3] ) );
132         retval << ( 0x7f & ( ( l[2] >> l[3] ) ^ ( l[0] + l[3] ) ) );
133         retval << ( 0x7f & ( (l[3] - ( l[2] << 2 )) ^ ( l[0] | l[1] ) ) );
134         retval << ( 0x7f & ( l[1] - l[2] + ( 0xf0 ^ ( l[3] << 4 ) ) ) );
135         
136         return retval;
137 }
138
139 // not used right now
140 MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
141 {
142         // handle host connection query
143         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host connection query: %1\n", bytes));
144         
145         if ( bytes.size() != 18 )
146         {
147                 finalise_init( false );
148                 ostringstream os;
149                 os << "expecting 18 bytes, read " << bytes << " from " << port().name();
150                 throw MackieControlException( os.str() );
151         }
152
153         // build and send host connection reply
154         MidiByteArray response;
155         response << 0x02;
156         copy( bytes.begin() + 6, bytes.begin() + 6 + 7, back_inserter( response ) );
157         response << calculate_challenge_response( bytes.begin() + 6 + 7, bytes.begin() + 6 + 7 + 4 );
158         return response;
159 }
160
161 // not used right now
162 MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
163 {
164         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("host_connection_confirmation: %1\n", bytes));
165         
166         // decode host connection confirmation
167         if ( bytes.size() != 14 )
168         {
169                 finalise_init( false );
170                 ostringstream os;
171                 os << "expecting 14 bytes, read " << bytes << " from " << port().name();
172                 throw MackieControlException( os.str() );
173         }
174         
175         // send version request
176         return MidiByteArray( 2, 0x13, 0x00 );
177 }
178
179 void MackiePort::probe_emulation (const MidiByteArray &)
180 {
181 #if 0
182         cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
183
184         MidiByteArray version_string;
185         for ( int i = 6; i < 11; ++i ) version_string << bytes[i];
186         cout << "version_string: " << version_string << endl;
187 #endif
188         
189         // TODO investigate using serial number. Also, possibly size of bytes might
190         // give an indication. Also, apparently MCU sends non-documented messages
191         // sometimes.
192         if (!_initialising)
193         {
194                 //cout << "MackiePort::probe_emulation out of sequence." << endl;
195                 return;
196         }
197
198         finalise_init( true );
199 }
200
201 void MackiePort::init()
202 {
203         DEBUG_TRACE (DEBUG::MackieControl,  "MackiePort::init\n");
204
205         init_mutex.lock();
206         _initialising = true;
207
208         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::init lock acquired\n");
209
210         // emit pre-init signal
211         init_event();
212         
213         // kick off initialisation. See docs in header file for init()
214         
215         // bypass the init sequence because sometimes the first
216         // message doesn't get to the unit, and there's no way
217         // to do a timed lock in Glib.
218         //write_sysex ( MidiByteArray ( 2, 0x13, 0x00 ) );
219         
220         finalise_init( true );
221 }
222
223 void MackiePort::finalise_init( bool yn )
224 {
225         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init\n");
226
227         bool emulation_ok = false;
228         
229         // probing doesn't work very well, so just use a config variable
230         // to set the emulation mode
231         // TODO This might have to be specified on a per-port basis
232         // in the config file
233         // if an mcu and a bcf are needed to work as one surface
234         if ( _emulation == none )
235         {
236                 // TODO same as code in mackie_control_protocol.cc
237                 if ( ARDOUR::Config->get_mackie_emulation() == "bcf" )
238                 {
239                         _emulation = bcf2000;
240                         emulation_ok = true;
241                 }
242                 else if ( ARDOUR::Config->get_mackie_emulation() == "mcu" )
243                 {
244                         _emulation = mackie;
245                         emulation_ok = true;
246                 }
247                 else
248                 {
249                         cout << "unknown mackie emulation: " << ARDOUR::Config->get_mackie_emulation() << endl;
250                         emulation_ok = false;
251                 }
252         }
253         
254         yn = yn && emulation_ok;
255         
256         SurfacePort::active( yn );
257
258         if (yn) {
259                 active_event();
260                 
261                 // start handling messages from controls
262                 connect_any();
263         }
264
265         _initialising = false;
266         init_cond.signal();
267         init_mutex.unlock();
268
269         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::finalise_init lock released\n");
270 }
271
272 void MackiePort::connect_any()
273 {
274         if (!any_connection.connected()) {
275                 port().input()->any.connect (any_connection, boost::bind (&MackiePort::handle_midi_any, this, _1, _2, _3));
276         }
277 }
278
279 bool MackiePort::wait_for_init()
280 {
281         Glib::Mutex::Lock lock( init_mutex );
282         while (_initialising) {
283                 DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active waiting\n");
284                 init_cond.wait( init_mutex );
285                 DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active released\n");
286         }
287         DEBUG_TRACE (DEBUG::MackieControl, "MackiePort::wait_for_active returning\n");
288         return SurfacePort::active();
289 }
290
291 void MackiePort::handle_midi_sysex (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count )
292 {
293         MidiByteArray bytes( count, raw_bytes );
294         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("handle_midi_sysex: %1\n", bytes));
295
296         switch( bytes[5] )
297         {
298                 case 0x01:
299                         // not used right now
300                         write_sysex (host_connection_query (bytes));
301                         break;
302                 case 0x03:
303                         // not used right now
304                         write_sysex (host_connection_confirmation (bytes));
305                         break;
306                 case 0x04:
307                         inactive_event ();
308                         cout << "host connection error" << bytes << endl;
309                         break;
310                 case 0x14:
311                         probe_emulation (bytes);
312                         break;
313                 default:
314                         cout << "unknown sysex: " << bytes << endl;
315         }
316 }
317
318 Control & MackiePort::lookup_control( MIDI::byte * bytes, size_t count )
319 {
320         // Don't instantiate a MidiByteArray here unless it's needed for exceptions.
321         // Reason being that this method is called for every single incoming
322         // midi event, and it needs to be as efficient as possible.
323
324         Control * control = 0;
325         MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
326
327         switch (midi_type) {
328                 // fader
329                 case MackieMidiBuilder::midi_fader_id:
330                 {
331                         int midi_id = bytes[0] & 0x0f;
332                         control = _mcp.surface().faders[midi_id];
333                         if ( control == 0 )
334                         {
335                                 MidiByteArray mba( count, bytes );
336                                 ostringstream os;
337                                 os << "control for fader" << bytes << " id " << midi_id << " is null";
338                                 throw MackieControlException( os.str() );
339                         }
340                         break;
341                 }
342                         
343                 // button
344                 case MackieMidiBuilder::midi_button_id:
345                         control = _mcp.surface().buttons[bytes[1]];
346                         if ( control == 0 )
347                         {
348                                 MidiByteArray mba( count, bytes );
349                                 ostringstream os;
350                                 os << "control for button " << bytes << " is null";
351                                 throw MackieControlException( os.str() );
352                         }
353                         break;
354                         
355                 // pot (jog wheel, external control)
356                 case MackieMidiBuilder::midi_pot_id:
357                         control = _mcp.surface().pots[bytes[1]];
358                         if ( control == 0 )
359                         {
360                                 MidiByteArray mba( count, bytes );
361                                 ostringstream os;
362                                 os << "control for rotary " << mba << " is null";
363                                 throw MackieControlException( os.str() );
364                         }
365                         break;
366                 
367                 default:
368                         MidiByteArray mba( count, bytes );
369                         ostringstream os;
370                         os << "Cannot find control for " << bytes;
371                         throw MackieControlException( os.str() );
372         }
373         return *control;
374 }
375
376 bool MackiePort::handle_control_timeout_event ( Control * control )
377 {
378         // empty control_state
379         ControlState control_state;
380         control->in_use( false );
381         control_event( *this, *control, control_state );
382         
383         // only call this method once from the timer
384         return false;
385 }
386
387 // converts midi messages into control_event signals
388 // it might be worth combining this with lookup_control
389 // because they have similar logic flows.
390 void MackiePort::handle_midi_any (MIDI::Parser &, MIDI::byte * raw_bytes, size_t count )
391 {
392         MidiByteArray bytes( count, raw_bytes );
393         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackiePort::handle_midi_any %1\n", bytes));
394
395         try
396         {
397                 // ignore sysex messages
398                 if ( raw_bytes[0] == MIDI::sysex ) return;
399
400                 // sanity checking
401                 if (count != 3) {
402                         ostringstream os;
403                         MidiByteArray mba( count, raw_bytes );
404                         os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba;
405                         throw MackieControlException( os.str() );
406                 }
407                 
408                 Control & control = lookup_control( raw_bytes, count );
409                 control.in_use( true );
410                 
411                 // This handles incoming bytes. Outgoing bytes
412                 // are sent by the signal handlers.
413                 switch (control.type()) {
414                         // fader
415                         case Control::type_fader:
416                         {
417                                 // only the top-order 10 bits out of 14 are used
418                                 int midi_pos = ( ( raw_bytes[2] << 7 ) + raw_bytes[1] ) >> 4;
419                                 
420                                 // in_use is set by the MackieControlProtocol::handle_strip_button
421                                 
422                                 // relies on implicit ControlState constructor
423                                 control_event( *this, control, float(midi_pos) / float(0x3ff) );
424                         }
425                         break;
426                                 
427                         // button
428                         case Control::type_button:
429                         {
430                                 ControlState control_state( raw_bytes[2] == 0x7f ? press : release );
431                                 control.in_use( control_state.button_state == press );
432                                 control_event( *this, control, control_state );
433                                 
434                                 break;
435                         }
436                                 
437                         // pot (jog wheel, external control)
438                         case Control::type_pot:
439                         {
440                                 ControlState state;
441                                 
442                                 // bytes[2] & 0b01000000 (0x40) give sign
443                                 state.sign = ( raw_bytes[2] & 0x40 ) == 0 ? 1 : -1; 
444                                 // bytes[2] & 0b00111111 (0x3f) gives delta
445                                 state.ticks = ( raw_bytes[2] & 0x3f);
446                                 state.delta = float( state.ticks ) / float( 0x3f );
447                                 
448                                 /*
449                                         Pots only emit events when they move, not when they
450                                         stop moving. So to get a stop event, we need to use a timeout.
451                                 */
452                                 // this is set to false ...
453                                 control.in_use( true );
454                                 
455                                 // ... by this timeout
456                                 
457                                 // first disconnect any previous timeouts
458                                 control.in_use_connection.disconnect();
459                                 
460                                 // now connect a new timeout to call handle_control_timeout_event
461                                 // XXX should this use the GUI event loop (default) or the
462                                 // MIDI UI event loop ?
463
464                                 sigc::slot<bool> timeout_slot = sigc::bind 
465                                         (sigc::mem_fun( *this, &MackiePort::handle_control_timeout_event), &control);
466
467                                 control.in_use_connection = Glib::signal_timeout().connect (timeout_slot , control.in_use_timeout());
468
469                                 // emit the control event
470                                 control_event( *this, control, state );
471                                 break;
472                         }
473                         default:
474                                 cerr << "Do not understand control type " << control;
475                 }
476         }
477
478         catch( MackieControlException & e ) {
479                 MidiByteArray bytes( count, raw_bytes );
480                 cout << bytes << ' ' << e.what() << endl;
481         }
482
483         DEBUG_TRACE (DEBUG::MackieControl, string_compose ("finished MackiePort::handle_midi_any %1\n", bytes));
484 }