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>
#endif
#ifdef CARDINAL_INIT_OSC_THREAD
#ifdef HAVE_LIBLO
# include <lo/lo.h>
#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)
{
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)
{
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_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");
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_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");
return 0;
}
@ -344,6 +344,7 @@ static int osc_host_param_handler(const char*, const char* types, lo_arg** argv,
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)
{
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_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");
return 0;
}
# endif
#endif
// -----------------------------------------------------------------------------------------------------------
@ -575,41 +577,32 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB
loadSettings(isRealInstance);
#ifdef CARDINAL_INIT_OSC_THREAD
#if defined(CARDINAL_INIT_OSC_THREAD)
INFO("Initializing OSC Remote control");
const char* port;
if (const char* const portEnv = std::getenv("CARDINAL_REMOTE_HOST_PORT"))
port = portEnv;
else
port = CARDINAL_DEFAULT_REMOTE_PORT;
oscServerThread = lo_server_thread_new_with_proto(port, LO_UDP, osc_error_handler);
DISTRHO_SAFE_ASSERT_RETURN(oscServerThread != nullptr,);
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
startRemoteServer(port);
#elif defined(HAVE_LIBLO)
if (isStandalone()) {
INFO("OSC Remote control is available on request");
} else {
INFO("OSC Remote control is not available on plugin variants");
}
#else
INFO("OSC Remote control is not enabled in this build");
#endif
#endif
}
Initializer::~Initializer()
{
using namespace rack;
#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 = nullptr;
}
#endif
#ifdef HAVE_LIBLO
stopRemoteServer();
#endif
if (shouldSaveSettings)
{
@ -670,6 +663,81 @@ void Initializer::loadSettings(const bool isRealInstance)
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

View file

@ -87,6 +87,7 @@ void openBrowser(const std::string& url);
# define CARDINAL_INIT_OSC_THREAD
#endif
typedef void* lo_server;
typedef void* lo_server_thread;
START_NAMESPACE_DISTRHO
@ -97,10 +98,6 @@ struct CardinalPluginContext;
struct Initializer
{
#ifdef CARDINAL_INIT_OSC_THREAD
lo_server_thread oscServerThread = nullptr;
CardinalBasePlugin* remotePluginInstance = nullptr;
#endif
std::string templatePath;
std::string factoryTemplatePath;
bool shouldSaveSettings = false;
@ -108,6 +105,18 @@ struct Initializer
Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui);
~Initializer();
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

View file

@ -167,7 +167,6 @@ struct ScopedContext {
}
};
// -----------------------------------------------------------------------------------------------------------
class CardinalPlugin : public CardinalBasePlugin
@ -333,7 +332,7 @@ public:
~CardinalPlugin() override
{
#ifdef CARDINAL_INIT_OSC_THREAD
#ifdef HAVE_LIBLO
if (fInitializer->remotePluginInstance == this)
fInitializer->remotePluginInstance = nullptr;
#endif
@ -359,6 +358,37 @@ public:
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:
/* --------------------------------------------------------------------------------------------------------
* Information */

View file

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

View file

@ -53,6 +53,7 @@
#include "../CardinalCommon.hpp"
#include "../CardinalRemote.hpp"
#include "../PluginContext.hpp"
#include "DistrhoPlugin.hpp"
#include "DistrhoStandaloneUtils.hpp"
@ -729,6 +730,10 @@ struct ViewButton : MenuButton {
struct EngineButton : MenuButton {
#ifdef HAVE_LIBLO
bool remoteServerStarted = false;
#endif
void onAction(const ActionEvent& e) override {
ui::Menu* menu = createMenu();
menu->cornerFlags = BND_CORNER_TOP;
@ -741,6 +746,34 @@ struct EngineButton : MenuButton {
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 (supportsAudioInput()) {
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
};