Add direct parameter changes to remote control, for LV2 and OSC
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
91cac905cc
commit
d0eba9e1ae
11 changed files with 136 additions and 33 deletions
|
@ -1550,7 +1550,7 @@ static void initStatic__BogaudioModules()
|
|||
{
|
||||
// Make sure to use dark theme as default
|
||||
Skins& skins(Skins::skins());
|
||||
skins._default = "dark";
|
||||
skins._default = settings::darkMode ? "dark" : "light";
|
||||
#define modelADSR modelBogaudioADSR
|
||||
#define modelLFO modelBogaudioLFO
|
||||
#define modelNoise modelBogaudioNoise
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <system.hpp>
|
||||
#include <app/Browser.hpp>
|
||||
#include <app/Scene.hpp>
|
||||
#include <engine/Engine.hpp>
|
||||
#include <window/Window.hpp>
|
||||
|
||||
#ifndef DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
|
||||
|
@ -236,7 +237,7 @@ 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_stdout("osc_hello_handler()");
|
||||
d_debug("osc_hello_handler()");
|
||||
const lo_address source = lo_message_get_source(m);
|
||||
const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread);
|
||||
lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok");
|
||||
|
@ -245,7 +246,7 @@ static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_m
|
|||
|
||||
static int osc_load_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self)
|
||||
{
|
||||
d_stdout("osc_load_handler()");
|
||||
d_debug("osc_load_handler()");
|
||||
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);
|
||||
|
||||
|
@ -283,9 +284,35 @@ static int osc_load_handler(const char*, const char* types, lo_arg** argv, int a
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int osc_param_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self)
|
||||
{
|
||||
d_debug("osc_param_handler()");
|
||||
DISTRHO_SAFE_ASSERT_RETURN(argc == 3, 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr, 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types[0] == 'h', 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types[1] == 'i', 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types[2] == 'f', 0);
|
||||
|
||||
if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->remotePluginInstance)
|
||||
{
|
||||
CardinalPluginContext* const context = plugin->context;
|
||||
|
||||
const int64_t moduleId = argv[0]->h;
|
||||
const int paramId = argv[1]->i;
|
||||
const float paramValue = argv[2]->f;
|
||||
|
||||
rack::engine::Module* const module = context->engine->getModule(moduleId);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr, 0);
|
||||
|
||||
context->engine->setParamValue(module, paramId, paramValue);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, int argc, const lo_message m, void* const self)
|
||||
{
|
||||
d_stdout("osc_screenshot_handler()");
|
||||
d_debug("osc_screenshot_handler()");
|
||||
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);
|
||||
|
||||
|
@ -298,7 +325,13 @@ static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv,
|
|||
bool ok = false;
|
||||
|
||||
if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->remotePluginInstance)
|
||||
ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer());
|
||||
{
|
||||
if (char* const screenshot = String::asBase64(blob, size).getAndReleaseBuffer())
|
||||
{
|
||||
ok = plugin->updateStateValue("screenshot", screenshot);
|
||||
std::free(screenshot);
|
||||
}
|
||||
}
|
||||
|
||||
const lo_address source = lo_message_get_source(m);
|
||||
const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread);
|
||||
|
@ -441,13 +474,20 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB
|
|||
|
||||
#ifdef CARDINAL_INIT_OSC_THREAD
|
||||
INFO("Initializing OSC Remote control");
|
||||
oscServerThread = lo_server_thread_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler);
|
||||
const char* port;
|
||||
if (const char* const portEnv = std::getenv("CARDINAL_REMOTE_HOST_PORT"))
|
||||
port = portEnv;
|
||||
else
|
||||
port = CARDINAL_DEFAULT_REMOTE_HOST_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, "/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
|
||||
INFO("OSC Remote control is not enabled in this build");
|
||||
#endif
|
||||
|
@ -460,6 +500,7 @@ Initializer::~Initializer()
|
|||
#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;
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
#define DISTRHO_PLUGIN_VST3_CATEGORIES "Fx|Generator"
|
||||
|
||||
// #ifdef __MOD_DEVICES__
|
||||
// # define DISTRHO_PLUGIN_USES_MODGUI 1
|
||||
# define DISTRHO_PLUGIN_USES_MODGUI 1
|
||||
// #endif
|
||||
|
||||
#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED
|
||||
|
|
|
@ -794,6 +794,22 @@ protected:
|
|||
|
||||
void setState(const char* const key, const char* const value) override
|
||||
{
|
||||
#if CARDINAL_VARIANT_MINI
|
||||
if (std::strcmp(key, "param") == 0)
|
||||
{
|
||||
int64_t moduleId = 0;
|
||||
int paramId = 0;
|
||||
float paramValue = 0.f;
|
||||
std::sscanf(value, "%lu:%d:%f", &moduleId, ¶mId, ¶mValue);
|
||||
|
||||
rack::engine::Module* const module = context->engine->getModule(moduleId);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);
|
||||
|
||||
context->engine->setParamValue(module, paramId, paramValue);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HEADLESS
|
||||
if (std::strcmp(key, "moduleInfos") == 0)
|
||||
{
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
# include <lo/lo.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBLO
|
||||
// # define REMOTE_HOST "localhost"
|
||||
# define REMOTE_HOST "192.168.51.1"
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace remoteUtils {
|
||||
|
@ -98,7 +103,10 @@ bool connectToRemote()
|
|||
lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, remoteDetails);
|
||||
}
|
||||
|
||||
if (const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT))
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT);
|
||||
DISTRHO_SAFE_ASSERT(addr != nullptr);
|
||||
|
||||
if (addr != nullptr)
|
||||
{
|
||||
lo_send(addr, "/hello", "");
|
||||
lo_address_free(addr);
|
||||
|
@ -127,7 +135,23 @@ void idleRemote(RemoteDetails* const remote)
|
|||
#endif
|
||||
}
|
||||
|
||||
void deployToRemote(RemoteDetails* const remote)
|
||||
void sendParamChangeToRemote(RemoteDetails* const remote, int64_t moduleId, int paramId, float value)
|
||||
{
|
||||
#if CARDINAL_VARIANT_MINI
|
||||
char paramBuf[512] = {};
|
||||
std::snprintf(paramBuf, sizeof(paramBuf), "%lu:%d:%f", moduleId, paramId, value);
|
||||
static_cast<CardinalBaseUI*>(remote->handle)->setState("param", paramBuf);
|
||||
#elif defined(HAVE_LIBLO)
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
|
||||
|
||||
lo_send(addr, "/param", "hif", moduleId, paramId, value);
|
||||
|
||||
lo_address_free(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sendFullPatchToRemote(RemoteDetails* const remote)
|
||||
{
|
||||
CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,);
|
||||
|
@ -146,7 +170,7 @@ void deployToRemote(RemoteDetails* const remote)
|
|||
std::free(patch);
|
||||
}
|
||||
#elif defined(HAVE_LIBLO)
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
|
||||
|
||||
if (const lo_blob blob = lo_blob_new(data.size(), data.data()))
|
||||
|
@ -162,7 +186,7 @@ void deployToRemote(RemoteDetails* const remote)
|
|||
void sendScreenshotToRemote(RemoteDetails*, const char* const screenshot)
|
||||
{
|
||||
#ifdef HAVE_LIBLO
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT);
|
||||
const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
|
||||
|
||||
std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot));
|
||||
|
|
|
@ -17,11 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef HAVE_LIBLO
|
||||
// # define REMOTE_HOST "localhost"
|
||||
# define REMOTE_HOST "192.168.51.1"
|
||||
# define REMOTE_HOST_PORT "2228"
|
||||
#endif
|
||||
#define CARDINAL_DEFAULT_REMOTE_HOST_PORT "2228"
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -37,7 +33,8 @@ RemoteDetails* getRemote();
|
|||
bool connectToRemote();
|
||||
void disconnectFromRemote(RemoteDetails* remote);
|
||||
void idleRemote(RemoteDetails* remote);
|
||||
void deployToRemote(RemoteDetails* remote);
|
||||
void sendParamChangeToRemote(RemoteDetails* remote, int64_t moduleId, int paramId, float value);
|
||||
void sendFullPatchToRemote(RemoteDetails* remote);
|
||||
void sendScreenshotToRemote(RemoteDetails* remote, const char* screenshot);
|
||||
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
namespace rack {
|
||||
namespace engine {
|
||||
void Engine_setAboutToClose(Engine*);
|
||||
void Engine_setRemoteDetails(Engine*, remoteUtils::RemoteDetails*);
|
||||
}
|
||||
namespace window {
|
||||
void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui);
|
||||
|
@ -344,8 +345,7 @@ public:
|
|||
rack::contextSet(context);
|
||||
|
||||
#if CARDINAL_VARIANT_MINI
|
||||
DISTRHO_SAFE_ASSERT_RETURN(remoteUtils::connectToRemote(),);
|
||||
DISTRHO_SAFE_ASSERT_RETURN(remoteDetails != nullptr,);
|
||||
remoteUtils::connectToRemote();
|
||||
|
||||
// create unique temporary path for this instance
|
||||
try {
|
||||
|
@ -387,6 +387,8 @@ public:
|
|||
context->window = new rack::window::Window;
|
||||
|
||||
context->scene->rackScroll->reset();
|
||||
|
||||
Engine_setRemoteDetails(context->engine, remoteDetails);
|
||||
#endif
|
||||
|
||||
Window& window(getWindow());
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
# undef DEBUG
|
||||
#endif
|
||||
|
||||
#include "../CardinalRemote.hpp"
|
||||
#include "DistrhoUtils.hpp"
|
||||
|
||||
|
||||
|
@ -97,6 +98,9 @@ struct Engine::Internal {
|
|||
int smoothParamId = 0;
|
||||
float smoothValue = 0.f;
|
||||
|
||||
// Remote control
|
||||
remoteUtils::RemoteDetails* remoteDetails = nullptr;
|
||||
|
||||
/** Mutex that guards the Engine state, such as settings, Modules, and Cables.
|
||||
Writers lock when mutating the engine's state or stepping the block.
|
||||
Readers lock when using the engine's state.
|
||||
|
@ -210,12 +214,13 @@ static void Engine_stepFrame(Engine* that) {
|
|||
Param* smoothParam = &smoothModule->params[smoothParamId];
|
||||
float value = smoothParam->value;
|
||||
float newValue;
|
||||
if (internal->blockFrames != 1) {
|
||||
if (internal->remoteDetails != nullptr) {
|
||||
newValue = value;
|
||||
sendParamChangeToRemote(internal->remoteDetails, smoothModule->id, smoothParamId, value);
|
||||
} else {
|
||||
// Use decay rate of roughly 1 graphics frame
|
||||
const float smoothLambda = 60.f;
|
||||
newValue = value + (smoothValue - value) * smoothLambda * internal->sampleTime;
|
||||
} else {
|
||||
newValue = value;
|
||||
}
|
||||
if (d_isEqual(value, newValue)) {
|
||||
// Snap to actual smooth value if the value doesn't change enough (due to the granularity of floats)
|
||||
|
@ -1013,6 +1018,9 @@ void Engine::setParamValue(Module* module, int paramId, float value) {
|
|||
internal->smoothModule = NULL;
|
||||
internal->smoothParamId = 0;
|
||||
}
|
||||
if (internal->remoteDetails != nullptr) {
|
||||
sendParamChangeToRemote(internal->remoteDetails, module->id, paramId, value);
|
||||
}
|
||||
module->params[paramId].value = value;
|
||||
}
|
||||
|
||||
|
@ -1263,5 +1271,10 @@ void Engine_setAboutToClose(Engine* const engine) {
|
|||
}
|
||||
|
||||
|
||||
void Engine_setRemoteDetails(Engine* const engine, remoteUtils::RemoteDetails* const remoteDetails) {
|
||||
engine->internal->remoteDetails = remoteDetails;
|
||||
}
|
||||
|
||||
|
||||
} // namespace engine
|
||||
} // namespace rack
|
||||
|
|
|
@ -66,7 +66,9 @@ namespace rack {
|
|||
namespace asset {
|
||||
std::string patchesPath();
|
||||
}
|
||||
|
||||
namespace engine {
|
||||
void Engine_setRemoteDetails(Engine*, remoteUtils::RemoteDetails*);
|
||||
}
|
||||
namespace plugin {
|
||||
void updateStaticPluginsDarkMode();
|
||||
}
|
||||
|
@ -168,26 +170,29 @@ struct FileButton : MenuButton {
|
|||
patchUtils::revertDialog();
|
||||
}, APP->patch->path.empty()));
|
||||
|
||||
// #if defined(HAVE_LIBLO) && ! CARDINAL_VARIANT_MINI
|
||||
#if defined(HAVE_LIBLO) && ! CARDINAL_VARIANT_MINI
|
||||
menu->addChild(new ui::MenuSeparator);
|
||||
|
||||
remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote();
|
||||
|
||||
if (remoteDetails != nullptr && remoteDetails->connected) {
|
||||
menu->addChild(createMenuItem("Deploy to MOD", "F7", [remoteDetails]() {
|
||||
remoteUtils::deployToRemote(remoteDetails);
|
||||
remoteUtils::sendFullPatchToRemote(remoteDetails);
|
||||
}));
|
||||
|
||||
menu->addChild(createCheckMenuItem("Auto deploy to MOD", "",
|
||||
[remoteDetails]() {return remoteDetails->autoDeploy;},
|
||||
[remoteDetails]() {remoteDetails->autoDeploy = !remoteDetails->autoDeploy;}
|
||||
[remoteDetails]() {
|
||||
remoteDetails->autoDeploy = !remoteDetails->autoDeploy;
|
||||
Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr);
|
||||
}
|
||||
));
|
||||
} else {
|
||||
menu->addChild(createMenuItem("Connect to MOD", "", []() {
|
||||
remoteUtils::connectToRemote();
|
||||
DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote());
|
||||
}));
|
||||
}
|
||||
// #endif
|
||||
#endif
|
||||
|
||||
#ifndef DISTRHO_OS_WASM
|
||||
menu->addChild(new ui::MenuSeparator);
|
||||
|
|
|
@ -212,11 +212,15 @@ void Scene::step() {
|
|||
if (remoteDetails->autoDeploy) {
|
||||
const int actionIndex = APP->history->actionIndex;
|
||||
const double time = system::getTime();
|
||||
if (internal->historyActionIndex != actionIndex && time - internal->lastSceneChangeTime >= 1.0) {
|
||||
if (internal->historyActionIndex != actionIndex && actionIndex > 0 && time - internal->lastSceneChangeTime >= 1.0) {
|
||||
const std::string& name(APP->history->actions[actionIndex - 1]->name);
|
||||
if (/*std::abs(internal->historyActionIndex = actionIndex) > 1 ||*/ name != "move knob") {
|
||||
printf("action '%s'\n", APP->history->actions[actionIndex - 1]->name.c_str());
|
||||
remoteUtils::sendFullPatchToRemote(remoteDetails);
|
||||
window::generateScreenshot();
|
||||
}
|
||||
internal->historyActionIndex = actionIndex;
|
||||
internal->lastSceneChangeTime = time;
|
||||
remoteUtils::deployToRemote(remoteDetails);
|
||||
window::generateScreenshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +323,7 @@ void Scene::onHoverKey(const HoverKeyEvent& e) {
|
|||
}
|
||||
if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) {
|
||||
if (remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote())
|
||||
remoteUtils::deployToRemote(remoteDetails);
|
||||
remoteUtils::sendFullPatchToRemote(remoteDetails);
|
||||
window::generateScreenshot();
|
||||
e.consume(this);
|
||||
}
|
||||
|
|
|
@ -585,9 +585,10 @@ static void Window__downscaleBitmap(uint8_t* pixels, int& width, int& height) {
|
|||
static void Window__writeImagePNG(void* context, void* data, int size) {
|
||||
USE_NAMESPACE_DISTRHO
|
||||
CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context);
|
||||
if (const char* const screenshot = String::asBase64(data, size).buffer()) {
|
||||
if (char* const screenshot = String::asBase64(data, size).getAndReleaseBuffer()) {
|
||||
ui->setState("screenshot", screenshot);
|
||||
remoteUtils::sendScreenshotToRemote(ui->remoteDetails, screenshot);
|
||||
std::free(screenshot);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue