/* Copyright (C) 2009 Paul Davis 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. */ #include "boost/lambda/lambda.hpp" #include "pbd/control_math.h" #include "ardour/session.h" #include "ardour/track.h" #include "ardour/monitor_control.h" #include "ardour/dB.h" #include "ardour/meter.h" #include "ardour/solo_isolate_control.h" #include "osc.h" #include "osc_route_observer.h" #include "pbd/i18n.h" using namespace std; using namespace PBD; using namespace ARDOUR; using namespace ArdourSurface; OSCRouteObserver::OSCRouteObserver (boost::shared_ptr s, uint32_t ss, ArdourSurface::OSC::OSCSurface* su) : _strip (s) ,ssid (ss) ,sur (su) ,_last_gain (0.0) ,_init (true) { addr = lo_address_new_from_url (sur->remote_url.c_str()); gainmode = sur->gainmode; feedback = sur->feedback; as = ARDOUR::Off; if (feedback[0]) { // buttons are separate feedback _strip->PropertyChanged.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::name_changed, this, boost::lambda::_1), OSC::instance()); name_changed (ARDOUR::Properties::name); _strip->mute_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/mute"), _strip->mute_control()), OSC::instance()); send_change_message ("/strip/mute", _strip->mute_control()); _strip->solo_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/solo"), _strip->solo_control()), OSC::instance()); send_change_message ("/strip/solo", _strip->solo_control()); _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()); send_change_message ("/strip/solo_iso", _strip->solo_isolate_control()); _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()); send_change_message ("/strip/solo_safe", _strip->solo_safe_control()); boost::shared_ptr track = boost::dynamic_pointer_cast (_strip); if (track) { track->monitoring_control()->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_monitor_status, this, track->monitoring_control()), OSC::instance()); send_monitor_status (track->monitoring_control()); } boost::shared_ptr rec_controllable = _strip->rec_enable_control (); if (rec_controllable) { rec_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_change_message, this, X_("/strip/recenable"), _strip->rec_enable_control()), OSC::instance()); send_change_message ("/strip/recenable", _strip->rec_enable_control()); } boost::shared_ptr recsafe_controllable = _strip->rec_safe_control (); if (rec_controllable) { 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()); send_change_message ("/strip/record_safe", _strip->rec_safe_control()); } _strip->presentation_info().PropertyChanged.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_select_status, this, _1), OSC::instance()); send_select_status (ARDOUR::Properties::selected); } if (feedback[1]) { // level controls boost::shared_ptr gain_cont = _strip->gain_control(); if (gainmode) { gain_cont->alist()->automation_state_changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::gain_automation, this, X_("/strip/fader")), OSC::instance()); gain_cont->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_gain_message, this, X_("/strip/fader"), gain_cont), OSC::instance()); gain_automation ("/strip/fader"); } else { gain_cont->alist()->automation_state_changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::gain_automation, this, X_("/strip/gain")), OSC::instance()); gain_cont->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_gain_message, this, X_("/strip/gain"), gain_cont), OSC::instance()); gain_automation ("/strip/gain"); } boost::shared_ptr trim_controllable = boost::dynamic_pointer_cast(_strip->trim_control()); if (trim_controllable) { trim_controllable->Changed.connect (strip_connections, MISSING_INVALIDATOR, boost::bind (&OSCRouteObserver::send_trim_message, this, X_("/strip/trimdB"), _strip->trim_control()), OSC::instance()); send_trim_message ("/strip/trimdB", _strip->trim_control()); } boost::shared_ptr pan_controllable = boost::dynamic_pointer_cast(_strip->pan_azimuth_control()); if (pan_controllable) { 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()); send_change_message ("/strip/pan_stereo_position", _strip->pan_azimuth_control()); } } _init = false; tick(); } OSCRouteObserver::~OSCRouteObserver () { _init = true; strip_connections.drop_connections (); if (sur->no_clear) { // some surfaces destroy their own strips and don't need the extra noise lo_address_free (addr); return; } // all strip buttons should be off and faders 0 and etc. clear_strip ("/strip/expand", 0); if (feedback[0]) { // buttons are separate feedback text_with_id ("/strip/name", ssid, " "); clear_strip ("/strip/mute", 0); clear_strip ("/strip/solo", 0); clear_strip ("/strip/recenable", 0); clear_strip ("/strip/record_safe", 0); clear_strip ("/strip/monitor_input", 0); clear_strip ("/strip/monitor_disk", 0); clear_strip ("/strip/gui_select", 0); clear_strip ("/strip/select", 0); } if (feedback[1]) { // level controls if (gainmode) { clear_strip ("/strip/fader", 0); } else { clear_strip ("/strip/gain", -193); } clear_strip ("/strip/trimdB", 0); clear_strip ("/strip/pan_stereo_position", 0.5); } if (feedback[9]) { clear_strip ("/strip/signal", 0); } if (feedback[7]) { if (gainmode) { clear_strip ("/strip/meter", 0); } else { clear_strip ("/strip/meter", -193); } }else if (feedback[8]) { clear_strip ("/strip/meter", 0); } lo_address_free (addr); } void OSCRouteObserver::tick () { if (_init) { return; } if (feedback[7] || feedback[8] || feedback[9]) { // meters enabled // the only meter here is master float now_meter; if (_strip->peak_meter()) { now_meter = _strip->peak_meter()->meter_level(0, MeterMCP); } else { now_meter = -193; } if (now_meter < -120) now_meter = -193; if (_last_meter != now_meter) { if (feedback[7] || feedback[8]) { string path = "/strip/meter"; lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } if (gainmode && feedback[7]) { lo_message_add_float (msg, ((now_meter + 94) / 100)); lo_send_message (addr, path.c_str(), msg); } else if ((!gainmode) && feedback[7]) { lo_message_add_float (msg, now_meter); lo_send_message (addr, path.c_str(), msg); } else if (feedback[8]) { uint32_t ledlvl = (uint32_t)(((now_meter + 54) / 3.75)-1); uint16_t ledbits = ~(0xfff<name()); } gain_timeout--; } if (trim_timeout) { if (trim_timeout == 1) { text_with_id ("/strip/name", ssid, _strip->name()); } trim_timeout--; } if (as == ARDOUR::Play || as == ARDOUR::Touch) { if(_last_gain != _strip->gain_control()->get_value()) { _last_gain = _strip->gain_control()->get_value(); if (gainmode) { send_gain_message ("/strip/fader", _strip->gain_control()); } else { send_gain_message ("/strip/gain", _strip->gain_control()); } } } } } void OSCRouteObserver::name_changed (const PBD::PropertyChange& what_changed) { if (!what_changed.contains (ARDOUR::Properties::name)) { return; } if (!_strip) { return; } text_with_id ("/strip/name", ssid, _strip->name()); } void OSCRouteObserver::send_change_message (string path, boost::shared_ptr controllable) { lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } float val = controllable->get_value(); lo_message_add_float (msg, (float) controllable->internal_to_interface (val)); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::text_with_id (string path, uint32_t id, string name) { lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, id); } lo_message_add_string (msg, name.c_str()); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::send_monitor_status (boost::shared_ptr controllable) { int disk, input; float val = controllable->get_value(); switch ((int) val) { case 1: disk = 0; input = 1; break; case 2: disk = 1; input = 0; break; default: disk = 0; input = 0; } lo_message msg = lo_message_new (); string path = "/strip/monitor_input"; if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } lo_message_add_int32 (msg, (float) input); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); msg = lo_message_new (); path = "/strip/monitor_disk"; if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } lo_message_add_int32 (msg, (float) disk); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::send_trim_message (string path, boost::shared_ptr controllable) { if (gainmode) { text_with_id ("/strip/name", ssid, string_compose ("%1%2%3", std::fixed, std::setprecision(2), accurate_coefficient_to_dB (controllable->get_value()))); trim_timeout = 8; } lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } lo_message_add_float (msg, (float) accurate_coefficient_to_dB (controllable->get_value())); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::send_gain_message (string path, boost::shared_ptr controllable) { lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } if (gainmode) { lo_message_add_float (msg, controllable->internal_to_interface (controllable->get_value())); text_with_id ("/strip/name", ssid, string_compose ("%1%2%3", std::fixed, std::setprecision(2), accurate_coefficient_to_dB (controllable->get_value()))); gain_timeout = 8; } else { if (controllable->get_value() < 1e-15) { lo_message_add_float (msg, -200); } else { lo_message_add_float (msg, accurate_coefficient_to_dB (controllable->get_value())); } } lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::gain_automation (string path) { lo_message msg = lo_message_new (); string apath = string_compose ("%1/automation", path); string npath = string_compose ("%1/automation_name", path); if (feedback[2]) { apath = set_path (apath); } else { lo_message_add_int32 (msg, ssid); } boost::shared_ptr control = _strip->gain_control(); send_gain_message (path, control); as = control->alist()->automation_state(); string auto_name; float output = 0; switch (as) { case ARDOUR::Off: output = 0; auto_name = "Manual"; break; case ARDOUR::Play: output = 1; auto_name = "Play"; break; case ARDOUR::Write: output = 2; auto_name = "Write"; break; case ARDOUR::Touch: output = 3; auto_name = "Touch"; break; default: break; } lo_message_add_float (msg, output); lo_send_message (addr, apath.c_str(), msg); lo_message_free (msg); text_with_id (npath, ssid, auto_name); } string OSCRouteObserver::set_path (string path) { if (feedback[2]) { path = string_compose ("%1/%2", path, ssid); } return path; } void OSCRouteObserver::clear_strip (string path, float val) { lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } lo_message_add_float (msg, val); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } void OSCRouteObserver::send_select_status (const PropertyChange& what) { if (what == PropertyChange(ARDOUR::Properties::selected)) { if (_strip) { string path = "/strip/select"; lo_message msg = lo_message_new (); if (feedback[2]) { path = set_path (path); } else { lo_message_add_int32 (msg, ssid); } lo_message_add_float (msg, _strip->is_selected()); lo_send_message (addr, path.c_str(), msg); lo_message_free (msg); } } }