diff --git a/plugins/plugins.cpp b/plugins/plugins.cpp index e6e5db2..318c9f7 100644 --- a/plugins/plugins.cpp +++ b/plugins/plugins.cpp @@ -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 diff --git a/src/CardinalCommon.cpp b/src/CardinalCommon.cpp index c6efe8b..fb8e664 100644 --- a/src/CardinalCommon.cpp +++ b/src/CardinalCommon.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #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(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(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(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(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; diff --git a/src/CardinalMini/DistrhoPluginInfo.h b/src/CardinalMini/DistrhoPluginInfo.h index ac93536..f4faaa2 100644 --- a/src/CardinalMini/DistrhoPluginInfo.h +++ b/src/CardinalMini/DistrhoPluginInfo.h @@ -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 diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index 7d26c11..ed2f555 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -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) { diff --git a/src/CardinalRemote.cpp b/src/CardinalRemote.cpp index 6f451cf..f059690 100644 --- a/src/CardinalRemote.cpp +++ b/src/CardinalRemote.cpp @@ -35,6 +35,11 @@ # include #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(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(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 data(d_getChunkFromBase64String(screenshot)); diff --git a/src/CardinalRemote.hpp b/src/CardinalRemote.hpp index 6467641..e45c569 100644 --- a/src/CardinalRemote.hpp +++ b/src/CardinalRemote.hpp @@ -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); } diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index 0664a76..4485d39 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -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()); diff --git a/src/override/Engine.cpp b/src/override/Engine.cpp index 2eed5e0..c0b03a1 100644 --- a/src/override/Engine.cpp +++ b/src/override/Engine.cpp @@ -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 diff --git a/src/override/MenuBar.cpp b/src/override/MenuBar.cpp index ffb5fb4..d1ff0d0 100644 --- a/src/override/MenuBar.cpp +++ b/src/override/MenuBar.cpp @@ -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); diff --git a/src/override/Scene.cpp b/src/override/Scene.cpp index e3af71f..c2e34fa 100644 --- a/src/override/Scene.cpp +++ b/src/override/Scene.cpp @@ -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); } diff --git a/src/override/Window.cpp b/src/override/Window.cpp index 7c32b0c..2591bd7 100644 --- a/src/override/Window.cpp +++ b/src/override/Window.cpp @@ -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(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