ardour/libs/surfaces/osc/osc.cc
Paul Davis 5ad7071614 OSC: use namespaces and deletion to avoid conflicts over LocationMarker type
note that this type is also declared by the GTK2 GUI, and at runtime that was the definition being
used, which leads to crashes due to differences in the type definition.
2025-11-25 17:59:20 -07:00

6719 lines
179 KiB
C++

/*
* Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
* Copyright (C) 2009-2014 David Robillard <d@drobilla.net>
* Copyright (C) 2009-2017 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2012-2016 Tim Mayberry <mojofunk@gmail.com>
* Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
* Copyright (C) 2015-2016 Ben Loftis <ben@harrisonconsoles.com>
* Copyright (C) 2015-2018 John Emmas <john@creativepost.co.uk>
* Copyright (C) 2015 Johannes Mueller <github@johannes-mueller.org>
* Copyright (C) 2016-2022 Len Ovens <len@ovenwerks.net>
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <algorithm>
#include <unistd.h>
#include <fcntl.h>
#include "pbd/gstdio_compat.h"
#include <glibmm.h>
#include "pbd/control_math.h"
#include "pbd/controllable.h"
#include <pbd/convert.h>
#include <pbd/pthread_utils.h>
#include <pbd/file_utils.h>
#include <pbd/failed_constructor.h>
#include "temporal/timeline.h"
#include "ardour/amp.h"
#include "ardour/session.h"
#include "ardour/route.h"
#include "ardour/route_group.h"
#include "ardour/audio_track.h"
#include "ardour/midi_track.h"
#include "ardour/mixer_scene.h"
#include "ardour/vca.h"
#include "ardour/monitor_control.h"
#include "ardour/dB.h"
#include "ardour/filesystem_paths.h"
#include "ardour/panner.h"
#include "ardour/panner_shell.h"
#include "ardour/pannable.h"
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
#include "ardour/presentation_info.h"
#include "ardour/profile.h"
#include "ardour/send.h"
#include "ardour/internal_send.h"
#include "ardour/phase_control.h"
#include "ardour/solo_isolate_control.h"
#include "ardour/solo_safe_control.h"
#include "ardour/vca_manager.h"
#include "ardour/well_known_enum.h"
#include "ardour/zeroconf.h"
#include "osc_select_observer.h"
#include "osc.h"
#include "osc_controllable.h"
#include "osc_route_observer.h"
#include "osc_global_observer.h"
#include "osc_cue_observer.h"
#include "pbd/i18n.h"
using namespace ARDOUR;
using namespace std;
using namespace Glib;
using namespace ArdourSurface;
#include "pbd/abstract_ui.inc.cc" // instantiate template
#ifdef DEBUG
static void error_callback(int num, const char *m, const char *path)
{
fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m);
}
#else
static void error_callback(int, const char *, const char *)
{
}
#endif
OSC::OSC (Session& s, uint32_t port)
: ControlProtocol (s, X_("Open Sound Control (OSC)"))
, AbstractUI<OSCUIRequest> (name())
, local_server (0)
, remote_server (0)
, _port(port)
, _ok (true)
, _shutdown (false)
, _osc_server (0)
, _osc_unix_server (0)
, _debugmode (Off)
, address_only (true)
, remote_port ("8000")
, default_banksize (0)
, default_strip (31)
, default_feedback (0)
, default_gainmode (0)
, default_send_size (0)
, default_plugin_size (0)
, tick (true)
, bank_dirty (false)
, observer_busy (true)
, scrub_speed (0)
, scrub_place (0)
, scrub_time (0)
, global_init (true)
, _zeroconf (0)
, gui (0)
{
session->Exported.connect (*this, MISSING_INVALIDATOR, std::bind (&OSC::session_exported, this, _1, _2), this);
}
OSC::~OSC()
{
tick = false;
stop ();
}
void
OSC::do_request (OSCUIRequest* req)
{
if (req->type == CallSlot) {
call_slot (MISSING_INVALIDATOR, req->the_slot);
} else if (req->type == Quit) {
stop ();
}
}
int
OSC::set_active (bool yn)
{
if (yn != active()) {
if (yn) {
if (start ()) {
return -1;
}
} else {
if (stop ()) {
return -1;
}
}
}
return ControlProtocol::set_active (yn);
}
bool
OSC::get_active () const
{
return _osc_server != 0;
}
int
OSC::start ()
{
char tmpstr[255];
if (_osc_server) {
/* already started */
return 0;
}
for (int j=0; j < 20; ++j) {
snprintf(tmpstr, sizeof(tmpstr), "%d", _port);
//if ((_osc_server = lo_server_new_with_proto (tmpstr, LO_TCP, error_callback))) {
// break;
//}
if ((_osc_server = lo_server_new (tmpstr, error_callback))) {
break;
}
#ifdef DEBUG
cerr << "can't get osc at port: " << _port << endl;
#endif
_port++;
continue;
}
if (!_osc_server) {
return 1;
}
#ifdef ARDOUR_OSC_UNIX_SERVER
// APPEARS sluggish for now
// attempt to create unix socket server too
snprintf(tmpstr, sizeof(tmpstr), "/tmp/sooperlooper_XXXXXX");
int fd = mkstemp(tmpstr);
if (fd >= 0 ) {
::g_unlink (tmpstr);
close (fd);
_osc_unix_server = lo_server_new (tmpstr, error_callback);
if (_osc_unix_server) {
_osc_unix_socket_path = tmpstr;
}
}
#endif
std::string server_url (get_server_url ());
PBD::info << "OSC @ " << server_url << endmsg;
_zeroconf = new ZeroConf ("_osc._udp", _port, lo_url_get_hostname (server_url.c_str()));
std::string url_file;
if (find_file (ardour_config_search_path(), "osc_url", url_file)) {
_osc_url_file = url_file;
if (g_file_set_contents (_osc_url_file.c_str(), get_server_url().c_str(), -1, NULL)) {
cerr << "Couldn't write '" << _osc_url_file << "'" <<endl;
}
}
observer_busy = false;
register_callbacks();
session_loaded (*session);
// lo_server_thread_add_method(_sthread, NULL, NULL, OSC::_dummy_handler, this);
/* startup the event loop thread */
BaseUI::run ();
// start timers for metering, timecode and heartbeat.
// timecode and metering run at 100
Glib::RefPtr<Glib::TimeoutSource> periodic_timeout = Glib::TimeoutSource::create (100); // milliseconds
periodic_connection = periodic_timeout->connect (sigc::mem_fun (*this, &OSC::periodic));
periodic_timeout->attach (main_loop()->get_context());
// catch track reordering
// receive routes added
session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, std::bind (&OSC::notify_routes_added, this, _1), this);
// receive VCAs added
session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, std::bind (&OSC::notify_vca_added, this, _1), this);
// order changed
PresentationInfo::Change.connect (session_connections, MISSING_INVALIDATOR, std::bind (&OSC::recalcbanks, this), this);
_select = ControlProtocol::first_selected_stripable();
if(!_select) {
_select = session->master_out ();
}
return 0;
}
void
OSC::thread_init ()
{
if (_osc_unix_server) {
Glib::RefPtr<IOSource> src = IOSource::create (lo_server_get_socket_fd (_osc_unix_server), IO_IN|IO_HUP|IO_ERR);
src->connect (sigc::bind (sigc::mem_fun (*this, &OSC::osc_input_handler), _osc_unix_server));
src->attach (_main_loop->get_context());
local_server = src->gobj();
g_source_ref (local_server);
}
if (_osc_server) {
#ifdef PLATFORM_WINDOWS
Glib::RefPtr<IOChannel> chan = Glib::IOChannel::create_from_win32_socket (lo_server_get_socket_fd (_osc_server));
Glib::RefPtr<IOSource> src = IOSource::create (chan, IO_IN|IO_HUP|IO_ERR);
#else
Glib::RefPtr<IOSource> src = IOSource::create (lo_server_get_socket_fd (_osc_server), IO_IN|IO_HUP|IO_ERR);
#endif
src->connect (sigc::bind (sigc::mem_fun (*this, &OSC::osc_input_handler), _osc_server));
src->attach (_main_loop->get_context());
remote_server = src->gobj();
g_source_ref (remote_server);
}
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
SessionEvent::create_per_thread_pool (event_loop_name(), 128);
}
int
OSC::stop ()
{
tear_down_gui ();
periodic_connection.disconnect ();
session_connections.drop_connections ();
delete _zeroconf;
_zeroconf = NULL;
// clear surfaces
observer_busy = true;
for (uint32_t it = 0; it < _surface.size (); ++it) {
OSCSurface* sur = &_surface[it];
surface_destroy (sur);
}
_surface.clear();
/* stop main loop */
if (local_server) {
g_source_destroy (local_server);
g_source_unref (local_server);
local_server = 0;
}
if (remote_server) {
g_source_destroy (remote_server);
g_source_unref (remote_server);
remote_server = 0;
}
BaseUI::quit ();
if (_osc_server) {
lo_server_free (_osc_server);
_osc_server = 0;
}
if (_osc_unix_server) {
lo_server_free (_osc_unix_server);
_osc_unix_server = 0;
}
if (!_osc_unix_socket_path.empty()) {
::g_unlink (_osc_unix_socket_path.c_str());
}
if (!_osc_url_file.empty() ) {
::g_unlink (_osc_url_file.c_str() );
}
return 0;
}
void
OSC::surface_destroy (OSCSurface* sur)
{
OSCSelectObserver* so;
if ((so = dynamic_cast<OSCSelectObserver*>(sur->sel_obs)) != 0) {
so->clear_observer ();
delete so;
sur->sel_obs = 0;
PBD::ScopedConnection pc = sur->proc_connection;
pc.disconnect ();
}
OSCCueObserver* co;
if ((co = dynamic_cast<OSCCueObserver*>(sur->cue_obs)) != 0) {
delete co;
sur->cue_obs = 0;
sur->sends.clear ();
}
OSCGlobalObserver* go;
if ((go = dynamic_cast<OSCGlobalObserver*>(sur->global_obs)) != 0) {
go->clear_observer ();
delete go;
sur->global_obs = 0;
}
uint32_t st_end = sur->observers.size ();
for (uint32_t i = 0; i < st_end; i++) {
OSCRouteObserver* ro;
if ((ro = dynamic_cast<OSCRouteObserver*>(sur->observers[i])) != 0) {
ro->clear_strip ();
delete ro;
ro = 0;
}
}
sur->observers.clear();
}
void
OSC::register_callbacks()
{
lo_server srvs[2];
lo_server serv;
srvs[0] = _osc_server;
srvs[1] = _osc_unix_server;
for (size_t i = 0; i < 2; ++i) {
if (!srvs[i]) {
continue;
}
serv = srvs[i];
#define REGISTER_CALLBACK(serv,path,types, function) lo_server_add_method (serv, path, types, OSC::_ ## function, this)
// Some controls have optional "f" for feedback or touchosc
// http://hexler.net/docs/touchosc-controls-reference
REGISTER_CALLBACK (serv, X_("/refresh"), "", refresh_surface);
REGISTER_CALLBACK (serv, X_("/refresh"), "f", refresh_surface);
REGISTER_CALLBACK (serv, X_("/group/list"), "", group_list);
REGISTER_CALLBACK (serv, X_("/group/list"), "f", group_list);
REGISTER_CALLBACK (serv, X_("/surface/list"), "", surface_list);
REGISTER_CALLBACK (serv, X_("/surface/list"), "f", surface_list);
REGISTER_CALLBACK (serv, X_("/add_marker"), "", add_marker);
REGISTER_CALLBACK (serv, X_("/add_marker"), "f", add_marker);
REGISTER_CALLBACK (serv, X_("/add_marker"), "s", add_marker_name);
REGISTER_CALLBACK (serv, X_("/access_action"), "s", access_action);
REGISTER_CALLBACK (serv, X_("/loop_toggle"), "", loop_toggle);
REGISTER_CALLBACK (serv, X_("/loop_toggle"), "f", loop_toggle);
REGISTER_CALLBACK (serv, X_("/loop_location"), "ii", loop_location);
REGISTER_CALLBACK (serv, X_("/goto_start"), "", goto_start);
REGISTER_CALLBACK (serv, X_("/goto_start"), "f", goto_start);
REGISTER_CALLBACK (serv, X_("/goto_end"), "", goto_end);
REGISTER_CALLBACK (serv, X_("/goto_end"), "f", goto_end);
REGISTER_CALLBACK (serv, X_("/scrub"), "f", scrub);
REGISTER_CALLBACK (serv, X_("/jog"), "f", jog);
REGISTER_CALLBACK (serv, X_("/jog/mode"), "f", jog_mode);
REGISTER_CALLBACK (serv, X_("/rewind"), "", rewind);
REGISTER_CALLBACK (serv, X_("/rewind"), "f", rewind);
REGISTER_CALLBACK (serv, X_("/ffwd"), "", ffwd);
REGISTER_CALLBACK (serv, X_("/ffwd"), "f", ffwd);
REGISTER_CALLBACK (serv, X_("/transport_stop"), "", transport_stop);
REGISTER_CALLBACK (serv, X_("/transport_stop"), "f", transport_stop);
REGISTER_CALLBACK (serv, X_("/transport_play"), "", transport_play);
REGISTER_CALLBACK (serv, X_("/transport_play"), "f", transport_play);
REGISTER_CALLBACK (serv, X_("/transport_frame"), "", transport_sample);
REGISTER_CALLBACK (serv, X_("/transport_speed"), "", transport_speed);
REGISTER_CALLBACK (serv, X_("/record_enabled"), "", record_enabled);
REGISTER_CALLBACK (serv, X_("/set_transport_speed"), "f", set_transport_speed);
// locate ii is position and bool roll
REGISTER_CALLBACK (serv, X_("/locate"), "ii", locate);
REGISTER_CALLBACK (serv, X_("/trigger_cue_row"), "i", trigger_cue_row);
REGISTER_CALLBACK (serv, X_("/trigger_stop_all"), "i", trigger_stop_all);
REGISTER_CALLBACK (serv, X_("/trigger_stop"), "ii", trigger_stop); //Route num (position on the Cue page), Stop now
REGISTER_CALLBACK (serv, X_("/trigger_bang"), "ii", trigger_bang); //Route num (position on the Cue page), Trigger index
REGISTER_CALLBACK (serv, X_("/trigger_unbang"), "ii", trigger_unbang); //Route num (position on the Cue page), Trigger index
REGISTER_CALLBACK (serv, X_("/tbank_step_route"), "i", osc_tbank_step_routes);
REGISTER_CALLBACK (serv, X_("/tbank_step_row"), "i", osc_tbank_step_rows);
REGISTER_CALLBACK (serv, X_("/store_mixer_scene"), "i", store_mixer_scene);
REGISTER_CALLBACK (serv, X_("/recall_mixer_scene"), "i", apply_mixer_scene);
REGISTER_CALLBACK (serv, X_("/save_state"), "", save_state);
REGISTER_CALLBACK (serv, X_("/save_state"), "f", save_state);
REGISTER_CALLBACK (serv, X_("/prev_marker"), "", prev_marker);
REGISTER_CALLBACK (serv, X_("/prev_marker"), "f", prev_marker);
REGISTER_CALLBACK (serv, X_("/next_marker"), "", next_marker);
REGISTER_CALLBACK (serv, X_("/next_marker"), "f", next_marker);
REGISTER_CALLBACK (serv, X_("/undo"), "", undo);
REGISTER_CALLBACK (serv, X_("/undo"), "f", undo);
REGISTER_CALLBACK (serv, X_("/redo"), "", redo);
REGISTER_CALLBACK (serv, X_("/redo"), "f", redo);
REGISTER_CALLBACK (serv, X_("/toggle_punch_in"), "", toggle_punch_in);
REGISTER_CALLBACK (serv, X_("/toggle_punch_in"), "f", toggle_punch_in);
REGISTER_CALLBACK (serv, X_("/toggle_punch_out"), "", toggle_punch_out);
REGISTER_CALLBACK (serv, X_("/toggle_punch_out"), "f", toggle_punch_out);
REGISTER_CALLBACK (serv, X_("/rec_enable_toggle"), "", rec_enable_toggle);
REGISTER_CALLBACK (serv, X_("/rec_enable_toggle"), "f", rec_enable_toggle);
REGISTER_CALLBACK (serv, X_("/toggle_all_rec_enables"), "", toggle_all_rec_enables);
REGISTER_CALLBACK (serv, X_("/toggle_all_rec_enables"), "f", toggle_all_rec_enables);
REGISTER_CALLBACK (serv, X_("/all_tracks_rec_in"), "f", all_tracks_rec_in);
REGISTER_CALLBACK (serv, X_("/all_tracks_rec_out"), "f", all_tracks_rec_out);
REGISTER_CALLBACK (serv, X_("/cancel_all_solos"), "f", cancel_all_solos);
REGISTER_CALLBACK (serv, X_("/remove_marker"), "", remove_marker_at_playhead);
REGISTER_CALLBACK (serv, X_("/remove_marker"), "f", remove_marker_at_playhead);
REGISTER_CALLBACK (serv, X_("/jump_bars"), "f", jump_by_bars);
REGISTER_CALLBACK (serv, X_("/jump_seconds"), "f", jump_by_seconds);
REGISTER_CALLBACK (serv, X_("/mark_in"), "", mark_in);
REGISTER_CALLBACK (serv, X_("/mark_in"), "f", mark_in);
REGISTER_CALLBACK (serv, X_("/mark_out"), "", mark_out);
REGISTER_CALLBACK (serv, X_("/mark_out"), "f", mark_out);
REGISTER_CALLBACK (serv, X_("/toggle_click"), "", toggle_click);
REGISTER_CALLBACK (serv, X_("/toggle_click"), "f", toggle_click);
REGISTER_CALLBACK (serv, X_("/click/level"), "f", click_level);
REGISTER_CALLBACK (serv, X_("/midi_panic"), "", midi_panic);
REGISTER_CALLBACK (serv, X_("/midi_panic"), "f", midi_panic);
REGISTER_CALLBACK (serv, X_("/stop_forget"), "", stop_forget);
REGISTER_CALLBACK (serv, X_("/stop_forget"), "f", stop_forget);
REGISTER_CALLBACK (serv, X_("/set_punch_range"), "", set_punch_range);
REGISTER_CALLBACK (serv, X_("/set_punch_range"), "f", set_punch_range);
REGISTER_CALLBACK (serv, X_("/set_loop_range"), "", set_loop_range);
REGISTER_CALLBACK (serv, X_("/set_loop_range"), "f", set_loop_range);
REGISTER_CALLBACK (serv, X_("/set_session_range"), "", set_session_range);
REGISTER_CALLBACK (serv, X_("/set_session_range"), "f", set_session_range);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_mute"), "", toggle_monitor_mute);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_mute"), "f", toggle_monitor_mute);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_dim"), "", toggle_monitor_dim);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_dim"), "f", toggle_monitor_dim);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_mono"), "", toggle_monitor_mono);
REGISTER_CALLBACK (serv, X_("/toggle_monitor_mono"), "f", toggle_monitor_mono);
REGISTER_CALLBACK (serv, X_("/quick_snapshot_switch"), "", quick_snapshot_switch);
REGISTER_CALLBACK (serv, X_("/quick_snapshot_switch"), "f", quick_snapshot_switch);
REGISTER_CALLBACK (serv, X_("/quick_snapshot_stay"), "", quick_snapshot_stay);
REGISTER_CALLBACK (serv, X_("/quick_snapshot_stay"), "f", quick_snapshot_stay);
REGISTER_CALLBACK (serv, X_("/session_name"), "s", name_session);
REGISTER_CALLBACK (serv, X_("/fit_1_track"), "", fit_1_track);
REGISTER_CALLBACK (serv, X_("/fit_1_track"), "f", fit_1_track);
REGISTER_CALLBACK (serv, X_("/fit_2_tracks"), "", fit_2_tracks);
REGISTER_CALLBACK (serv, X_("/fit_2_tracks"), "f", fit_2_tracks);
REGISTER_CALLBACK (serv, X_("/fit_4_tracks"), "", fit_4_tracks);
REGISTER_CALLBACK (serv, X_("/fit_4_tracks"), "f", fit_4_tracks);
REGISTER_CALLBACK (serv, X_("/fit_8_tracks"), "", fit_8_tracks);
REGISTER_CALLBACK (serv, X_("/fit_8_tracks"), "f", fit_8_tracks);
REGISTER_CALLBACK (serv, X_("/fit_16_tracks"), "", fit_16_tracks);
REGISTER_CALLBACK (serv, X_("/fit_16_tracks"), "f", fit_16_tracks);
REGISTER_CALLBACK (serv, X_("/fit_32_tracks"), "", fit_32_tracks);
REGISTER_CALLBACK (serv, X_("/fit_32_tracks"), "f", fit_32_tracks);
REGISTER_CALLBACK (serv, X_("/fit_all_tracks"), "", fit_all_tracks);
REGISTER_CALLBACK (serv, X_("/fit_all_tracks"), "f", fit_all_tracks);
REGISTER_CALLBACK (serv, X_("/zoom_100_ms"), "", zoom_100_ms);
REGISTER_CALLBACK (serv, X_("/zoom_100_ms"), "f", zoom_100_ms);
REGISTER_CALLBACK (serv, X_("/zoom_1_sec"), "", zoom_1_sec);
REGISTER_CALLBACK (serv, X_("/zoom_1_sec"), "f", zoom_1_sec);
REGISTER_CALLBACK (serv, X_("/zoom_10_sec"), "", zoom_10_sec);
REGISTER_CALLBACK (serv, X_("/zoom_10_sec"), "f", zoom_10_sec);
REGISTER_CALLBACK (serv, X_("/zoom_1_min"), "", zoom_1_min);
REGISTER_CALLBACK (serv, X_("/zoom_1_min"), "f", zoom_1_min);
REGISTER_CALLBACK (serv, X_("/zoom_5_min"), "", zoom_5_min);
REGISTER_CALLBACK (serv, X_("/zoom_5_min"), "f", zoom_5_min);
REGISTER_CALLBACK (serv, X_("/zoom_10_min"), "", zoom_10_min);
REGISTER_CALLBACK (serv, X_("/zoom_10_min"), "f", zoom_10_min);
REGISTER_CALLBACK (serv, X_("/zoom_to_session"), "", zoom_to_session);
REGISTER_CALLBACK (serv, X_("/zoom_to_session"), "f", zoom_to_session);
REGISTER_CALLBACK (serv, X_("/temporal_zoom_in"), "f", temporal_zoom_in);
REGISTER_CALLBACK (serv, X_("/temporal_zoom_in"), "", temporal_zoom_in);
REGISTER_CALLBACK (serv, X_("/temporal_zoom_out"), "", temporal_zoom_out);
REGISTER_CALLBACK (serv, X_("/temporal_zoom_out"), "f", temporal_zoom_out);
REGISTER_CALLBACK (serv, X_("/scroll_up_1_track"), "f", scroll_up_1_track);
REGISTER_CALLBACK (serv, X_("/scroll_up_1_track"), "", scroll_up_1_track);
REGISTER_CALLBACK (serv, X_("/scroll_dn_1_track"), "f", scroll_dn_1_track);
REGISTER_CALLBACK (serv, X_("/scroll_dn_1_track"), "", scroll_dn_1_track);
REGISTER_CALLBACK (serv, X_("/scroll_up_1_page"), "f", scroll_up_1_page);
REGISTER_CALLBACK (serv, X_("/scroll_up_1_page"), "", scroll_up_1_page);
REGISTER_CALLBACK (serv, X_("/scroll_dn_1_page"), "f", scroll_dn_1_page);
REGISTER_CALLBACK (serv, X_("/scroll_dn_1_page"), "", scroll_dn_1_page);
REGISTER_CALLBACK (serv, X_("/bank_up"), "", bank_up);
REGISTER_CALLBACK (serv, X_("/bank_up"), "f", bank_delta);
REGISTER_CALLBACK (serv, X_("/bank_down"), "", bank_down);
REGISTER_CALLBACK (serv, X_("/bank_down"), "f", bank_down);
REGISTER_CALLBACK (serv, X_("/use_group"), "f", use_group);
// Controls for the Selected strip
REGISTER_CALLBACK (serv, X_("/select/previous"), "f", sel_previous);
REGISTER_CALLBACK (serv, X_("/select/previous"), "", sel_previous);
REGISTER_CALLBACK (serv, X_("/select/next"), "f", sel_next);
REGISTER_CALLBACK (serv, X_("/select/next"), "", sel_next);
REGISTER_CALLBACK (serv, X_("/select/send_gain"), "if", sel_sendgain);
REGISTER_CALLBACK (serv, X_("/select/send_fader"), "if", sel_sendfader);
REGISTER_CALLBACK (serv, X_("/select/send_enable"), "if", sel_sendenable);
REGISTER_CALLBACK (serv, X_("/select/master_send_enable"), "i", sel_master_send_enable);
REGISTER_CALLBACK (serv, X_("/select/send_page"), "f", sel_send_page);
REGISTER_CALLBACK (serv, X_("/select/plug_page"), "f", sel_plug_page);
REGISTER_CALLBACK (serv, X_("/select/plugin"), "f", sel_plugin);
REGISTER_CALLBACK (serv, X_("/select/plugin/activate"), "f", sel_plugin_activate);
REGISTER_CALLBACK (serv, X_("/select/expand"), "i", sel_expand);
REGISTER_CALLBACK (serv, X_("/select/pan_elevation_position"), "f", sel_pan_elevation);
REGISTER_CALLBACK (serv, X_("/select/pan_frontback_position"), "f", sel_pan_frontback);
REGISTER_CALLBACK (serv, X_("/select/pan_lfe_control"), "f", sel_pan_lfe);
REGISTER_CALLBACK (serv, X_("/select/comp_enable"), "f", sel_comp_enable);
REGISTER_CALLBACK (serv, X_("/select/comp_threshold"), "f", sel_comp_threshold);
REGISTER_CALLBACK (serv, X_("/select/comp_mode"), "f", sel_comp_mode);
REGISTER_CALLBACK (serv, X_("/select/comp_makeup"), "f", sel_comp_makeup);
REGISTER_CALLBACK (serv, X_("/select/eq_enable"), "f", sel_eq_enable);
REGISTER_CALLBACK (serv, X_("/select/eq_hpf/freq"), "f", sel_eq_hpf_freq);
REGISTER_CALLBACK (serv, X_("/select/eq_hpf/enable"), "f", sel_eq_hpf_enable);
REGISTER_CALLBACK (serv, X_("/select/eq_hpf/slope"), "f", sel_eq_hpf_slope);
REGISTER_CALLBACK (serv, X_("/select/eq_lpf/freq"), "f", sel_eq_lpf_freq);
REGISTER_CALLBACK (serv, X_("/select/eq_lpf/enable"), "f", sel_eq_lpf_enable);
REGISTER_CALLBACK (serv, X_("/select/eq_lpf/slope"), "f", sel_eq_lpf_slope);
REGISTER_CALLBACK (serv, X_("/select/eq_gain"), "if", sel_eq_gain);
REGISTER_CALLBACK (serv, X_("/select/eq_freq"), "if", sel_eq_freq);
REGISTER_CALLBACK (serv, X_("/select/eq_q"), "if", sel_eq_q);
REGISTER_CALLBACK (serv, X_("/select/eq_shape"), "if", sel_eq_shape);
REGISTER_CALLBACK (serv, X_("/select/add_personal_send"), "s", sel_new_personal_send);
REGISTER_CALLBACK (serv, X_("/select/add_fldbck_send"), "s", sel_new_personal_send);
/* These commands require the route index in addition to the arg; TouchOSC (et al) can't use these */
REGISTER_CALLBACK (serv, X_("/strip/custom/mode"), "f", custom_mode);
REGISTER_CALLBACK (serv, X_("/strip/custom/clear"), "f", custom_clear);
REGISTER_CALLBACK (serv, X_("/strip/custom/clear"), "", custom_clear);
REGISTER_CALLBACK (serv, X_("/strip/plugin/parameter"), "iiif", route_plugin_parameter);
// prints to cerr only
REGISTER_CALLBACK (serv, X_("/strip/plugin/parameter/print"), "iii", route_plugin_parameter_print);
REGISTER_CALLBACK (serv, X_("/strip/plugin/activate"), "ii", route_plugin_activate);
REGISTER_CALLBACK (serv, X_("/strip/plugin/deactivate"), "ii", route_plugin_deactivate);
REGISTER_CALLBACK (serv, X_("/strip/send/gain"), "iif", route_set_send_gain_dB);
REGISTER_CALLBACK (serv, X_("/strip/send/fader"), "iif", route_set_send_fader);
REGISTER_CALLBACK (serv, X_("/strip/send/enable"), "iif", route_set_send_enable);
REGISTER_CALLBACK (serv, X_("/strip/sends"), "i", route_get_sends);
REGISTER_CALLBACK (serv, X_("/strip/receives"), "i", route_get_receives);
REGISTER_CALLBACK (serv, X_("/strip/plugin/list"), "i", route_plugin_list);
REGISTER_CALLBACK (serv, X_("/strip/plugin/descriptor"), "ii", route_plugin_descriptor);
REGISTER_CALLBACK (serv, X_("/strip/plugin/reset"), "ii", route_plugin_reset);
/* this is a special catchall handler,
* register at the end so this is only called if no
* other handler matches (also used for debug) */
lo_server_add_method (serv, 0, 0, _catchall, this);
}
}
bool
OSC::osc_input_handler (IOCondition ioc, lo_server srv)
{
if (ioc & IO_IN) {
lo_server_recv (srv);
}
if (ioc & ~(IO_IN|IO_PRI)) {
return false;
}
return true;
}
std::string
OSC::get_server_url()
{
string url;
char * urlstr;
if (_osc_server) {
urlstr = lo_server_get_url (_osc_server);
url = urlstr;
free (urlstr);
}
return url;
}
std::string
OSC::get_unix_server_url()
{
string url;
char * urlstr;
if (_osc_unix_server) {
urlstr = lo_server_get_url (_osc_unix_server);
url = urlstr;
free (urlstr);
}
return url;
}
void
OSC::gui_changed ()
{
session->set_dirty();
}
void
OSC::current_value_query (const char* path, size_t len, lo_arg **argv, int argc, lo_message msg)
{
char* subpath;
subpath = (char*) malloc (len-15+1);
memcpy (subpath, path, len-15);
subpath[len-15] = '\0';
send_current_value (subpath, argv, argc, msg);
free (subpath);
}
void
OSC::send_current_value (const char* path, lo_arg** argv, int argc, lo_message msg)
{
if (!session) {
return;
}
lo_message reply = lo_message_new ();
std::shared_ptr<Route> r;
int id;
lo_message_add_string (reply, path);
if (argc == 0) {
lo_message_add_string (reply, "bad syntax");
} else {
id = argv[0]->i;
r = session->get_remote_nth_route (id);
if (!r) {
lo_message_add_string (reply, "not found");
} else {
if (strcmp (path, X_("/strip/state")) == 0) {
if (std::dynamic_pointer_cast<AudioTrack>(r)) {
lo_message_add_string (reply, "AT");
} else if (std::dynamic_pointer_cast<MidiTrack>(r)) {
lo_message_add_string (reply, "MT");
} else {
lo_message_add_string (reply, "B");
}
lo_message_add_string (reply, r->name().c_str());
lo_message_add_int32 (reply, r->n_inputs().n_audio());
lo_message_add_int32 (reply, r->n_outputs().n_audio());
lo_message_add_int32 (reply, r->muted());
lo_message_add_int32 (reply, r->soloed());
} else if (strcmp (path, X_("/strip/mute")) == 0) {
lo_message_add_int32 (reply, (float) r->muted());
} else if (strcmp (path, X_("/strip/solo")) == 0) {
lo_message_add_int32 (reply, r->soloed());
}
}
}
OSCSurface *sur = get_surface(get_address (msg));
if (sur->feedback[14]) {
lo_send_message (get_address (msg), X_("/reply"), reply);
} else {
lo_send_message (get_address (msg), X_("#reply"), reply);
}
lo_message_free (reply);
}
int
OSC::_catchall (const char *path, const char *types, lo_arg **argv, int argc, lo_message msg, void *user_data)
{
return ((OSC*)user_data)->catchall (path, types, argv, argc, msg);
}
int
OSC::catchall (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
size_t len;
int ret = 1; /* unhandled */
len = strlen (path);
OSCSurface *sur = get_surface(get_address (msg), true);
LinkSet *set = 0;
uint32_t ls = sur->linkset;
if (ls) {
set = &(link_sets[ls]);
sur->custom_mode = set->custom_mode;
sur->custom_strips = set->custom_strips;
sur->temp_mode = set->temp_mode;
sur->temp_strips = set->temp_strips;
sur->temp_master = set->temp_master;
}
if (strstr (path, X_("/automation"))) {
ret = set_automation (path, types, argv, argc, msg);
} else if (strstr (path, X_("/touch"))) {
ret = touch_detect (path, types, argv, argc, msg);
} else if (strstr (path, X_("/toggle_roll"))) {
if (!argc) {
ret = osc_toggle_roll (false);
} else {
if ((types[0] == 'f' && argv[0]->f == 1.0) || (types[0] == 'i' && argv[0]->i == 1)) {
ret = osc_toggle_roll (true);
} else if ((types[0] == 'f' && argv[0]->f == 0.0) || (types[0] == 'i' && argv[0]->i == 0)) {
ret = osc_toggle_roll (false);
}
}
} else if (strstr (path, X_("/spill"))) {
ret = spill (path, types, argv, argc, msg);
} else if (len >= 17 && !strcmp (&path[len-15], X_("/#current_value"))) {
current_value_query (path, len, argv, argc, msg);
ret = 0;
} else if (!strncmp (path, X_("/cue/"), 5)) {
ret = cue_parse (path, types, argv, argc, msg);
} else if (!strncmp (path, X_("/select/plugin/parameter"), 24)) {
ret = select_plugin_parameter (path, types, argv, argc, msg);
} else if (!strncmp (path, X_("/access_action/"), 15)) {
check_surface (msg);
if (!(argc && !argv[0]->i)) {
std::string action_path = path;
access_action (action_path.substr(15));
}
ret = 0;
} else if (strcmp (path, X_("/strip/listen")) == 0) {
if (argc <= 0) {
PBD::warning << "OSC: Wrong number of parameters." << endmsg;
} else if (sur->custom_mode && !sur->temp_mode) {
PBD::warning << "OSC: Can't add strips with custom enabled." << endmsg;
} else {
for (int n = 0; n < argc; ++n) {
std::shared_ptr<Stripable> s = std::shared_ptr<Stripable>();
if (types[n] == 'f') {
s = get_strip ((uint32_t) argv[n]->f, get_address (msg));
} else if (types[n] == 'i') {
s = get_strip (argv[n]->i, get_address (msg));
}
if (s) {
sur->custom_strips.push_back (s);
}
}
if (set) {
set->custom_strips = sur->custom_strips;
}
}
ret = 0;
} else if (strcmp (path, X_("/strip/ignore")) == 0) {
if (argc <= 0) {
PBD::warning << "OSC: Wrong number of parameters." << endmsg;
} else if (!sur->custom_mode || sur->temp_mode) {
PBD::warning << "OSC: Can't remove strips without custom enabled." << endmsg;
} else {
for (int n = 0; n < argc; ++n) {
uint32_t st_no = 0;
if (types[n] == 'f') {
st_no = (uint32_t) argv[n]->f;
} else if (types[n] == 'i') {
st_no = (uint32_t) argv[n]->i;
}
if (st_no && st_no <= sur->custom_strips.size ()) {
sur->custom_strips[argv[n]->i - 1] = std::shared_ptr<Stripable>();
}
}
if (ls) {
set->custom_strips = sur->custom_strips;
}
ret = set_bank (sur->bank, msg);
}
ret = 0;
} else if (!strncmp (path, X_("/set_surface"), 12)) {
ret = surface_parse (path, types, argv, argc, msg);
} else if (strstr (path, X_("/strip"))) {
ret = strip_parse (path, types, argv, argc, msg);
} else if (strstr (path, X_("/master"))) {
ret = master_parse (path, types, argv, argc, msg);
} else if (strstr (path, X_("/monitor"))) {
ret = monitor_parse (path, types, argv, argc, msg);
} else if (strstr (path, X_("/select"))) {
ret = select_parse (path, types, argv, argc, msg);
} else if (!strncmp (path, X_("/marker"), 7)) {
ret = set_marker (types, argv, argc, msg);
} else if (strstr (path, X_("/link"))) {
ret = parse_link (path, types, argv, argc, msg);
}
if (ret) {
check_surface (msg);
}
if ((ret && _debugmode != Off)) {
debugmsg (_("Unhandled OSC message"), path, types, argv, argc);
} else if (!ret && _debugmode == All) {
debugmsg (_("OSC"), path, types, argv, argc);
}
return ret;
}
void
OSC::debugmsg (const char *prefix, const char *path, const char* types, lo_arg **argv, int argc)
{
std::stringstream ss;
for (int i = 0; i < argc; ++i) {
lo_type type = (lo_type)types[i];
ss << " ";
switch (type) {
case LO_INT32:
ss << "i:" << argv[i]->i;
break;
case LO_FLOAT:
ss << "f:" << argv[i]->f;
break;
case LO_DOUBLE:
ss << "d:" << argv[i]->d;
break;
case LO_STRING:
ss << "s:" << &argv[i]->s;
break;
case LO_INT64:
ss << "h:" << argv[i]->h;
break;
case LO_CHAR:
ss << "c:" << argv[i]->s;
break;
case LO_TIMETAG:
ss << "<Timetag>";
break;
case LO_BLOB:
ss << "<BLOB>";
break;
case LO_TRUE:
ss << "#T";
break;
case LO_FALSE:
ss << "#F";
break;
case LO_NIL:
ss << "NIL";
break;
case LO_INFINITUM:
ss << "#inf";
break;
case LO_MIDI:
ss << "<MIDI>";
break;
case LO_SYMBOL:
ss << "<SYMBOL>";
break;
default:
ss << "< ?? >";
break;
}
}
PBD::info << prefix << ": " << path << ss.str() << endmsg;
}
// "Application Hook" Handlers //
void
OSC::session_loaded (Session& s)
{
// lo_address listener = lo_address_new (NULL, "7770");
// lo_send (listener, "/session/loaded", "ss", s.path().c_str(), s.name().c_str());
}
void
OSC::session_exported (std::string path, std::string name)
{
lo_address listener = lo_address_new (NULL, "7770");
lo_send (listener, X_("/session/exported"), "ss", path.c_str(), name.c_str());
lo_address_free (listener);
}
// end "Application Hook" Handlers //
/* path callbacks */
int
OSC::current_value (const char */*path*/, const char */*types*/, lo_arg **/*argv*/, int /*argc*/, lo_message /*msg*/, void* /*user_data*/)
{
#if 0
const char* returl;
if (argc < 3 || types == 0 || strlen (types) < 3 || types[0] != 's' || types[1] != 's' || types[2] != s) {
return 1;
}
const char *returl = argv[1]->s;
lo_address addr = find_or_cache_addr (returl);
const char *retpath = argv[2]->s;
/** this call back looks wrong. It appears to send the same information for all queries */
if (strcmp (argv[0]->s, X_("transport_frame")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else if (strcmp (argv[0]->s, X_("transport_speed")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else if (strcmp (argv[0]->s, X_("transport_locked")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else if (strcmp (argv[0]->s, X_("punch_in")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else if (strcmp (argv[0]->s, X_("punch_out")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else if (strcmp (argv[0]->s, X_("rec_enable")) == 0) {
if (session) {
lo_send (addr, retpath, "i", session->transport_sample());
}
} else {
/* error */
}
#endif
return 0;
}
void
OSC::routes_list (lo_message msg)
{
if (!session) {
return;
}
OSCSurface *sur = get_surface(get_address (msg), true);
for (int n = 0; n < (int) sur->nstrips; ++n) {
std::shared_ptr<Stripable> s = get_strip (n + 1, get_address (msg));
if (s) {
// some things need the route
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
lo_message reply = lo_message_new ();
if (std::dynamic_pointer_cast<AudioTrack>(s)) {
lo_message_add_string (reply, "AT");
} else if (std::dynamic_pointer_cast<MidiTrack>(s)) {
lo_message_add_string (reply, "MT");
} else if (std::dynamic_pointer_cast<VCA>(s)) {
lo_message_add_string (reply, "V");
} else if (s->is_master()) {
lo_message_add_string (reply, "MA");
} else if (s->is_monitor()) {
lo_message_add_string (reply, "MO");
} else if (s->is_surround_master()) {
lo_message_add_string (reply, "SM");
} else if (std::dynamic_pointer_cast<Route>(s) && !std::dynamic_pointer_cast<Track>(s)) {
if (!(s->presentation_info().flags() & PresentationInfo::MidiBus)) {
if (s->is_foldbackbus()) {
lo_message_add_string (reply, "FB");
} else {
lo_message_add_string (reply, "B");
}
} else {
lo_message_add_string (reply, "MB");
}
}
lo_message_add_string (reply, s->name().c_str());
if (r) {
// routes have inputs and outputs
lo_message_add_int32 (reply, r->n_inputs().n_audio());
lo_message_add_int32 (reply, r->n_outputs().n_audio());
} else {
// non-routes like VCAs don't
lo_message_add_int32 (reply, 0);
lo_message_add_int32 (reply, 0);
}
if (s->mute_control()) {
lo_message_add_int32 (reply, s->mute_control()->get_value());
} else {
lo_message_add_int32 (reply, 0);
}
if (s->solo_control()) {
lo_message_add_int32 (reply, s->solo_control()->get_value());
} else {
lo_message_add_int32 (reply, 0);
}
lo_message_add_int32 (reply, n + 1);
if (s->rec_enable_control()) {
lo_message_add_int32 (reply, s->rec_enable_control()->get_value());
}
if (sur->feedback[14]) {
lo_send_message (get_address (msg), X_("/reply"), reply);
} else {
lo_send_message (get_address (msg), X_("#reply"), reply);
}
lo_message_free (reply);
}
}
// Send end of listing message
lo_message reply = lo_message_new ();
lo_message_add_string (reply, X_("end_route_list"));
lo_message_add_int64 (reply, session->sample_rate());
lo_message_add_int64 (reply, session->current_end_sample());
if (session->monitor_out()) {
// this session has a monitor section
lo_message_add_int32 (reply, 1);
} else {
lo_message_add_int32 (reply, 0);
}
if (sur->feedback[14]) {
lo_send_message (get_address (msg), X_("/reply"), reply);
} else {
lo_send_message (get_address (msg), X_("#reply"), reply);
}
lo_message_free (reply);
// send feedback for newly created control surface
strip_feedback (sur, true);
global_feedback (sur);
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), get_address (msg));
}
void
OSC::surface_list (lo_message msg)
{
get_surfaces ();
}
void
OSC::get_surfaces ()
{
/* this function is for debugging and prints lots of
* information about what surfaces Ardour knows about and their
* internal parameters. It is best accessed by sending:
* /surface/list from oscsend. This command does not create
* a surface entry.
*/
PBD::info << string_compose ("\nList of known Surfaces (%1):\n", _surface.size());
Glib::Threads::Mutex::Lock lm (surfaces_lock);
for (uint32_t it = 0; it < _surface.size(); it++) {
OSCSurface* sur = &_surface[it];
char *chost = lo_url_get_hostname (sur->remote_url.c_str());
string host = chost;
free (chost);
string port = get_port (host);
if (port != "auto") {
port = "Manual port";
} else {
port = "Auto port";
}
PBD::info << string_compose ("\n Surface: %1 - URL: %2 %3\n", it, sur->remote_url, port);
PBD::info << string_compose (" Number of strips: %1 Bank size: %2 Current Bank %3\n", sur->nstrips, sur->bank_size, sur->bank);
PBD::info << string_compose (" Use Custom: %1 Custom Strips: %2\n", sur->custom_mode, sur->custom_strips.size ());
PBD::info << string_compose (" Temp Mode: %1 Temp Strips: %2\n", sur->temp_mode, sur->temp_strips.size ());
bool ug = false;
if (sur->usegroup == PBD::Controllable::UseGroup) {
ug = true;
}
PBD::info << string_compose (" Strip Types: %1 Feedback: %2 No_clear flag: %3 Gain mode: %4 Use groups flag %5\n", \
sur->strip_types.to_ulong(), sur->feedback.to_ulong(), sur->no_clear, sur->gainmode, ug);
PBD::info << string_compose (" Using plugin: %1 of %2 plugins, with %3 params. Page size: %4 Page: %5\n", \
sur->plugin_id, sur->plugins.size(), sur->plug_params.size(), sur->plug_page_size, sur->plug_page);
PBD::info << string_compose (" Send page size: %1 Page: %2\n", sur->send_page_size, sur->send_page);
PBD::info << string_compose (" Expanded flag %1 Track: %2 Jogmode: %3\n", sur->expand_enable, sur->expand, sur->jogmode);
PBD::info << string_compose (" Personal monitor flag %1, Aux master: %2, Number of sends: %3\n", sur->cue, sur->aux, sur->sends.size());
PBD::info << string_compose (" Linkset: %1 Device Id: %2\n", sur->linkset, sur->linkid);
PBD::info << string_compose (" Global Observer: %1\n", sur->global_obs != NULL ? "yes" : "NO");
}
PBD::info << string_compose ("\nList of LinkSets (%1):\n", link_sets.size());
std::map<uint32_t, LinkSet>::iterator it;
for (it = link_sets.begin(); it != link_sets.end(); it++) {
if (!(*it).first) {
continue;
}
uint32_t devices = 0;
LinkSet* set = &(*it).second;
if (set->urls.size()) {
devices = set->urls.size() - 1;
}
PBD::info << string_compose ("\n Linkset %1 has %2 devices and sees %3 strips\n", (*it).first, devices, set->strips.size());
PBD::info << string_compose (" Bank size: %1 Current bank: %2 Strip Types: %3\n", set->banksize, set->bank, set->strip_types.to_ulong());
PBD::info << string_compose (" Auto bank sizing: %1 Linkset not ready flag: %2\n", set->autobank, set->not_ready);
PBD::info << string_compose (" Use Custom: %1 Number of Custom Strips: %2\n", set->custom_mode, set->custom_strips.size ());
PBD::info << string_compose (" Temp Mode: %1 Number of Temp Strips: %2\n", set->temp_mode, set->temp_strips.size ());
}
PBD::info << endmsg;
}
int
OSC::custom_clear (lo_message msg)
{
if (!session) {
return 0;
}
OSCSurface *sur = get_surface(get_address (msg), true);
sur->custom_mode = 0;
sur->custom_strips.clear ();
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, false, sur->custom_strips);
sur->nstrips = sur->strips.size();
LinkSet *set;
uint32_t ls = sur->linkset;
if (ls) {
set = &(link_sets[ls]);
set->custom_mode = 0;
set->custom_strips.clear ();
set->strips = sur->strips;
}
return set_bank (1, msg);
}
int
OSC::custom_mode (float state, lo_message msg)
{
return _custom_mode ((uint32_t) state, get_address (msg));
}
int
OSC::_custom_mode (uint32_t state, lo_address addr)
{
if (!session) {
return 0;
}
OSCSurface *sur = get_surface(addr, true);
LinkSet *set = 0;
uint32_t ls = sur->linkset;
if (ls) {
set = &(link_sets[ls]);
sur->custom_mode = set->custom_mode;
sur->custom_strips = set->custom_strips;
}
sur->temp_mode = TempOff;
if (state > 0){
if (sur->custom_strips.size () == 0) {
PBD::warning << "No custom strips set to enable" << endmsg;
sur->custom_mode = 0;
if (ls) {
set->custom_mode = 0;
}
return -1;
} else {
if (sur->bank_size) {
sur->custom_mode = state | 0x4;
} else {
sur->custom_mode = state;
}
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, sur->custom_mode, sur->custom_strips);
sur->nstrips = sur->custom_strips.size();
}
} else {
sur->custom_mode = 0;
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, 0, sur->custom_strips);
sur->nstrips = sur->strips.size();
}
if (set) {
set->custom_mode = sur->custom_mode;
set->strips = sur->strips;
set->temp_mode = sur->temp_mode;
}
return _set_bank (1, addr);
}
int
OSC::cancel_all_solos ()
{
session->cancel_all_solo ();
return 0;
}
int
OSC::osc_toggle_roll (bool ret2strt)
{
if (!session) {
return 0;
}
if (session->is_auditioning()) {
session->cancel_audition ();
return 0;
}
bool rolling = transport_rolling();
if (rolling) {
session->request_stop (ret2strt, true);
} else {
if (session->get_play_loop() && Config->get_loop_is_mode()) {
session->request_locate (session->locations()->auto_loop_location()->start().samples(), false, MustRoll);
} else {
session->request_roll (TRS_UI);
}
}
return 0;
}
int
OSC::osc_tbank_step_routes (int step, lo_message msg)
{
tbank_step_routes(step);
trigger_bank_state(get_address(msg));
return 0;
}
int
OSC::osc_tbank_step_rows (int step, lo_message msg)
{
tbank_step_rows(step);
trigger_bank_state(get_address(msg));
return 0;
}
lo_address
OSC::get_address (lo_message msg)
{
lo_address addr = lo_message_get_source (msg);
string host = lo_address_get_hostname (addr);
string port = lo_address_get_port (addr);
int protocol = lo_address_get_protocol (addr);
string saved_port = get_port (host);
if (saved_port != "") {
if (saved_port != "auto") {
port = saved_port;
return lo_address_new_with_proto (protocol, host.c_str(), port.c_str());
} else {
return lo_message_get_source (msg);
}
}
// if we get here we need to add a new entry for this surface
PortAdd new_port;
new_port.host = host;
if (address_only) {
new_port.port = remote_port;
_ports.push_back (new_port);
return lo_address_new_with_proto (protocol, host.c_str(), remote_port.c_str());
} else {
new_port.port = "auto";
_ports.push_back (new_port);
return lo_message_get_source (msg);
}
}
string
OSC::get_port (string host)
{
for (uint32_t i = 0; i < _ports.size (); i++) {
if (_ports[i].host == host) {
return _ports[i].port;
}
}
return "";
}
int
OSC::refresh_surface (lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg), true);
uint32_t bs = s->bank_size;
uint32_t st = (uint32_t) s->strip_types.to_ulong();
uint32_t fb = (uint32_t) s->feedback.to_ulong();
uint32_t gm = (uint32_t) s->gainmode;
uint32_t sp = s->send_page_size;
uint32_t pp = s->plug_page_size;
surface_destroy (s);
// restart all observers
set_surface (bs, st, fb, gm, sp, pp, msg);
return 0;
}
void
OSC::clear_devices ()
{
tick = false;
observer_busy = true;
session_connections.drop_connections ();
// clear out surfaces
for (uint32_t it = 0; it < _surface.size(); ++it) {
OSCSurface* sur = &_surface[it];
surface_destroy (sur);
}
_surface.clear();
link_sets.clear ();
_ports.clear ();
PresentationInfo::Change.connect (session_connections, MISSING_INVALIDATOR, std::bind (&OSC::recalcbanks, this), this);
observer_busy = false;
tick = true;
}
int
OSC::parse_link (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
int ret = 1; /* unhandled */
int set = 0;
if (!argc) {
PBD::warning << "OSC: /link/* needs at least one parameter" << endmsg;
return ret;
}
float data = 0;
if (types[argc - 1] == 'f') {
data = argv[argc - 1]->f;
} else {
data = argv[argc - 1]->i;
}
if (isdigit(strrchr (path, '/')[1])) {
set = atoi (&(strrchr (path, '/')[1]));
} else if (argc == 2) {
if (types[0] == 'f') {
set = (int) argv[0]->f;
} else {
set = argv[0]->i;
}
} else {
PBD::warning << "OSC: wrong number of parameters." << endmsg;
return ret;
}
LinkSet *ls = get_linkset (set, get_address (msg));
if (!set) {
return 0;
}
if (!strncmp (path, X_("/link/bank_size"), 15)) {
ls->banksize = (uint32_t) data;
ls->autobank = false;
ls->not_ready = link_check (set);
if (ls->not_ready) {
ls->bank = 1;
surface_link_state (ls);
} else {
_set_bank (ls->bank, get_address (msg));
}
ret = 0;
} else if (!strncmp (path, X_("/link/set"), 9)) {
ret = set_link (set, (uint32_t) data, get_address (msg));
}
return ret;
}
OSC::LinkSet *
OSC::get_linkset (uint32_t set, lo_address addr)
{
OSCSurface *sur = get_surface(addr);
LinkSet *ls = 0;
if (set) {
// need to check if set is wanted
std::map<uint32_t, LinkSet>::iterator it;
it = link_sets.find(set);
if (it == link_sets.end()) {
// no such linkset make it
LinkSet new_ls;
new_ls.banksize = 0;
new_ls.bank = 1;
new_ls.autobank = true;
new_ls.not_ready = true;
new_ls.strip_types = sur->strip_types;
new_ls.strips = sur->strips;
new_ls.custom_strips = sur->custom_strips;
new_ls.custom_mode = sur->custom_mode;
new_ls.temp_mode = sur->temp_mode;
new_ls.urls.resize (2);
link_sets[set] = new_ls;
}
ls = &link_sets[set];
} else {
// User expects this surface to be removed from any sets
uint32_t oldset = sur->linkset;
if (oldset) {
uint32_t oldid = sur->linkid;
sur->linkid = 1;
sur->linkset = 0;
LinkSet *ols = &link_sets[oldset];
if (ols) {
ols->not_ready = oldid;
ols->urls[oldid] = "";
surface_link_state (ols);
}
}
}
return ls;
}
int
OSC::set_link (uint32_t set, uint32_t id, lo_address addr)
{
OSCSurface *sur = get_surface(addr, true);
sur->linkset = set;
sur->linkid = id;
LinkSet *ls = get_linkset (set, addr);
if (ls->urls.size() <= (uint32_t) id) {
ls->urls.resize ((int) id + 1);
}
ls->urls[(uint32_t) id] = sur->remote_url;
ls->not_ready = link_check (set);
if (ls->not_ready) {
surface_link_state (ls);
} else {
_set_bank (1, addr);
}
return 0;
}
void
OSC::link_strip_types (uint32_t linkset, uint32_t striptypes)
{
LinkSet *ls = 0;
if (!linkset) {
return;
}
std::map<uint32_t, LinkSet>::iterator it;
it = link_sets.find(linkset);
if (it == link_sets.end()) {
// this should never happen... but
return;
}
ls = &link_sets[linkset];
ls->strip_types = striptypes;
ls->temp_mode = TempOff;
for (uint32_t dv = 1; dv < ls->urls.size(); dv++) {
OSCSurface *su;
if (ls->urls[dv] != "") {
string url = ls->urls[dv];
su = get_surface (lo_address_new_from_url (url.c_str()), true);
if (su->linkset == linkset) {
su->strip_types = striptypes;
if (su->strip_types[10]) {
su->usegroup = PBD::Controllable::UseGroup;
} else {
su->usegroup = PBD::Controllable::NoGroup;
}
} else {
ls->urls[dv] = "";
}
}
}
}
void
OSC::surface_link_state (LinkSet * set)
{
for (uint32_t dv = 1; dv < set->urls.size(); dv++) {
if (set->urls[dv] != "") {
string url = set->urls[dv];
OSCSurface *sur = get_surface (lo_address_new_from_url (url.c_str()), true);
for (uint32_t i = 0; i < sur->observers.size(); i++) {
sur->observers[i]->set_link_ready (set->not_ready);
}
}
}
}
int
OSC::link_check (uint32_t set)
{
LinkSet *ls = 0;
if (!set) {
return 1;
}
std::map<uint32_t, LinkSet>::iterator it;
it = link_sets.find(set);
if (it == link_sets.end()) {
// this should never happen... but
return 1;
}
ls = &link_sets[set];
uint32_t bank_total = 0;
for (uint32_t dv = 1; dv < ls->urls.size(); dv++) {
OSCSurface *su;
if (ls->urls[dv] != "") {
string url = ls->urls[dv];
su = get_surface (lo_address_new_from_url (url.c_str()), true);
} else {
return dv;
}
if (su->linkset == set) {
bank_total = bank_total + su->bank_size;
} else {
ls->urls[dv] = "";
return dv;
}
if (ls->autobank) {
ls->banksize = bank_total;
} else {
if (bank_total != ls->banksize) {
return ls->urls.size();
}
}
}
return 0;
}
int
OSC::surface_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
int ret = 1; /* unhandled */
OSCSurface *sur = get_surface(get_address (msg), true);
int pi_page = sur->plug_page_size;
int se_page = sur->send_page_size;
int fadermode = sur->gainmode;
int feedback = sur->feedback.to_ulong();
int strip_types = sur->strip_types.to_ulong();
int bank_size = sur->bank_size;
int linkset = sur->linkset;
int linkid = sur->linkid;
string host = lo_url_get_hostname(sur->remote_url.c_str());
int port = atoi (get_port (host).c_str());
int data = 0;
if (argc) {
if (types[0] == 'f') {
data = (int)argv[0]->f;
} else if (types[0] == 'i') {
data = argv[0]->i;
} else if (types[0] == 's') {
if (isdigit(argv[0]->s)) {
data = atoi (&(argv[0]->s));
} else {
PBD::warning << "OSC: Parameter is not numerical." << endmsg;
return 1;
}
} else {
PBD::warning << "OSC: Wrong parameter type." << endmsg;
return 1;
}
}
if (argc == 1 && !strncmp (path, X_("/set_surface/feedback"), 21)) {
ret = set_surface_feedback (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/bank_size"), 22)) {
ret = set_surface_bank_size (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/gainmode"), 21)) {
ret = set_surface_gainmode (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/strip_types"), 24)) {
ret = set_surface_strip_types (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/send_page_size"), 27)) {
ret = sel_send_pagesize (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/plugin_page_size"), 29)) {
ret = sel_plug_pagesize (data, msg);
}
else if (argc == 1 && !strncmp (path, X_("/set_surface/port"), 17)) {
ret = set_surface_port (data, msg);
} else if (strlen(path) == 12) {
// command is in /set_surface iii form
switch (argc) {
case 9:
if (types[8] == 'f') {
linkid = (int) argv[8]->f;
} else {
linkid = argv[8]->i;
}
/* fallthrough */
case 8:
if (types[7] == 'f') {
linkset = (int) argv[7]->f;
} else {
linkset = argv[7]->i;
}
/* fallthrough */
case 7:
if (types[6] == 'f') {
port = (int) argv[6]->f;
} else {
port = argv[6]->i;
}
/* fallthrough */
case 6:
if (types[5] == 'f') {
pi_page = (int) argv[5]->f;
} else {
pi_page = argv[5]->i;
}
/* fallthrough */
case 5:
if (types[4] == 'f') {
se_page = (int) argv[4]->f;
} else {
se_page = argv[4]->i;
}
/* fallthrough */
case 4:
if (types[3] == 'f') {
fadermode = (int) argv[3]->f;
} else {
fadermode = argv[3]->i;
}
/* fallthrough */
case 3:
if (types[2] == 'f') {
feedback = (int) argv[2]->f;
} else {
feedback = argv[2]->i;
}
/* fallthrough */
case 2:
if (types[1] == 'f') {
strip_types = (int) argv[1]->f;
} else {
strip_types = argv[1]->i;
}
/* fallthrough */
case 1:
bank_size = data;
set_surface_port (port, msg);
ret = set_surface (bank_size, strip_types, feedback, fadermode, se_page, pi_page, msg);
if ((uint32_t) linkset != sur->linkset) {
set_link (linkset, linkid, get_address (msg));
}
break;
case 0:
// send current setup
{
lo_message reply = lo_message_new ();
lo_message_add_int32 (reply, bank_size);
lo_message_add_int32 (reply, strip_types);
lo_message_add_int32 (reply, feedback);
lo_message_add_int32 (reply, fadermode);
lo_message_add_int32 (reply, se_page);
lo_message_add_int32 (reply, pi_page);
lo_message_add_int32 (reply, (int) linkset);
lo_message_add_int32 (reply, (int) linkid);
lo_message_add_int32 (reply, (int) port);
lo_send_message (get_address (msg), X_("/set_surface"), reply);
lo_message_free (reply);
return 0;
}
break;
default:
PBD::warning << "OSC: Too many parameters." << endmsg;
return 1;
break;
}
} else if (isdigit(path[13])) {
// some of our parameters must be "in-lined"
bank_size = atoi (&path[13]);
const char * par = strstr (&path[13], "/");
if (par) {
strip_types = atoi (&par[1]);
const char * fb = strstr (&par[1], "/");
if (fb) {
feedback = atoi (&fb[1]);
const char * fm = strstr (&fb[1], "/");
if (fm) {
fadermode = atoi (&fm[1]);
const char * sp = strstr (&fm[1], "/");
if (sp) {
se_page = atoi (&sp[1]);
const char * pp = strstr (&sp[1], "/");
if (pp) {
pi_page = atoi (&pp[1]);
const char * po = strstr (&pp[1], "/");
if (po) {
port = atoi (&po[1]);
const char * ls = strstr (&po[1], "/");
if (ls) {
linkset = atoi (&ls[1]);
const char * li = strstr (&ls[1], "/");
if (li) {
linkid = atoi (&li[1]);
} else {
if (types[0] == 'f') {
linkid = (int) argv[0]->f;
} else if (types[0] == 'i') {
linkid = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
linkset = (int) argv[0]->f;
} else if (types[0] == 'i') {
linkset = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
port = (int) argv[0]->f;
} else if (types[0] == 'i') {
port = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
pi_page = (int) argv[0]->f;
} else if (types[0] == 'i') {
pi_page = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
se_page = (int) argv[0]->f;
} else if (types[0] == 'i') {
se_page = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
fadermode = (int) argv[0]->f;
} else if (types[0] == 'i') {
fadermode = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
feedback = (int) argv[0]->f;
} else if (types[0] == 'i') {
feedback = argv[0]->i;
}
}
} else {
if (types[0] == 'f') {
strip_types = (int) argv[0]->f;
} else if (types[0] == 'i') {
strip_types = argv[0]->i;
}
}
set_surface_port (port, msg);
ret = set_surface (bank_size, strip_types, feedback, fadermode, se_page, pi_page, msg);
if ((uint32_t) linkset != sur->linkset) {
set_link (linkset, linkid, get_address (msg));
}
}
return ret;
}
int
OSC::set_surface (uint32_t b_size, uint32_t strips, uint32_t fb, uint32_t gm, uint32_t se_size, uint32_t pi_size, lo_message msg)
{
if (observer_busy) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg), true);
s->bank_size = b_size;
s->strip_types = strips;
s->feedback = fb;
if (s->sel_obs) {
s->sel_obs->set_feedback(fb);
}
s->gainmode = gm;
if (s->strip_types[10]) {
s->usegroup = PBD::Controllable::UseGroup;
} else {
s->usegroup = PBD::Controllable::NoGroup;
}
if (s->temp_mode) {
s->temp_mode = TempOff;
}
if (s->linkset) {
set_link (s->linkset, s->linkid, get_address (msg));
link_strip_types (s->linkset, s->strip_types.to_ulong());
} else {
// set bank and strip feedback
strip_feedback(s, true);
_set_bank (1, get_address (msg));
_strip_select (std::shared_ptr<Stripable> (), get_address (msg));
}
global_feedback (s);
sel_send_pagesize (se_size, msg);
sel_plug_pagesize (pi_size, msg);
return 0;
}
int
OSC::set_surface_bank_size (uint32_t bs, lo_message msg)
{
if (observer_busy) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg), true);
s->bank_size = bs;
if (s->custom_mode && bs) {
s->custom_mode = s->custom_mode | 0x4;
}
if (s->linkset) {
set_link (s->linkset, s->linkid, get_address (msg));
} else {
// set bank and strip feedback
_set_bank (1, get_address (msg));
}
return 0;
}
int
OSC::set_surface_strip_types (uint32_t st, lo_message msg)
{
if (observer_busy) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg), true);
s->strip_types = st;
s->temp_mode = TempOff;
if (s->strip_types[10]) {
s->usegroup = PBD::Controllable::UseGroup;
} else {
s->usegroup = PBD::Controllable::NoGroup;
}
if (s->linkset) {
link_strip_types (s->linkset, st);
}
// set bank and strip feedback
strip_feedback(s, false);
set_bank (1, msg);
_strip_select (std::shared_ptr<Stripable> (), get_address (msg));
return 0;
}
int
OSC::set_surface_feedback (uint32_t fb, lo_message msg)
{
if (observer_busy) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg), true);
s->feedback = fb;
if (s->sel_obs) {
s->sel_obs->set_feedback(fb);
}
strip_feedback (s, true);
global_feedback (s);
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), get_address (msg));
return 0;
}
int
OSC::set_surface_gainmode (uint32_t gm, lo_message msg)
{
if (observer_busy) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg), true);
s->gainmode = gm;
strip_feedback (s, true);
global_feedback (s);
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), get_address (msg));
return 0;
}
int
OSC::set_surface_port (uint32_t po, lo_message msg)
{
string new_port;
if (!po) {
new_port = "auto";
} else if (po > 1024) {
new_port = string_compose ("%1", po);
} else {
PBD::warning << "Port value must be greater than 1024" << endmsg;
return -1;
}
OSCSurface *sur = get_surface(get_address (msg), true);
lo_address addr = lo_message_get_source (msg);
string host = lo_address_get_hostname (addr);
string port = lo_address_get_port (addr);
int protocol = lo_address_get_protocol (addr);
for (uint32_t i = 0; i < _ports.size (); i++) {
if (_ports[i].host == host) {
if (_ports[i].port == new_port) {
// no change - do nothing
return 0;
} else {
lo_address new_addr;
_ports[i].port = new_port;
if (new_port == "auto") {
new_addr = addr;
} else {
new_addr = lo_address_new_with_proto (protocol, host.c_str(), new_port.c_str());
}
char * rurl;
rurl = lo_address_get_url (new_addr);
sur->remote_url = rurl;
free (rurl);
for (uint32_t it = 0; it < _surface.size();) {
if (&_surface[it] == sur) {
it++;
continue;
}
char *sur_host = lo_url_get_hostname(_surface[it].remote_url.c_str());
if (strstr (sur_host, host.c_str())) {
surface_destroy (&_surface[it]);
_surface.erase (_surface.begin() + it);
} else {
it++;
}
}
if (sur->feedback.to_ulong()) {
refresh_surface (msg);
}
return 0;
}
}
}
// should not get here
return -1;
}
int
OSC::check_surface (lo_message msg)
{
if (!session) {
return -1;
}
get_surface (get_address (msg));
return 0;
}
OSC::OSCSurface *
OSC::get_surface (lo_address addr , bool quiet)
{
string r_url;
char * rurl;
rurl = lo_address_get_url (addr);
r_url = rurl;
free (rurl);
for (uint32_t it = 0; it < _surface.size(); ++it) {
//find setup for this surface
if (!_surface[it].remote_url.find(r_url)){
return &_surface[it];
}
}
// No surface create one with default values
OSCSurface s;
s.remote_url = r_url;
s.no_clear = false;
s.jogmode = 0;
s.bank = 1;
s.bank_size = default_banksize;
s.observers.clear();
s.sel_obs = 0;
s.global_obs = 0;
s.strip_types = default_strip;
s.feedback = default_feedback;
s.gainmode = default_gainmode;
s.usegroup = PBD::Controllable::NoGroup;
s.custom_strips.clear ();
s.custom_mode = 0;
s.temp_mode = TempOff;
s.sel_obs = 0;
s.expand = 0;
s.expand_enable = false;
s.expand_strip = std::shared_ptr<Stripable> ();
s.cue = false;
s.aux = 0;
s.cue_obs = 0;
s.strips = get_sorted_stripables(s.strip_types, s.cue, false, s.custom_strips);
s.send_page = 1;
s.send_page_size = default_send_size;
s.plug_page = 1;
s.plug_page_size = default_plugin_size;
s.plugin_id = 1;
s.linkset = 0;
s.linkid = 1;
s.nstrips = s.strips.size();
{
_surface.push_back (s);
}
if (!quiet) {
strip_feedback (&s, true);
global_feedback (&s);
}
_strip_select2 (std::shared_ptr<ARDOUR::Stripable>(), &_surface[_surface.size() - 1], addr);
return &_surface[_surface.size() - 1];
}
// setup global feedback for a surface
void
OSC::global_feedback (OSCSurface* sur)
{
OSCGlobalObserver* o = sur->global_obs;
if (o) {
delete o;
sur->global_obs = 0;
}
if (sur->feedback[4] || sur->feedback[3] || sur->feedback[5] || sur->feedback[6] || sur->feedback[15] || sur->feedback[16]) {
// create a new Global Observer for this surface
sur->global_obs = new OSCGlobalObserver (*this, *session, sur);
sur->global_obs->jog_mode (sur->jogmode);
}
}
void
OSC::strip_feedback (OSCSurface* sur, bool new_bank_size)
{
LinkSet *set = 0;
uint32_t ls = sur->linkset;
if (ls) {
set = &(link_sets[ls]);
if (set->not_ready) {
return;
}
sur->custom_mode = set->custom_mode;
sur->custom_strips = set->custom_strips;
sur->temp_mode = set->temp_mode;
sur->temp_strips = set->temp_strips;
sur->temp_master = set->temp_master;
}
if (!sur->temp_mode) {
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, sur->custom_mode, sur->custom_strips);
} else {
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, 1, sur->temp_strips);
}
uint32_t old_size = sur->nstrips;
sur->nstrips = sur->strips.size();
if (old_size != sur->nstrips) {
new_bank_size = true;
}
if (set) {
set->strips = sur->strips;
}
if (new_bank_size || (!sur->feedback[0] && !sur->feedback[1])) {
// delete old observers
for (uint32_t i = 0; i < sur->observers.size(); i++) {
delete sur->observers[i];
}
sur->observers.clear();
uint32_t bank_size = sur->bank_size;
if (!bank_size) {
bank_size = sur->nstrips;
}
if (sur->feedback[0] || sur->feedback[1]) {
for (uint32_t i = 0; i < bank_size; i++) {
OSCRouteObserver* o = new OSCRouteObserver (*this, i + 1, sur);
sur->observers.push_back (o);
if (sur->temp_mode == BusOnly) {
std::shared_ptr<ARDOUR::Stripable> str = get_strip (i + 1, lo_address_new_from_url (sur->remote_url.c_str()));
std::shared_ptr<ARDOUR::Send> send = get_send (str, lo_address_new_from_url (sur->remote_url.c_str()));
if (send) {
o->refresh_send (send, true);
}
}
}
}
} else {
if (sur->feedback[0] || sur->feedback[1]) {
for (uint32_t i = 0; i < sur->observers.size(); i++) {
std::shared_ptr<ARDOUR::Stripable> str = get_strip (i + 1, lo_address_new_from_url (sur->remote_url.c_str()));
sur->observers[i]->refresh_strip(str, true);
if (sur->temp_mode == BusOnly) {
std::shared_ptr<ARDOUR::Send> send = get_send (str, lo_address_new_from_url (sur->remote_url.c_str()));
if (send) {
sur->observers[i]->refresh_send (send, true);
}
}
}
}
}
bank_leds (sur);
}
void
OSC::notify_routes_added (ARDOUR::RouteList &)
{
// not sure if we need this PI change seems to cover
//recalcbanks();
}
void
OSC::notify_vca_added (ARDOUR::VCAList &)
{
// not sure if we need this PI change seems to cover
//recalcbanks();
}
void
OSC::recalcbanks ()
{
tick = false;
bank_dirty = true;
}
void
OSC::_recalcbanks ()
{
if (observer_busy) {
return;
}
/*
* We have two different ways of working here:
* 1) banked: The controller has a bank of strips and only can deal
* with banksize strips. We start banksize observers which run until
* either banksize is changed or Ardour exits.
*
* 2) banksize is 0 or unlimited and so is the same size as the number
* of strips.
*/
// refresh each surface we know about.
for (uint32_t it = 0; it < _surface.size(); ++it) {
OSCSurface* sur = &_surface[it];
// find lo_address
lo_address addr = lo_address_new_from_url (sur->remote_url.c_str());
if (sur->cue) {
_cue_set (sur->aux, addr);
} else if (!sur->bank_size) {
strip_feedback (sur, false);
// This surface uses /strip/list tell it routes have changed
lo_message reply;
reply = lo_message_new ();
lo_send_message (addr, X_("/strip/list"), reply);
lo_message_free (reply);
} else {
strip_feedback (sur, false);
}
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), addr);
}
}
int
OSC::set_bank (uint32_t bank_start, lo_message msg)
{
return _set_bank (bank_start, get_address (msg));
}
// set bank is callable with either message or address
int
OSC::_set_bank (uint32_t bank_start, lo_address addr)
{
if (!session) {
return -1;
}
if (!session->nroutes()) {
return -1;
}
OSCSurface *s = get_surface (addr, true);
Sorted striplist = s->strips;
uint32_t nstrips = s->nstrips;
LinkSet *set;
uint32_t ls = s->linkset;
if (ls) {
//we have a linkset... deal with each surface
set = &(link_sets[ls]);
if (set->not_ready) {
return 1;
}
uint32_t d_count = set->urls.size();
set->strips = striplist;
bank_start = bank_limits_check (bank_start, set->banksize, nstrips);
set->bank = bank_start;
uint32_t not_ready = 0;
for (uint32_t dv = 1; dv < d_count; dv++) {
if (set->urls[dv] != "") {
string url = set->urls[dv];
OSCSurface *sur = get_surface (lo_address_new_from_url (url.c_str()));
if (sur->linkset != ls) {
set->urls[dv] = "";
not_ready = dv;
} else {
lo_address sur_addr = lo_address_new_from_url (sur->remote_url.c_str());
sur->bank = bank_start;
bank_start = bank_start + sur->bank_size;
strip_feedback (sur, false);
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), sur_addr);
bank_leds (sur);
lo_address_free (sur_addr);
}
} else {
not_ready = dv;
}
if (not_ready) {
if (!set->not_ready) {
set->not_ready = not_ready;
}
set->bank = 1;
break;
}
}
if (not_ready) {
surface_link_state (set);
}
} else {
s->bank = bank_limits_check (bank_start, s->bank_size, nstrips);
strip_feedback (s, true);
_strip_select (std::shared_ptr<ARDOUR::Stripable>(), addr);
bank_leds (s);
}
bank_dirty = false;
tick = true;
return 0;
}
uint32_t
OSC::bank_limits_check (uint32_t bank, uint32_t size, uint32_t total)
{
uint32_t b_size;
if (!size) {
// no banking - bank includes all stripables
b_size = total;
} else {
b_size = size;
}
// Do limits checking
if (bank < 1) bank = 1;
if (b_size >= total) {
bank = 1;
} else if (bank > ((total - b_size) + 1)) {
// top bank is always filled if there are enough strips for at least one bank
bank = (uint32_t)((total - b_size) + 1);
}
return bank;
}
void
OSC::bank_leds (OSCSurface* s)
{
uint32_t bank = 0;
uint32_t size = 0;
uint32_t total = 0;
// light bankup or bankdown buttons if it is possible to bank in that direction
lo_address addr = lo_address_new_from_url (s->remote_url.c_str());
if (s->linkset) {
LinkSet *set;
set = &(link_sets[s->linkset]);
bank = set->bank;
size = set->banksize;
total = s->nstrips;
if (set->not_ready) {
total = 1;
}
} else {
bank = s->bank;
size = s->bank_size;
total = s->nstrips;
}
if (size && (s->feedback[0] || s->feedback[1] || s->feedback[4])) {
lo_message reply;
reply = lo_message_new ();
if ((total <= size) || (bank > (total - size))) {
lo_message_add_int32 (reply, 0);
} else {
lo_message_add_int32 (reply, 1);
}
lo_send_message (addr, X_("/bank_up"), reply);
lo_message_free (reply);
reply = lo_message_new ();
if (bank > 1) {
lo_message_add_int32 (reply, 1);
} else {
lo_message_add_int32 (reply, 0);
}
lo_send_message (addr, X_("/bank_down"), reply);
lo_message_free (reply);
}
}
int
OSC::bank_up (lo_message msg)
{
return bank_delta (1.0, msg);
}
int
OSC::bank_delta (float delta, lo_message msg)
{
if (!session) {
return -1;
}
// only do deltas of -1 0 or 1
if (delta > 0) {
delta = 1;
} else if (delta < 0) {
delta = -1;
} else {
// 0 key release ignore
return 0;
}
OSCSurface *s = get_surface(get_address (msg));
if (!s->bank_size) {
// bank size of 0 means use all strips no banking
return 0;
}
uint32_t old_bank = 0;
uint32_t bank_size = 0;
if (s->linkset) {
old_bank = link_sets[s->linkset].bank;
bank_size = link_sets[s->linkset].banksize;
} else {
old_bank = s->bank;
bank_size = s->bank_size;
}
uint32_t new_bank = old_bank + (bank_size * (int) delta);
if ((int)new_bank < 1) {
new_bank = 1;
}
if (new_bank != old_bank) {
set_bank (new_bank, msg);
}
return 0;
}
int
OSC::bank_down (lo_message msg)
{
return bank_delta (-1.0, msg);
}
int
OSC::use_group (float value, lo_message msg)
{
if (!session) {
return -1;
}
OSCSurface *s = get_surface(get_address (msg));
if (value) {
s->usegroup = PBD::Controllable::UseGroup;
} else {
s->usegroup = PBD::Controllable::NoGroup;
}
return 0;
}
// this gets called for anything that starts with /select/group
int
OSC::parse_sel_group (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s = sur->select;
int ret = 1; /* unhandled */
/// these could be added to strip
if (s) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (s);
if (!rt) {
PBD::warning << "OSC: VCAs can not be part of a group." << endmsg;
return ret;
}
RouteGroup *rg = rt->route_group();
if (!rg) {
PBD::warning << "OSC: This strip is not part of a group." << endmsg;
}
float value = 0;
if (argc == 1) {
if (types[0] == 'f') {
value = (uint32_t) argv[0]->f;
} else if (types[0] == 'i') {
value = (uint32_t) argv[0]->i;
}
}
if (!strncmp (path, X_("/select/group/enable"), 20)) {
if (rg) {
if (argc == 1) {
rg->set_active (value, this);
ret = 0;
}
} else {
int_message (X_("/select/group/enable"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/gain")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_gain ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/gain"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/relative")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_relative ((bool) value, this);
ret = 0;
}
} else {
int_message (X_("/select/group/relative"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/mute")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_mute ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/mute"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/solo")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_solo ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/solo"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/recenable")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_recenable ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/recenable"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/select")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_select ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/select"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/active")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_route_active ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/active"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/color")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_color ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/color"), 0, get_address (msg));
}
}
else if (strcmp (path, X_("/select/group/monitoring")) == 0) {
if (rg) {
if (argc == 1) {
rg->set_monitoring ((bool) value);
ret = 0;
}
} else {
int_message (X_("/select/group/monitoring"), 0, get_address (msg));
}
}
}
return ret;
}
std::shared_ptr<VCA>
OSC::get_vca_by_name (std::string vname)
{
StripableList stripables;
session->get_stripables (stripables);
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> s = *it;
std::shared_ptr<VCA> v = std::dynamic_pointer_cast<VCA> (s);
if (v) {
if (vname == v->name()) {
return v;
}
}
}
return std::shared_ptr<VCA>();
}
int
OSC::set_temp_mode (lo_address addr)
{
bool ret = 1;
OSCSurface *sur = get_surface(addr);
std::shared_ptr<Stripable> s = sur->temp_master;
if (s) {
if (sur->temp_mode == GroupOnly) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (s);
if (rt) {
RouteGroup *rg = rt->route_group();
if (rg) {
sur->temp_strips.clear();
std::shared_ptr<RouteList> rl = rg->route_list();
for (RouteList::iterator it = rl->begin(); it != rl->end(); ++it) {
std::shared_ptr<Route> r = *it;
std::shared_ptr<Stripable> st = std::dynamic_pointer_cast<Stripable> (r);
sur->temp_strips.push_back(st);
}
// check if this group feeds a bus or is slaved
std::shared_ptr<Stripable> mstr = std::shared_ptr<Stripable> ();
if (rg->has_control_master()) {
std::shared_ptr<VCA> vca = session->vca_manager().vca_by_number (rg->group_master_number());
if (vca) {
mstr = std::dynamic_pointer_cast<Stripable> (vca);
}
} else if (rg->has_subgroup()) {
std::shared_ptr<Route> sgr = rg->subgroup_bus().lock();
if (sgr) {
mstr = std::dynamic_pointer_cast<Stripable> (sgr);
}
}
if (mstr) {
sur->temp_strips.push_back(mstr);
}
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, 1, sur->temp_strips);
sur->nstrips = sur->temp_strips.size();
ret = 0;
}
}
} else if (sur->temp_mode == VCAOnly) {
std::shared_ptr<VCA> vca = std::dynamic_pointer_cast<VCA> (s);
if (vca) {
sur->temp_strips.clear();
StripableList stripables;
session->get_stripables (stripables);
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> st = *it;
if (st->slaved_to (vca)) {
sur->temp_strips.push_back(st);
}
}
sur->temp_strips.push_back(s);
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, 1, sur->temp_strips);
sur->nstrips = sur->temp_strips.size();
ret = 0;
}
} else if (sur->temp_mode == BusOnly) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (s);
if (rt) {
if (!rt->is_track () && rt->can_solo ()) {
// this is a bus, but not master, monitor or audition
sur->temp_strips.clear();
StripableList stripables;
session->get_stripables (stripables, PresentationInfo::AllStripables);
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> st = *it;
std::shared_ptr<Route> ri = std::dynamic_pointer_cast<Route> (st);
bool sends = true;
if (ri && ri->direct_feeds_according_to_graph (rt, &sends)) {
sur->temp_strips.push_back(st);
}
}
sur->temp_strips.push_back(s);
sur->strips = get_sorted_stripables(sur->strip_types, sur->cue, 1, sur->temp_strips);
sur->nstrips = sur->temp_strips.size();
ret = 0;
}
}
} else if (sur->temp_mode == TempOff) {
sur->temp_mode = TempOff;
ret = 0;
}
}
LinkSet *set;
uint32_t ls = sur->linkset;
if (ls) {
set = &(link_sets[ls]);
set->temp_mode = sur->temp_mode;
set->temp_strips.clear ();
set->temp_strips = sur->temp_strips;
set->temp_master = sur->temp_master;
set->strips = sur->strips;
}
if (ret) {
sur->temp_mode = TempOff;
}
return ret;
}
std::shared_ptr<Send>
OSC::get_send (std::shared_ptr<Stripable> st, lo_address addr)
{
OSCSurface *sur = get_surface(addr);
std::shared_ptr<Stripable> s = sur->temp_master;
if (st && s && (st != s)) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (s);
std::shared_ptr<Route> rst = std::dynamic_pointer_cast<Route> (st);
//find what send number feeds s
return rst->internal_send_for (rt);
}
return std::shared_ptr<Send> ();
}
int
OSC::name_session (char *n, lo_message msg)
{
if (!session) {
return -1;
}
string new_name = n;
std::string const& illegal = Session::session_name_is_legal (new_name);
if (!illegal.empty()) {
PBD::warning << (string_compose (_("To ensure compatibility with various systems\n"
"session names may not contain a '%1' character"), illegal)) << endmsg;
return -1;
}
switch (session->rename (new_name)) {
case -1:
PBD::warning << (_("That name is already in use by another directory/folder. Please try again.")) << endmsg;
break;
case 0:
return 0;
break;
default:
PBD::warning << (_("Renaming this session failed.\nThings could be seriously messed up at this point")) << endmsg;
break;
}
return -1;
}
uint32_t
OSC::get_sid (std::shared_ptr<ARDOUR::Stripable> strip, lo_address addr)
{
if (!strip) {
return 0;
}
OSCSurface *s = get_surface(addr);
uint32_t b_size;
if (!s->bank_size) {
// no banking
b_size = s->nstrips;
} else {
b_size = s->bank_size;
}
for (uint32_t n = s->bank; n < (min ((b_size + s->bank), s->nstrips + 1)); ++n) {
if (n <= s->strips.size()) {
if (strip == s->strips[n-1]) {
return n - s->bank + 1;
}
}
}
// strip not in current bank
return 0;
}
std::shared_ptr<ARDOUR::Stripable>
OSC::get_strip (uint32_t ssid, lo_address addr)
{
OSCSurface *s = get_surface(addr);
if (ssid && ((ssid + s->bank - 2) < s->nstrips)) {
return s->strips[ssid + s->bank - 2];
}
// guess it is out of range
return std::shared_ptr<ARDOUR::Stripable>();
}
// send and plugin paging commands
int
OSC::sel_send_pagesize (uint32_t size, lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg));
if (size != s->send_page_size) {
s->send_page_size = size;
s->sel_obs->set_send_size(size);
}
return 0;
}
int
OSC::sel_send_page (int page, lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg));
uint32_t send_size = s->send_page_size;
if (!send_size) {
send_size = s->nsends;
}
uint32_t max_page = (uint32_t)(s->nsends / send_size) + 1;
s->send_page = s->send_page + page;
if (s->send_page < 1) {
s->send_page = 1;
} else if ((uint32_t)s->send_page > max_page) {
s->send_page = max_page;
}
s->sel_obs->set_send_page (s->send_page);
return 0;
}
int
OSC::sel_plug_pagesize (uint32_t size, lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg));
if (size != s->plug_page_size) {
s->plug_page_size = size;
s->sel_obs->set_plugin_size(size);
}
return 0;
}
int
OSC::sel_plug_page (int page, lo_message msg)
{
if (!page) {
return 0;
}
int new_page = 0;
OSCSurface *s = get_surface(get_address (msg));
if (page > 0) {
new_page = s->plug_page + s->plug_page_size;
if ((uint32_t) new_page > s->plug_params.size ()) {
new_page = s->plug_page;
}
} else {
new_page = s->plug_page - s->plug_page_size;
if (new_page < 1) {
new_page = 1;
}
}
if (new_page != s->plug_page) {
s->plug_page = new_page;
s->sel_obs->set_plugin_page(s->plug_page);
}
return 0;
}
int
OSC::sel_plugin (int delta, lo_message msg)
{
if (!delta) {
return 0;
}
OSCSurface *sur = get_surface(get_address (msg));
return _sel_plugin (sur->plugin_id + delta, get_address (msg));
}
int
OSC::_sel_plugin (int id, lo_address addr)
{
OSCSurface *sur = get_surface(addr);
std::shared_ptr<Stripable> s = sur->select;
if (s) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(s);
if (!r) {
return 1;
}
/* find out how many plugins we have */
sur->plugins.clear();
for (int nplugs = 0; true; ++nplugs) {
std::shared_ptr<Processor> proc = r->nth_plugin (nplugs);
if (!proc) {
break;
}
if (!r->nth_plugin(nplugs)->display_to_user()) {
continue;
}
#ifdef MIXBUS
/* need to check for mixbus channel strips (and exclude them) */
std::shared_ptr<PluginInsert> pi = std::dynamic_pointer_cast<PluginInsert>(proc);
if (pi && pi->is_channelstrip()) {
continue;
}
#endif
sur->plugins.push_back (nplugs);
}
// limit plugin_id to actual plugins
if (sur->plugins.size() < 1) {
sur->plugin_id = 0;
sur->plug_page = 1;
if (sur->sel_obs) {
sur->sel_obs->set_plugin_id(-1, 1);
}
return 0;
} else if (id < 1) {
sur->plugin_id = 1;
} else if (sur->plugins.size() < (uint32_t) id) {
sur->plugin_id = sur->plugins.size();
} else {
sur->plugin_id = id;
}
// we have a plugin number now get the processor
std::shared_ptr<Processor> proc = r->nth_plugin (sur->plugins[sur->plugin_id - 1]);
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(proc))) {
PBD::warning << "OSC: Plugin: " << sur->plugin_id << " does not seem to be a plugin" << endmsg;
return 1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
bool ok = false;
// put only input controls into a vector
sur->plug_params.clear ();
uint32_t nplug_params = pip->parameter_count();
for ( uint32_t ppi = 0; ppi < nplug_params; ++ppi) {
uint32_t controlid = pip->nth_parameter(ppi, ok);
if (!ok) {
continue;
}
if (pip->parameter_is_input(controlid)) {
sur->plug_params.push_back (ppi);
}
}
sur->plug_page = 1;
if (sur->sel_obs) {
sur->sel_obs->set_plugin_id(sur->plugins[sur->plugin_id - 1], sur->plug_page);
}
return 0;
}
return 1;
}
void
OSC::transport_sample (lo_message msg)
{
if (!session) {
return;
}
check_surface (msg);
samplepos_t pos = session->transport_sample ();
lo_message reply = lo_message_new ();
lo_message_add_int64 (reply, pos);
lo_send_message (get_address (msg), X_("/transport_frame"), reply);
lo_message_free (reply);
}
void
OSC::transport_speed (lo_message msg)
{
if (!session) {
return;
}
check_surface (msg);
double ts = get_transport_speed();
lo_message reply = lo_message_new ();
lo_message_add_double (reply, ts);
lo_send_message (get_address (msg), X_("/transport_speed"), reply);
lo_message_free (reply);
}
void
OSC::record_enabled (lo_message msg)
{
if (!session) {
return;
}
check_surface (msg);
int re = (int)session->get_record_enabled ();
lo_message reply = lo_message_new ();
lo_message_add_int32 (reply, re);
lo_send_message (get_address (msg), X_("/record_enabled"), reply);
lo_message_free (reply);
}
int
OSC::scrub (float delta, lo_message msg)
{
if (!session) return -1;
check_surface (msg);
scrub_place = session->transport_sample ();
float speed;
int64_t now = PBD::get_microseconds ();
int64_t diff = now - scrub_time;
if (diff > 35000) {
// speed 1 (or 0 if jog wheel supports touch)
speed = delta;
} else if ((diff > 20000) && (fabs(scrub_speed) == 1)) {
// add some hysteresis to stop excess speed jumps
speed = delta;
} else {
speed = (int)(delta * 2);
}
scrub_time = now;
if (scrub_speed == speed) {
// Already at that speed no change
return 0;
}
scrub_speed = speed;
if (speed > 0) {
if (speed == 1) {
session->request_transport_speed (.5);
} else {
session->request_transport_speed (9.9);
}
} else if (speed < 0) {
if (speed == -1) {
session->request_transport_speed (-.5);
} else {
session->request_transport_speed (-1);
}
} else {
session->request_stop ();
}
return 0;
}
int
OSC::jog (float delta, lo_message msg)
{
if (!session) return -1;
OSCSurface *s = get_surface(get_address (msg));
switch(s->jogmode)
{
case 0:
if (delta) {
jump_by_seconds (delta / 5);
}
break;
case 1:
if (delta > 0) {
access_action ("Common/nudge-playhead-forward");
} else if (delta < 0) {
access_action ("Common/nudge-playhead-backward");
}
break;
case 2:
scrub (delta, msg);
break;
case 3:
if (delta) {
double speed = get_transport_speed ();
set_transport_speed (speed + (delta / 8.1));
} else {
set_transport_speed (0);
}
break;
case 4:
if (delta > 0) {
next_marker ();
} else if (delta < 0) {
prev_marker ();
}
break;
case 5:
if (delta > 0) {
access_action ("Editor/scroll-forward");
} else if (delta < 0) {
access_action ("Editor/scroll-backward");
}
break;
case 6:
if (delta > 0) {
set_bank (s->bank + 1, msg);
} else if (delta < 0) {
set_bank (s->bank - 1, msg);
}
break;
case 7:
if (delta > 0) {
bank_up (msg);
} else if (delta < 0) {
bank_down (msg);
}
break;
default:
break;
}
return 0;
}
int
OSC::jog_mode (float mode, lo_message msg)
{
if (!session) return -1;
OSCSurface *s = get_surface(get_address (msg));
if (get_transport_speed () != 1.0) {
set_transport_speed (0);
}
s->jogmode = (uint32_t) mode;
s->global_obs->jog_mode (mode);
return 0;
}
int
OSC::trigger_bank_state (lo_address addr)
{
if (!session) return -1;
lo_message bank_msg = lo_message_new ();
lo_message_add_int32 (bank_msg, session->num_triggerboxes()); //total avail routes (with triggers)
lo_message_add_int32 (bank_msg, _tbank_start_route); //route start offs
lo_message_add_int32 (bank_msg, TriggerBox::default_triggers_per_box); //total avail triggers
lo_message_add_int32 (bank_msg, _tbank_start_row); //trigger start offs
lo_send_message (addr, X_("/trigger_grid/bank"), bank_msg);
lo_message_free (bank_msg);
return 0;
}
int
OSC::trigger_grid_state (lo_address addr, bool zero_it)
{
if (!session) return -1;
for (int rt = 0; rt < 8; rt++) { //TODO: route bank size
lo_message trig_msg = lo_message_new ();
lo_message_add_float (trig_msg, zero_it ? -1 : trigger_progress_at(rt)); //progress
for (int row = 0; row < 8; row++) { //ToDo: trigger bank size
lo_message_add_int32 (trig_msg, zero_it ? -1 : trigger_display_at(rt, row).state); // -1 = empty; 0 stopped; 1 playing
}
lo_send_message (addr, string_compose(X_("/trigger_grid/%1/state"), rt).c_str(), trig_msg);
lo_message_free (trig_msg);
}
return 0;
}
int
OSC::mixer_scene_state (lo_address addr, bool zero_it)
{
if (!session) return -1;
for (int scn = 0; scn < 8; scn++) { //TODO: mixer scene size
lo_message scene_msg = lo_message_new ();
if (!zero_it && session->nth_mixer_scene_valid(scn)) {
std::shared_ptr<ARDOUR::MixerScene> scene = session->nth_mixer_scene(scn);
lo_message_add_string (scene_msg, scene->name().c_str());
} else {
lo_message_add_string (scene_msg, "");
}
lo_send_message (addr, string_compose(X_("/mixer_scene/%1/name"), scn).c_str(), scene_msg);
lo_message_free (scene_msg);
}
return 0;
}
int
OSC::set_marker (const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (argc != 1) {
PBD::warning << "Wrong number of parameters, one only." << endmsg;
return -1;
}
const Locations::LocationList& ll (session->locations ()->list ());
uint32_t marker = 0;
switch (types[0]) {
case 's':
{
Location *cur_mark = 0;
for (Locations::LocationList::const_iterator l = ll.begin(); l != ll.end(); ++l) {
if ((*l)->is_mark ()) {
if (strcmp (&argv[0]->s, (*l)->name().c_str()) == 0) {
session->request_locate ((*l)->start_sample (), false, MustStop);
return 0;
} else if ((*l)->start () == session->transport_sample()) {
cur_mark = (*l);
}
}
}
if (cur_mark) {
cur_mark->set_name (&argv[0]->s);
return 0;
}
PBD::warning << string_compose ("Marker: \"%1\" - does not exist", &argv[0]->s) << endmsg;
return -1;
}
break;
case 'i':
marker = (uint32_t) argv[0]->i - 1;
break;
case 'f':
marker = (uint32_t) argv[0]->f - 1;
break;
default:
return -1;
break;
}
std::vector<ArdourSurface::LocationMarker> lm;
// get Locations that are marks
for (Locations::LocationList::const_iterator l = ll.begin(); l != ll.end(); ++l) {
if ((*l)->is_mark ()) {
lm.push_back (ArdourSurface::LocationMarker((*l)->name(), (*l)->start_sample ()));
}
}
// sort them by position
ArdourSurface::LocationMarkerSort location_marker_sort;
std::sort (lm.begin(), lm.end(), location_marker_sort);
// go there
if (marker < lm.size()) {
session->request_locate (lm[marker].when, false, MustStop);
return 0;
}
// we were unable to deal with things
return -1;
}
int
OSC::group_list (lo_message msg)
{
return send_group_list (get_address (msg));
}
int
OSC::send_group_list (lo_address addr)
{
lo_message reply;
reply = lo_message_new ();
lo_message_add_string (reply, X_("none"));
std::list<RouteGroup*> groups = session->route_groups ();
for (std::list<RouteGroup *>::iterator i = groups.begin(); i != groups.end(); ++i) {
RouteGroup *rg = *i;
lo_message_add_string (reply, rg->name().c_str());
}
lo_send_message (addr, X_("/group/list"), reply);
lo_message_free (reply);
return 0;
}
int
OSC::click_level (float position)
{
if (!session) return -1;
if (session->click_gain()->gain_control()) {
session->click_gain()->gain_control()->set_value (session->click_gain()->gain_control()->interface_to_internal (position), PBD::Controllable::NoGroup);
}
return 0;
}
void
OSC::loop_location (int start, int end)
{
BasicUI::loop_location (timepos_t (start), timepos_t (end));
}
int
OSC::route_get_sends(lo_message msg) {
if (!session) {
return -1;
}
lo_arg **argv = lo_message_get_argv(msg);
int rid = argv[0]->i;
std::shared_ptr<Stripable> strip = get_strip(rid, get_address(msg));
if (!strip) {
return -1;
}
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (strip);
if (!r) {
return -1;
}
lo_message reply = lo_message_new();
lo_message_add_int32(reply, rid);
int i = 0;
for (;;) {
std::shared_ptr<Processor> p = r->nth_send(i++);
if (!p) {
break;
}
std::shared_ptr<InternalSend> isend = std::dynamic_pointer_cast<InternalSend> (p);
if (isend) {
lo_message_add_int32(reply, get_sid(isend->target_route(), get_address(msg)));
lo_message_add_string(reply, isend->name().c_str());
lo_message_add_int32(reply, i);
lo_message_add_float(reply, isend->gain_control()->internal_to_interface (isend->gain_control()->get_value()));
lo_message_add_int32(reply, p->active() ? 1 : 0);
}
}
// if used dedicated message path to identify this reply in async operation.
// Naming it #reply wont help the client to identify the content.
lo_send_message(get_address (msg), X_("/strip/sends"), reply);
lo_message_free(reply);
return 0;
}
int
OSC::route_get_receives(lo_message msg) {
if (!session) {
return -1;
}
lo_arg **argv = lo_message_get_argv(msg);
uint32_t rid = argv[0]->i;
std::shared_ptr<Stripable> strip = get_strip(rid, get_address(msg));
if (!strip) {
return -1;
}
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (strip);
if (!r) {
return -1;
}
std::shared_ptr<RouteList const> route_list = session->get_routes();
lo_message reply = lo_message_new();
lo_message_add_int32(reply, rid);
for (auto const& i : *route_list) {
std::shared_ptr<Route> tr = std::dynamic_pointer_cast<Route> (i);
if (!tr) {
continue;
}
int j = 0;
for (;;) {
std::shared_ptr<Processor> p = tr->nth_send(j++);
if (!p) {
break;
}
std::shared_ptr<InternalSend> isend = std::dynamic_pointer_cast<InternalSend> (p);
if (isend) {
if( isend->target_route()->id() == r->id()){
lo_message_add_int32(reply, get_sid(tr, get_address(msg)));
lo_message_add_string(reply, tr->name().c_str());
lo_message_add_int32(reply, j);
lo_message_add_float(reply, isend->gain_control()->internal_to_interface (isend->gain_control()->get_value()));
lo_message_add_int32(reply, p->active() ? 1 : 0);
}
}
}
}
// I have used a dedicated message path to identify this reply in async operation.
// Naming it #reply wont help the client to identify the content.
lo_send_message(get_address (msg), X_("/strip/receives"), reply);
lo_message_free(reply);
return 0;
}
// strip calls
int
OSC::master_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
// set sub_path to null string if path is /master
const char* sub_path = &path[7];
if (strlen(path) > 8) {
// reset sub_path to char after /master/ if at least 1 char longer
sub_path = &path[8];
} else if (strlen(path) == 8) {
PBD::warning << "OSC: trailing / not valid." << endmsg;
}
//OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s = session->master_out();
if (s) {
ret = _strip_parse (path, sub_path, types, argv, argc, s, 0, false, msg);
} else {
PBD::warning << "OSC: No Master strip" << endmsg;
}
return ret;
}
int
OSC::monitor_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
// set sub_path to null string if path is /monitor
const char* sub_path = &path[8];
if (strlen(path) > 9) {
// reset sub_path to char after /monitor/ if at least 1 char longer
sub_path = &path[9];
} else if (strlen(path) == 9) {
PBD::warning << "OSC: trailing / not valid." << endmsg;
}
//OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s = session->monitor_out();
if (s) {
std::shared_ptr<MonitorProcessor> mon = session->monitor_out()->monitor_control();
int state = 0;
if (types[0] == 'f') {
state = (uint32_t) argv[0]->f;
} else if (types[0] == 'i') {
state = argv[0]->i;
}
// these are only in the monitor section
if (!strncmp (sub_path, X_("mute"), 4)) {
if (argc) {
mon->set_cut_all (state);
} else {
int_message (path, mon->cut_all (), get_address (msg));
}
} else if (!strncmp (sub_path, X_("dim"), 3)) {
if (argc) {
mon->set_dim_all (state);
} else {
int_message (path, mon->dim_all (), get_address (msg));
}
} else if (!strncmp (sub_path, X_("mono"), 4)) {
if (argc) {
mon->set_mono (state);
} else {
int_message (path, mon->mono (), get_address (msg));
}
} else {
ret = _strip_parse (path, sub_path, types, argv, argc, s, 0, false, msg);
}
} else {
PBD::warning << "OSC: No Monitor strip" << endmsg;
}
return ret;
}
int
OSC::select_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
// set sub_path to null string if path is /select
const char* sub_path = &path[7];
if (strlen(path) > 8) {
// reset sub_path to char after /select/ if at least 1 char longer
sub_path = &path[8];
} else if (strlen(path) == 8) {
PBD::warning << "OSC: trailing / not valid." << endmsg;
}
OSCSurface *sur = get_surface(get_address (msg));
if (!strncmp (sub_path, X_("select"), 6)) {
PBD::warning << "OSC: select is already selected." << endmsg;
return 1;
}
if (!strncmp (path, X_("/select/group"), 13) && strlen (path) > 13) {
/** this needs fixing as it blocks /group s name */
PBD::info << "OSC: select_parse /select/group/." << endmsg;
ret = parse_sel_group (path, types, argv, argc, msg);
}
else if (!strncmp (path, X_("/select/send_gain/"), 18) && strlen (path) > 18) {
int ssid = atoi (&path[18]);
ret = sel_sendgain (ssid, argv[0]->f, msg);
}
else if (!strncmp (path, X_("/select/send_fader/"), 19) && strlen (path) > 19) {
int ssid = atoi (&path[19]);
ret = sel_sendfader (ssid, argv[0]->f, msg);
}
else if (!strncmp (path, X_("/select/send_enable/"), 20) && strlen (path) > 20) {
int ssid = atoi (&path[20]);
ret = sel_sendenable (ssid, argv[0]->f, msg);
}
else if (!strncmp (path, X_("/select/eq_gain/"), 16) && strlen (path) > 16) {
int ssid = atoi (&path[16]);
ret = sel_eq_gain (ssid, argv[0]->f, msg);
}
else if (!strncmp (path, X_("/select/eq_freq/"), 16) && strlen (path) > 16) {
int ssid = atoi (&path[16]);
ret = sel_eq_freq (ssid, argv[0]->f , msg);
}
else if (!strncmp (path, X_("/select/eq_q/"), 13) && strlen (path) > 13) {
int ssid = atoi (&path[13]);
ret = sel_eq_q (ssid, argv[0]->f, msg);
}
else if (!strncmp (path, X_("/select/eq_shape/"), 17) && strlen (path) > 17) {
int ssid = atoi (&path[17]);
ret = sel_eq_shape (ssid, argv[0]->f, msg);
}
else {
/// this is in both strip and select
std::shared_ptr<Stripable> s = sur->select;
if (s) {
if (!strncmp (sub_path, X_("expand"), 6)) {
int yn = 0;
if (types[0] == 'f') {
yn = (int) argv[0]->f;
} else if (types[0] == 'i') {
yn = argv[0]->i;
} else {
return 1;
}
if (types[0] != 'f' && types[0] != 'i') {
return 1;
}
sur->expand_strip = s;
sur->expand_enable = (bool) yn;
std::shared_ptr<Stripable> sel;
if (yn) {
sel = s;
} else {
sel = std::shared_ptr<Stripable> ();
}
return _strip_select (sel, get_address (msg));
} else {
ret = _strip_parse (path, sub_path, types, argv, argc, s, 0, false, msg);
}
} else {
PBD::warning << "OSC: No selected strip" << endmsg;
}
}
return ret;
}
int
OSC::strip_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
int ssid = 0;
int param_1 = 1;
uint32_t nparam = argc;
const char* sub_path = &path[6];
if (strlen(path) > 7) {
// reset sub_path to char after /strip/ if at least 1 char longer
sub_path = &path[7];
} else if (strlen(path) == 7) {
PBD::warning << "OSC: trailing / not valid." << endmsg;
return 1;
}
OSCSurface *sur = get_surface(get_address (msg));
// ssid may be in three places
if (atoi(sub_path)) {
// test for /strip/<ssid>/subpath
ssid = atoi(sub_path);
nparam++;
param_1 = 0;
if (strchr(sub_path, (int) '/')) {
sub_path = &(strchr(sub_path, (int) '/')[1]);
} else {
sub_path = &(strchr(sub_path, 0)[1]);
}
} else if (atoi (&(strrchr(path, (int) '/')[1]))) {
// check for /path/<ssid>
ssid = atoi (&(strrchr(path, (int) '/')[1]));
nparam++;
param_1 = 0;
} else if (argc) {
if (types[0] == 'i') {
ssid = argv[0]->i;
} else if (types[0] == 'f') {
ssid = argv[0]->f;
}
}
if (!nparam && !ssid) {
// only list works here
if (!strcmp (path, X_("/strip/list"))) {
// /strip/list is legacy
routes_list (msg);
ret = 0;
}
else if (!strcmp (path, X_("/strip"))) {
strip_list (msg);
ret = 0;
} else {
PBD::warning << "OSC: missing parameters." << endmsg;
return 1;
}
}
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
if (s) {
if (!strncmp (sub_path, X_("expand"), 6)) {
/// this is in both strip and select should be in _parse_strip
int yn = 0;
if (types[param_1] == 'f') {
yn = (int) argv[param_1]->f;
} else if (types[param_1] == 'i') {
yn = argv[param_1]->i;
} else {
return 1;
}
if (types[param_1] != 'f' && types[param_1] != 'i') {
return 1;
}
sur->expand_strip = s;
sur->expand_enable = (bool) yn;
sur->expand = ssid;
std::shared_ptr<Stripable> sel;
if (yn) {
sel = s;
} else {
sel = std::shared_ptr<Stripable> ();
}
return _strip_select (sel, get_address (msg));
} else {
ret = _strip_parse (path, sub_path, types, argv, argc, s, param_1, true, msg);
}
} else {
PBD::warning << "OSC: No such strip" << endmsg;
}
return ret;
}
int
OSC::_strip_parse (const char *path, const char *sub_path, const char* types, lo_arg **argv, int argc, std::shared_ptr<ARDOUR::Stripable> s, int param_1, bool strp, lo_message msg)
{
int ret = 1;
int yn = 0;
float value = 0.0;
string strng = "";
char *text;
bool s_flt = false;
bool s_int = false;
if (types[param_1] == 'f') {
yn = (int) argv[param_1]->f;
s_int = true;
value = argv[param_1]->f;
s_flt = true;
} else if (types[param_1] == 'i') {
yn = argv[param_1]->i;
s_int = true;
} else if (types[param_1] == 's') {
text = &argv[param_1]->s;
strng = &argv[param_1]->s;
if (atoi(text) || text[0] == '0') {
yn = atoi(text);
s_int = true;
}
if (atof(text)) {
value = atof(text);
s_flt = true;
}
}
OSCSurface *sur = get_surface(get_address (msg));
bool send_active = strp && sur->temp_mode == BusOnly && get_send (s, get_address (msg));
bool control_disabled = strp && (sur->temp_mode == BusOnly) && (s != sur->temp_master);
bool n_mo = !s->is_monitor();
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (s);
if (!strlen(sub_path)) {
// send stripable info
int sid = 0;
if (param_1) {
if (types[0] == 'f') {
sid = (int) argv[0]->f;
} else if (types[0] == 'i') {
sid = argv[0]->i;
}
}
ret = strip_state (path, s, sid, msg);
}
else if (!strncmp (sub_path, X_("gain"), 4) || !strncmp (sub_path, X_("fader"), 5) || !strncmp (sub_path, X_("db_delta"), 8)){
std::shared_ptr<GainControl> gain_control;
gain_control = s->gain_control();
if (gain_control) {
if (argc > (param_1)) {
if (s_flt) {
if (send_active) {
gain_control = get_send(s, get_address (msg))->gain_control();
}
float abs;
if (!strncmp (sub_path, X_("gain"), 4)) {
if (value < -192) {
abs = 0;
} else {
abs = dB_to_coefficient (value);
}
} else if (!strncmp (sub_path, X_("fader"), 5)) {
abs = gain_control->interface_to_internal (value);
} else if (!strncmp (sub_path, X_("db_delta"), 8)) {
float db = accurate_coefficient_to_dB (gain_control->get_value()) + value;
if (db < -192) {
abs = 0;
} else {
abs = dB_to_coefficient (db);
}
} else {
abs = 0;
}
float top = gain_control->upper();
if (abs > top) {
abs = top;
}
fake_touch (gain_control);
gain_control->set_value (abs, sur->usegroup);
ret = 0;
}
} else {
float ret_v;
if (!strncmp (sub_path, X_("gain"), 4)) {
ret_v = fast_coefficient_to_dB (gain_control->get_value ());
ret = 0;
} else if (!strncmp (sub_path, X_("fader"), 5)) {
ret_v = gain_control->internal_to_interface (gain_control->get_value ());
ret = 0;
} else {
PBD::warning << "OSC: delta has no info" << endmsg;
}
if (!ret) {
float_message (path, ret_v, get_address (msg));
}
}
}
}
else if (!strncmp (sub_path, X_("trimdB"), 6)) {
if (!control_disabled && s->trim_control() && n_mo) {
if (argc > (param_1)) {
if (s_flt) {
float abs = dB_to_coefficient (value);
s->trim_control()->set_value (abs, sur->usegroup);
fake_touch (s->trim_control());
ret = 0;
}
} else {
float_message (path, fast_coefficient_to_dB (s->trim_control()->get_value ()), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("pan_stereo_position"), 19)) {
std::shared_ptr<PBD::Controllable> pan_control = std::shared_ptr<PBD::Controllable>();
pan_control = s->pan_azimuth_control();
if (n_mo && pan_control) {
if (argc > (param_1)) {
if (s_flt) {
if (send_active) {
std::shared_ptr<ARDOUR::Send> send = get_send (s, get_address (msg));
if (send->pan_outs() > 1) {
pan_control = send->panner_shell()->panner()->pannable()->pan_azimuth_control;
} else {
pan_control = std::shared_ptr<PBD::Controllable>();
}
}
if(pan_control) {
pan_control->set_value (s->pan_azimuth_control()->interface_to_internal (value), sur->usegroup);
std::shared_ptr<AutomationControl>pan_automate = std::dynamic_pointer_cast<AutomationControl> (pan_control);
fake_touch (pan_automate);
ret = 0;
}
}
} else {
float_message (path, pan_control->internal_to_interface (pan_control->get_value ()), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("pan_stereo_width"), 16)) {
if (!control_disabled && s->pan_width_control()) {
if (argc > (param_1)) {
if (s_flt) {
/// this should maybe be active in send mode (see above)
s->pan_width_control()->set_value (value, sur->usegroup);
fake_touch (s->pan_width_control());
ret = 0;
}
} else {
float_message (path, s->pan_width_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("mute"), 4)) {
if (!control_disabled && s->mute_control()) {
if (argc > (param_1)) {
if (s_int) {
s->mute_control()->set_value (yn ? 1.0 : 0.0, sur->usegroup);
fake_touch (s->mute_control());
ret = 0;
}
} else {
int_message (path, s->mute_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("solo_iso"), 8)) {
if (!control_disabled && s->solo_isolate_control()) {
if (argc > (param_1)) {
if (s_int) {
s->solo_isolate_control()->set_value (yn ? 1.0 : 0.0, sur->usegroup);
ret = 0;
}
} else {
int_message (path, s->solo_isolate_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("solo_safe"), 9)) {
if (!control_disabled && s->solo_safe_control()) {
if (argc > (param_1)) {
if (s_int) {
s->solo_safe_control()->set_value (yn ? 1.0 : 0.0, sur->usegroup);
ret = 0;
}
} else {
int_message (path, s->solo_safe_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("solo"), 4)) {
if (!control_disabled && s->solo_control() && !s->is_master() && !s->is_monitor()) {
if (argc > (param_1)) {
if (s_int) {
fake_touch (s->solo_control());
session->set_control (s->solo_control(), yn ? 1.0 : 0.0, sur->usegroup);
ret = 0;
}
} else {
int_message (path, s->solo_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("monitor_input"), 13)) {
std::shared_ptr<Track> track = std::dynamic_pointer_cast<Track> (s);
if (!control_disabled && track && track->monitoring_control()) {
std::bitset<32> mon_bs = track->monitoring_control()->get_value ();
if (argc > (param_1)) {
if (s_int) {
mon_bs[0] = yn ? 1 : 0;
track->monitoring_control()->set_value (mon_bs.to_ulong(), sur->usegroup);
ret = 0;
}
} else {
int_message (path, (int) mon_bs[0], get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("monitor_disk"), 12)) {
std::shared_ptr<Track> track = std::dynamic_pointer_cast<Track> (s);
if (!control_disabled && track && track->monitoring_control()) {
std::bitset<32> mon_bs = track->monitoring_control()->get_value ();
if (argc > (param_1)) {
if (s_int) {
mon_bs[1] = yn ? 1 : 0;
track->monitoring_control()->set_value (mon_bs.to_ulong(), sur->usegroup);
ret = 0;
}
} else {
int_message (path, (int) mon_bs[1], get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("recenable"), 9)) {
if (!control_disabled && s->rec_enable_control()) {
if (argc > (param_1)) {
if (s_int) {
s->rec_enable_control()->set_value (yn, sur->usegroup);
ret = 0;
}
} else {
int_message (path, s->rec_enable_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("record_safe"), 11)) {
if (!control_disabled && s->rec_safe_control()) {
if (argc > (param_1)) {
if (s_int) {
s->rec_safe_control()->set_value (yn, sur->usegroup);
ret = 0;
}
} else {
int_message (path, s->rec_safe_control()->get_value (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("hide"), 4)) {
if (!control_disabled) {
if (argc > (param_1)) {
if (s_int && yn != s->is_hidden ()) {
s->presentation_info().set_hidden ((bool) yn);
ret = 0;
} else {
PBD::warning << string_compose("OSC: value already %1 not changed.", yn) << endmsg;
}
} else {
int_message (path, s->is_hidden (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("select"), 6)) {
if (argc > (param_1)) {
if (s_int) {
//ignore button release
if (yn) return 0;
sur->expand_enable = false;
set_stripable_selection (s);
ret = 0;
}
} else {
int_message (path, s->is_selected(), get_address (msg));
ret = 0;
}
}
else if (!strncmp (sub_path, X_("polarity"), 8)) {
if (!control_disabled && s->phase_control()) {
if (argc > (param_1)) {
if (s_int) {
for (uint64_t i = 0; i < s->phase_control()->size(); i++) {
s->phase_control()->set_phase_invert(i, yn ? 1.0 : 0.0);
/** maybe consider adding a param for which channel
* polarity/1 for channel one for example
* at that point maybe do in own call
*/
}
ret = 0;
}
} else {
int inv = 0;
for (uint64_t i = 0; i < s->phase_control()->size(); i++) {
if (s->phase_control()->inverted (i)) {
// just check if any are inverted
inv = 1;
}
}
int_message (path, inv, get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("name"), 4)) {
if (argc > (param_1)) {
if (types[param_1] == 's') {
if (!control_disabled) {
s->set_name(strng);
ret = 0;
}
}
} else {
text_message (path, s->name(), get_address (msg));
ret = 0;
}
}
else if (!strncmp (sub_path, X_("group"), 5)) {
if (!control_disabled) {
if (rt) {
RouteGroup *rg = rt->route_group();
if (argc > (param_1)) {
if (types[param_1] == 's') {
if (strng == "" || strng == " ") {
strng = "none";
}
RouteGroup* new_rg = session->route_group_by_name (strng);
if (rg) {
string old_group = rg->name();
if (strng == "none") {
if (rg->size () == 1) {
session->remove_route_group (*rg);
} else {
rg->remove (rt);
}
ret = 0;
} else if (strng != old_group) {
if (new_rg) {
// group exists switch to it
if (rg->size () == 1) {
session->remove_route_group (rg);
} else {
rg->remove (rt);
}
new_rg->add (rt);
} else {
rg->set_name (strng);
}
ret = 0;
} else {
// asked for same group
ret = 1;
}
} else {
if (strng == "none") {
ret = 1;
} else if (new_rg) {
new_rg->add (rt);
ret = 0;
} else {
// create new group with this strip in it
RouteGroup* new_rg = new RouteGroup (*session, strng);
session->add_route_group (new_rg);
new_rg->add (rt);
ret = 0;
}
}
}
} else {
if (rg) {
text_message (path, rg->name(), get_address (msg));
} else {
text_message (path, "none", get_address (msg));
}
ret = 0;
}
} else {
PBD::warning << "OSC: VCAs can not be part of a group." << endmsg;
///return -1;
}
}
}
else if (!strncmp (sub_path, X_("comment"), 7)) {
if (!control_disabled && rt) {
if (argc > (param_1)) {
if (types[param_1] == 's') {
rt->set_comment (strng, this);
ret = 0;
}
} else {
text_message (path, rt->comment (), get_address (msg));
ret = 0;
}
}
}
else if (!strncmp (sub_path, X_("vca"), 3)) {
std::shared_ptr<Slavable> slv = std::dynamic_pointer_cast<Slavable> (s);
if (!control_disabled && slv) {
if (argc > (param_1)) {
string svalue = strng;
string v_name = svalue.substr (0, svalue.rfind (" ["));
std::shared_ptr<VCA> vca = get_vca_by_name (v_name);
uint32_t ivalue = 0;
if (!strncmp (sub_path, X_("vca/toggle"), 10)) {
if (vca) {
if (s->slaved_to (vca)) {
slv->unassign (vca);
} else {
slv->assign (vca);
}
ret = 0;
}
}
else if (strcmp (sub_path, X_("vca")) == 0) {
if (argc > (param_1 + 1)) {
if (vca) {
bool p_good = false;
if (types[param_1 + 1] == 'i') {
ivalue = argv[1]->i;
p_good = true;
} else if (types[1] == 'f') {
ivalue = (uint32_t) argv[1]->f;
p_good = true;
}
if (vca && p_good) {
if (ivalue) {
slv->assign (vca);
} else {
slv->unassign (vca);
}
ret = 0;
} else {
PBD::warning << "OSC: setting a vca needs both the vca name and it's state" << endmsg;
}
}
}
}
} else {
/// put list of VCAs this strip is controlled by
_lo_lock.lock ();
lo_message rmsg = lo_message_new ();
if (param_1) {
int sid = 0;
if (types[0] == 'f') {
sid = (int) argv[0]->f;
} else if (types[0] == 'i') {
sid = argv[0]->i;
}
lo_message_add_int32 (rmsg, sid);
}
StripableList stripables;
session->get_stripables (stripables);
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> st = *it;
std::shared_ptr<VCA> v = std::dynamic_pointer_cast<VCA> (st);
if (v && s->slaved_to (v)) {
lo_message_add_string (rmsg, v->name().c_str());
}
}
lo_send_message (get_address (msg), path, rmsg);
lo_message_free (rmsg);
_lo_lock.unlock ();
ret = 0;
}
}
}
if (ret) {
int sid = 0;
_lo_lock.lock ();
lo_message rmsg = lo_message_new ();
if (param_1) {
if (types[0] == 'f') {
sid = (int) argv[0]->f;
} else if (types[0] == 'i') {
sid = argv[0]->i;
}
lo_message_add_int32 (rmsg, sid);
}
if (types[param_1] == 'f') {
if (!strncmp (sub_path, X_("gain"), 4)) {
lo_message_add_float (rmsg, -200);
} else {
lo_message_add_float (rmsg, 0);
}
} else if (types[param_1] == 'i') {
lo_message_add_int32 (rmsg, 0);
} else if (types[param_1] == 's') {
//lo_message_add_string (rmsg, val.c_str());
lo_message_add_string (rmsg, " ");
}
lo_send_message (get_address (msg), path, rmsg);
lo_message_free (rmsg);
_lo_lock.unlock ();
}
return ret;
/*
* for reference
REGISTER_CALLBACK (serv, X_("/strip/sends"), "i", route_get_sends);
REGISTER_CALLBACK (serv, X_("/strip/send/gain"), "iif", route_set_send_gain_dB);
REGISTER_CALLBACK (serv, X_("/strip/send/fader"), "iif", route_set_send_fader);
REGISTER_CALLBACK (serv, X_("/strip/send/enable"), "iif", route_set_send_enable);
REGISTER_CALLBACK (serv, X_("/strip/receives"), "i", route_get_receives);
REGISTER_CALLBACK (serv, X_("/strip/plugin/list"), "i", route_plugin_list);
REGISTER_CALLBACK (serv, X_("/strip/plugin/parameter"), "iiif", route_plugin_parameter);
// prints to cerr only
REGISTER_CALLBACK (serv, X_("/strip/plugin/parameter/print"), "iii", route_plugin_parameter_print);
REGISTER_CALLBACK (serv, X_("/strip/plugin/activate"), "ii", route_plugin_activate);
REGISTER_CALLBACK (serv, X_("/strip/plugin/deactivate"), "ii", route_plugin_deactivate);
REGISTER_CALLBACK (serv, X_("/strip/plugin/descriptor"), "ii", route_plugin_descriptor);
REGISTER_CALLBACK (serv, X_("/strip/plugin/reset"), "ii", route_plugin_reset);
*/
}
int
OSC::strip_state (const char *path, std::shared_ptr<ARDOUR::Stripable> s, int ssid, lo_message msg)
{
PBD::info << string_compose("OSC: strip_state path:%1", path) << endmsg;
// some things need the route
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
lo_message reply = lo_message_new ();
if (ssid) {
// strip number not in path
lo_message_add_int32 (reply, ssid);
}
if (std::dynamic_pointer_cast<AudioTrack>(s)) {
lo_message_add_string (reply, "AT");
} else if (std::dynamic_pointer_cast<MidiTrack>(s)) {
lo_message_add_string (reply, "MT");
} else if (std::dynamic_pointer_cast<VCA>(s)) {
lo_message_add_string (reply, "V");
} else if (s->is_master()) {
lo_message_add_string (reply, "MA");
} else if (s->is_monitor()) {
lo_message_add_string (reply, "MO");
} else if (std::dynamic_pointer_cast<Route>(s) && !std::dynamic_pointer_cast<Track>(s)) {
if (!(s->presentation_info().flags() & PresentationInfo::MidiBus)) {
if (s->is_foldbackbus()) {
lo_message_add_string (reply, "FB");
} else {
lo_message_add_string (reply, "B");
}
} else {
lo_message_add_string (reply, "MB");
}
}
lo_message_add_string (reply, s->name().c_str());
if (r) {
// routes have inputs and outputs
lo_message_add_int32 (reply, r->n_inputs().n_audio());
lo_message_add_int32 (reply, r->n_outputs().n_audio());
} else {
// non-routes like VCAs don't
lo_message_add_int32 (reply, -1);
lo_message_add_int32 (reply, -1);
}
if (s->mute_control()) {
lo_message_add_int32 (reply, s->mute_control()->get_value());
} else {
lo_message_add_int32 (reply, -1);
}
if (s->solo_control()) {
lo_message_add_int32 (reply, s->solo_control()->get_value());
} else {
lo_message_add_int32 (reply, -1);
}
if (s->rec_enable_control()) {
lo_message_add_int32 (reply, s->rec_enable_control()->get_value());
} else {
lo_message_add_int32 (reply, -1);
}
lo_send_message (get_address (msg), X_(path), reply);
lo_message_free (reply);
return 0;
}
int
OSC::strip_list (lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg), true);
string temppath = "/strip";
int ssid = 0;
for (int n = 0; n < (int) sur->nstrips; ++n) {
if (sur->feedback[2]) {
temppath = string_compose ("/strip/%1", n+1);
} else {
ssid = n + 1;
}
std::shared_ptr<Stripable> s = get_strip (n + 1, get_address (msg));
if (s) {
strip_state (temppath.c_str(), s, ssid, msg);
}
}
return 0;
}
int
OSC::set_automation (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> strp = std::shared_ptr<Stripable>();
uint32_t ctr = 0;
uint32_t aut = 0;
uint32_t ssid;
std::shared_ptr<Send> send = std::shared_ptr<Send> ();
if (argc) {
if (types[argc - 1] == 'f') {
aut = (int)argv[argc - 1]->f;
} else {
aut = argv[argc - 1]->i;
}
}
//parse path first to find stripable
if (!strncmp (path, X_("/strip/"), 7)) {
// find ssid and stripable
if (argc > 1) {
if (types[1] == 'f') {
ssid = (uint32_t)argv[0]->f;
} else {
ssid = argv[0]->i;
}
strp = get_strip (ssid, get_address (msg));
} else {
ssid = atoi (&(strrchr (path, '/' ))[1]);
strp = get_strip (ssid, get_address (msg));
}
send = get_send (strp, get_address (msg));
ctr = 7;
} else if (!strncmp (path, X_("/select/"), 8)) {
strp = sur->select;
ctr = 8;
} else {
return ret;
}
if (strp) {
std::shared_ptr<AutomationControl> control = std::shared_ptr<AutomationControl>();
// other automatable controls can be added by repeating the next 6.5 lines
if ((!strncmp (&path[ctr], X_("fader"), 5)) || (!strncmp (&path[ctr], X_("gain"), 4))) {
if (send) {
control = send->gain_control ();
} else if (strp->gain_control ()) {
control = strp->gain_control ();
} else {
PBD::warning << "No fader for this strip" << endmsg;
}
} else if (!strncmp (&path[ctr], X_("pan"), 3)) {
if (send) {
if (send->panner_linked_to_route () || !send->has_panner ()) {
PBD::warning << "Send panner not available" << endmsg;
} else {
std::shared_ptr<Delivery> _send_del = std::dynamic_pointer_cast<Delivery> (send);
std::shared_ptr<Pannable> pannable = _send_del->panner()->pannable();
if (pannable->pan_azimuth_control) {
control = pannable->pan_azimuth_control;
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
}
} else if (strp->pan_azimuth_control ()) {
control = strp->pan_azimuth_control ();
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
} else if (!strncmp (&path[ctr], X_("trimdB"), 6)) {
if (send) {
PBD::warning << "Send trim not available" << endmsg;
} else if (strp->trim_control ()) {
control = strp->trim_control ();
} else {
PBD::warning << "No trim for this strip" << endmsg;
}
} else if (!strncmp (&path[ctr], X_("mute"), 4)) {
if (send) {
PBD::warning << "Send mute not automatable" << endmsg;
} else if (strp->mute_control ()) {
control = strp->mute_control ();
} else {
PBD::warning << "No mute for this strip" << endmsg;
}
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
if (control) {
switch (aut) {
case 0:
control->set_automation_state (ARDOUR::Off);
ret = 0;
break;
case 1:
control->set_automation_state (ARDOUR::Play);
ret = 0;
break;
case 2:
control->set_automation_state (ARDOUR::Write);
ret = 0;
break;
case 3:
control->set_automation_state (ARDOUR::Touch);
ret = 0;
break;
case 4:
control->set_automation_state (ARDOUR::Latch);
ret = 0;
break;
default:
break;
}
}
}
return ret;
}
int
OSC::touch_detect (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
if (!session) return -1;
int ret = 1;
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> strp = std::shared_ptr<Stripable>();
std::shared_ptr<Send> send = std::shared_ptr<Send> ();
uint32_t ctr = 0;
uint32_t touch = 0;
uint32_t ssid;
if (argc) {
if (types[argc - 1] == 'f') {
touch = (int)argv[argc - 1]->f;
} else {
touch = argv[argc - 1]->i;
}
}
//parse path first to find stripable
if (!strncmp (path, X_("/strip/"), 7)) {
// find ssid and stripable
if (argc > 1) {
if (types[0] == 'f') {
ssid = (uint32_t)argv[0]->f;
} else {
ssid = argv[0]->i;
}
strp = get_strip (ssid, get_address (msg));
} else {
ssid = atoi (&(strrchr (path, '/' ))[1]);
strp = get_strip (ssid, get_address (msg));
}
send = get_send (strp, get_address (msg));
ctr = 7;
} else if (!strncmp (path, X_("/select/"), 8)) {
strp = sur->select;
ctr = 8;
} else {
return ret;
}
if (strp) {
std::shared_ptr<AutomationControl> control = std::shared_ptr<AutomationControl>();
// other automatable controls can be added by repeating the next 6.5 lines
if ((!strncmp (&path[ctr], X_("fader"), 5)) || (!strncmp (&path[ctr], X_("gain"), 4))) {
if (strp->gain_control ()) {
control = strp->gain_control ();
} else {
PBD::warning << "No fader for this strip" << endmsg;
}
if (send) {
control = send->gain_control ();
}
} else if (!strncmp (&path[ctr], X_("pan"), 3)) {
if (send) {
if (send->panner_linked_to_route () || !send->has_panner ()) {
PBD::warning << "Send panner not available" << endmsg;
} else {
std::shared_ptr<Delivery> _send_del = std::dynamic_pointer_cast<Delivery> (send);
std::shared_ptr<Pannable> pannable = _send_del->panner()->pannable();
if (!strncmp (&path[ctr], X_("pan_stereo_position"), 19)) {
if (pannable->pan_azimuth_control) {
control = pannable->pan_azimuth_control;
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
} else if (!strncmp (&path[ctr], X_("pan_stereo_width"), 16)) {
if (strp->pan_width_control ()) {
control = strp->pan_width_control ();
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
}
}
}
} else if (!strncmp (&path[ctr], X_("trimdB"), 6)) {
if (send) {
PBD::warning << "Send trim not available" << endmsg;
} else if (strp->trim_control ()) {
control = strp->trim_control ();
} else {
PBD::warning << "No trim for this strip" << endmsg;
}
} else if (!strncmp (&path[ctr], X_("mute"), 4)) {
if (send) {
PBD::warning << "Send mute not automatable" << endmsg;
} else if (strp->mute_control ()) {
control = strp->mute_control ();
} else {
PBD::warning << "No trim for this strip" << endmsg;
}
} else {
PBD::warning << "Automation not available for " << path << endmsg;
}
if (control) {
if (touch) {
//start touch
control->start_touch (timepos_t (control->session().transport_sample()));
ret = 0;
} else {
// end touch
control->stop_touch (timepos_t (control->session().transport_sample()));
ret = 0;
}
// just in case some crazy surface starts sending control values before touch
FakeTouchMap::iterator x = _touch_timeout.find(control);
if (x != _touch_timeout.end()) {
_touch_timeout.erase (x);
}
}
}
return ret;
}
int
OSC::fake_touch (std::shared_ptr<ARDOUR::AutomationControl> ctrl)
{
if (ctrl) {
//start touch
if (ctrl->automation_state() == Touch && !ctrl->touching ()) {
ctrl->start_touch (timepos_t (ctrl->session().transport_sample()));
_touch_timeout[ctrl] = 10;
}
}
return 0;
}
int
OSC::spill (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
/*
* spill should have the form of:
* /select/spill (may have i or f keypress/release)
* /strip/spill i (may have keypress and i may be inline)
*/
if (!session || argc > 1) return -1;
int ret = 1;
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> strp = std::shared_ptr<Stripable>();
uint32_t value = 0;
OSCTempMode new_mode = TempOff;
if (argc) {
if (types[0] == 'f') {
value = (int)argv[0]->f;
} else {
value = argv[0]->i;
}
if (!value) {
// key release ignore
return 0;
}
}
//parse path first to find stripable
if (!strncmp (path, X_("/strip/"), 7)) {
/*
* we don't know if value is press or ssid
* so we have to check if the last / has an int after it first
* if not then we use value
*/
uint32_t ssid = 0;
ssid = atoi (&(strrchr (path, '/' ))[1]);
if (!ssid) {
ssid = value;
}
strp = get_strip (ssid, get_address (msg));
} else if (!strncmp (path, X_("/select/"), 8)) {
strp = sur->select;
} else {
return ret;
}
if (strp) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (strp);
std::shared_ptr<VCA> v = std::dynamic_pointer_cast<VCA> (strp);
if (strstr (path, X_("/vca")) || v) {
//strp must be a VCA
if (v) {
new_mode = VCAOnly;
} else {
return ret;
}
} else
if (strstr (path, X_("/group"))) {
//strp must be in a group
if (rt) {
RouteGroup *rg = rt->route_group();
if (rg) {
new_mode = GroupOnly;
} else {
return ret;
}
}
} else
if (strstr (path, X_("/bus"))) {
//strp must be a bus with either sends or no inputs
if (rt) {
if (!rt->is_track () && rt->can_solo ()) {
new_mode = BusOnly;
}
}
} else {
// decide by auto
// vca should never get here
if (rt->is_track ()) {
if (rt->route_group()) {
new_mode = GroupOnly;
}
} else if (!rt->is_track () && rt->can_solo ()) {
new_mode = BusOnly;
}
}
if (new_mode) {
sur->temp_mode = new_mode;
sur->temp_master = strp;
set_temp_mode (get_address (msg));
set_bank (1, msg);
return 0;
}
}
return ret;
}
int
OSC::sel_new_personal_send (char *foldback, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
std::shared_ptr<Route> rt = std::shared_ptr<Route> ();
if (s) {
rt = std::dynamic_pointer_cast<Route> (s);
if (!rt) {
PBD::warning << "OSC: can not send from VCAs." << endmsg;
return -1;
}
}
/* if a foldbackbus called foldback exists use it
* other wise create it. Then create a foldback send from
* this route to that bus.
*/
string foldbackbus = foldback;
string foldback_name = foldbackbus;
if (foldbackbus.find ("- FB") == string::npos) {
foldback_name = string_compose ("%1 - FB", foldbackbus);
}
std::shared_ptr<Route> lsn_rt = session->route_by_name (foldback_name);
if (!lsn_rt) {
// doesn't exist but check if raw name does and is foldbackbus
std::shared_ptr<Route> raw_rt = session->route_by_name (foldbackbus);
if (raw_rt && raw_rt->is_foldbackbus()) {
lsn_rt = raw_rt;
} else {
// create the foldbackbus
RouteList list = session->new_audio_route (1, 1, 0, 1, foldback_name, PresentationInfo::FoldbackBus, (uint32_t) -1);
lsn_rt = *(list.begin());
lsn_rt->presentation_info().set_hidden (true);
session->set_dirty();
}
}
if (lsn_rt) {
//std::shared_ptr<Route> rt_send = ;
if (rt && (lsn_rt != rt)) {
// make sure there isn't one already
if (!rt->feeds (lsn_rt)) {
// create send
rt->add_foldback_send (lsn_rt, false);
//std::shared_ptr<Send> snd = rt->internal_send_for (aux);
session->dirty ();
return 0;
} else {
PBD::warning << "OSC: new_send - duplicate send, ignored." << endmsg;
}
} else {
PBD::warning << "OSC: new_send - can't send to self." << endmsg;
}
} else {
PBD::warning << "OSC: new_send - no FoldbackBus to send to." << endmsg;
}
return -1;
}
int
OSC::_strip_select (std::shared_ptr<Stripable> s, lo_address addr)
{
if (!session) {
return -1;
}
OSCSurface *sur = get_surface(addr, true);
return _strip_select2 (s, sur, addr);
}
int
OSC::_strip_select2 (std::shared_ptr<Stripable> s, OSCSurface *sur, lo_address addr)
{
// this allows get_surface to call this part without calling itself
std::weak_ptr<Stripable> o_sel = sur->select;
std::shared_ptr<Stripable> old_sel= o_sel.lock ();
std::weak_ptr<Stripable> o_expand = sur->expand_strip;
std::shared_ptr<Stripable> old_expand= o_expand.lock ();
// we got a null strip check that old strips are valid
if (!s) {
if (old_expand && sur->expand_enable) {
sur->expand = get_sid (old_expand, addr);
if (sur->strip_types[11] || sur->expand) {
s = old_expand;
} else {
sur->expand_strip = std::shared_ptr<Stripable> ();
}
}
}
if (!s) {
sur->expand = 0;
sur->expand_enable = false;
if (ControlProtocol::first_selected_stripable()) {
s = ControlProtocol::first_selected_stripable();
} else {
s = session->master_out ();
}
_select = s;
}
if (!s) {
return 0;
}
if (s != old_sel) {
sur->select = s;
}
bool sends;
uint32_t nsends = 0;
do {
sends = false;
if (s->send_level_controllable (nsends)) {
sends = true;
nsends++;
}
} while (sends);
sur->nsends = nsends;
s->DropReferences.connect (*this, MISSING_INVALIDATOR, std::bind (&OSC::recalcbanks, this), this);
OSCSelectObserver* so = dynamic_cast<OSCSelectObserver*>(sur->sel_obs);
if (sur->feedback[13]) {
if (so != 0) {
so->refresh_strip (s, nsends, sur->gainmode, true);
} else {
OSCSelectObserver* sel_fb = new OSCSelectObserver (*this, *session, sur);
sur->sel_obs = sel_fb;
}
sur->sel_obs->set_expand (sur->expand_enable);
} else {
if (so != 0) {
delete so;
sur->sel_obs = 0;
}
}
if (sur->feedback[0] || sur->feedback[1]) {
uint32_t obs_expand = 0;
if (sur->expand_enable) {
sur->expand = get_sid (s, addr);
obs_expand = sur->expand;
} else {
obs_expand = 0;
}
for (uint32_t i = 0; i < sur->observers.size(); i++) {
sur->observers[i]->set_expand (obs_expand);
}
}
// need to set monitor for processor changed signal (for paging)
string address = lo_address_get_url (addr);
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(s);
if (r) {
r->processors_changed.connect (sur->proc_connection, MISSING_INVALIDATOR, std::bind (&OSC::processor_changed, this, address), this);
_sel_plugin (sur->plugin_id, addr);
}
return 0;
}
void
OSC::processor_changed (string address)
{
lo_address addr = lo_address_new_from_url (address.c_str());
OSCSurface *sur = get_surface (addr);
_sel_plugin (sur->plugin_id, addr);
if (sur->sel_obs) {
sur->sel_obs->renew_sends ();
sur->sel_obs->eq_restart (-1);
}
}
int
OSC::sel_expand (uint32_t state, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
if (!sur->expand_strip) {
state = 0;
float_message (X_("/select/expand"), 0.0, get_address (msg));
}
if (state) {
sur->expand_enable = (bool) state;
s = std::shared_ptr<Stripable> ();
} else {
sur->expand_enable = false;
s = std::shared_ptr<Stripable> ();
}
return _strip_select (s, get_address (msg));
}
int
OSC::sel_previous (lo_message msg)
{
return sel_delta (-1, msg);
}
int
OSC::sel_next (lo_message msg)
{
return sel_delta (1, msg);
}
int
OSC::sel_delta (int delta, lo_message msg)
{
if (!delta) {
return 0;
}
OSCSurface *sur = get_surface(get_address (msg));
Sorted sel_strips;
sel_strips = sur->strips;
// the current selected strip _should_ be in sel_strips
uint32_t nstps = sel_strips.size ();
if (!nstps) {
return -1;
}
std::shared_ptr<Stripable> new_sel = std::shared_ptr<Stripable> ();
std::weak_ptr<Stripable> o_sel = sur->select;
std::shared_ptr<Stripable> old_sel= o_sel.lock ();
for (uint32_t i = 0; i < nstps; i++) {
if (old_sel == sel_strips[i]) {
if (i && delta < 0) {
// i is > 0 and delta is -1
new_sel = sel_strips[i - 1];
} else if ((i + 1) < nstps && delta > 0) {
// i is at least 1 less than greatest and delta = 1
new_sel = sel_strips[i + 1];
} else if ((i + 1) >= nstps && delta > 0) {
// i is greatest strip and delta 1
new_sel = sel_strips[0];
} else if (!i && delta < 0) {
// i = 0 and delta -1
new_sel = sel_strips[nstps - 1];
} else {
// should not happen
return -1;
}
}
}
if (!new_sel) {
// our selected strip has vanished use the first one
new_sel = sel_strips[0];
}
if (new_sel) {
if (!sur->expand_enable) {
set_stripable_selection (new_sel);
} else {
sur->expand_strip = new_sel;
_strip_select (new_sel, get_address (msg));
}
return 0;
}
return -1;
}
int
OSC::route_set_send_gain_dB (int ssid, int id, float val, lo_message msg)
{
if (!session) {
return -1;
}
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
OSCSurface *sur = get_surface(get_address (msg));
float abs;
if (s) {
if (id > 0) {
--id;
}
if (val < -192) {
abs = 0;
} else {
abs = dB_to_coefficient (val);
}
if (s->send_level_controllable (id)) {
s->send_level_controllable (id)->set_value (abs, sur->usegroup);
return 0;
}
}
return 0;
}
int
OSC::route_set_send_fader (int ssid, int id, float val, lo_message msg)
{
if (!session) {
return -1;
}
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
OSCSurface *sur = get_surface(get_address (msg));
float abs;
if (s) {
if (id > 0) {
--id;
}
if (s->send_level_controllable (id)) {
abs = s->send_level_controllable(id)->interface_to_internal (val);
s->send_level_controllable (id)->set_value (abs, sur->usegroup);
return 0;
}
}
return 0;
}
int
OSC::sel_sendgain (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
if (sur->send_page_size && (id > (int)sur->send_page_size)) {
return float_message_with_id (X_("/select/send_gain"), id, -193, sur->feedback[2], get_address (msg));
}
std::shared_ptr<Stripable> s;
s = sur->select;
float abs;
int send_id = 0;
if (s) {
if (id > 0) {
send_id = id - 1;
}
if (val < -192) {
abs = 0;
} else {
abs = dB_to_coefficient (val);
}
if (sur->send_page_size) {
send_id = send_id + ((sur->send_page - 1) * sur->send_page_size);
}
if (s->send_level_controllable (send_id)) {
s->send_level_controllable (send_id)->set_value (abs, PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/send_gain"), id, -193, sur->feedback[2], get_address (msg));
}
int
OSC::sel_sendfader (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
if (sur->send_page_size && (id > (int)sur->send_page_size)) {
return float_message_with_id (X_("/select/send_fader"), id, 0, sur->feedback[2], get_address (msg));
}
std::shared_ptr<Stripable> s;
s = sur->select;
float abs;
int send_id = 0;
if (s) {
if (id > 0) {
send_id = id - 1;
}
if (sur->send_page_size) {
send_id = send_id + ((sur->send_page - 1) * sur->send_page_size);
}
if (s->send_level_controllable (send_id)) {
abs = s->send_level_controllable(send_id)->interface_to_internal (val);
s->send_level_controllable (send_id)->set_value (abs, PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/send_fader"), id, 0, sur->feedback[2], get_address (msg));
}
int
OSC::route_set_send_enable (int ssid, int sid, float val, lo_message msg)
{
if (!session) {
return -1;
}
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
OSCSurface *sur = get_surface(get_address (msg));
if (s) {
/* revert to zero-based counting */
if (sid > 0) {
--sid;
}
if (s->send_enable_controllable (sid)) {
s->send_enable_controllable (sid)->set_value (val, sur->usegroup);
return 0;
}
if (s->send_level_controllable (sid)) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
return 0;
}
std::shared_ptr<Send> snd = std::dynamic_pointer_cast<Send> (r->nth_send(sid));
if (snd) {
if (val) {
snd->activate();
} else {
snd->deactivate();
}
}
return 0;
}
}
return -1;
}
int
OSC::sel_sendenable (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
if (sur->send_page_size && (id > (int)sur->send_page_size)) {
return float_message_with_id (X_("/select/send_enable"), id, 0, sur->feedback[2], get_address (msg));
}
std::shared_ptr<Stripable> s;
s = sur->select;
int send_id = 0;
if (s) {
if (id > 0) {
send_id = id - 1;
}
if (sur->send_page_size) {
send_id = send_id + ((sur->send_page - 1) * sur->send_page_size);
}
if (s->send_enable_controllable (send_id)) {
s->send_enable_controllable (send_id)->set_value (val, PBD::Controllable::NoGroup);
return 0;
}
if (s->send_level_controllable (send_id)) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
// should never get here
return float_message_with_id (X_("/select/send_enable"), id, 0, sur->feedback[2], get_address (msg));
}
std::shared_ptr<Send> snd = std::dynamic_pointer_cast<Send> (r->nth_send(send_id));
if (snd) {
if (val) {
snd->activate();
} else {
snd->deactivate();
}
}
return 0;
}
}
return float_message_with_id (X_("/select/send_enable"), id, 0, sur->feedback[2], get_address (msg));
}
int
OSC::sel_master_send_enable (int state, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->master_send_enable_controllable ()) {
s->master_send_enable_controllable()->set_value (state, PBD::Controllable::NoGroup);
return 0;
}
}
return float_message (X_("/select/master_send_enable"), 0, get_address(msg));
}
int
OSC::select_plugin_parameter (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg) {
OSCSurface *sur = get_surface(get_address (msg));
int paid;
uint32_t piid = sur->plugin_id;
float value = 0;
if (argc > 1) {
// no inline args
if (argc == 2) {
// change parameter in already selected plugin
if (types[0] == 'f') {
paid = (int) argv[0]->f;
} else {
paid = argv[0]->i;
}
value = argv[1]->f;
} else if (argc == 3) {
if (types[0] == 'f') {
piid = (int) argv[0]->f;
} else {
piid = argv[0]->i;
}
_sel_plugin (piid, get_address (msg));
if (types[1] == 'f') {
paid = (int) argv[1]->f;
} else {
paid = argv[1]->i;
}
value = argv[2]->f;
} else if (argc > 3) {
PBD::warning << "OSC: Too many parameters: " << argc << endmsg;
return -1;
}
} else if (argc) {
const char * par = strstr (&path[25], "/");
if (par) {
piid = atoi (&path[25]);
_sel_plugin (piid, get_address (msg));
paid = atoi (&par[1]);
value = argv[0]->f;
// we have plugin id too
} else {
// just parameter
paid = atoi (&path[25]);
value = argv[0]->f;
}
} else {
PBD::warning << "OSC: Must have parameters." << endmsg;
return -1;
}
if (!piid || piid > sur->plugins.size ()) {
return float_message_with_id (X_("/select/plugin/parameter"), paid, 0, sur->feedback[2], get_address (msg));
}
if (sur->plug_page_size && (paid > (int)sur->plug_page_size)) {
return float_message_with_id (X_("/select/plugin/parameter"), paid, 0, sur->feedback[2], get_address (msg));
}
std::shared_ptr<Stripable> s = sur->select;
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(s);
if (!r) {
return 1;
}
std::shared_ptr<Processor> proc = r->nth_plugin (sur->plugins[sur->plugin_id - 1]);
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(proc))) {
return 1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
// paid is paged parameter convert to absolute
int parid = paid + (int)sur->plug_page - 1;
if (parid > (int) sur->plug_params.size ()) {
if (sur->feedback[13]) {
float_message_with_id (X_("/select/plugin/parameter"), paid, 0, sur->feedback[2], get_address (msg));
}
return 0;
}
bool ok = false;
uint32_t controlid = pip->nth_parameter(sur->plug_params[parid - 1], ok);
if (!ok) {
return 1;
}
ParameterDescriptor pd;
pip->get_parameter_descriptor(controlid, pd);
if ( pip->parameter_is_input(controlid) || pip->parameter_is_control(controlid) ) {
std::shared_ptr<AutomationControl> c = pi->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
if (c) {
if (pd.integer_step && pd.upper == 1) {
if (c->get_value () && value < 1.0) {
c->set_value (0, PBD::Controllable::NoGroup);
} else if (!c->get_value () && value) {
c->set_value (1, PBD::Controllable::NoGroup);
}
} else {
c->set_value (c->interface_to_internal (value), PBD::Controllable::NoGroup);
}
return 0;
}
}
return 1;
}
int
OSC::sel_plugin_activate (float state, lo_message msg)
{
if (!session) {
return -1;
}
OSCSurface *sur = get_surface(get_address (msg));
if (sur->plugins.size() > 0) {
std::shared_ptr<Stripable> s = sur->select;
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (r) {
std::shared_ptr<Processor> redi=r->nth_plugin (sur->plugins[sur->plugin_id -1]);
if (redi) {
std::shared_ptr<PluginInsert> pi;
if ((pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
if(state > 0) {
pi->activate();
} else {
pi->deactivate();
}
return 0;
}
}
}
}
float_message (X_("/select/plugin/activate"), 0, get_address (msg));
PBD::warning << "OSC: Select has no Plugin." << endmsg;
return 0;
}
int
OSC::route_plugin_list (int ssid, lo_message msg) {
if (!session) {
return -1;
}
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(get_strip (ssid, get_address (msg)));
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
int piid = 0;
lo_message reply = lo_message_new ();
lo_message_add_int32 (reply, ssid);
for (;;) {
std::shared_ptr<Processor> redi = r->nth_plugin(piid);
if ( !redi ) {
break;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
continue;
}
lo_message_add_int32 (reply, piid + 1);
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
lo_message_add_string (reply, pip->name());
lo_message_add_int32(reply, redi->enabled() ? 1 : 0);
piid++;
}
lo_send_message (get_address (msg), X_("/strip/plugin/list"), reply);
lo_message_free (reply);
return 0;
}
int
OSC::route_plugin_descriptor (int ssid, int piid, lo_message msg) {
if (!session) {
return -1;
}
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(get_strip (ssid, get_address (msg)));
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<Processor> redi = r->nth_plugin(piid - 1);
if (!redi) {
PBD::error << "OSC: cannot find plugin # " << piid << " for RID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
return -1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
bool ok = false;
for ( uint32_t ppi = 0; ppi < pip->parameter_count(); ppi++) {
uint32_t controlid = pip->nth_parameter(ppi, ok);
if (!ok) {
continue;
}
std::shared_ptr<AutomationControl> c = pi->automation_control(Evoral::Parameter(PluginAutomation, 0, controlid));
lo_message reply = lo_message_new();
lo_message_add_int32 (reply, ssid);
lo_message_add_int32 (reply, piid);
lo_message_add_int32 (reply, ppi + 1);
ParameterDescriptor pd;
pi->plugin()->get_parameter_descriptor(controlid, pd);
lo_message_add_string (reply, pd.label.c_str());
// I've combined those binary descriptor parts in a bit-field to reduce lilo message elements
int flags = 0;
flags |= pd.enumeration ? 1 : 0;
flags |= pd.integer_step ? 2 : 0;
flags |= pd.logarithmic ? 4 : 0;
flags |= pd.sr_dependent ? 32 : 0;
flags |= pd.toggled ? 64 : 0;
flags |= pip->parameter_is_input(controlid) ? 0x80 : 0;
std::string param_desc = pi->plugin()->describe_parameter(Evoral::Parameter(PluginAutomation, 0, controlid));
flags |= (param_desc == X_("hidden")) ? 0x100 : 0;
lo_message_add_int32 (reply, flags);
switch(pd.datatype) {
case ARDOUR::Variant::BEATS:
lo_message_add_string(reply, _("BEATS"));
break;
case ARDOUR::Variant::BOOL:
lo_message_add_string(reply, _("BOOL"));
break;
case ARDOUR::Variant::DOUBLE:
lo_message_add_string(reply, _("DOUBLE"));
break;
case ARDOUR::Variant::FLOAT:
lo_message_add_string(reply, _("FLOAT"));
break;
case ARDOUR::Variant::INT:
lo_message_add_string(reply, _("INT"));
break;
case ARDOUR::Variant::LONG:
lo_message_add_string(reply, _("LONG"));
break;
case ARDOUR::Variant::NOTHING:
lo_message_add_string(reply, _("NOTHING"));
break;
case ARDOUR::Variant::PATH:
lo_message_add_string(reply, _("PATH"));
break;
case ARDOUR::Variant::STRING:
lo_message_add_string(reply, _("STRING"));
break;
case ARDOUR::Variant::URI:
lo_message_add_string(reply, _("URI"));
break;
default:
lo_message_add_string(reply, _("UNKNOWN"));
break;
}
lo_message_add_float (reply, pd.lower);
lo_message_add_float (reply, pd.upper);
lo_message_add_string (reply, pd.print_fmt.c_str());
if ( pd.scale_points ) {
lo_message_add_int32 (reply, pd.scale_points->size());
for ( ARDOUR::ScalePoints::const_iterator i = pd.scale_points->begin(); i != pd.scale_points->end(); ++i) {
lo_message_add_float (reply, i->second);
lo_message_add_string (reply, ((std::string)i->first).c_str());
}
}
else {
lo_message_add_int32 (reply, 0);
}
if ( c ) {
lo_message_add_double (reply, c->get_value());
}
else {
lo_message_add_double (reply, 0);
}
lo_send_message (get_address (msg), X_("/strip/plugin/descriptor"), reply);
lo_message_free (reply);
}
lo_message reply = lo_message_new ();
lo_message_add_int32 (reply, ssid);
lo_message_add_int32 (reply, piid);
lo_send_message (get_address (msg), X_("/strip/plugin/descriptor_end"), reply);
lo_message_free (reply);
return 0;
}
int
OSC::route_plugin_reset (int ssid, int piid, lo_message msg) {
if (!session) {
return -1;
}
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(get_strip (ssid, get_address (msg)));
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<Processor> redi = r->nth_plugin(piid - 1);
if (!redi) {
PBD::error << "OSC: cannot find plugin # " << piid << " for RID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
return -1;
}
pi->reset_parameters_to_default ();
return 0;
}
int
OSC::route_plugin_parameter (int ssid, int piid, int par, float val, lo_message msg)
{
if (!session)
return -1;
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<Processor> redi=r->nth_plugin (piid - 1);
if (!redi) {
PBD::error << "OSC: cannot find plugin # " << piid << " for RID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
return -1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
bool ok=false;
uint32_t controlid = pip->nth_parameter (par - 1,ok);
if (!ok) {
PBD::error << "OSC: Cannot find parameter # " << par << " for plugin # " << piid << " on RID '" << ssid << "'" << endmsg;
return -1;
}
if (!pip->parameter_is_input(controlid)) {
PBD::error << "OSC: Parameter # " << par << " for plugin # " << piid << " on RID '" << ssid << "' is not a control input" << endmsg;
return -1;
}
ParameterDescriptor pd;
pi->plugin()->get_parameter_descriptor (controlid,pd);
if (val >= pd.lower && val <= pd.upper) {
std::shared_ptr<AutomationControl> c = pi->automation_control (Evoral::Parameter(PluginAutomation, 0, controlid));
// cerr << "parameter:" << redi->describe_parameter(controlid) << " val:" << val << "\n";
c->set_value (val, PBD::Controllable::NoGroup);
} else {
PBD::warning << "OSC: Parameter # " << par << " for plugin # " << piid << " on RID '" << ssid << "' is out of range" << endmsg;
PBD::info << "OSC: Valid range min=" << pd.lower << " max=" << pd.upper << endmsg;
}
return 0;
}
int
OSC::trigger_stop (int route_index, int now, lo_message msg)
{
if (!session) {
return -1;
}
/* this function doesn't refer to surface banking but instead directly controls routes in the order they appear on the trigger_page
*/
int index = 0;
StripableList sl;
session->get_stripables (sl);
sl.sort (Stripable::Sorter ());
for (StripableList::iterator s = sl.begin (); s != sl.end (); ++s) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (*s);
if (!r || !r->triggerbox ()) {
continue;
}
/* we're only interested in Trigger Tracks */
if (!(r->presentation_info ().trigger_track ())) {
continue;
}
if (index == route_index) {
r->stop_triggers(now!=0);
break;
}
index++;
}
return 0;
}
int
OSC::trigger_bang (int route_index, int row_index, lo_message msg)
{
if (!session) {
return -1;
}
bang_trigger_at(route_index, row_index);
return 0;
}
int
OSC::trigger_unbang (int route_index, int row_index, lo_message msg)
{
if (!session) {
return -1;
}
unbang_trigger_at(route_index, row_index);
return 0;
}
//prints to cerr only
int
OSC::route_plugin_parameter_print (int ssid, int piid, int par, lo_message msg)
{
if (!session) {
return -1;
}
std::shared_ptr<Stripable> s = get_strip (ssid, get_address (msg));
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
return -1;
}
std::shared_ptr<Processor> redi=r->nth_plugin (piid - 1);
if (!redi) {
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
return -1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
bool ok=false;
uint32_t controlid = pip->nth_parameter (par - 1,ok);
if (!ok) {
return -1;
}
ParameterDescriptor pd;
if (pi->plugin()->get_parameter_descriptor (controlid, pd) == 0) {
std::shared_ptr<AutomationControl> c = pi->automation_control (Evoral::Parameter(PluginAutomation, 0, controlid));
cerr << "parameter: " << pd.label << "\n";
if (c) {
cerr << "current value: " << c->get_value () << "\n";
} else {
cerr << "current value not available, control does not exist\n";
}
cerr << "lower value: " << pd.lower << "\n";
cerr << "upper value: " << pd.upper << "\n";
}
return 0;
}
int
OSC::route_plugin_activate (int ssid, int piid, lo_message msg)
{
if (!session)
return -1;
std::shared_ptr<Stripable> s = get_strip (ssid, lo_message_get_source (msg));
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<Processor> redi=r->nth_plugin (piid - 1);
if (!redi) {
PBD::error << "OSC: cannot find plugin # " << piid << " for RID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
return -1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
pi->activate();
return 0;
}
int
OSC::route_plugin_deactivate (int ssid, int piid, lo_message msg)
{
if (!session)
return -1;
std::shared_ptr<Stripable> s = get_strip (ssid, lo_message_get_source (msg));
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s);
if (!r) {
PBD::error << "OSC: Invalid Remote Control ID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<Processor> redi=r->nth_plugin (piid - 1);
if (!redi) {
PBD::error << "OSC: cannot find plugin # " << piid << " for RID '" << ssid << "'" << endmsg;
return -1;
}
std::shared_ptr<PluginInsert> pi;
if (!(pi = std::dynamic_pointer_cast<PluginInsert>(redi))) {
PBD::error << "OSC: given processor # " << piid << " on RID '" << ssid << "' is not a Plugin." << endmsg;
return -1;
}
std::shared_ptr<ARDOUR::Plugin> pip = pi->plugin();
pi->deactivate();
return 0;
}
// select
int
OSC::sel_pan_elevation (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->pan_elevation_control()) {
s->pan_elevation_control()->set_value (s->pan_elevation_control()->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/pan_elevation_position"), 0, get_address (msg));
}
int
OSC::sel_pan_frontback (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->pan_frontback_control()) {
s->pan_frontback_control()->set_value (s->pan_frontback_control()->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/pan_frontback_position"), 0.5, get_address (msg));
}
int
OSC::sel_pan_lfe (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->pan_lfe_control()) {
s->pan_lfe_control()->set_value (s->pan_lfe_control()->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/pan_lfe_control"), 0, get_address (msg));
}
// compressor control
int
OSC::sel_comp_enable (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (Comp_Enable)) {
s->mapped_control (Comp_Enable)->set_value (s->mapped_control (Comp_Enable)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/comp_enable"), 0, get_address (msg));
}
int
OSC::sel_comp_threshold (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (Comp_Threshold)) {
s->mapped_control (Comp_Threshold)->set_value (s->mapped_control (Comp_Threshold)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/comp_threshold"), 0, get_address (msg));
}
int
OSC::sel_comp_mode (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (Comp_Mode)) {
s->mapped_control (Comp_Mode)->set_value (s->mapped_control (Comp_Mode)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/comp_mode"), 0, get_address (msg));
}
int
OSC::sel_comp_makeup (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (Comp_Makeup)) {
s->mapped_control (Comp_Makeup)->set_value (s->mapped_control (Comp_Makeup)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/comp_makeup"), 0, get_address (msg));
}
// EQ control
int
OSC::sel_eq_enable (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control(EQ_Enable)) {
s->mapped_control(EQ_Enable)->set_value (s->mapped_control(EQ_Enable)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_enable"), 0, get_address (msg));
}
int
OSC::sel_eq_hpf_freq (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (HPF_Freq)) {
s->mapped_control (HPF_Freq)->set_value (s->mapped_control (HPF_Freq)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_hpf/freq"), 0, get_address (msg));
}
int
OSC::sel_eq_lpf_freq (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (LPF_Freq)) {
s->mapped_control (LPF_Freq)->set_value (s->mapped_control (LPF_Freq)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_lpf/freq"), 0, get_address (msg));
}
int
OSC::sel_eq_hpf_enable (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (HPF_Enable)) {
s->mapped_control (HPF_Enable)->set_value (s->mapped_control (HPF_Enable)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_hpf/enable"), 0, get_address (msg));
}
int
OSC::sel_eq_lpf_enable (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (LPF_Enable)) {
s->mapped_control (LPF_Enable)->set_value (s->mapped_control (LPF_Enable)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_lpf/enable"), 0, get_address (msg));
}
int
OSC::sel_eq_hpf_slope (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (HPF_Slope)) {
s->mapped_control (HPF_Slope)->set_value (s->mapped_control (HPF_Slope)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_hpf/slope"), 0, get_address (msg));
}
int
OSC::sel_eq_lpf_slope (float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (s->mapped_control (LPF_Slope)) {
s->mapped_control (LPF_Slope)->set_value (s->mapped_control (LPF_Slope)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message(X_("/select/eq_lpf/slope"), 0, get_address (msg));
}
int
OSC::sel_eq_gain (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (id > 0) {
--id;
}
if (s->mapped_control (EQ_BandGain, id)) {
s->mapped_control (EQ_BandGain, id)->set_value (s->mapped_control(EQ_BandGain, id)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/eq_gain"), id + 1, 0, sur->feedback[2], get_address (msg));
}
int
OSC::sel_eq_freq (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (id > 0) {
--id;
}
if (s->mapped_control (EQ_BandFreq, id)) {
s->mapped_control (EQ_BandFreq, id)->set_value (s->mapped_control (EQ_BandFreq, id)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/eq_freq"), id + 1, 0, sur->feedback[2], get_address (msg));
}
int
OSC::sel_eq_q (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (id > 0) {
--id;
}
if (s->mapped_control (EQ_BandQ, id)) {
s->mapped_control (EQ_BandQ, id)->set_value (s->mapped_control (EQ_BandQ, id)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/eq_q"), id + 1, 0, sur->feedback[2], get_address (msg));
}
int
OSC::sel_eq_shape (int id, float val, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg));
std::shared_ptr<Stripable> s;
s = sur->select;
if (s) {
if (id > 0) {
--id;
}
if (s->mapped_control (EQ_BandShape, id)) {
s->mapped_control (EQ_BandShape, id)->set_value (s->mapped_control (EQ_BandShape, id)->interface_to_internal (val), PBD::Controllable::NoGroup);
return 0;
}
}
return float_message_with_id (X_("/select/eq_shape"), id + 1, 0, sur->feedback[2], get_address (msg));
}
// timer callbacks
bool
OSC::periodic (void)
{
if (observer_busy) {
return true;
}
if (!tick) {
Glib::usleep(100); // let flurry of signals subside
if (global_init) {
for (uint32_t it = 0; it < _surface.size(); it++) {
OSCSurface* sur = &_surface[it];
global_feedback (sur);
}
global_init = false;
tick = true;
}
if (bank_dirty) {
_recalcbanks ();
bank_dirty = false;
tick = true;
}
return true;
}
if (scrub_speed != 0) {
// for those jog wheels that don't have 0 on release (touch), time out.
int64_t now = PBD::get_microseconds ();
int64_t diff = now - scrub_time;
if (diff > 120000) {
scrub_speed = 0;
// locate to the place PH was at last tick
session->request_locate (scrub_place, false, MustStop);
}
}
for (uint32_t it = 0; it < _surface.size(); it++) {
OSCSurface* sur = &_surface[it];
OSCSelectObserver* so;
if ((so = dynamic_cast<OSCSelectObserver*>(sur->sel_obs)) != 0) {
so->tick ();
}
OSCCueObserver* co;
if ((co = dynamic_cast<OSCCueObserver*>(sur->cue_obs)) != 0) {
co->tick ();
}
if (sur->global_obs) {
sur->global_obs->tick ();
}
for (uint32_t i = 0; i < sur->observers.size(); i++) {
OSCRouteObserver* ro;
if ((ro = dynamic_cast<OSCRouteObserver*>(sur->observers[i])) != 0) {
ro->tick ();
}
}
}
for (FakeTouchMap::iterator x = _touch_timeout.begin(); x != _touch_timeout.end();) {
_touch_timeout[(*x).first] = (*x).second - 1;
if (!(*x).second) {
std::shared_ptr<ARDOUR::AutomationControl> ctrl = (*x).first;
// turn touch off
ctrl->stop_touch (timepos_t (ctrl->session().transport_sample()));
_touch_timeout.erase (x++);
} else {
x++;
}
}
return true;
}
XMLNode&
OSC::get_state () const
{
XMLNode& node (ControlProtocol::get_state());
node.set_property (X_("debugmode"), (int32_t) _debugmode); // TODO: enum2str
node.set_property (X_("address-only"), address_only);
node.set_property (X_("remote-port"), remote_port);
node.set_property (X_("banksize"), default_banksize);
node.set_property (X_("striptypes"), default_strip);
node.set_property (X_("feedback"), default_feedback);
node.set_property (X_("gainmode"), default_gainmode);
node.set_property (X_("send-page-size"), default_send_size);
node.set_property (X_("plug-page-size"), default_plugin_size);
return node;
}
int
OSC::set_state (const XMLNode& node, int version)
{
if (ControlProtocol::set_state (node, version)) {
return -1;
}
int32_t debugmode;
if (node.get_property (X_("debugmode"), debugmode)) {
_debugmode = OSCDebugMode (debugmode);
}
node.get_property (X_("address-only"), address_only);
node.get_property (X_("remote-port"), remote_port);
node.get_property (X_("banksize"), default_banksize);
node.get_property (X_("striptypes"), default_strip);
node.get_property (X_("feedback"), default_feedback);
node.get_property (X_("gainmode"), default_gainmode);
node.get_property (X_("send-page-size"), default_send_size);
node.get_property (X_("plugin-page-size"), default_plugin_size);
global_init = true;
tick = false;
return 0;
}
// predicate for sort call in get_sorted_stripables
struct StripableByPresentationOrder
{
bool operator () (const std::shared_ptr<Stripable> & a, const std::shared_ptr<Stripable> & b) const
{
return a->presentation_info().order() < b->presentation_info().order();
}
bool operator () (const Stripable & a, const Stripable & b) const
{
return a.presentation_info().order() < b.presentation_info().order();
}
bool operator () (const Stripable * a, const Stripable * b) const
{
return a->presentation_info().order() < b->presentation_info().order();
}
};
OSC::Sorted
OSC::get_sorted_stripables(std::bitset<32> types, bool cue, uint32_t custom, Sorted my_list)
{
Sorted sorted;
StripableList stripables;
StripableList custom_list;
// fetch all stripables
session->get_stripables (stripables, PresentationInfo::AllStripables);
if (custom) {
uint32_t nstps = my_list.size ();
// check each custom strip to see if it still exists
std::shared_ptr<Stripable> s;
for (uint32_t i = 0; i < nstps; i++) {
bool exists = false;
s = my_list[i];
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> sl = *it;
if (s == sl) {
exists = true;
break;
}
}
if(!exists) {
my_list[i] = std::shared_ptr<Stripable>();
} else {
custom_list.push_back (s);
}
}
if (custom == 1) {
return my_list;
} else {
stripables = custom_list;
}
}
// Look for stripables that match bit in sur->strip_types
for (StripableList::iterator it = stripables.begin(); it != stripables.end(); ++it) {
std::shared_ptr<Stripable> s = *it;
if (!s) {
break;
}
if (custom == 2) {
// banking off use all valid custom strips
sorted.push_back (s);
} else
if ((!cue) && (!types[9]) && (s->presentation_info().flags() & PresentationInfo::Hidden)) {
// do nothing... skip it
} else if (types[8] && (s->is_selected())) {
sorted.push_back (s);
} else if (types[9] && (s->presentation_info().flags() & PresentationInfo::Hidden)) {
sorted.push_back (s);
} else if (s->is_master() || s->is_monitor() || s->is_auditioner()) {
// do nothing for these either (we add them later)
} else {
if (types[0] && std::dynamic_pointer_cast<AudioTrack>(s)) {
sorted.push_back (s);
} else if (types[1] && std::dynamic_pointer_cast<MidiTrack>(s)) {
sorted.push_back (s);
} else if (types[4] && std::dynamic_pointer_cast<VCA>(s)) {
sorted.push_back (s);
} else if (s->is_foldbackbus()) {
if (types[7]) {
sorted.push_back (s);
}
} else
#ifdef MIXBUS
if (types[2] && Profile->get_mixbus() && s->mixbus()) {
sorted.push_back (s);
} else
#endif
if (std::dynamic_pointer_cast<Route>(s) && !std::dynamic_pointer_cast<Track>(s)) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(s);
if (!(s->presentation_info().flags() & PresentationInfo::MidiBus)) {
// note some older sessions will show midibuses as busses
// this is a bus
if (types[2]) {
sorted.push_back (s);
}
} else if (types[3]) {
sorted.push_back (s);
}
}
}
}
if (!custom || (custom & 0x2)) {
sort (sorted.begin(), sorted.end(), StripableByPresentationOrder());
}
if (!custom) {
// Master/Monitor might be anywhere... we put them at the end - Sorry ;)
if (types[5]) {
if (session->master_out()) {
sorted.push_back (session->master_out());
}
}
if (types[6]) {
if (session->monitor_out()) {
sorted.push_back (session->monitor_out());
}
}
}
return sorted;
}
int
OSC::cue_parse (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg), true);
s->bank_size = 0;
float value = 0;
if (argc == 1) {
if (types[0] == 'f') {
value = argv[0]->f;
} else if (types[0] == 'i') {
value = (float) argv[0]->i;
}
}
int ret = 1; /* unhandled */
if (!strncmp (path, X_("/cue/bus"), 8) || !strncmp (path, X_("/cue/aux"), 8)) {
// set our Foldback bus
if (argc) {
if (value) {
ret = cue_set ((uint32_t) value, msg);
} else {
ret = 0;
}
}
}
else if (!strncmp (path, X_("/cue/connect_output"), 16) || !strncmp (path, X_("/cue/connect_aux"), 16)) {
// connect Foldback bus output
string dest = "";
if (argc == 1 && types[0] == 's') {
dest = &argv[0]->s;
ret = cue_connect_aux (dest, msg);
} else {
PBD::warning << "OSC: connect_aux has wrong number or type of parameters." << endmsg;
}
}
else if (!strncmp (path, X_("/cue/connect"), 12)) {
// Connect to default Aux bus
if ((!argc) || argv[0]->f || argv[0]->i) {
ret = cue_set (1, msg);
} else {
ret = 0;
}
}
else if (!strncmp (path, X_("/cue/new_bus"), 12) || !strncmp (path, X_("/cue/new_aux"), 12)) {
// Create new Aux bus
string name = "";
string dest_1 = "";
string dest_2 = "";
if (argc == 3 && types[0] == 's' && types[1] == 's' && types[2] == 's') {
name = &argv[0]->s;
dest_1 = &argv[1]->s;
dest_2 = &argv[2]->s;
ret = cue_new_aux (name, dest_1, dest_2, 2, msg);
} else if (argc == 2 && types[0] == 's' && types[1] == 's') {
name = &argv[0]->s;
dest_1 = &argv[1]->s;
dest_2 = dest_1;
ret = cue_new_aux (name, dest_1, dest_2, 1, msg);
} else if (argc == 1 && types[0] == 's') {
name = &argv[0]->s;
ret = cue_new_aux (name, dest_1, dest_2, 1, msg);
} else {
PBD::warning << "OSC: new_aux has wrong number or type of parameters." << endmsg;
}
}
else if (!strncmp (path, X_("/cue/new_send"), 13)) {
// Create new send to Foldback
string rt_name = "";
if (argc == 1 && types[0] == 's') {
rt_name = &argv[0]->s;
ret = cue_new_send (rt_name, msg);
} else {
PBD::warning << "OSC: new_send has wrong number or type of parameters." << endmsg;
}
}
else if (!strncmp (path, X_("/cue/next_bus"), 13) || !strncmp (path, X_("/cue/next_aux"), 13)) {
// switch to next Foldback bus
if ((!argc) || argv[0]->f || argv[0]->i) {
ret = cue_next (msg);
} else {
ret = 0;
}
}
else if (!strncmp (path, X_("/cue/previous_bus"), 17) || !strncmp (path, X_("/cue/previous_aux"), 17)) {
// switch to previous Foldback bus
if ((!argc) || argv[0]->f || argv[0]->i) {
ret = cue_previous (msg);
} else {
ret = 0;
}
}
else if (!strncmp (path, X_("/cue/send/fader/"), 16) && strlen (path) > 16) {
if (argc == 1) {
int id = atoi (&path[16]);
ret = cue_send_fader (id, value, msg);
}
}
else if (!strncmp (path, X_("/cue/send/enable/"), 17) && strlen (path) > 17) {
if (argc == 1) {
int id = atoi (&path[17]);
ret = cue_send_enable (id, value, msg);
}
}
else if (!strncmp (path, X_("/cue/fader"), 10)) {
if (argc == 1) {
ret = cue_aux_fader (value, msg);
}
}
else if (!strncmp (path, X_("/cue/mute"), 9)) {
if (argc == 1) {
ret = cue_aux_mute (value, msg);
}
}
return ret;
}
int
OSC::cue_set (uint32_t aux, lo_message msg)
{
return _cue_set (aux, get_address (msg));
}
int
OSC::_cue_set (uint32_t aux, lo_address addr)
{
int ret = 1;
OSCSurface *s = get_surface(addr, true);
s->bank_size = 0;
s->strip_types = 128;
s->feedback = 0;
s->gainmode = 1;
s->cue = true;
s->strips = get_sorted_stripables(s->strip_types, s->cue, false, s->custom_strips);
s->nstrips = s->strips.size();
if (!s->nstrips) {
surface_destroy (s);
return 0;
}
if (aux < 1) {
aux = 1;
} else if (aux > s->nstrips) {
aux = s->nstrips;
}
s->aux = aux;
// get a list of Auxes
for (uint32_t n = 0; n < s->nstrips; ++n) {
std::shared_ptr<Stripable> stp = s->strips[n];
if (stp) {
text_message (string_compose (X_("/cue/name/%1"), n+1), stp->name(), addr);
if (aux == n+1) {
// aux must be at least one
stp->DropReferences.connect (*this, MISSING_INVALIDATOR, std::bind (&OSC::_cue_set, this, aux, addr), this);
// make a list of stripables with sends that go to this bus
s->sends = cue_get_sorted_stripables(stp, aux, addr);
if (s->cue_obs) {
s->cue_obs->refresh_strip (stp, s->sends, true);
} else {
// start cue observer
OSCCueObserver* co = new OSCCueObserver (*this, s);
s->cue_obs = co;
}
ret = 0;
}
}
}
return ret;
}
int
OSC::cue_new_aux (string name, string dest_1, string dest_2, uint32_t count, lo_message msg)
{
// create a new bus named name - monitor
RouteList list;
std::shared_ptr<Stripable> aux;
name = string_compose ("%1 - FB", name);
list = session->new_audio_route (count, count, 0, 1, name, PresentationInfo::FoldbackBus, (uint32_t) -1);
aux = *(list.begin());
if (aux) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route>(aux);
if (dest_1.size()) {
std::shared_ptr<PortSet> ports = r->output()->ports ();
if (atoi( dest_1.c_str())) {
dest_1 = string_compose ("system:playback_%1", dest_1);
}
r->output ()->connect (*(ports->begin()), dest_1, this);
if (count == 2) {
if (atoi( dest_2.c_str())) {
dest_2 = string_compose ("system:playback_%1", dest_2);
}
PortSet::iterator i = ports->begin();
++i;
r->output ()->connect (*(i), dest_2, this);
}
}
cue_set ((uint32_t) -1, msg);
session->set_dirty();
return 0;
}
return -1;
}
int
OSC::cue_new_send (string rt_name, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg), true);
if (sur->cue) {
std::shared_ptr<Route> aux = std::dynamic_pointer_cast<Route> (get_strip (sur->aux, get_address(msg)));
if (aux) {
std::shared_ptr<Route> rt_send = session->route_by_name (rt_name);
if (rt_send && (aux != rt_send)) {
// make sure there isn't one already
if (!rt_send->feeds (aux)) {
// create send
rt_send->add_foldback_send (aux, false);
std::shared_ptr<Send> snd = rt_send->internal_send_for (aux);
session->dirty ();
return 0;
} else {
PBD::warning << "OSC: new_send - duplicate send, ignored." << endmsg;
}
} else {
PBD::warning << "OSC: new_send - route doesn't exist or is aux." << endmsg;
}
} else {
PBD::warning << "OSC: new_send - No Aux to send to." << endmsg;
}
} else {
PBD::warning << "OSC: new_send - monitoring not set, select aux first." << endmsg;
}
return 1;
}
int
OSC::cue_connect_aux (std::string dest, lo_message msg)
{
OSCSurface *sur = get_surface(get_address (msg), true);
int ret = 1;
if (sur->cue) {
std::shared_ptr<Route> rt = std::dynamic_pointer_cast<Route> (get_strip (sur->aux, get_address(msg)));
if (rt) {
if (dest.size()) {
rt->output()->disconnect (this);
if (atoi( dest.c_str())) {
dest = string_compose ("system:playback_%1", dest);
}
std::shared_ptr<PortSet> ports = rt->output()->ports ();
rt->output ()->connect (*(ports->begin()), dest, this);
session->set_dirty();
ret = 0;
}
}
}
if (ret) {
PBD::warning << "OSC: cannot connect, no Aux bus chosen." << endmsg;
}
return ret;
}
int
OSC::cue_next (lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg), true);
int ret = 1;
if (!s->cue) {
ret = cue_set (1, msg);
}
if (s->aux < s->nstrips) {
ret = cue_set (s->aux + 1, msg);
} else {
ret = cue_set (s->nstrips, msg);
}
return ret;
}
int
OSC::cue_previous (lo_message msg)
{
OSCSurface *s = get_surface(get_address (msg), true);
int ret = 1;
if (!s->cue) {
ret = cue_set (1, msg);
}
if (s->aux > 1) {
ret = cue_set (s->aux - 1, msg);
} else {
ret = cue_set (1, msg);
}
return ret;
}
std::shared_ptr<Send>
OSC::cue_get_send (uint32_t id, lo_address addr)
{
OSCSurface *s = get_surface(addr, true);
if (id && s->aux > 0 && id <= s->sends.size()) {
std::shared_ptr<Route> r = std::dynamic_pointer_cast<Route> (s->sends[id - 1]);
std::shared_ptr<Stripable> aux = get_strip (s->aux, addr);
if (r && aux) {
return r->internal_send_for (std::dynamic_pointer_cast<Route> (aux));
}
}
return std::shared_ptr<Send>();
}
int
OSC::cue_aux_fader (float position, lo_message msg)
{
if (!session) return -1;
OSCSurface *sur = get_surface(get_address (msg), true);
if (sur->cue) {
if (sur->aux) {
std::shared_ptr<Stripable> s = get_strip (sur->aux, get_address (msg));
if (s) {
if (s->gain_control()) {
s->gain_control()->set_value (s->gain_control()->interface_to_internal (position), PBD::Controllable::NoGroup);
return 0;
}
}
}
}
float_message (X_("/cue/fader"), 0, get_address (msg));
return -1;
}
int
OSC::cue_aux_mute (float state, lo_message msg)
{
if (!session) return -1;
OSCSurface *sur = get_surface(get_address (msg), true);
if (sur->cue) {
if (sur->aux) {
std::shared_ptr<Stripable> s = get_strip (sur->aux, get_address (msg));
if (s) {
if (s->mute_control()) {
s->mute_control()->set_value (state ? 1.0 : 0.0, PBD::Controllable::NoGroup);
return 0;
}
}
}
}
float_message (X_("/cue/mute"), 0, get_address (msg));
return -1;
}
int
OSC::cue_send_fader (uint32_t id, float val, lo_message msg)
{
if (!session) {
return -1;
}
std::shared_ptr<Send> s = cue_get_send (id, get_address (msg));
if (s) {
if (s->gain_control()) {
s->gain_control()->set_value (s->gain_control()->interface_to_internal(val), PBD::Controllable::NoGroup);
return 0;
}
}
float_message (string_compose (X_("/cue/send/fader/%1"), id), 0, get_address (msg));
return -1;
}
int
OSC::cue_send_enable (uint32_t id, float state, lo_message msg)
{
if (!session)
return -1;
std::shared_ptr<Send> s = cue_get_send (id, get_address (msg));
if (s) {
if (state) {
s->activate ();
} else {
s->deactivate ();
}
return 0;
}
float_message (string_compose (X_("/cue/send/enable/%1"), id), 0, get_address (msg));
return -1;
}
// generic send message
int
OSC::float_message (string path, float val, lo_address addr)
{
_lo_lock.lock ();
lo_message reply;
reply = lo_message_new ();
lo_message_add_float (reply, (float) val);
lo_send_message (addr, path.c_str(), reply);
Glib::usleep(1);
lo_message_free (reply);
_lo_lock.unlock ();
return 0;
}
int
OSC::float_message_with_id (std::string path, uint32_t ssid, float value, bool in_line, lo_address addr)
{
_lo_lock.lock ();
lo_message msg = lo_message_new ();
if (in_line) {
path = string_compose ("%1/%2", path, ssid);
} else {
lo_message_add_int32 (msg, ssid);
}
lo_message_add_float (msg, value);
lo_send_message (addr, path.c_str(), msg);
Glib::usleep(1);
lo_message_free (msg);
_lo_lock.unlock ();
return 0;
}
int
OSC::int_message (string path, int val, lo_address addr)
{
_lo_lock.lock ();
lo_message reply;
reply = lo_message_new ();
lo_message_add_int32 (reply, (float) val);
lo_send_message (addr, path.c_str(), reply);
Glib::usleep(1);
lo_message_free (reply);
_lo_lock.unlock ();
return 0;
}
int
OSC::int_message_with_id (std::string path, uint32_t ssid, int value, bool in_line, lo_address addr)
{
_lo_lock.lock ();
lo_message msg = lo_message_new ();
if (in_line) {
path = string_compose ("%1/%2", path, ssid);
} else {
lo_message_add_int32 (msg, ssid);
}
lo_message_add_int32 (msg, value);
lo_send_message (addr, path.c_str(), msg);
Glib::usleep(1);
lo_message_free (msg);
_lo_lock.unlock ();
return 0;
}
int
OSC::text_message (string path, string val, lo_address addr)
{
_lo_lock.lock ();
lo_message reply;
reply = lo_message_new ();
lo_message_add_string (reply, val.c_str());
lo_send_message (addr, path.c_str(), reply);
Glib::usleep(1);
lo_message_free (reply);
_lo_lock.unlock ();
return 0;
}
int
OSC::text_message_with_id (std::string path, uint32_t ssid, std::string val, bool in_line, lo_address addr)
{
_lo_lock.lock ();
lo_message msg = lo_message_new ();
if (in_line) {
path = string_compose ("%1/%2", path, ssid);
} else {
lo_message_add_int32 (msg, ssid);
}
lo_message_add_string (msg, val.c_str());
lo_send_message (addr, path.c_str(), msg);
Glib::usleep(1);
lo_message_free (msg);
_lo_lock.unlock ();
return 0;
}
// we have to have a sorted list of stripables that have sends pointed at our aux
// we can use the one in osc.cc to get an aux list
OSC::Sorted
OSC::cue_get_sorted_stripables(std::shared_ptr<Stripable> aux, uint32_t id, lo_address addr)
{
Sorted sorted;
std::shared_ptr<Route> aux_rt = std::dynamic_pointer_cast<Route> (aux);
for (auto const& s : aux_rt->signal_sources (true)) {
sorted.push_back (s);
s->DropReferences.connect (*this, MISSING_INVALIDATOR, std::bind (&OSC::_cue_set, this, id, addr), this);
}
sort (sorted.begin(), sorted.end(), StripableByPresentationOrder());
return sorted;
}