Proper OSC remote control implementation, allowed on standalones

Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
falkTX 2023-07-27 14:01:02 +02:00
parent 0ad791dee0
commit 3d44fb9d79
No known key found for this signature in database
GPG key ID: CDBAA37ABC74FBA0
5 changed files with 192 additions and 34 deletions

View file

@ -63,7 +63,7 @@
# include <unistd.h> # include <unistd.h>
#endif #endif
#ifdef CARDINAL_INIT_OSC_THREAD #ifdef HAVE_LIBLO
# include <lo/lo.h> # include <lo/lo.h>
#endif #endif
@ -231,7 +231,7 @@ void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message,
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
#ifdef CARDINAL_INIT_OSC_THREAD #ifdef HAVE_LIBLO
static void osc_error_handler(int num, const char* msg, const char* path) static void osc_error_handler(int num, const char* msg, const char* path)
{ {
d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path); d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path);
@ -245,9 +245,9 @@ static int osc_fallback_handler(const char* const path, const char* const types,
static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self) static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self)
{ {
d_debug("osc_hello_handler()"); d_stdout("Hello received from OSC, saying hello back to them o/");
const lo_address source = lo_message_get_source(m); const lo_address source = lo_message_get_source(m);
const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread); const lo_server server = static_cast<Initializer*>(self)->oscServer;
lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok"); lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok");
return 0; return 0;
} }
@ -287,7 +287,7 @@ static int osc_load_handler(const char*, const char* types, lo_arg** argv, int a
} }
const lo_address source = lo_message_get_source(m); const lo_address source = lo_message_get_source(m);
const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread); const lo_server server = static_cast<Initializer*>(self)->oscServer;
lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail"); lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail");
return 0; return 0;
} }
@ -344,6 +344,7 @@ static int osc_host_param_handler(const char*, const char* types, lo_arg** argv,
return 0; return 0;
} }
# ifdef CARDINAL_INIT_OSC_THREAD
static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self) static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self)
{ {
d_debug("osc_screenshot_handler()"); d_debug("osc_screenshot_handler()");
@ -368,10 +369,11 @@ static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv,
} }
const lo_address source = lo_message_get_source(m); const lo_address source = lo_message_get_source(m);
const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread); const lo_server server = static_cast<Initializer*>(self)->oscServer;
lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail"); lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail");
return 0; return 0;
} }
# endif
#endif #endif
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
@ -575,41 +577,32 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB
loadSettings(isRealInstance); loadSettings(isRealInstance);
#ifdef CARDINAL_INIT_OSC_THREAD #if defined(CARDINAL_INIT_OSC_THREAD)
INFO("Initializing OSC Remote control"); INFO("Initializing OSC Remote control");
const char* port; const char* port;
if (const char* const portEnv = std::getenv("CARDINAL_REMOTE_HOST_PORT")) if (const char* const portEnv = std::getenv("CARDINAL_REMOTE_HOST_PORT"))
port = portEnv; port = portEnv;
else else
port = CARDINAL_DEFAULT_REMOTE_PORT; port = CARDINAL_DEFAULT_REMOTE_PORT;
oscServerThread = lo_server_thread_new_with_proto(port, LO_UDP, osc_error_handler); startRemoteServer(port);
DISTRHO_SAFE_ASSERT_RETURN(oscServerThread != nullptr,); #elif defined(HAVE_LIBLO)
if (isStandalone()) {
lo_server_thread_add_method(oscServerThread, "/hello", "", osc_hello_handler, this); INFO("OSC Remote control is available on request");
lo_server_thread_add_method(oscServerThread, "/host-param", "if", osc_host_param_handler, this); } else {
lo_server_thread_add_method(oscServerThread, "/load", "b", osc_load_handler, this); INFO("OSC Remote control is not available on plugin variants");
lo_server_thread_add_method(oscServerThread, "/param", "hif", osc_param_handler, this); }
lo_server_thread_add_method(oscServerThread, "/screenshot", "b", osc_screenshot_handler, this); #else
lo_server_thread_add_method(oscServerThread, nullptr, nullptr, osc_fallback_handler, nullptr);
lo_server_thread_start(oscServerThread);
#else
INFO("OSC Remote control is not enabled in this build"); INFO("OSC Remote control is not enabled in this build");
#endif #endif
} }
Initializer::~Initializer() Initializer::~Initializer()
{ {
using namespace rack; using namespace rack;
#ifdef CARDINAL_INIT_OSC_THREAD #ifdef HAVE_LIBLO
if (oscServerThread != nullptr) stopRemoteServer();
{ #endif
lo_server_thread_stop(oscServerThread);
lo_server_thread_del_method(oscServerThread, nullptr, nullptr);
lo_server_thread_free(oscServerThread);
oscServerThread = nullptr;
}
#endif
if (shouldSaveSettings) if (shouldSaveSettings)
{ {
@ -670,6 +663,81 @@ void Initializer::loadSettings(const bool isRealInstance)
switchDarkMode(settings::uiTheme == "dark"); switchDarkMode(settings::uiTheme == "dark");
} }
#ifdef HAVE_LIBLO
bool Initializer::startRemoteServer(const char* const port)
{
#ifdef CARDINAL_INIT_OSC_THREAD
if (oscServerThread != nullptr)
return true;
if ((oscServerThread = lo_server_thread_new_with_proto(port, LO_UDP, osc_error_handler)) == nullptr)
return false;
oscServer = lo_server_thread_get_server(oscServerThread);
lo_server_thread_add_method(oscServerThread, "/hello", "", osc_hello_handler, this);
lo_server_thread_add_method(oscServerThread, "/host-param", "if", osc_host_param_handler, this);
lo_server_thread_add_method(oscServerThread, "/load", "b", osc_load_handler, this);
lo_server_thread_add_method(oscServerThread, "/param", "hif", osc_param_handler, this);
lo_server_thread_add_method(oscServerThread, "/screenshot", "b", osc_screenshot_handler, this);
lo_server_thread_add_method(oscServerThread, nullptr, nullptr, osc_fallback_handler, nullptr);
lo_server_thread_start(oscServerThread);
#else
if (oscServer != nullptr)
return true;
if ((oscServer = lo_server_new_with_proto(port, LO_UDP, osc_error_handler)) == nullptr)
return false;
lo_server_add_method(oscServer, "/hello", "", osc_hello_handler, this);
lo_server_add_method(oscServer, "/host-param", "if", osc_host_param_handler, this);
lo_server_add_method(oscServer, "/load", "b", osc_load_handler, this);
lo_server_add_method(oscServer, "/param", "hif", osc_param_handler, this);
lo_server_add_method(oscServer, nullptr, nullptr, osc_fallback_handler, nullptr);
#endif
return true;
}
void Initializer::stopRemoteServer()
{
DISTRHO_SAFE_ASSERT(remotePluginInstance == nullptr);
#ifdef CARDINAL_INIT_OSC_THREAD
if (oscServerThread != nullptr)
{
lo_server_thread_stop(oscServerThread);
lo_server_thread_del_method(oscServerThread, nullptr, nullptr);
lo_server_thread_free(oscServerThread);
oscServerThread = oscServer = nullptr;
}
#else
if (oscServer != nullptr)
{
lo_server_del_method(oscServer, nullptr, nullptr);
lo_server_free(oscServer);
oscServer = nullptr;
}
#endif
}
void Initializer::stepRemoteServer()
{
DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(remotePluginInstance != nullptr,);
#ifndef CARDINAL_INIT_OSC_THREAD
for (;;)
{
try {
if (lo_server_recv_noblock(oscServer, 0) == 0)
break;
} DISTRHO_SAFE_EXCEPTION_CONTINUE("stepRemoteServer")
}
#endif
}
#endif // HAVE_LIBLO
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
END_NAMESPACE_DISTRHO END_NAMESPACE_DISTRHO

View file

@ -87,6 +87,7 @@ void openBrowser(const std::string& url);
# define CARDINAL_INIT_OSC_THREAD # define CARDINAL_INIT_OSC_THREAD
#endif #endif
typedef void* lo_server;
typedef void* lo_server_thread; typedef void* lo_server_thread;
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
@ -97,10 +98,6 @@ struct CardinalPluginContext;
struct Initializer struct Initializer
{ {
#ifdef CARDINAL_INIT_OSC_THREAD
lo_server_thread oscServerThread = nullptr;
CardinalBasePlugin* remotePluginInstance = nullptr;
#endif
std::string templatePath; std::string templatePath;
std::string factoryTemplatePath; std::string factoryTemplatePath;
bool shouldSaveSettings = false; bool shouldSaveSettings = false;
@ -108,6 +105,18 @@ struct Initializer
Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui); Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui);
~Initializer(); ~Initializer();
void loadSettings(bool isRealInstance); void loadSettings(bool isRealInstance);
#ifdef HAVE_LIBLO
lo_server oscServer = nullptr;
#ifdef CARDINAL_INIT_OSC_THREAD
lo_server_thread oscServerThread = nullptr;
#endif
CardinalBasePlugin* remotePluginInstance = nullptr;
bool startRemoteServer(const char* port);
void stopRemoteServer();
void stepRemoteServer();
#endif
}; };
#ifndef HEADLESS #ifndef HEADLESS

