9e35a084217a812c1946b387dbcc90094ec9d485
[ardour.git] / libs / surfaces / osc / osc_route_observer.cc
1 /*
2     Copyright (C) 2009 Paul Davis
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 */
19
20 #include "boost/lambda/lambda.hpp"
21
22 #include "pbd/control_math.h"
23
24 #include "ardour/session.h"
25 #include "ardour/track.h"
26 #include "ardour/monitor_control.h"
27 #include "ardour/dB.h"
28 #include "ardour/meter.h"
29 #include "ardour/solo_isolate_control.h"
30
31 #include "osc.h"
32 #include "osc_route_observer.h"
33
34 #include "pbd/i18n.h"
35
36 using namespace std;
37 using namespace PBD;
38 using namespace ARDOUR;
39 using namespace ArdourSurface;
40
41 OSCRouteObserver::OSCRouteObserver (boost::shared_ptr<Stripable> s, uint32_t ss, ArdourSurface::OSC::OSCSurface* su)
42         : _strip (s)
43         ,ssid (ss)
44         ,sur (su)
45         ,_last_gain (0.0)
46         ,_last_trim (0.0)
47         ,_init (true)
48 {
49         addr = lo_address_new_from_url  (sur->remote_url.c_str());
50         gainmode = sur->gainmode;
51         feedback = sur->feedback;
52         as = ARDOUR::Off;
53
54         if (feedback[0]) { // buttons are separate feedback
55                 _strip->PropertyChanged.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::name_changed, this, boost::lambda::_1), OSC::instance());
56                 name_changed (ARDOUR::Properties::name);
57
58                 _strip->mute_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/mute"), _strip->mute_control()), OSC::instance());
59                 send_change_message ("/strip/mute", _strip->mute_control());
60
61                 _strip->solo_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/solo"), _strip->solo_control()), OSC::instance());
62                 send_change_message ("/strip/solo", _strip->solo_control());
63
64                 if (_strip->solo_isolate_control()) {
65                         _strip->solo_isolate_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, bind (&OSCRouteObserver::send_change_message, this, X_("/strip/solo_iso"), _strip->solo_isolate_control()), OSC::instance());
66                         send_change_message ("/strip/solo_iso", _strip->solo_isolate_control());
67                 }
68
69                 if (_strip->solo_safe_control()) {
70                         _strip->solo_safe_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, bind (&OSCRouteObserver::send_change_message, this, X_("/strip/solo_safe"), _strip->solo_safe_control()), OSC::instance());
71                         send_change_message ("/strip/solo_safe", _strip->solo_safe_control());
72                 }
73
74                 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (_strip);
75                 if (track) {
76                         track->monitoring_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_monitor_status, this, track->monitoring_control()), OSC::instance());
77                         send_monitor_status (track->monitoring_control());
78                 }
79
80                 boost::shared_ptr<AutomationControl> rec_controllable = _strip->rec_enable_control ();
81                 if (rec_controllable) {
82                         rec_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/recenable"), _strip->rec_enable_control()), OSC::instance());
83                         send_change_message ("/strip/recenable", _strip->rec_enable_control());
84                 }
85                 boost::shared_ptr<AutomationControl> recsafe_controllable = _strip->rec_safe_control ();
86                 if (rec_controllable) {
87                         recsafe_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/record_safe"), _strip->rec_safe_control()), OSC::instance());
88                         send_change_message ("/strip/record_safe", _strip->rec_safe_control());
89                 }
90                 _strip->presentation_info().PropertyChanged.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_select_status, this, _1), OSC::instance());
91                 send_select_status (ARDOUR::Properties::selected);
92         }
93
94         if (feedback[1]) { // level controls
95                 boost::shared_ptr<GainControl> gain_cont = _strip->gain_control();
96                 if (gainmode) {
97                         gain_cont->alist()->automation_state_changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::gain_automation, this, X_("/strip/fader")), OSC::instance());
98                         gain_cont->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_gain_message, this, X_("/strip/fader"), gain_cont), OSC::instance());
99                         gain_automation ("/strip/fader");
100                 } else {
101                         gain_cont->alist()->automation_state_changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::gain_automation, this, X_("/strip/gain")), OSC::instance());
102                         gain_cont->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_gain_message, this, X_("/strip/gain"), gain_cont), OSC::instance());
103                         gain_automation ("/strip/gain");
104                 }
105
106                 boost::shared_ptr<Controllable> trim_controllable = boost::dynamic_pointer_cast<Controllable>(_strip->trim_control());
107                 if (trim_controllable) {
108                         trim_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_trim_message, this, X_("/strip/trimdB"), _strip->trim_control()), OSC::instance());
109                         send_trim_message ("/strip/trimdB", _strip->trim_control());
110                 }
111
112                 boost::shared_ptr<Controllable> pan_controllable = boost::dynamic_pointer_cast<Controllable>(_strip->pan_azimuth_control());
113                 if (pan_controllable) {
114                         pan_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/pan_stereo_position"), _strip->pan_azimuth_control()), OSC::instance());
115                         send_change_message ("/strip/pan_stereo_position", _strip->pan_azimuth_control());
116                 }
117         }
118         _init = false;
119         tick();
120 }
121
122 OSCRouteObserver::~OSCRouteObserver ()
123 {
124         _init = true;
125
126         strip_connections.drop_connections ();
127         if (sur->no_clear) {
128                 // some surfaces destroy their own strips and don't need the extra noise
129                 lo_address_free (addr);
130                 return;
131         }
132
133         // all strip buttons should be off and faders 0 and etc.
134         clear_strip ("/strip/expand", 0);
135         if (feedback[0]) { // buttons are separate feedback
136                 text_with_id ("/strip/name", ssid, " ");
137                 clear_strip ("/strip/mute", 0);
138                 clear_strip ("/strip/solo", 0);
139                 clear_strip ("/strip/recenable", 0);
140                 clear_strip ("/strip/record_safe", 0);
141                 clear_strip ("/strip/monitor_input", 0);
142                 clear_strip ("/strip/monitor_disk", 0);
143                 clear_strip ("/strip/gui_select", 0);
144                 clear_strip ("/strip/select", 0);
145         }
146         if (feedback[1]) { // level controls
147                 if (gainmode) {
148                         clear_strip ("/strip/fader", 0);
149                 } else {
150                         clear_strip ("/strip/gain", -193);
151                 }
152                 clear_strip ("/strip/trimdB", 0);
153                 clear_strip ("/strip/pan_stereo_position", 0.5);
154         }
155         if (feedback[9]) {
156                 clear_strip ("/strip/signal", 0);
157         }
158         if (feedback[7]) {
159                 if (gainmode) {
160                         clear_strip ("/strip/meter", 0);
161                 } else {
162                         clear_strip ("/strip/meter", -193);
163                 }
164         }else if (feedback[8]) {
165                 clear_strip ("/strip/meter", 0);
166         }
167
168         lo_address_free (addr);
169 }
170
171 void
172 OSCRouteObserver::tick ()
173 {
174         if (_init) {
175                 return;
176         }
177         if (feedback[7] || feedback[8] || feedback[9]) { // meters enabled
178                 // the only meter here is master
179                 float now_meter;
180                 if (_strip->peak_meter()) {
181                         now_meter = _strip->peak_meter()->meter_level(0, MeterMCP);
182                 } else {
183                         now_meter = -193;
184                 }
185                 if (now_meter < -120) now_meter = -193;
186                 if (_last_meter != now_meter) {
187                         if (feedback[7] || feedback[8]) {
188                                 string path = "/strip/meter";
189                                 lo_message msg = lo_message_new ();
190                                 if (feedback[2]) {
191                                         path = set_path (path);
192                                 } else {
193                                         lo_message_add_int32 (msg, ssid);
194                                 }
195                                 if (gainmode && feedback[7]) {
196                                         lo_message_add_float (msg, ((now_meter + 94) / 100));
197                                         lo_send_message (addr, path.c_str(), msg);
198                                 } else if ((!gainmode) && feedback[7]) {
199                                         lo_message_add_float (msg, now_meter);
200                                         lo_send_message (addr, path.c_str(), msg);
201                                 } else if (feedback[8]) {
202                                         uint32_t ledlvl = (uint32_t)(((now_meter + 54) / 3.75)-1);
203                                         uint16_t ledbits = ~(0xfff<<ledlvl);
204                                         lo_message_add_int32 (msg, ledbits);
205                                         lo_send_message (addr, path.c_str(), msg);
206                                 }
207                                 lo_message_free (msg);
208                         }
209                         if (feedback[9]) {
210                                 string path = "/strip/signal";
211                                 lo_message msg = lo_message_new ();
212                                 if (feedback[2]) {
213                                         path = set_path (path);
214                                 } else {
215                                         lo_message_add_int32 (msg, ssid);
216                                 }
217                                 float signal;
218                                 if (now_meter < -40) {
219                                         signal = 0;
220                                 } else {
221                                         signal = 1;
222                                 }
223                                 lo_message_add_float (msg, signal);
224                                 lo_send_message (addr, path.c_str(), msg);
225                                 lo_message_free (msg);
226                         }
227                 }
228                 _last_meter = now_meter;
229
230         }
231         if (feedback[1]) {
232                 if (gain_timeout) {
233                         if (gain_timeout == 1) {
234                                 text_with_id ("/strip/name", ssid, _strip->name());
235                         }
236                         gain_timeout--;
237                 }
238                 if (trim_timeout) {
239                         if (trim_timeout == 1) {
240                                 text_with_id ("/strip/name", ssid, _strip->name());
241                         }
242                         trim_timeout--;
243                 }
244                 if (as == ARDOUR::Play ||  as == ARDOUR::Touch) {
245                         if(_last_gain != _strip->gain_control()->get_value()) {
246                                 _last_gain = _strip->gain_control()->get_value();
247                                 if (gainmode) {
248                                         send_gain_message ("/strip/fader", _strip->gain_control());
249                                         gain_timeout = 8;
250                                 } else {
251                                         send_gain_message ("/strip/gain", _strip->gain_control());
252                                 }
253                         }
254                 }
255         }
256
257 }
258
259 void
260 OSCRouteObserver::name_changed (const PBD::PropertyChange& what_changed)
261 {
262         if (!what_changed.contains (ARDOUR::Properties::name)) {
263             return;
264         }
265
266         if (!_strip) {
267                 return;
268         }
269         text_with_id ("/strip/name", ssid, _strip->name());
270 }
271
272 void
273 OSCRouteObserver::send_change_message (string path, boost::shared_ptr<Controllable> controllable)
274 {
275         lo_message msg = lo_message_new ();
276
277         if (feedback[2]) {
278                 path = set_path (path);
279         } else {
280                 lo_message_add_int32 (msg, ssid);
281         }
282         float val = controllable->get_value();
283         lo_message_add_float (msg, (float) controllable->internal_to_interface (val));
284
285         lo_send_message (addr, path.c_str(), msg);
286         lo_message_free (msg);
287 }
288
289 void
290 OSCRouteObserver::text_with_id (string path, uint32_t id, string name)
291 {
292         lo_message msg = lo_message_new ();
293         if (feedback[2]) {
294                 path = set_path (path);
295         } else {
296                 lo_message_add_int32 (msg, id);
297         }
298
299         lo_message_add_string (msg, name.c_str());
300
301         lo_send_message (addr, path.c_str(), msg);
302         lo_message_free (msg);
303 }
304
305 void
306 OSCRouteObserver::send_monitor_status (boost::shared_ptr<Controllable> controllable)
307 {
308         int disk, input;
309         float val = controllable->get_value();
310         switch ((int) val) {
311                 case 1:
312                         disk = 0;
313                         input = 1;
314                         break;
315                 case 2:
316                         disk = 1;
317                         input = 0;
318                         break;
319                 default:
320                         disk = 0;
321                         input = 0;
322         }
323
324         lo_message msg = lo_message_new ();
325         string path = "/strip/monitor_input";
326         if (feedback[2]) {
327                 path = set_path (path);
328         } else {
329                 lo_message_add_int32 (msg, ssid);
330         }
331         lo_message_add_int32 (msg, (float) input);
332         lo_send_message (addr, path.c_str(), msg);
333         lo_message_free (msg);
334
335         msg = lo_message_new ();
336         path = "/strip/monitor_disk";
337         if (feedback[2]) {
338                 path = set_path (path);
339         } else {
340                 lo_message_add_int32 (msg, ssid);
341         }
342         lo_message_add_int32 (msg, (float) disk);
343         lo_send_message (addr, path.c_str(), msg);
344         lo_message_free (msg);
345
346 }
347
348 void
349 OSCRouteObserver::send_trim_message (string path, boost::shared_ptr<Controllable> controllable)
350 {
351         if (_last_trim != controllable->get_value()) {
352                 _last_trim = controllable->get_value();
353         } else {
354                 return;
355         }
356         if (gainmode) {
357                 text_with_id ("/strip/name", ssid, string_compose ("%1%2%3", std::fixed, std::setprecision(2), accurate_coefficient_to_dB (controllable->get_value())));
358                 trim_timeout = 8;
359         }
360
361         lo_message msg = lo_message_new ();
362
363         if (feedback[2]) {
364                 path = set_path (path);
365         } else {
366                 lo_message_add_int32 (msg, ssid);
367         }
368
369         lo_message_add_float (msg, (float) accurate_coefficient_to_dB (controllable->get_value()));
370
371         lo_send_message (addr, path.c_str(), msg);
372         lo_message_free (msg);
373 }
374
375 void
376 OSCRouteObserver::send_gain_message (string path, boost::shared_ptr<Controllable> controllable)
377 {
378         if (_last_gain != controllable->get_value()) {
379                 _last_gain = controllable->get_value();
380         } else {
381                 return;
382         }
383         lo_message msg = lo_message_new ();
384
385         if (feedback[2]) {
386                 path = set_path (path);
387         } else {
388                 lo_message_add_int32 (msg, ssid);
389         }
390
391         if (gainmode) {
392                 lo_message_add_float (msg, controllable->internal_to_interface (controllable->get_value()));
393                 text_with_id ("/strip/name", ssid, string_compose ("%1%2%3", std::fixed, std::setprecision(2), accurate_coefficient_to_dB (controllable->get_value())));
394                 gain_timeout = 8;
395         } else {
396                 if (controllable->get_value() < 1e-15) {
397                         lo_message_add_float (msg, -200);
398                 } else {
399                         lo_message_add_float (msg, accurate_coefficient_to_dB (controllable->get_value()));
400                 }
401         }
402
403         lo_send_message (addr, path.c_str(), msg);
404         lo_message_free (msg);
405 }
406
407 void
408 OSCRouteObserver::gain_automation (string path)
409 {
410         lo_message msg = lo_message_new ();
411         string apath = string_compose ("%1/automation", path);
412         string npath = string_compose ("%1/automation_name", path);
413
414         if (feedback[2]) {
415                 apath = set_path (apath);
416         } else {
417                 lo_message_add_int32 (msg, ssid);
418         }
419
420         boost::shared_ptr<GainControl> control = _strip->gain_control();
421         send_gain_message (path, control);
422         as = control->alist()->automation_state();
423         string auto_name;
424         float output = 0;
425         switch (as) {
426                 case ARDOUR::Off:
427                         output = 0;
428                         auto_name = "Manual";
429                         break;
430                 case ARDOUR::Play:
431                         output = 1;
432                         auto_name = "Play";
433                         break;
434                 case ARDOUR::Write:
435                         output = 2;
436                         auto_name = "Write";
437                         break;
438                 case ARDOUR::Touch:
439                         output = 3;
440                         auto_name = "Touch";
441                         break;
442                 default:
443                         break;
444         }
445
446         lo_message_add_float (msg, output);
447         lo_send_message (addr, apath.c_str(), msg);
448         lo_message_free (msg);
449         text_with_id (npath, ssid, auto_name);
450 }
451
452 string
453 OSCRouteObserver::set_path (string path)
454 {
455         if (feedback[2]) {
456                 path = string_compose ("%1/%2", path, ssid);
457         }
458         return path;
459 }
460
461 void
462 OSCRouteObserver::clear_strip (string path, float val)
463 {
464         lo_message msg = lo_message_new ();
465         if (feedback[2]) {
466                 path = set_path (path);
467         } else {
468                 lo_message_add_int32 (msg, ssid);
469         }
470         lo_message_add_float (msg, val);
471
472         lo_send_message (addr, path.c_str(), msg);
473         lo_message_free (msg);
474
475 }
476
477 void
478 OSCRouteObserver::send_select_status (const PropertyChange& what)
479 {
480         if (what == PropertyChange(ARDOUR::Properties::selected)) {
481                 if (_strip) {
482                         string path = "/strip/select";
483
484                         lo_message msg = lo_message_new ();
485                         if (feedback[2]) {
486                                 path = set_path (path);
487                         } else {
488                                 lo_message_add_int32 (msg, ssid);
489                         }
490                         lo_message_add_float (msg, _strip->is_selected());
491                         lo_send_message (addr, path.c_str(), msg);
492                         lo_message_free (msg);
493                 }
494         }
495 }