Signed-off-by: falkTX <falktx@falktx.com>tags/23.02
| @@ -0,0 +1 @@ | |||||
| ../CardinalRemote.cpp | |||||
| @@ -42,6 +42,14 @@ | |||||
| #include <app/Scene.hpp> | #include <app/Scene.hpp> | ||||
| #include <window/Window.hpp> | #include <window/Window.hpp> | ||||
| #ifndef DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| # error wrong build | |||||
| #endif | |||||
| #if defined(STATIC_BUILD) || CARDINAL_VARIANT_MINI | |||||
| # undef CARDINAL_INIT_OSC_THREAD | |||||
| #endif | |||||
| #ifdef NDEBUG | #ifdef NDEBUG | ||||
| # undef DEBUG | # undef DEBUG | ||||
| #endif | #endif | ||||
| @@ -54,12 +62,12 @@ | |||||
| # include <unistd.h> | # include <unistd.h> | ||||
| #endif | #endif | ||||
| #ifdef DISTRHO_OS_WASM | |||||
| # include <emscripten/emscripten.h> | |||||
| #ifdef CARDINAL_INIT_OSC_THREAD | |||||
| # include <lo/lo.h> | |||||
| #endif | #endif | ||||
| #ifndef DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| # error wrong build | |||||
| #ifdef DISTRHO_OS_WASM | |||||
| # include <emscripten/emscripten.h> | |||||
| #endif | #endif | ||||
| #if defined(CARDINAL_COMMON_DSP_ONLY) || defined(HEADLESS) | #if defined(CARDINAL_COMMON_DSP_ONLY) || defined(HEADLESS) | ||||
| @@ -230,7 +238,8 @@ static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_m | |||||
| { | { | ||||
| d_stdout("osc_hello_handler()"); | d_stdout("osc_hello_handler()"); | ||||
| const lo_address source = lo_message_get_source(m); | const lo_address source = lo_message_get_source(m); | ||||
| lo_send_from(source, static_cast<Initializer*>(self)->oscServer, LO_TT_IMMEDIATE, "/resp", "ss", "hello", "ok"); | |||||
| 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"); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -248,7 +257,7 @@ static int osc_load_handler(const char*, const char* types, lo_arg** argv, int a | |||||
| bool ok = false; | bool ok = false; | ||||
| if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin) | |||||
| if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->remotePluginInstance) | |||||
| { | { | ||||
| CardinalPluginContext* const context = plugin->context; | CardinalPluginContext* const context = plugin->context; | ||||
| std::vector<uint8_t> data(size); | std::vector<uint8_t> data(size); | ||||
| @@ -269,8 +278,8 @@ 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); | ||||
| lo_send_from(source, static_cast<Initializer*>(self)->oscServer, | |||||
| LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail"); | |||||
| const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread); | |||||
| lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "load", ok ? "ok" : "fail"); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| @@ -288,12 +297,12 @@ static int osc_screenshot_handler(const char*, const char* types, lo_arg** argv, | |||||
| bool ok = false; | bool ok = false; | ||||
| if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin) | |||||
| if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->remotePluginInstance) | |||||
| ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer()); | ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer()); | ||||
| const lo_address source = lo_message_get_source(m); | const lo_address source = lo_message_get_source(m); | ||||
| lo_send_from(source, static_cast<Initializer*>(self)->oscServer, | |||||
| LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail"); | |||||
| const lo_server server = lo_server_thread_get_server(static_cast<Initializer*>(self)->oscServerThread); | |||||
| lo_send_from(source, server, LO_TT_IMMEDIATE, "/resp", "ss", "screenshot", ok ? "ok" : "fail"); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -432,15 +441,13 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB | |||||
| #ifdef CARDINAL_INIT_OSC_THREAD | #ifdef CARDINAL_INIT_OSC_THREAD | ||||
| INFO("Initializing OSC Remote control"); | INFO("Initializing OSC Remote control"); | ||||
| oscServer = lo_server_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr,); | |||||
| lo_server_add_method(oscServer, "/hello", "", osc_hello_handler, this); | |||||
| lo_server_add_method(oscServer, "/load", "b", osc_load_handler, this); | |||||
| lo_server_add_method(oscServer, "/screenshot", "b", osc_screenshot_handler, this); | |||||
| lo_server_add_method(oscServer, nullptr, nullptr, osc_fallback_handler, nullptr); | |||||
| oscServerThread = lo_server_thread_new_with_proto(REMOTE_HOST_PORT, LO_UDP, osc_error_handler); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(oscServerThread != nullptr,); | |||||
| startThread(); | |||||
| 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, "/screenshot", "b", osc_screenshot_handler, this); | |||||
| lo_server_thread_add_method(oscServerThread, nullptr, nullptr, osc_fallback_handler, nullptr); | |||||
| #else | #else | ||||
| INFO("OSC Remote control is not enabled in this build"); | INFO("OSC Remote control is not enabled in this build"); | ||||
| #endif | #endif | ||||
| @@ -451,12 +458,11 @@ Initializer::~Initializer() | |||||
| using namespace rack; | using namespace rack; | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | #ifdef CARDINAL_INIT_OSC_THREAD | ||||
| if (oscServer != nullptr) | |||||
| if (oscServerThread != nullptr) | |||||
| { | { | ||||
| stopThread(5000); | |||||
| lo_server_del_method(oscServer, nullptr, nullptr); | |||||
| lo_server_free(oscServer); | |||||
| oscServer = nullptr; | |||||
| lo_server_thread_del_method(oscServerThread, nullptr, nullptr); | |||||
| lo_server_thread_free(oscServerThread); | |||||
| oscServerThread = nullptr; | |||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -478,21 +484,6 @@ Initializer::~Initializer() | |||||
| logger::destroy(); | logger::destroy(); | ||||
| } | } | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | |||||
| void Initializer::run() | |||||
| { | |||||
| INFO("OSC Thread Listening for remote commands"); | |||||
| while (! shouldThreadExit()) | |||||
| { | |||||
| d_msleep(200); | |||||
| while (lo_server_recv_noblock(oscServer, 0) != 0) {} | |||||
| } | |||||
| INFO("OSC Thread Closed"); | |||||
| } | |||||
| #endif | |||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
| @@ -21,13 +21,6 @@ | |||||
| #include <string> | #include <string> | ||||
| #ifdef HAVE_LIBLO | |||||
| // # define REMOTE_HOST "localhost" | |||||
| # define REMOTE_HOST "192.168.51.1" | |||||
| # define REMOTE_HOST_PORT "2228" | |||||
| # include "extra/Thread.hpp" | |||||
| #endif | |||||
| #ifdef DISTRHO_OS_WASM | #ifdef DISTRHO_OS_WASM | ||||
| # ifdef STATIC_BUILD | # ifdef STATIC_BUILD | ||||
| # define CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME "welcome-wasm-mini.vcv" | # define CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME "welcome-wasm-mini.vcv" | ||||
| @@ -85,21 +78,17 @@ void saveAsDialogUncompressed(); | |||||
| void appendSelectionContextMenu(rack::ui::Menu* menu); | void appendSelectionContextMenu(rack::ui::Menu* menu); | ||||
| void openBrowser(const std::string& url); | void openBrowser(const std::string& url); | ||||
| bool connectToRemote(); | |||||
| bool isRemoteConnected(); | |||||
| bool isRemoteAutoDeployed(); | |||||
| void setRemoteAutoDeploy(bool autoDeploy); | |||||
| void deployToRemote(); | |||||
| void sendScreenshotToRemote(const char* screenshot); | |||||
| } // namespace patchUtils | } // namespace patchUtils | ||||
| // ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
| #if defined(HAVE_LIBLO) && defined(HEADLESS) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| // && defined(HEADLESS) | |||||
| #if defined(HAVE_LIBLO) | |||||
| # define CARDINAL_INIT_OSC_THREAD | # define CARDINAL_INIT_OSC_THREAD | ||||
| #endif | #endif | ||||
| typedef void* lo_server_thread; | |||||
| START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
| class CardinalBasePlugin; | class CardinalBasePlugin; | ||||
| @@ -107,22 +96,16 @@ class CardinalBaseUI; | |||||
| struct CardinalPluginContext; | struct CardinalPluginContext; | ||||
| struct Initializer | struct Initializer | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | |||||
| : public Thread | |||||
| #endif | |||||
| { | { | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | #ifdef CARDINAL_INIT_OSC_THREAD | ||||
| lo_server oscServer = nullptr; | |||||
| CardinalBasePlugin* oscPlugin = nullptr; | |||||
| lo_server_thread oscServerThread = nullptr; | |||||
| CardinalBasePlugin* remotePluginInstance = nullptr; | |||||
| #endif | #endif | ||||
| std::string templatePath; | std::string templatePath; | ||||
| std::string factoryTemplatePath; | std::string factoryTemplatePath; | ||||
| Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui); | Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui); | ||||
| ~Initializer(); | ~Initializer(); | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | |||||
| void run() override; | |||||
| #endif | |||||
| }; | }; | ||||
| #ifndef HEADLESS | #ifndef HEADLESS | ||||
| @@ -0,0 +1 @@ | |||||
| ../CardinalRemote.cpp | |||||
| @@ -0,0 +1 @@ | |||||
| ../CardinalRemote.cpp | |||||
| @@ -0,0 +1 @@ | |||||
| ../CardinalRemote.cpp | |||||
| @@ -274,14 +274,15 @@ public: | |||||
| } | } | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | #ifdef CARDINAL_INIT_OSC_THREAD | ||||
| fInitializer->oscPlugin = this; | |||||
| fInitializer->remotePluginInstance = this; | |||||
| #endif | #endif | ||||
| } | } | ||||
| ~CardinalPlugin() override | ~CardinalPlugin() override | ||||
| { | { | ||||
| #ifdef CARDINAL_INIT_OSC_THREAD | #ifdef CARDINAL_INIT_OSC_THREAD | ||||
| fInitializer->oscPlugin = nullptr; | |||||
| if (fInitializer->remotePluginInstance == this) | |||||
| fInitializer->remotePluginInstance = nullptr; | |||||
| #endif | #endif | ||||
| { | { | ||||
| @@ -844,9 +845,6 @@ protected: | |||||
| if (std::strcmp(key, "screenshot") == 0) | if (std::strcmp(key, "screenshot") == 0) | ||||
| { | { | ||||
| fState.screenshot = value; | fState.screenshot = value; | ||||
| #if defined(HAVE_LIBLO) && !defined(HEADLESS) | |||||
| patchUtils::sendScreenshotToRemote(value); | |||||
| #endif | |||||
| return; | return; | ||||
| } | } | ||||
| @@ -0,0 +1,162 @@ | |||||
| /* | |||||
| * DISTRHO Cardinal Plugin | |||||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License as | |||||
| * published by the Free Software Foundation; either version 3 of | |||||
| * the License, or any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| */ | |||||
| #include <engine/Engine.hpp> | |||||
| #include <patch.hpp> | |||||
| #include <system.hpp> | |||||
| #ifdef NDEBUG | |||||
| # undef DEBUG | |||||
| #endif | |||||
| #include "CardinalRemote.hpp" | |||||
| #include "PluginContext.hpp" | |||||
| #include "extra/Base64.hpp" | |||||
| #if defined(STATIC_BUILD) || CARDINAL_VARIANT_MINI | |||||
| # undef HAVE_LIBLO | |||||
| #endif | |||||
| #ifdef HAVE_LIBLO | |||||
| # include <lo/lo.h> | |||||
| #endif | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| namespace remoteUtils { | |||||
| #ifdef HAVE_LIBLO | |||||
| static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self) | |||||
| { | |||||
| d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc); | |||||
| if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') | |||||
| { | |||||
| d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s); | |||||
| if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0) | |||||
| static_cast<RemoteDetails*>(self)->connected = true; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| #endif | |||||
| RemoteDetails* getRemote() | |||||
| { | |||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr); | |||||
| CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, nullptr); | |||||
| return ui->remoteDetails; | |||||
| } | |||||
| bool connectToRemote() | |||||
| { | |||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, false); | |||||
| CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, false); | |||||
| RemoteDetails* remoteDetails = ui->remoteDetails; | |||||
| #ifdef HAVE_LIBLO | |||||
| if (remoteDetails == nullptr) | |||||
| { | |||||
| const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr, false); | |||||
| remoteDetails = new RemoteDetails; | |||||
| remoteDetails->handle = oscServer; | |||||
| remoteDetails->connected = false; | |||||
| remoteDetails->autoDeploy = false; | |||||
| 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)) | |||||
| { | |||||
| lo_send(addr, "/hello", ""); | |||||
| lo_address_free(addr); | |||||
| } | |||||
| #endif | |||||
| return remoteDetails != nullptr; | |||||
| } | |||||
| void disconnectFromRemote(RemoteDetails* const remote) | |||||
| { | |||||
| if (remote != nullptr) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| lo_server_free(static_cast<lo_server>(remote->handle)); | |||||
| delete remote; | |||||
| #endif | |||||
| } | |||||
| } | |||||
| void idleRemote(RemoteDetails* const remote) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| while (lo_server_recv_noblock(static_cast<lo_server>(remote->handle), 0) != 0) {} | |||||
| #endif | |||||
| } | |||||
| void deployToRemote(RemoteDetails* const remote) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | |||||
| APP->engine->prepareSave(); | |||||
| APP->patch->saveAutosave(); | |||||
| APP->patch->cleanAutosave(); | |||||
| std::vector<uint8_t> data(rack::system::archiveDirectory(APP->patch->autosavePath, 1)); | |||||
| if (const lo_blob blob = lo_blob_new(data.size(), data.data())) | |||||
| { | |||||
| lo_send(addr, "/load", "b", blob); | |||||
| lo_blob_free(blob); | |||||
| } | |||||
| lo_address_free(addr); | |||||
| #endif | |||||
| } | |||||
| void sendScreenshotToRemote(RemoteDetails* const remote, const char* const screenshot) | |||||
| { | |||||
| #ifdef HAVE_LIBLO | |||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | |||||
| std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot)); | |||||
| if (const lo_blob blob = lo_blob_new(data.size(), data.data())) | |||||
| { | |||||
| lo_send(addr, "/screenshot", "b", blob); | |||||
| lo_blob_free(blob); | |||||
| } | |||||
| lo_address_free(addr); | |||||
| #endif | |||||
| } | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,45 @@ | |||||
| /* | |||||
| * DISTRHO Cardinal Plugin | |||||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License as | |||||
| * published by the Free Software Foundation; either version 3 of | |||||
| * the License, or any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * For a full copy of the GNU General Public License see the LICENSE file. | |||||
| */ | |||||
| #pragma once | |||||
| #ifdef HAVE_LIBLO | |||||
| // # define REMOTE_HOST "localhost" | |||||
| # define REMOTE_HOST "192.168.51.1" | |||||
| # define REMOTE_HOST_PORT "2228" | |||||
| #endif | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| namespace remoteUtils { | |||||
| struct RemoteDetails { | |||||
| void* handle; | |||||
| bool connected; | |||||
| bool autoDeploy; | |||||
| }; | |||||
| RemoteDetails* getRemote(); | |||||
| bool connectToRemote(); | |||||
| void disconnectFromRemote(RemoteDetails* remote); | |||||
| void idleRemote(RemoteDetails* remote); | |||||
| void deployToRemote(RemoteDetails* remote); | |||||
| void sendScreenshotToRemote(RemoteDetails* remote, const char* screenshot); | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1 @@ | |||||
| ../CardinalRemote.cpp | |||||
| @@ -96,6 +96,7 @@ endif | |||||
| FILES_DSP = CardinalPlugin.cpp | FILES_DSP = CardinalPlugin.cpp | ||||
| FILES_DSP += CardinalCommon.cpp | FILES_DSP += CardinalCommon.cpp | ||||
| FILES_DSP += CardinalRemote.cpp | |||||
| FILES_DSP += common.cpp | FILES_DSP += common.cpp | ||||
| ifeq ($(CARDINAL_VARIANT),mini) | ifeq ($(CARDINAL_VARIANT),mini) | ||||
| @@ -251,6 +252,7 @@ ifeq ($(CARDINAL_VARIANT),mini) | |||||
| ifneq ($(HEADLESS),true) | ifneq ($(HEADLESS),true) | ||||
| FILES_UI = CardinalUI.cpp | FILES_UI = CardinalUI.cpp | ||||
| FILES_UI += CardinalCommon-UI.cpp | FILES_UI += CardinalCommon-UI.cpp | ||||
| FILES_UI += CardinalRemote.cpp | |||||
| FILES_UI += common.cpp | FILES_UI += common.cpp | ||||
| FILES_UI += glfw.cpp | FILES_UI += glfw.cpp | ||||
| FILES_UI += Window.cpp | FILES_UI += Window.cpp | ||||
| @@ -25,8 +25,8 @@ | |||||
| # undef DEBUG | # undef DEBUG | ||||
| #endif | #endif | ||||
| #include "CardinalRemote.hpp" | |||||
| #include "DistrhoPlugin.hpp" | #include "DistrhoPlugin.hpp" | ||||
| #include "extra/Mutex.hpp" | |||||
| #ifndef HEADLESS | #ifndef HEADLESS | ||||
| # include "DistrhoUI.hpp" | # include "DistrhoUI.hpp" | ||||
| @@ -148,6 +148,7 @@ struct WasmRemotePatchLoadingDialog; | |||||
| class CardinalBaseUI : public UI { | class CardinalBaseUI : public UI { | ||||
| public: | public: | ||||
| CardinalPluginContext* const context; | CardinalPluginContext* const context; | ||||
| remoteUtils::RemoteDetails* remoteDetails; | |||||
| bool saving; | bool saving; | ||||
| bool savingUncompressed; | bool savingUncompressed; | ||||
| @@ -166,6 +167,7 @@ public: | |||||
| #else | #else | ||||
| context(new CardinalPluginContext(nullptr)), | context(new CardinalPluginContext(nullptr)), | ||||
| #endif | #endif | ||||
| remoteDetails(nullptr), | |||||
| saving(false), | saving(false), | ||||
| savingUncompressed(false), | savingUncompressed(false), | ||||
| #ifdef DISTRHO_OS_WASM | #ifdef DISTRHO_OS_WASM | ||||
| @@ -180,6 +182,8 @@ public: | |||||
| ~CardinalBaseUI() override | ~CardinalBaseUI() override | ||||
| { | { | ||||
| disconnectFromRemote(remoteDetails); | |||||
| if (filebrowserhandle != nullptr) | if (filebrowserhandle != nullptr) | ||||
| fileBrowserClose(filebrowserhandle); | fileBrowserClose(filebrowserhandle); | ||||
| @@ -52,6 +52,7 @@ | |||||
| #include <library.hpp> | #include <library.hpp> | ||||
| #include "../CardinalCommon.hpp" | #include "../CardinalCommon.hpp" | ||||
| #include "../CardinalRemote.hpp" | |||||
| #include "DistrhoStandaloneUtils.hpp" | #include "DistrhoStandaloneUtils.hpp" | ||||
| #ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
| @@ -169,19 +170,20 @@ struct FileButton : MenuButton { | |||||
| #ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
| menu->addChild(new ui::MenuSeparator); | menu->addChild(new ui::MenuSeparator); | ||||
| if (patchUtils::isRemoteConnected()) { | |||||
| menu->addChild(createMenuItem("Deploy to MOD", "F7", []() { | |||||
| patchUtils::deployToRemote(); | |||||
| remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote(); | |||||
| if (remoteDetails != nullptr && remoteDetails->connected) { | |||||
| menu->addChild(createMenuItem("Deploy to MOD", "F7", [remoteDetails]() { | |||||
| remoteUtils::deployToRemote(remoteDetails); | |||||
| })); | })); | ||||
| const bool autoDeploy = patchUtils::isRemoteAutoDeployed(); | |||||
| menu->addChild(createCheckMenuItem("Auto deploy to MOD", "", | menu->addChild(createCheckMenuItem("Auto deploy to MOD", "", | ||||
| [=]() {return autoDeploy;}, | |||||
| [=]() {patchUtils::setRemoteAutoDeploy(!autoDeploy);} | |||||
| [remoteDetails]() {return remoteDetails->autoDeploy;}, | |||||
| [remoteDetails]() {remoteDetails->autoDeploy = !remoteDetails->autoDeploy;} | |||||
| )); | )); | ||||
| } else { | } else { | ||||
| menu->addChild(createMenuItem("Connect to MOD", "", []() { | menu->addChild(createMenuItem("Connect to MOD", "", []() { | ||||
| patchUtils::connectToRemote(); | |||||
| remoteUtils::connectToRemote(); | |||||
| })); | })); | ||||
| } | } | ||||
| #endif | #endif | ||||
| @@ -25,10 +25,6 @@ | |||||
| * the License, or (at your option) any later version. | * the License, or (at your option) any later version. | ||||
| */ | */ | ||||
| #include <thread> | |||||
| #include <osdialog.h> | |||||
| #include <app/Scene.hpp> | #include <app/Scene.hpp> | ||||
| #include <app/Browser.hpp> | #include <app/Browser.hpp> | ||||
| #include <app/TipWindow.hpp> | #include <app/TipWindow.hpp> | ||||
| @@ -46,17 +42,8 @@ | |||||
| # undef DEBUG | # undef DEBUG | ||||
| #endif | #endif | ||||
| #ifdef STATIC_BUILD | |||||
| # undef HAVE_LIBLO | |||||
| #endif | |||||
| #ifdef HAVE_LIBLO | |||||
| # include <lo/lo.h> | |||||
| #endif | |||||
| #include "../CardinalCommon.hpp" | #include "../CardinalCommon.hpp" | ||||
| #include "extra/Base64.hpp" | |||||
| #include "DistrhoUtils.hpp" | |||||
| #include "../CardinalRemote.hpp" | |||||
| namespace rack { | namespace rack { | ||||
| @@ -131,30 +118,8 @@ struct Scene::Internal { | |||||
| bool heldArrowKeys[4] = {}; | bool heldArrowKeys[4] = {}; | ||||
| #ifdef HAVE_LIBLO | |||||
| double lastSceneChangeTime = 0.0; | double lastSceneChangeTime = 0.0; | ||||
| int historyActionIndex = -1; | int historyActionIndex = -1; | ||||
| bool oscAutoDeploy = false; | |||||
| bool oscConnected = false; | |||||
| lo_server oscServer = nullptr; | |||||
| static int osc_handler(const char* const path, const char* const types, lo_arg** argv, const int argc, lo_message, void* const self) | |||||
| { | |||||
| d_stdout("osc_handler(\"%s\", \"%s\", %p, %i)", path, types, argv, argc); | |||||
| if (std::strcmp(path, "/resp") == 0 && argc == 2 && types[0] == 's' && types[1] == 's') { | |||||
| d_stdout("osc_handler(\"%s\", ...) - got resp | '%s' '%s'", path, &argv[0]->s, &argv[1]->s); | |||||
| if (std::strcmp(&argv[0]->s, "hello") == 0 && std::strcmp(&argv[1]->s, "ok") == 0) | |||||
| static_cast<Internal*>(self)->oscConnected = true; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| ~Internal() { | |||||
| lo_server_free(oscServer); | |||||
| } | |||||
| #endif | |||||
| }; | }; | ||||
| @@ -238,22 +203,20 @@ void Scene::step() { | |||||
| rackScroll->offset += arrowDelta * arrowSpeed; | rackScroll->offset += arrowDelta * arrowSpeed; | ||||
| } | } | ||||
| #ifdef HAVE_LIBLO | |||||
| if (internal->oscServer != nullptr) { | |||||
| while (lo_server_recv_noblock(internal->oscServer, 0) != 0) {} | |||||
| if (remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote()) { | |||||
| idleRemote(remoteDetails); | |||||
| if (internal->oscAutoDeploy) { | |||||
| if (remoteDetails->autoDeploy) { | |||||
| const int actionIndex = APP->history->actionIndex; | const int actionIndex = APP->history->actionIndex; | ||||
| const double time = system::getTime(); | const double time = system::getTime(); | ||||
| if (internal->historyActionIndex != actionIndex && time - internal->lastSceneChangeTime >= 5.0) { | if (internal->historyActionIndex != actionIndex && time - internal->lastSceneChangeTime >= 5.0) { | ||||
| internal->historyActionIndex = actionIndex; | internal->historyActionIndex = actionIndex; | ||||
| internal->lastSceneChangeTime = time; | internal->lastSceneChangeTime = time; | ||||
| patchUtils::deployToRemote(); | |||||
| remoteUtils::deployToRemote(remoteDetails); | |||||
| window::generateScreenshot(); | window::generateScreenshot(); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| Widget::step(); | Widget::step(); | ||||
| } | } | ||||
| @@ -352,7 +315,7 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) { | if (e.key == GLFW_KEY_F7 && (e.mods & RACK_MOD_MASK) == 0) { | ||||
| patchUtils::deployToRemote(); | |||||
| remoteUtils::deployToRemote(remoteUtils::getRemote()); | |||||
| window::generateScreenshot(); | window::generateScreenshot(); | ||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| @@ -489,94 +452,3 @@ void Scene::onPathDrop(const PathDropEvent& e) { | |||||
| } // namespace app | } // namespace app | ||||
| } // namespace rack | } // namespace rack | ||||
| namespace patchUtils { | |||||
| bool connectToRemote() { | |||||
| #ifdef HAVE_LIBLO | |||||
| rack::app::Scene::Internal* const internal = APP->scene->internal; | |||||
| if (internal->oscServer == nullptr) { | |||||
| const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(oscServer != nullptr, false); | |||||
| lo_server_add_method(oscServer, "/resp", nullptr, rack::app::Scene::Internal::osc_handler, internal); | |||||
| internal->oscServer = oscServer; | |||||
| } | |||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr, false); | |||||
| lo_send(addr, "/hello", ""); | |||||
| lo_address_free(addr); | |||||
| return true; | |||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| bool isRemoteConnected() { | |||||
| #ifdef HAVE_LIBLO | |||||
| return APP->scene->internal->oscConnected; | |||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| bool isRemoteAutoDeployed() { | |||||
| #ifdef HAVE_LIBLO | |||||
| return APP->scene->internal->oscAutoDeploy; | |||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| void setRemoteAutoDeploy(bool autoDeploy) { | |||||
| #ifdef HAVE_LIBLO | |||||
| APP->scene->internal->oscAutoDeploy = autoDeploy; | |||||
| #endif | |||||
| } | |||||
| void deployToRemote() { | |||||
| #ifdef HAVE_LIBLO | |||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | |||||
| APP->engine->prepareSave(); | |||||
| APP->patch->saveAutosave(); | |||||
| APP->patch->cleanAutosave(); | |||||
| std::vector<uint8_t> data(rack::system::archiveDirectory(APP->patch->autosavePath, 1)); | |||||
| if (const lo_blob blob = lo_blob_new(data.size(), data.data())) { | |||||
| lo_send(addr, "/load", "b", blob); | |||||
| lo_blob_free(blob); | |||||
| } | |||||
| lo_address_free(addr); | |||||
| #endif | |||||
| } | |||||
| void sendScreenshotToRemote(const char* const screenshot) { | |||||
| #ifdef HAVE_LIBLO | |||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, REMOTE_HOST_PORT); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | |||||
| std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot)); | |||||
| if (const lo_blob blob = lo_blob_new(data.size(), data.data())) { | |||||
| lo_send(addr, "/screenshot", "b", blob); | |||||
| lo_blob_free(blob); | |||||
| } | |||||
| lo_address_free(addr); | |||||
| #endif | |||||
| } | |||||
| } // namespace patchUtils | |||||
| @@ -584,8 +584,11 @@ static void Window__downscaleBitmap(uint8_t* pixels, int& width, int& height) { | |||||
| static void Window__writeImagePNG(void* context, void* data, int size) { | static void Window__writeImagePNG(void* context, void* data, int size) { | ||||
| USE_NAMESPACE_DISTRHO | USE_NAMESPACE_DISTRHO | ||||
| UI* const ui = static_cast<UI*>(context); | |||||
| ui->setState("screenshot", String::asBase64(data, size).buffer()); | |||||
| CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context); | |||||
| if (const char* const screenshot = String::asBase64(data, size).buffer()) { | |||||
| ui->setState("screenshot", screenshot); | |||||
| remoteUtils::sendScreenshotToRemote(ui->remoteDetails, screenshot); | |||||
| } | |||||
| } | } | ||||
| #endif | #endif | ||||
| #endif | #endif | ||||