summaryrefslogtreecommitdiff
path: root/libs/surfaces/mackie/mackie_control_protocol.h
blob: 3d1dea1ed47dce3466d0550b481d03e4277ce3e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*
	Copyright (C) 2006,2007 John Anderson

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	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.
*/
#ifndef ardour_mackie_control_protocol_h
#define ardour_mackie_control_protocol_h

#include <vector>

#include <sys/time.h>
#include <pthread.h>

#include <glibmm/thread.h>

#include <ardour/types.h>
#include <ardour/session.h>
#include <midi++/types.h>

#include <control_protocol/control_protocol.h>
#include "midi_byte_array.h"
#include "controls.h"
#include "dummy_port.h"
#include "route_signal.h"
#include "mackie_button_handler.h"
#include "mackie_port.h"
#include "mackie_jog_wheel.h"
#include "timer.h"

namespace MIDI {
	class Port;
	class Parser;
}

namespace Mackie {
	class Surface;
}

/**
	This handles the plugin duties, and the midi encoding and decoding,
	and the signal callbacks, mostly from ARDOUR::Route.

	The model of the control surface is handled by classes in controls.h

	What happens is that each strip on the control surface has
	a corresponding route in ControlProtocol::route_table. When
	an incoming midi message is signaled, the correct route
	is looked up, and the relevant changes made to it.

	For each route currently in route_table, there's a RouteSignal object
	which encapsulates the signals that indicate that there are changes
	to be sent to the surface. The signals are handled by this class.

	Calls to signal handlers pass a Route object which is used to look
	up the relevant Strip in Surface. Then the state is retrieved from
	the Route and encoded as the correct midi message.
*/
class MackieControlProtocol
: public ARDOUR::ControlProtocol
, public Mackie::MackieButtonHandler
{
  public:
	MackieControlProtocol( ARDOUR::Session & );
	virtual ~MackieControlProtocol();

	int set_active (bool yn);

	XMLNode& get_state ();
	int set_state (const XMLNode&, int version = 3000);
  
	static bool probe();
	
	Mackie::Surface & surface();

   // control events
   void handle_control_event( Mackie::SurfacePort & port, Mackie::Control & control, const Mackie::ControlState & state );

  // strip/route related stuff
  public:	
	/// Signal handler for Route::solo
	void notify_solo_changed( Mackie::RouteSignal * );
	/// Signal handler for Route::mute
	void notify_mute_changed( Mackie::RouteSignal * );
	/// Signal handler for Route::record_enable_changed
	void notify_record_enable_changed( Mackie::RouteSignal * );
	/// Signal handler for Route::gain_changed ( from IO )
	void notify_gain_changed( Mackie::RouteSignal *, bool force_update = true );
	/// Signal handler for Route::name_change
	void notify_name_changed( Mackie::RouteSignal * );
	/// Signal handler from Panner::Change
	void notify_panner_changed( Mackie::RouteSignal *, bool force_update = true );
	/// Signal handler for new routes added
	void notify_route_added( ARDOUR::RouteList & );
	/// Signal handler for Route::active_changed
	void notify_active_changed( Mackie::RouteSignal * );
 
	void notify_remote_id_changed();

	/// rebuild the current bank. Called on route added/removed and
   /// remote id changed.
	void refresh_current_bank();

  // global buttons (ie button not part of strips)
  public:
   // button-related signals
	void notify_record_state_changed();
   void notify_transport_state_changed();
   // mainly to pick up punch-in and punch-out
	void notify_parameter_changed( std::string const & );
   void notify_solo_active_changed( bool );

	/// 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 );
  
	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 & );
	virtual Mackie::LedState frm_left_release( Mackie::Button & );

	virtual Mackie::LedState frm_right_press( Mackie::Button & );
	virtual Mackie::LedState frm_right_release( Mackie::Button & );

	virtual Mackie::LedState stop_press( Mackie::Button & );
	virtual Mackie::LedState stop_release( Mackie::Button & );

	virtual Mackie::LedState play_press( Mackie::Button & );
	virtual Mackie::LedState play_release( Mackie::Button & );

	virtual Mackie::LedState record_press( Mackie::Button & );
	virtual Mackie::LedState record_release( Mackie::Button & );

	virtual Mackie::LedState loop_press( Mackie::Button & );
	virtual Mackie::LedState loop_release( Mackie::Button & );

	virtual Mackie::LedState punch_in_press( Mackie::Button & );
	virtual Mackie::LedState punch_in_release( Mackie::Button & );

	virtual Mackie::LedState punch_out_press( Mackie::Button & );
	virtual Mackie::LedState punch_out_release( Mackie::Button & );

	virtual Mackie::LedState home_press( Mackie::Button & );
	virtual Mackie::LedState home_release( Mackie::Button & );

	virtual Mackie::LedState end_press( Mackie::Button & );
	virtual Mackie::LedState end_release( Mackie::Button & );
	
	virtual Mackie::LedState rewind_press( Mackie::Button & button );
	virtual Mackie::LedState rewind_release( Mackie::Button & button );

	virtual Mackie::LedState ffwd_press( Mackie::Button & button );
	virtual Mackie::LedState ffwd_release( Mackie::Button & button );

	// bank switching button handler methods from MackieButtonHandler
	virtual Mackie::LedState left_press( Mackie::Button & );
	virtual Mackie::LedState left_release( Mackie::Button & );

	virtual Mackie::LedState right_press( Mackie::Button & );
	virtual Mackie::LedState right_release( Mackie::Button & );

	virtual Mackie::LedState channel_left_press( Mackie::Button & );
	virtual Mackie::LedState channel_left_release( Mackie::Button & );

	virtual Mackie::LedState channel_right_press( Mackie::Button & );
	virtual Mackie::LedState channel_right_release( Mackie::Button & );
	
	virtual Mackie::LedState clicking_press( Mackie::Button & );
	virtual Mackie::LedState clicking_release( Mackie::Button & );
	
	virtual Mackie::LedState global_solo_press( Mackie::Button & );
	virtual Mackie::LedState global_solo_release( Mackie::Button & );
	
	// function buttons
	virtual Mackie::LedState marker_press( Mackie::Button & );
	virtual Mackie::LedState marker_release( Mackie::Button & );

	virtual Mackie::LedState drop_press( Mackie::Button & );
	virtual Mackie::LedState drop_release( Mackie::Button & );

	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 & );

	virtual Mackie::LedState scrub_press( Mackie::Button & );
	virtual Mackie::LedState scrub_release( Mackie::Button & );
	
   /// This is the main MCU port, ie not an extender port
	/// Only for use by JogWheel
	const Mackie::SurfacePort & mcu_port() const;
	Mackie::SurfacePort & mcu_port();
	ARDOUR::Session & get_session() { return *session; }
 
  protected:
	// create instances of MackiePort, depending on what's found in ardour.rc
	void create_ports();
  
	// shut down the surface
	void close();
  
	// create the Surface object, with the correct number
	// of strips for the currently connected ports and 
	// hook up the control event notification
	void initialize_surface();
  
	// This sets up the notifications and sets the
   // controls to the correct values
	void update_surface();
  
   // connects global (not strip) signals from the Session to here
   // so the surface can be notified of changes from the other UIs.
   void connect_session_signals();
  
   // set all controls to their zero position
	void zero_all();
	
	/**
		Fetch the set of routes to be considered for control by the
		surface. Excluding master, hidden and control routes, and inactive routes
	*/
	typedef std::vector<boost::shared_ptr<ARDOUR::Route> > Sorted;
	Sorted get_sorted_routes();
  
   // bank switching
   void switch_banks( int initial );
   void prev_track();
   void next_track();
  
   // delete all RouteSignal objects connecting Routes to Strips
   void clear_route_signals();
	
	typedef std::vector<Mackie::RouteSignal*> RouteSignals;
	RouteSignals route_signals;
	
   // return which of the ports a particular route_table
   // index belongs to
	Mackie::MackiePort & port_for_id( uint32_t index );

	/**
		Handle a button press for the control and return whether
		the corresponding light should be on or off.
	*/
	bool handle_strip_button( Mackie::Control &, Mackie::ButtonState, boost::shared_ptr<ARDOUR::Route> );

	/// thread started. Calls monitor_work.
	static void* _monitor_work (void* arg);
	
	/// Polling midi port(s) for incoming messages
	void* monitor_work ();
	
	/// rebuild the set of ports for this surface
	void update_ports();
	
	/// Returns true if there is pending data, false otherwise
	bool poll_ports();
	
	/// Trigger the MIDI::Parser
	void read_ports();

	void add_port( MIDI::Port &, int number );

	/**
		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
		for new data.
	*/
	void handle_port_init( Mackie::SurfacePort * );

	/// notification from a MackiePort that it's now active
	void handle_port_active( Mackie::SurfacePort * );
	
	/// notification from a MackiePort that it's now inactive
	void handle_port_inactive( Mackie::SurfacePort * );
	
	boost::shared_ptr<ARDOUR::Route> master_route();
	Mackie::Strip & master_strip();

  private:
	boost::shared_ptr<Mackie::RouteSignal> master_route_signal;
  
   static const char * default_port_name;
  
	/// The Midi port(s) connected to the units
	typedef std::vector<Mackie::MackiePort*> MackiePorts;
	MackiePorts _ports;
  
	/// Sometimes the real port goes away, and we want to contain the breakage
	Mackie::DummyPort _dummy_port;
  
   // the thread that polls the ports for incoming midi data
	pthread_t thread;
  
	/// The initial remote_id of the currently switched in bank.
   uint32_t _current_initial_bank;
	
   /// protects the port list, and polling structures
	Glib::Mutex update_mutex;
  
	/// Protects set_active, and allows waiting on the poll thread
	Glib::Cond update_cond;

	// because sigc::trackable doesn't seem to be working
	std::vector<sigc::connection> _connections;
	std::back_insert_iterator<std::vector<sigc::connection> > connections_back;

   /// The representation of the physical controls on the surface.
  	Mackie::Surface * _surface;
	
	/// If a port is opened or closed, this will be
	/// true until the port configuration is updated;
	bool _ports_changed;

	bool _polling;
	struct pollfd * pfd;
	int nfds;
	
	bool _transport_previously_rolling;
	
	// timer for two quick marker left presses
	Mackie::Timer _frm_left_last;
	
	Mackie::JogWheel _jog_wheel;
	
	// 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