*
*/
-#include <iostream>
-#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>
-#include <glib/gstdio.h>
+#include "pbd/gstdio_compat.h"
#include <glibmm/miscutils.h>
#include <pbd/convert.h>
OSC::OSC (Session& s, uint32_t port)
: ControlProtocol (s, X_("Open Sound Control (OSC)"))
- , AbstractUI<OSCUIRequest> ("osc")
+ , AbstractUI<OSCUIRequest> (name())
, local_server (0)
, remote_server (0)
, _port(port)
, _osc_unix_server (0)
, _namespace_root ("/ardour")
, _send_route_changes (true)
+ , _debugmode (Off)
+ , gui (0)
{
_instance = this;
_instance = 0;
}
+void*
+OSC::request_factory (uint32_t num_requests)
+{
+ /* AbstractUI<T>::request_buffer_factory() is a template method only
+ instantiated in this source module. To provide something visible for
+ use in the interface/descriptor, we have this static method that is
+ template-free.
+ */
+ return request_buffer_factory (num_requests);
+}
+
void
OSC::do_request (OSCUIRequest* req)
{
std::string url_file;
if (find_file (ardour_config_search_path(), "osc_url", url_file)) {
-
_osc_url_file = url_file;
- ofstream urlfile;
- urlfile.open(_osc_url_file.c_str(), ios::trunc);
-
- if (urlfile) {
- urlfile << get_server_url () << endl;
- urlfile.close();
- } else {
+ 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;
}
}
void
OSC::thread_init ()
{
- pthread_set_name (X_("OSC"));
+ pthread_set_name (event_loop_name().c_str());
if (_osc_unix_server) {
Glib::RefPtr<IOSource> src = IOSource::create (lo_server_get_socket_fd (_osc_unix_server), IO_IN|IO_HUP|IO_ERR);
g_source_ref (remote_server);
}
- PBD::notify_gui_about_thread_creation (X_("gui"), pthread_self(), X_("OSC"), 2048);
- SessionEvent::create_per_thread_pool (X_("OSC"), 128);
+ PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
+ SessionEvent::create_per_thread_pool (event_loop_name(), 128);
}
int
serv = srvs[i];
- /* this is a special catchall handler */
-
- lo_server_add_method (serv, 0, 0, _catchall, this);
#define REGISTER_CALLBACK(serv,path,types, function) lo_server_add_method (serv, path, types, OSC::_ ## function, this)
REGISTER_CALLBACK (serv, "/ardour/transport_stop", "", transport_stop);
REGISTER_CALLBACK (serv, "/ardour/transport_play", "", transport_play);
REGISTER_CALLBACK (serv, "/ardour/transport_frame", "", transport_frame);
+ REGISTER_CALLBACK (serv, "/ardour/transport_speed", "", transport_speed);
+ REGISTER_CALLBACK (serv, "/ardour/record_enabled", "", record_enabled);
REGISTER_CALLBACK (serv, "/ardour/set_transport_speed", "f", set_transport_speed);
REGISTER_CALLBACK (serv, "/ardour/locate", "ii", locate);
REGISTER_CALLBACK (serv, "/ardour/save_state", "", save_state);
REGISTER_CALLBACK (serv, "/ardour/toggle_punch_out", "", toggle_punch_out);
REGISTER_CALLBACK (serv, "/ardour/rec_enable_toggle", "", rec_enable_toggle);
REGISTER_CALLBACK (serv, "/ardour/toggle_all_rec_enables", "", toggle_all_rec_enables);
-
+ REGISTER_CALLBACK (serv, "/ardour/all_tracks_rec_in", "f", all_tracks_rec_in);
+ REGISTER_CALLBACK (serv, "/ardour/all_tracks_rec_out", "f", all_tracks_rec_out);
+ REGISTER_CALLBACK (serv, "/ardour/remove_marker", "", remove_marker_at_playhead);
+ REGISTER_CALLBACK (serv, "/ardour/jump_bars", "f", jump_by_bars);
+ REGISTER_CALLBACK (serv, "/ardour/jump_seconds", "f", jump_by_seconds);
+ REGISTER_CALLBACK (serv, "/ardour/mark_in", "", mark_in);
+ REGISTER_CALLBACK (serv, "/ardour/mark_out", "", mark_out);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_click", "", toggle_click);
+ REGISTER_CALLBACK (serv, "/ardour/midi_panic", "", midi_panic);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_roll", "", toggle_roll);
+ REGISTER_CALLBACK (serv, "/ardour/stop_forget", "", stop_forget);
+ REGISTER_CALLBACK (serv, "/ardour/set_punch_range", "", set_punch_range);
+ REGISTER_CALLBACK (serv, "/ardour/set_loop_range", "", set_loop_range);
+ REGISTER_CALLBACK (serv, "/ardour/set_session_range", "", set_session_range);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_monitor_mute", "", toggle_monitor_mute);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_monitor_dim", "", toggle_monitor_dim);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_monitor_mono", "", toggle_monitor_mono);
+ REGISTER_CALLBACK (serv, "/ardour/quick_snapshot_switch", "", quick_snapshot_switch);
+ REGISTER_CALLBACK (serv, "/ardour/quick_snapshot_stay", "", quick_snapshot_stay);
+ REGISTER_CALLBACK (serv, "/ardour/fit_1_track", "", fit_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/fit_2_tracks", "", fit_2_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/fit_4_tracks", "", fit_4_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/fit_8_tracks", "", fit_8_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/fit_16_tracks", "", fit_16_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/fit_32_tracks", "", fit_32_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/fit_all_tracks", "", fit_all_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_100_ms", "", zoom_100_ms);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_1_sec", "", zoom_1_sec);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_10_sec", "", zoom_10_sec);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_1_min", "", zoom_1_min);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_5_min", "", zoom_5_min);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_10_min", "", zoom_10_min);
+ REGISTER_CALLBACK (serv, "/ardour/zoom_to_session", "", zoom_to_session);
+ REGISTER_CALLBACK (serv, "/ardour/temporal_zoom_in", "f", temporal_zoom_in);
+ REGISTER_CALLBACK (serv, "/ardour/temporal_zoom_out", "f", temporal_zoom_out);
+ REGISTER_CALLBACK (serv, "/ardour/scroll_up_1_track", "f", scroll_up_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/scroll_dn_1_track", "f", scroll_dn_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/scroll_up_1_page", "f", scroll_up_1_page);
+ REGISTER_CALLBACK (serv, "/ardour/scroll_dn_1_page", "f", scroll_dn_1_page);
+
+
+ /*
+ * NOTE: these messages are provided for (arguably broken) apps
+ * that MUST send float args ( TouchOSC and Lemur ).
+ * Normally these ardour transport messages don't require an argument,
+ * so we're providing redundant calls with vestigial "float" args.
+ *
+ * These controls are active on 1.0 only (to prevent duplicate action on
+ * press "/button 1", and release "/button 0")
+ * http://hexler.net/docs/touchosc-controls-reference
+ */
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/loop_toggle", "f", loop_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/add_marker", "f", add_marker);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/goto_start", "f", goto_start);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/goto_end", "f", goto_end);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/rewind", "f", rewind);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/ffwd", "f", ffwd);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/transport_stop", "f", transport_stop);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/transport_play", "f", transport_play);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/save_state", "f", save_state);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/prev_marker", "f", prev_marker);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/next_marker", "f", next_marker);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/undo", "f", undo);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/redo", "f", redo);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_punch_in", "f", toggle_punch_in);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_punch_out", "f", toggle_punch_out);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/rec_enable_toggle", "f", rec_enable_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_all_rec_enables", "f", toggle_all_rec_enables);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/all_tracks_rec_in", "f", all_tracks_rec_in);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/all_tracks_rec_out", "f", all_tracks_rec_out);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/remove_marker", "f", remove_marker_at_playhead);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/mark_in", "f", mark_in);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/mark_out", "f", mark_out);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_click", "f", toggle_click);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/midi_panic", "f", midi_panic);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_roll", "f", toggle_roll);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/stop_forget", "f", stop_forget);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/set_punch_range", "f", set_punch_range);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/set_loop_range", "f", set_loop_range);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/set_session_range", "f", set_session_range);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_monitor_mute", "f", toggle_monitor_mute);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_monitor_dim", "f", toggle_monitor_dim);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/toggle_monitor_mono", "f", toggle_monitor_mono);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/quick_snapshot_switch", "f", quick_snapshot_switch);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/quick_snapshot_stay", "f", quick_snapshot_stay);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_1_track", "f", fit_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_2_tracks", "f", fit_2_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_4_tracks", "f", fit_4_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_8_tracks", "f", fit_8_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_16_tracks", "f", fit_16_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_32_tracks", "f", fit_32_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/fit_all_tracks", "f", fit_all_tracks);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_100_ms", "f", zoom_100_ms);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_1_sec", "f", zoom_1_sec);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_10_sec", "f", zoom_10_sec);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_1_min", "f", zoom_1_min);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_5_min", "f", zoom_5_min);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_10_min", "f", zoom_10_min);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/zoom_to_session", "f", zoom_to_session);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/temporal_zoom_in", "f", temporal_zoom_in);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/temporal_zoom_out", "f", temporal_zoom_out);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/scroll_up_1_track", "f", scroll_up_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/scroll_dn_1_track", "f", scroll_dn_1_track);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/scroll_up_1_page", "f", scroll_up_1_page);
+ REGISTER_CALLBACK (serv, "/ardour/pushbutton/scroll_dn_1_page", "f", scroll_dn_1_page);
+
+ /* These commands require the route index in addition to the arg; TouchOSC (et al) can't use these */
REGISTER_CALLBACK (serv, "/ardour/routes/mute", "ii", route_mute);
REGISTER_CALLBACK (serv, "/ardour/routes/solo", "ii", route_solo);
REGISTER_CALLBACK (serv, "/ardour/routes/recenable", "ii", route_recenable);
//lo_server_add_method(serv, "/register_auto_update", "siss", OSC::global_register_auto_update_handler, this);
//lo_server_add_method(serv, "/unregister_auto_update", "sss", OSC::_global_unregister_auto_update_handler, this);
+ /* this is a special catchall handler,
+ * register at the end so this is only called if no
+ * other handler matches (used for debug) */
+ lo_server_add_method (serv, 0, 0, _catchall, this);
}
}
}
int
-OSC::catchall (const char *path, const char* /*types*/, lo_arg **argv, int argc, lo_message msg)
+OSC::catchall (const char *path, const char* types, lo_arg **argv, int argc, lo_message msg)
{
size_t len;
int ret = 1; /* unhandled */
}
ret = 0;
+ } else if (argc == 1 && types[0] == 'f') { // single float -- probably TouchOSC
+ if (!strncmp (path, "/ardour/routes/gainabs/", 23) && strlen (path) > 23) {
+ int rid = atoi (&path[23]);
+ // use some power-scale mapping??
+ route_set_gain_abs (rid, argv[0]->f);
+ ret = 0;
+ }
+ else if (!strncmp (path, "/ardour/routes/trimabs/", 23) && strlen (path) > 23) {
+ int rid = atoi (&path[23]);
+ // normalize 0..1 ?
+ route_set_trim_abs (rid, argv[0]->f);
+ ret = 0;
+ }
+ else if (!strncmp (path, "/ardour/routes/mute/", 20) && strlen (path) > 20) {
+ int rid = atoi (&path[20]);
+ route_mute (rid, argv[0]->f == 1.0);
+ ret = 0;
+ }
+ else if (!strncmp (path, "/ardour/routes/solo/", 20) && strlen (path) > 20) {
+ int rid = atoi (&path[20]);
+ route_solo (rid, argv[0]->f == 1.0);
+ ret = 0;
+ }
+ else if (!strncmp (path, "/ardour/routes/recenable/", 25) && strlen (path) > 25) {
+ int rid = atoi (&path[25]);
+ route_recenable (rid, argv[0]->f == 1.0);
+ ret = 0;
+ }
+ }
+
+ if ((ret && _debugmode == Unhandled)) {
+ debugmsg (_("Unhandled OSC message"), 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;
+}
+
void
OSC::update_clock ()
{
void
OSC::routes_list (lo_message msg)
{
+ if (!session) {
+ return;
+ }
for (int n = 0; n < (int) session->nroutes(); ++n) {
boost::shared_ptr<Route> r = session->route_by_remote_id (n);
void
OSC::transport_frame (lo_message msg)
{
+ if (!session) {
+ return;
+ }
framepos_t pos = session->transport_frame ();
lo_message reply = lo_message_new ();
lo_message_free (reply);
}
+void
+OSC::transport_speed (lo_message msg)
+{
+ if (!session) {
+ return;
+ }
+ double ts = session->transport_speed ();
+
+ lo_message reply = lo_message_new ();
+ lo_message_add_double (reply, ts);
+
+ lo_send_message (lo_message_get_source (msg), "/ardour/transport_speed", reply);
+
+ lo_message_free (reply);
+}
+
+void
+OSC::record_enabled (lo_message msg)
+{
+ if (!session) {
+ return;
+ }
+ int re = (int)session->get_record_enabled ();
+
+ lo_message reply = lo_message_new ();
+ lo_message_add_int32 (reply, re);
+
+ lo_send_message (lo_message_get_source (msg), "/ardour/record_enabled", reply);
+
+ lo_message_free (reply);
+}
+
+
int
OSC::route_mute (int rid, int yn)
{
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
- r->set_mute (yn, this);
+ r->set_mute (yn, PBD::Controllable::NoGroup);
}
return 0;
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
- r->set_solo (yn, this);
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (r);
+ session->set_solo (rl, yn, Session::rt_cleanup, PBD::Controllable::NoGroup);
}
return 0;
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
- r->set_record_enabled (yn, this);
+ r->set_record_enabled (yn, PBD::Controllable::NoGroup);
}
return 0;
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
- r->set_gain (level, this);
+ r->set_gain (level, PBD::Controllable::NoGroup);
}
return 0;
boost::shared_ptr<Route> r = session->route_by_remote_id (rid);
if (r) {
- r->set_trim (level, this);
+ r->set_trim (level, PBD::Controllable::NoGroup);
}
return 0;
boost::shared_ptr<Amp> a = s->amp();
if (a) {
- a->set_gain (val, this);
+ a->gain_control()->set_value (val, PBD::Controllable::NoGroup);
}
}
return 0;
boost::shared_ptr<Amp> a = s->amp();
if (a) {
- a->set_gain (dB_to_coefficient (val), this);
+ a->gain_control()->set_value (dB_to_coefficient (val), PBD::Controllable::NoGroup);
}
}
return 0;
boost::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);
+ c->set_value (val, PBD::Controllable::NoGroup);
} else {
PBD::warning << "OSC: Parameter # " << par << " for plugin # " << piid << " on RID '" << rid << "' is out of range" << endmsg;
PBD::info << "OSC: Valid range min=" << pd.lower << " max=" << pd.upper << endmsg;
XMLNode&
OSC::get_state ()
{
- return ControlProtocol::get_state();
+ XMLNode& node (ControlProtocol::get_state());
+ node.add_property("debugmode", (int) _debugmode); // TODO: enum2str
+ return node;
}
int
if (ControlProtocol::set_state (node, version)) {
return -1;
}
+ XMLProperty const * p = node.property (X_("debugmode"));
+ if (p) {
+ _debugmode = OSCDebugMode (PBD::atoi(p->value ()));
+ }
return 0;
}