Signed-off-by: falkTX <falktx@falktx.com>tags/23.07
| @@ -38,8 +38,6 @@ | |||||
| #ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
| # include <lo/lo.h> | # include <lo/lo.h> | ||||
| // # define REMOTE_HOST "localhost" | |||||
| # define REMOTE_HOST "192.168.51.1" | |||||
| #endif | #endif | ||||
| // ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
| @@ -77,7 +75,7 @@ RemoteDetails* getRemote() | |||||
| #endif | #endif | ||||
| } | } | ||||
| bool connectToRemote() | |||||
| bool connectToRemote(const char* const url) | |||||
| { | { | ||||
| #ifdef CARDINAL_REMOTE_ENABLED | #ifdef CARDINAL_REMOTE_ENABLED | ||||
| CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP); | ||||
| @@ -93,10 +91,14 @@ bool connectToRemote() | |||||
| { | { | ||||
| ui->remoteDetails = remoteDetails = new RemoteDetails; | ui->remoteDetails = remoteDetails = new RemoteDetails; | ||||
| remoteDetails->handle = ui; | remoteDetails->handle = ui; | ||||
| remoteDetails->url = strdup(url); | |||||
| remoteDetails->connected = true; | remoteDetails->connected = true; | ||||
| remoteDetails->autoDeploy = true; | remoteDetails->autoDeploy = true; | ||||
| } | } | ||||
| #elif defined(HAVE_LIBLO) | #elif defined(HAVE_LIBLO) | ||||
| const lo_address addr = lo_address_new_from_url(url); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr, false); | |||||
| if (remoteDetails == nullptr) | if (remoteDetails == nullptr) | ||||
| { | { | ||||
| const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr); | const lo_server oscServer = lo_server_new_with_proto(nullptr, LO_UDP, nullptr); | ||||
| @@ -104,20 +106,21 @@ bool connectToRemote() | |||||
| ui->remoteDetails = remoteDetails = new RemoteDetails; | ui->remoteDetails = remoteDetails = new RemoteDetails; | ||||
| remoteDetails->handle = oscServer; | remoteDetails->handle = oscServer; | ||||
| remoteDetails->url = strdup(url); | |||||
| remoteDetails->connected = false; | remoteDetails->connected = false; | ||||
| remoteDetails->autoDeploy = false; | remoteDetails->autoDeploy = false; | ||||
| lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, remoteDetails); | lo_server_add_method(oscServer, "/resp", nullptr, osc_handler, remoteDetails); | ||||
| } | } | ||||
| 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) | |||||
| else if (std::strcmp(remoteDetails->url, url) != 0) | |||||
| { | { | ||||
| lo_send(addr, "/hello", ""); | |||||
| lo_address_free(addr); | |||||
| ui->remoteDetails = nullptr; | |||||
| disconnectFromRemote(remoteDetails); | |||||
| return connectToRemote(url); | |||||
| } | } | ||||
| lo_send(addr, "/hello", ""); | |||||
| lo_address_free(addr); | |||||
| #endif | #endif | ||||
| return remoteDetails != nullptr; | return remoteDetails != nullptr; | ||||
| @@ -133,6 +136,7 @@ void disconnectFromRemote(RemoteDetails* const remote) | |||||
| #ifdef HAVE_LIBLO | #ifdef HAVE_LIBLO | ||||
| lo_server_free(static_cast<lo_server>(remote->handle)); | lo_server_free(static_cast<lo_server>(remote->handle)); | ||||
| #endif | #endif | ||||
| std::free(const_cast<char*>(remote->url)); | |||||
| delete remote; | delete remote; | ||||
| } | } | ||||
| } | } | ||||
| @@ -156,7 +160,7 @@ void sendParamChangeToRemote(RemoteDetails* const remote, int64_t moduleId, int | |||||
| } | } | ||||
| static_cast<CardinalBaseUI*>(remote->handle)->setState("param", paramBuf); | static_cast<CardinalBaseUI*>(remote->handle)->setState("param", paramBuf); | ||||
| #elif defined(HAVE_LIBLO) | #elif defined(HAVE_LIBLO) | ||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT); | |||||
| const lo_address addr = lo_address_new_from_url(remote->url); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | ||||
| lo_send(addr, "/param", "hif", moduleId, paramId, value); | lo_send(addr, "/param", "hif", moduleId, paramId, value); | ||||
| @@ -205,7 +209,7 @@ void sendFullPatchToRemote(RemoteDetails* const remote) | |||||
| DISTRHO_SAFE_ASSERT_RETURN(data.size() >= 4,); | DISTRHO_SAFE_ASSERT_RETURN(data.size() >= 4,); | ||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT); | |||||
| const lo_address addr = lo_address_new_from_url(remote->url); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | ||||
| if (const lo_blob blob = lo_blob_new(data.size(), data.data())) | if (const lo_blob blob = lo_blob_new(data.size(), data.data())) | ||||
| @@ -219,10 +223,10 @@ void sendFullPatchToRemote(RemoteDetails* const remote) | |||||
| #endif | #endif | ||||
| } | } | ||||
| void sendScreenshotToRemote(RemoteDetails*, const char* const screenshot) | |||||
| void sendScreenshotToRemote(RemoteDetails* const remote, const char* const screenshot) | |||||
| { | { | ||||
| #if defined(HAVE_LIBLO) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | #if defined(HAVE_LIBLO) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | ||||
| const lo_address addr = lo_address_new_with_proto(LO_UDP, REMOTE_HOST, CARDINAL_DEFAULT_REMOTE_HOST_PORT); | |||||
| const lo_address addr = lo_address_new_from_url(remote->url); | |||||
| DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,); | ||||
| std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot)); | std::vector<uint8_t> data(d_getChunkFromBase64String(screenshot)); | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * DISTRHO Cardinal Plugin | * DISTRHO Cardinal Plugin | ||||
| * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU General Public License as | * modify it under the terms of the GNU General Public License as | ||||
| @@ -17,7 +17,8 @@ | |||||
| #pragma once | #pragma once | ||||
| #define CARDINAL_DEFAULT_REMOTE_HOST_PORT "2228" | |||||
| // #define CARDINAL_DEFAULT_REMOTE_URL "osc.udp://localhost:2228" | |||||
| #define CARDINAL_DEFAULT_REMOTE_URL "osc.udp://192.168.51.1:2228" | |||||
| // ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
| @@ -25,12 +26,13 @@ namespace remoteUtils { | |||||
| struct RemoteDetails { | struct RemoteDetails { | ||||
| void* handle; | void* handle; | ||||
| const char* url; | |||||
| bool connected; | bool connected; | ||||
| bool autoDeploy; | bool autoDeploy; | ||||
| }; | }; | ||||
| RemoteDetails* getRemote(); | RemoteDetails* getRemote(); | ||||
| bool connectToRemote(); | |||||
| bool connectToRemote(const char* url); | |||||
| void disconnectFromRemote(RemoteDetails* remote); | void disconnectFromRemote(RemoteDetails* remote); | ||||
| void idleRemote(RemoteDetails* remote); | void idleRemote(RemoteDetails* remote); | ||||
| void sendParamChangeToRemote(RemoteDetails* remote, int64_t moduleId, int paramId, float value); | void sendParamChangeToRemote(RemoteDetails* remote, int64_t moduleId, int paramId, float value); | ||||
| @@ -406,7 +406,7 @@ public: | |||||
| context->patch->loadTemplate(); | context->patch->loadTemplate(); | ||||
| context->scene->rackScroll->reset(); | context->scene->rackScroll->reset(); | ||||
| DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote()); | |||||
| DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote(CARDINAL_DEFAULT_REMOTE_URL)); | |||||
| Engine_setRemoteDetails(context->engine, remoteDetails); | Engine_setRemoteDetails(context->engine, remoteDetails); | ||||
| #endif | #endif | ||||
| @@ -115,10 +115,15 @@ struct FileButton : MenuButton { | |||||
| async_dialog_text_input("Filename", nullptr, [](char* const filename) { | async_dialog_text_input("Filename", nullptr, [](char* const filename) { | ||||
| if (filename == nullptr) | if (filename == nullptr) | ||||
| return; | return; | ||||
| APP->patch->path = "/userfiles/"; | |||||
| APP->patch->path = asset::user("patches"); | |||||
| system::createDirectories(APP->patch->path); | |||||
| APP->patch->path += DISTRHO_OS_SEP_STR; | |||||
| APP->patch->path += filename; | APP->patch->path += filename; | ||||
| if (rack::system::getExtension(filename) != ".vcv") | if (rack::system::getExtension(filename) != ".vcv") | ||||
| APP->patch->path += ".vcv"; | APP->patch->path += ".vcv"; | ||||
| patchUtils::saveDialog(APP->patch->path); | patchUtils::saveDialog(APP->patch->path); | ||||
| std::free(filename); | std::free(filename); | ||||
| }); | }); | ||||
| @@ -161,11 +166,28 @@ struct FileButton : MenuButton { | |||||
| menu->addChild(createMenuItem("New (factory template)", "", []() { | menu->addChild(createMenuItem("New (factory template)", "", []() { | ||||
| patchUtils::loadTemplateDialog(true); | patchUtils::loadTemplateDialog(true); | ||||
| })); | })); | ||||
| #endif | |||||
| menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() { | |||||
| #ifndef DISTRHO_OS_WASM | |||||
| constexpr const char* const OpenName = "Open..."; | |||||
| #else | |||||
| constexpr const char* const OpenName = "Import patch..."; | |||||
| #endif | |||||
| menu->addChild(createMenuItem(OpenName, RACK_MOD_CTRL_NAME "+O", []() { | |||||
| patchUtils::loadDialog(); | patchUtils::loadDialog(); | ||||
| })); | })); | ||||
| const std::string patchesDir = asset::user("patches"); | |||||
| const std::vector<std::string> patches = system::isDirectory(patchesDir) ? system::getEntries(patchesDir) : std::vector<std::string>(); | |||||
| menu->addChild(createSubmenuItem("Open local patch", "", [patches](ui::Menu* menu) { | |||||
| for (const std::string& path : patches) { | |||||
| std::string name = system::getStem(path); | |||||
| menu->addChild(createMenuItem(name, "", [=]() { | |||||
| patchUtils::loadPathDialog(path, false); | |||||
| })); | |||||
| } | |||||
| }, patches.empty())); | |||||
| menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) { | menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) { | ||||
| for (const std::string& path : settings::recentPatchPaths) { | for (const std::string& path : settings::recentPatchPaths) { | ||||
| std::string name = system::getStem(path); | std::string name = system::getStem(path); | ||||
| @@ -175,15 +197,42 @@ struct FileButton : MenuButton { | |||||
| } | } | ||||
| }, settings::recentPatchPaths.empty())); | }, settings::recentPatchPaths.empty())); | ||||
| if (!demoPatches.empty()) | |||||
| { | |||||
| menu->addChild(createSubmenuItem("Open demo / example project", "", [=](ui::Menu* const menu) { | |||||
| for (std::string path : demoPatches) { | |||||
| std::string label = system::getStem(path); | |||||
| for (size_t i=0, len=label.size(); i<len; ++i) { | |||||
| if (label[i] == '_') | |||||
| label[i] = ' '; | |||||
| } | |||||
| menu->addChild(createMenuItem(label, "", [path]() { | |||||
| patchUtils::loadPathDialog(path, true); | |||||
| })); | |||||
| } | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| menu->addChild(createMenuItem("Open patchstorage.com for more patches", "", []() { | |||||
| patchUtils::openBrowser("https://patchstorage.com/platform/cardinal/"); | |||||
| })); | |||||
| })); | |||||
| } | |||||
| menu->addChild(createMenuItem("Import selection...", "", [=]() { | menu->addChild(createMenuItem("Import selection...", "", [=]() { | ||||
| patchUtils::loadSelectionDialog(); | patchUtils::loadSelectionDialog(); | ||||
| }, false, true)); | }, false, true)); | ||||
| #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| #ifndef DISTRHO_OS_WASM | #ifndef DISTRHO_OS_WASM | ||||
| menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { | menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { | ||||
| // NOTE: will do nothing if path is empty, intentionally | |||||
| // NOTE: for plugin versions it will do nothing if path is empty, intentionally | |||||
| patchUtils::saveDialog(APP->patch->path); | patchUtils::saveDialog(APP->patch->path); | ||||
| }, APP->patch->path.empty())); | |||||
| }, APP->patch->path.empty() && !isStandalone)); | |||||
| menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { | menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { | ||||
| patchUtils::saveAsDialog(); | patchUtils::saveAsDialog(); | ||||
| @@ -196,11 +245,11 @@ struct FileButton : MenuButton { | |||||
| patchUtils::saveDialog(APP->patch->path); | patchUtils::saveDialog(APP->patch->path); | ||||
| })); | })); | ||||
| menu->addChild(createMenuItem("Save as", "", []() { | |||||
| menu->addChild(createMenuItem("Save as...", "", []() { | |||||
| wasmSaveAs(); | wasmSaveAs(); | ||||
| })); | })); | ||||
| menu->addChild(createMenuItem("Save and download compressed", RACK_MOD_CTRL_NAME "+Shift+S", []() { | |||||
| menu->addChild(createMenuItem("Save and download compressed", "", []() { | |||||
| patchUtils::saveAsDialog(); | patchUtils::saveAsDialog(); | ||||
| })); | })); | ||||
| @@ -251,38 +300,21 @@ struct FileButton : MenuButton { | |||||
| Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr); | Engine_setRemoteDetails(APP->engine, remoteDetails->autoDeploy ? remoteDetails : nullptr); | ||||
| } | } | ||||
| )); | )); | ||||
| #ifndef __MOD_DEVICES__ | |||||
| } else { | } else { | ||||
| menu->addChild(createMenuItem("Connect to " REMOTE_NAME, "", []() { | |||||
| DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote()); | |||||
| menu->addChild(createMenuItem("Connect to " REMOTE_NAME "...", "", [remoteDetails]() { | |||||
| const std::string url = remoteDetails != nullptr ? remoteDetails->url : CARDINAL_DEFAULT_REMOTE_URL; | |||||
| async_dialog_text_input("Remote:", url.c_str(), [](char* const url) { | |||||
| if (url == nullptr) | |||||
| return; | |||||
| DISTRHO_SAFE_ASSERT(remoteUtils::connectToRemote(url)); | |||||
| std::free(url); | |||||
| }); | |||||
| })); | })); | ||||
| } | |||||
| #endif | #endif | ||||
| if (!demoPatches.empty()) | |||||
| { | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| menu->addChild(createSubmenuItem("Open Demo / Example project", "", [=](ui::Menu* const menu) { | |||||
| for (std::string path : demoPatches) { | |||||
| std::string label = system::getStem(path); | |||||
| for (size_t i=0, len=label.size(); i<len; ++i) { | |||||
| if (label[i] == '_') | |||||
| label[i] = ' '; | |||||
| } | |||||
| menu->addChild(createMenuItem(label, "", [path]() { | |||||
| patchUtils::loadPathDialog(path, true); | |||||
| })); | |||||
| } | |||||
| menu->addChild(new ui::MenuSeparator); | |||||
| menu->addChild(createMenuItem("Open PatchStorage.com for more patches", "", []() { | |||||
| patchUtils::openBrowser("https://patchstorage.com/platform/cardinal/"); | |||||
| })); | |||||
| })); | |||||
| } | } | ||||
| #endif | |||||
| #ifndef DISTRHO_OS_WASM | #ifndef DISTRHO_OS_WASM | ||||
| if (isStandalone) { | if (isStandalone) { | ||||
| @@ -277,8 +277,13 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| // NOTE: will do nothing if path is empty, intentionally | |||||
| patchUtils::saveDialog(APP->patch->path); | |||||
| // NOTE: for plugin versions it will do nothing if path is empty, intentionally | |||||
| if (APP->patch->path.empty()) { | |||||
| if (isStandalone()) | |||||
| patchUtils::saveAsDialog(); | |||||
| } else { | |||||
| patchUtils::saveDialog(APP->patch->path); | |||||
| } | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { | if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { | ||||