View file

@ -167,7 +167,6 @@ struct ScopedContext {
} }
}; };
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
class CardinalPlugin : public CardinalBasePlugin class CardinalPlugin : public CardinalBasePlugin
@ -333,7 +332,7 @@ public:
~CardinalPlugin() override ~CardinalPlugin() override
{ {
#ifdef CARDINAL_INIT_OSC_THREAD #ifdef HAVE_LIBLO
if (fInitializer->remotePluginInstance == this) if (fInitializer->remotePluginInstance == this)
fInitializer->remotePluginInstance = nullptr; fInitializer->remotePluginInstance = nullptr;
#endif #endif
@ -359,6 +358,37 @@ public:
return context; return context;
} }
#ifdef HAVE_LIBLO
bool startRemoteServer(const char* const port) override
{
if (fInitializer->remotePluginInstance != nullptr)
return false;
if (fInitializer->startRemoteServer(port))
{
fInitializer->remotePluginInstance = this;
return true;
}
return false;
}
void stopRemoteServer() override
{
DISTRHO_SAFE_ASSERT_RETURN(fInitializer->remotePluginInstance == this,);
fInitializer->remotePluginInstance = nullptr;
fInitializer->stopRemoteServer();
}
void stepRemoteServer() override
{
DISTRHO_SAFE_ASSERT_RETURN(fInitializer->remotePluginInstance == this,);
fInitializer->stepRemoteServer();
}
#endif
protected: protected:
/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Information */ * Information */

