|
- /*
- * 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"
- #include "extra/ScopedSafeLocale.hpp"
-
- #if defined(STATIC_BUILD) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- # undef HAVE_LIBLO
- #endif
-
- #if (defined(HAVE_LIBLO) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS) && !defined(HEADLESS)
- # define CARDINAL_REMOTE_ENABLED
- #endif
-
- #ifdef HAVE_LIBLO
- # include <lo/lo.h>
- // # define REMOTE_HOST "localhost"
- # define REMOTE_HOST "192.168.51.1"
- #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()
- {
- #ifdef CARDINAL_REMOTE_ENABLED
- 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;
- #else
- return nullptr;
- #endif
- }
-
- bool connectToRemote()
- {
- #ifdef CARDINAL_REMOTE_ENABLED
- 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;
-
- #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- if (remoteDetails == nullptr)
- {
- ui->remoteDetails = remoteDetails = new RemoteDetails;
- remoteDetails->handle = ui;
- remoteDetails->connected = true;
- remoteDetails->autoDeploy = true;
- }
- #elif defined(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);
-
- ui->remoteDetails = remoteDetails = new RemoteDetails;
- remoteDetails->handle = oscServer;
- remoteDetails->connected = false;
- remoteDetails->autoDeploy = false;
-
- 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)
- {
- lo_send(addr, "/hello", "");
- lo_address_free(addr);
- }
- #endif
-
- return remoteDetails != nullptr;
- #else
- return false;
- #endif
- }
-
- void disconnectFromRemote(RemoteDetails* const remote)
- {
- if (remote != nullptr)
- {
- #ifdef HAVE_LIBLO
- lo_server_free(static_cast<lo_server>(remote->handle));
- #endif
- delete remote;
- }
- }
-
- void idleRemote(RemoteDetails* const remote)
- {
- DISTRHO_SAFE_ASSERT_RETURN(remote != nullptr,);
- #ifdef HAVE_LIBLO
- while (lo_server_recv_noblock(static_cast<lo_server>(remote->handle), 0) != 0) {}
- #endif
- }
-
- void sendParamChangeToRemote(RemoteDetails* const remote, int64_t moduleId, int paramId, float value)
- {
- #ifdef CARDINAL_REMOTE_ENABLED
- #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- char paramBuf[512] = {};
- {
- const ScopedSafeLocale cssl;
- std::snprintf(paramBuf, sizeof(paramBuf), "%lld:%d:%f", (long long)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
- #endif
- }
-
- void sendFullPatchToRemote(RemoteDetails* const remote)
- {
- #ifdef CARDINAL_REMOTE_ENABLED
- CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
- DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,);
-
- context->engine->prepareSave();
- context->patch->saveAutosave();
- context->patch->cleanAutosave();
-
- std::vector<uint8_t> data;
- using namespace rack::system;
-
- #if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
- FILE* const f = std::fopen(join(context->patch->autosavePath, "patch.json").c_str(), "r");
- DISTRHO_SAFE_ASSERT_RETURN(f != nullptr,);
-
- DEFER({
- std::fclose(f);
- });
-
- std::fseek(f, 0, SEEK_END);
- const long fileSize = std::ftell(f);
- DISTRHO_SAFE_ASSERT_RETURN(fileSize > 0,);
-
- std::fseek(f, 0, SEEK_SET);
- char* const fileContent = new char[fileSize+1];
-
- DISTRHO_SAFE_ASSERT_RETURN(std::fread(fileContent, fileSize, 1, f) == 1,);
- fileContent[fileSize] = '\0';
- static_cast<CardinalBaseUI*>(remote->handle)->setState("patch", fileContent);
- delete[] fileContent;
- #elif defined(HAVE_LIBLO)
- try {
- data = archiveDirectory(context->patch->autosavePath, 1);
- } DISTRHO_SAFE_EXCEPTION_RETURN("sendFullPatchToRemote",);
-
- 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);
- DISTRHO_SAFE_ASSERT_RETURN(addr != nullptr,);
-
- 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
- #endif
- }
-
- void sendScreenshotToRemote(RemoteDetails*, const char* const screenshot)
- {
- #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);
- 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
- }
-
- }
-
- // -----------------------------------------------------------------------------------------------------------
|