View file

@ -218,6 +218,12 @@ public:
context(new CardinalPluginContext(this)) {} context(new CardinalPluginContext(this)) {}
~CardinalBasePlugin() override {} ~CardinalBasePlugin() override {}
#ifdef HAVE_LIBLO
virtual bool startRemoteServer(const char* port) = 0;
virtual void stopRemoteServer() = 0;
virtual void stepRemoteServer() = 0;
#endif
#ifndef HEADLESS #ifndef HEADLESS
friend class CardinalUI; friend class CardinalUI;
#endif #endif

View file

@ -53,6 +53,7 @@
#include "../CardinalCommon.hpp" #include "../CardinalCommon.hpp"
#include "../CardinalRemote.hpp" #include "../CardinalRemote.hpp"
#include "../PluginContext.hpp"
#include "DistrhoPlugin.hpp" #include "DistrhoPlugin.hpp"
#include "DistrhoStandaloneUtils.hpp" #include "DistrhoStandaloneUtils.hpp"
@ -729,6 +730,10 @@ struct ViewButton : MenuButton {
struct EngineButton : MenuButton { struct EngineButton : MenuButton {
#ifdef HAVE_LIBLO
bool remoteServerStarted = false;
#endif
void onAction(const ActionEvent& e) override { void onAction(const ActionEvent& e) override {
ui::Menu* menu = createMenu(); ui::Menu* menu = createMenu();
menu->cornerFlags = BND_CORNER_TOP; menu->cornerFlags = BND_CORNER_TOP;
@ -741,6 +746,34 @@ struct EngineButton : MenuButton {
settings::cpuMeter ^= true; settings::cpuMeter ^= true;
})); }));
#ifdef HAVE_LIBLO
if (isStandalone()) {
CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
CardinalBasePlugin* const plugin = static_cast<CardinalBasePlugin*>(context->plugin);
// const bool remoteServerStarted = this->remoteServerStarted;
const std::string remoteControlText = remoteServerStarted ? " " CHECKMARK_STRING : "";
menu->addChild(createMenuItem("Enable OSC remote control", remoteControlText, [=]() {
if (remoteServerStarted) {
remoteServerStarted = false;
plugin->stopRemoteServer();
return;
}
async_dialog_text_input("OSC network port", CARDINAL_DEFAULT_REMOTE_PORT, [=](char* const port) {
if (port == nullptr)
return;
if (plugin->startRemoteServer(port))
remoteServerStarted = true;
std::free(port);
});
}));
}
#endif
if (isUsingNativeAudio()) { if (isUsingNativeAudio()) {
if (supportsAudioInput()) { if (supportsAudioInput()) {
const bool enabled = isAudioInputEnabled(); const bool enabled = isAudioInputEnabled();
@ -782,6 +815,18 @@ struct EngineButton : MenuButton {
} }
} }
} }
#ifdef HAVE_LIBLO
void step() override {
MenuButton::step();
if (remoteServerStarted) {
CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
CardinalBasePlugin* const plugin = static_cast<CardinalBasePlugin*>(context->plugin);
plugin->stepRemoteServer();
}
}
#endif
}; };