Browse Source

Setup mini variant UI, move around more code to accomodate this

Signed-off-by: falkTX <falktx@falktx.com>
tags/23.02
falkTX 2 years ago
parent
commit
a85679758c
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
12 changed files with 534 additions and 334 deletions
  1. +1
    -1
      dpf
  2. +5
    -1
      plugins/Makefile
  3. +1
    -0
      plugins/plugins-mini.cpp
  4. +300
    -0
      src/CardinalCommon.cpp
  5. +43
    -2
      src/CardinalCommon.hpp
  6. +2
    -2
      src/CardinalMini/DistrhoPluginInfo.h
  7. +8
    -306
      src/CardinalPlugin.cpp
  8. +105
    -6
      src/CardinalUI.cpp
  9. +18
    -11
      src/Makefile.cardinal.mk
  10. +0
    -3
      src/PluginContext.hpp
  11. +19
    -0
      src/extra/SharedResourcePointer.hpp
  12. +32
    -2
      src/override/Window.cpp

+ 1
- 1
dpf

@@ -1 +1 @@
Subproject commit 564f6519b4bbf1c6cc8791a9adbb377f6cfd4984
Subproject commit 79948069d581c64bd19bc454234550de88b459a2

+ 5
- 1
plugins/Makefile View File

@@ -293,6 +293,10 @@ PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
PLUGIN_FILES += Cardinal/src/SassyScope.cpp
PLUGIN_FILES += Cardinal/src/DearImGui.cpp
PLUGIN_FILES += Cardinal/src/DearImGuiColorTextEditor.cpp
MINIPLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp
MINIPLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
MINIPLUGIN_FILES += Cardinal/src/DearImGui.cpp
MINIPLUGIN_FILES += Cardinal/src/DearImGuiColorTextEditor.cpp
endif

ifeq ($(shell $(PKG_CONFIG) --exists fftw3f && echo true),true)
@@ -1290,7 +1294,7 @@ endif
ifeq ($(NOPLUGINS),true)
TARGETS = noplugins$(TARGET_SUFFIX).a
else
TARGETS = plugins$(TARGET_SUFFIX).a plugins-mini-headless.a
TARGETS = plugins$(TARGET_SUFFIX).a plugins-mini.a
endif

all: $(TARGETS)


+ 1
- 0
plugins/plugins-mini.cpp View File

@@ -189,6 +189,7 @@ void destroyStaticPlugins()

void updateStaticPluginsDarkMode()
{
d_stdout("TODO");
}

}


+ 300
- 0
src/CardinalCommon.cpp View File

@@ -38,6 +38,7 @@
#include <settings.hpp>
#include <string.hpp>
#include <system.hpp>
#include <app/Browser.hpp>
#include <app/Scene.hpp>
#include <window/Window.hpp>

@@ -65,6 +66,27 @@
# define HEADLESS
#endif

#if CARDINAL_VARIANT_FX
# define CARDINAL_TEMPLATE_NAME "init/fx.vcv"
#elif CARDINAL_VARIANT_NATIVE
# define CARDINAL_TEMPLATE_NAME "init/native.vcv"
#elif CARDINAL_VARIANT_SYNTH
# define CARDINAL_TEMPLATE_NAME "init/synth.vcv"
#else
# define CARDINAL_TEMPLATE_NAME "init/main.vcv"
#endif

namespace rack {
namespace asset {
std::string patchesPath();
void destroy();
}
namespace plugin {
void initStaticPlugins();
void destroyStaticPlugins();
}
}

const std::string CARDINAL_VERSION = "22.12";

START_NAMESPACE_DISTRHO
@@ -172,6 +194,284 @@ void CardinalPluginContext::writeMidiMessage(const rack::midi::Message& message,
plugin->writeMidiEvent(event);
}

// -----------------------------------------------------------------------------------------------------------

#ifdef CARDINAL_INIT_OSC_THREAD
static void osc_error_handler(int num, const char* msg, const char* path)
{
d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path);
}

static int osc_fallback_handler(const char* const path, const char* const types, lo_arg**, int, lo_message, void*)
{
d_stderr("Cardinal OSC unhandled message \"%s\" with types \"%s\"", path, types);
return 0;
}

static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self)
{
d_stdout("osc_hello_handler()");
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");
return 0;
}

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()");
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);

const int32_t size = argv[0]->blob.size;
DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0);

const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data);
DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0);

bool ok = false;

if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin)
{
CardinalPluginContext* const context = plugin->context;
std::vector<uint8_t> data(size);
std::memcpy(data.data(), blob, size);

rack::contextSet(context);
rack::system::removeRecursively(context->patch->autosavePath);
rack::system::createDirectories(context->patch->autosavePath);
try {
rack::system::unarchiveToDirectory(data, context->patch->autosavePath);
context->patch->loadAutosave();
ok = true;
}
catch (rack::Exception& e) {
WARN("%s", e.what());
}
rack::contextSet(nullptr);
}

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");
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()");
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);

const int32_t size = argv[0]->blob.size;
DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0);

const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data);
DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0);

bool ok = false;

if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin)
ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer());

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");
return 0;
}
#endif

Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalBaseUI* const ui)
{
using namespace rack;

#ifdef DISTRHO_OS_WASM
settings::allowCursorLock = true;
#else
settings::allowCursorLock = false;
#endif
settings::autoCheckUpdates = false;
settings::autosaveInterval = 0;
settings::devMode = true;
settings::isPlugin = true;
settings::skipLoadOnLaunch = true;
settings::showTipsOnLaunch = false;
settings::windowPos = math::Vec(0, 0);
#ifdef HEADLESS
settings::headless = true;
#endif

// copied from https://community.vcvrack.com/t/16-colour-cable-palette/15951
settings::cableColors = {
color::fromHexString("#ff5252"),
color::fromHexString("#ff9352"),
color::fromHexString("#ffd452"),
color::fromHexString("#e8ff52"),
color::fromHexString("#a8ff52"),
color::fromHexString("#67ff52"),
color::fromHexString("#52ff7d"),
color::fromHexString("#52ffbe"),
color::fromHexString("#52ffff"),
color::fromHexString("#52beff"),
color::fromHexString("#527dff"),
color::fromHexString("#6752ff"),
color::fromHexString("#a852ff"),
color::fromHexString("#e952ff"),
color::fromHexString("#ff52d4"),
color::fromHexString("#ff5293"),
};

system::init();
logger::init();
random::init();
ui::init();

if (asset::systemDir.empty())
{
if (const char* const bundlePath = (plugin != nullptr ? plugin->getBundlePath() :
ui != nullptr ? ui->getBundlePath() : nullptr))
{
if (const char* const resourcePath = getResourcePath(bundlePath))
{
asset::systemDir = resourcePath;
asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
}
}

if (asset::systemDir.empty() || ! system::exists(asset::systemDir) || ! system::exists(asset::bundlePath))
{
#ifdef CARDINAL_PLUGIN_SOURCE_DIR
// Make system dir point to source code location as fallback
asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack";
asset::bundlePath.clear();

// If source code dir does not exist use install target prefix as system dir
if (!system::exists(system::join(asset::systemDir, "res")))
#endif
{
#if defined(DISTRHO_OS_WASM)
asset::systemDir = "/resources";
#elif defined(ARCH_MAC)
asset::systemDir = "/Library/Application Support/Cardinal";
#elif defined(ARCH_WIN)
const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles);
if (! commonprogfiles.empty())
asset::systemDir = system::join(commonprogfiles, "Cardinal");
#else
asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal";
#endif

asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
}
}

asset::userDir = asset::systemDir;
}

const std::string patchesPath = asset::patchesPath();
#ifdef DISTRHO_OS_WASM
templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME);
#else
templatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME);
#endif
factoryTemplatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME);

// Log environment
INFO("%s %s %s, compatible with Rack version %s", APP_NAME.c_str(), APP_EDITION.c_str(), CARDINAL_VERSION.c_str(), APP_VERSION.c_str());
INFO("%s", system::getOperatingSystemInfo().c_str());
INFO("Binary filename: %s", getBinaryFilename());
if (plugin != nullptr) {
INFO("Bundle path: %s", plugin->getBundlePath());
} else if (ui != nullptr) {
INFO("Bundle path: %s", ui->getBundlePath());
}
INFO("System directory: %s", asset::systemDir.c_str());
INFO("User directory: %s", asset::userDir.c_str());
INFO("Template patch: %s", templatePath.c_str());
INFO("System template patch: %s", factoryTemplatePath.c_str());

// Report to user if something is wrong with the installation
if (asset::systemDir.empty())
{
d_stderr2("Failed to locate Cardinal plugin bundle.\n"
"Install Cardinal with its bundle folder intact and try again.");
}
else if (! system::exists(asset::systemDir))
{
d_stderr2("System directory \"%s\" does not exist.\n"
"Make sure Cardinal was downloaded and installed correctly.", asset::systemDir.c_str());
}

INFO("Initializing plugins");
plugin::initStaticPlugins();

INFO("Initializing plugin browser DB");
app::browserInit();

#ifdef CARDINAL_INIT_OSC_THREAD
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);

startThread();
#else
INFO("OSC Remote control is not enabled in this build");
#endif
}

Initializer::~Initializer()
{
using namespace rack;

#ifdef CARDINAL_INIT_OSC_THREAD
if (oscServer != nullptr)
{
stopThread(5000);
lo_server_del_method(oscServer, nullptr, nullptr);
lo_server_free(oscServer);
oscServer = nullptr;
}
#endif

INFO("Clearing asset paths");
asset::bundlePath.clear();
asset::systemDir.clear();
asset::userDir.clear();

INFO("Destroying plugins");
plugin::destroyStaticPlugins();

INFO("Destroying colourized assets");
asset::destroy();

INFO("Destroying settings");
settings::destroy();

INFO("Destroying logger");
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

// --------------------------------------------------------------------------------------------------------------------


+ 43
- 2
src/CardinalCommon.hpp View File

@@ -15,14 +15,17 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/

#include <string>

#pragma once

#include "DistrhoUtils.hpp"

#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
@@ -35,6 +38,8 @@

extern const std::string CARDINAL_VERSION;

// -----------------------------------------------------------------------------------------------------------

namespace rack {

namespace ui {
@@ -65,6 +70,8 @@ extern char* patchStorageSlug;

} // namespace rack

// -----------------------------------------------------------------------------------------------------------

namespace patchUtils {

void loadDialog();
@@ -86,3 +93,37 @@ void deployToRemote();
void sendScreenshotToRemote(const char* screenshot);

} // namespace patchUtils

// -----------------------------------------------------------------------------------------------------------

#if defined(HAVE_LIBLO) && defined(HEADLESS) && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
# define CARDINAL_INIT_OSC_THREAD
#endif

START_NAMESPACE_DISTRHO

class CardinalBasePlugin;
class CardinalBaseUI;

struct Initializer
#ifdef CARDINAL_INIT_OSC_THREAD
: public Thread
#endif
{
#ifdef CARDINAL_INIT_OSC_THREAD
lo_server oscServer = nullptr;
CardinalBasePlugin* oscPlugin = nullptr;
#endif
std::string templatePath;
std::string factoryTemplatePath;

Initializer(const CardinalBasePlugin* plugin, const CardinalBaseUI* ui);
~Initializer();
#ifdef CARDINAL_INIT_OSC_THREAD
void run() override;
#endif
};

END_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------------------------------------------

+ 2
- 2
src/CardinalMini/DistrhoPluginInfo.h View File

@@ -31,8 +31,8 @@
#define DISTRHO_PLUGIN_URI "https://distrho.kx.studio/plugins/cardinal#mini"
#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.cardinal#mini"

#define DISTRHO_PLUGIN_NAME "Cardinal FX"
#define DISTRHO_PLUGIN_LABEL "CardinalFX"
#define DISTRHO_PLUGIN_NAME "Cardinal Mini"
#define DISTRHO_PLUGIN_LABEL "CardinalMini"

#define DISTRHO_PLUGIN_HAS_UI 1
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 0


+ 8
- 306
src/CardinalPlugin.cpp View File

@@ -15,7 +15,6 @@
* For a full copy of the GNU General Public License see the LICENSE file.
*/
#include <asset.hpp>
#include <library.hpp>
#include <midi.hpp>
#include <patch.hpp>
@@ -24,7 +23,6 @@
#include <settings.hpp>
#include <system.hpp>
#include <app/Browser.hpp>
#include <app/Scene.hpp>
#include <engine/Engine.hpp>
#include <ui/common.hpp>
@@ -52,16 +50,6 @@
# include "extra/SharedResourcePointer.hpp"
#endif
#if CARDINAL_VARIANT_FX
# define CARDINAL_TEMPLATE_NAME "init/fx.vcv"
#elif CARDINAL_VARIANT_NATIVE
# define CARDINAL_TEMPLATE_NAME "init/native.vcv"
#elif CARDINAL_VARIANT_SYNTH
# define CARDINAL_TEMPLATE_NAME "init/synth.vcv"
#else
# define CARDINAL_TEMPLATE_NAME "init/main.vcv"
#endif
static const constexpr uint kCardinalStateBaseCount = 3; // patch, screenshot, comment
#ifndef HEADLESS
@@ -76,17 +64,9 @@ static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount;
extern const std::string CARDINAL_VERSION;
namespace rack {
namespace asset {
std::string patchesPath();
void destroy();
}
namespace engine {
void Engine_setAboutToClose(Engine*);
}
namespace plugin {
void initStaticPlugins();
void destroyStaticPlugins();
}
}
START_NAMESPACE_DISTRHO
@@ -98,6 +78,10 @@ bool d_isDiffHigherThanLimit(const T& v1, const T& v2, const T& limit)
return v1 != v2 ? (v1 > v2 ? v1 - v2 : v2 - v1) > limit : false;
}
#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
const char* UI::getBundlePath() const noexcept { return nullptr; }
#endif
// -----------------------------------------------------------------------------------------------------------
#ifdef DISTRHO_OS_WASM
@@ -143,288 +127,6 @@ static char* getPatchStorageSlug() {
// -----------------------------------------------------------------------------------------------------------
struct Initializer
#if defined(HAVE_LIBLO) && defined(HEADLESS)
: public Thread
#endif
{
#if defined(HAVE_LIBLO) && defined(HEADLESS)
lo_server oscServer = nullptr;
CardinalBasePlugin* oscPlugin = nullptr;
#endif
std::string templatePath;
std::string factoryTemplatePath;
Initializer(const CardinalBasePlugin* const plugin)
{
using namespace rack;
#ifdef DISTRHO_OS_WASM
settings::allowCursorLock = true;
#else
settings::allowCursorLock = false;
#endif
settings::autoCheckUpdates = false;
settings::autosaveInterval = 0;
settings::devMode = true;
settings::isPlugin = true;
settings::skipLoadOnLaunch = true;
settings::showTipsOnLaunch = false;
settings::windowPos = math::Vec(0, 0);
#ifdef HEADLESS
settings::headless = true;
#endif
// copied from https://community.vcvrack.com/t/16-colour-cable-palette/15951
settings::cableColors = {
color::fromHexString("#ff5252"),
color::fromHexString("#ff9352"),
color::fromHexString("#ffd452"),
color::fromHexString("#e8ff52"),
color::fromHexString("#a8ff52"),
color::fromHexString("#67ff52"),
color::fromHexString("#52ff7d"),
color::fromHexString("#52ffbe"),
color::fromHexString("#52ffff"),
color::fromHexString("#52beff"),
color::fromHexString("#527dff"),
color::fromHexString("#6752ff"),
color::fromHexString("#a852ff"),
color::fromHexString("#e952ff"),
color::fromHexString("#ff52d4"),
color::fromHexString("#ff5293"),
};
system::init();
logger::init();
random::init();
ui::init();
if (asset::systemDir.empty())
{
if (const char* const bundlePath = plugin->getBundlePath())
{
if (const char* const resourcePath = getResourcePath(bundlePath))
{
asset::systemDir = resourcePath;
asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
}
}
if (asset::systemDir.empty() || ! system::exists(asset::systemDir) || ! system::exists(asset::bundlePath))
{
#ifdef CARDINAL_PLUGIN_SOURCE_DIR
// Make system dir point to source code location as fallback
asset::systemDir = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR "Rack";
asset::bundlePath.clear();
// If source code dir does not exist use install target prefix as system dir
if (!system::exists(system::join(asset::systemDir, "res")))
#endif
{
#if defined(DISTRHO_OS_WASM)
asset::systemDir = "/resources";
#elif defined(ARCH_MAC)
asset::systemDir = "/Library/Application Support/Cardinal";
#elif defined(ARCH_WIN)
const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles);
if (! commonprogfiles.empty())
asset::systemDir = system::join(commonprogfiles, "Cardinal");
#else
asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal";
#endif
asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
}
}
asset::userDir = asset::systemDir;
}
const std::string patchesPath = asset::patchesPath();
#ifdef DISTRHO_OS_WASM
templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME);
#else
templatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME);
#endif
factoryTemplatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME);
// Log environment
INFO("%s %s %s, compatible with Rack version %s", APP_NAME.c_str(), APP_EDITION.c_str(), CARDINAL_VERSION.c_str(), APP_VERSION.c_str());
INFO("%s", system::getOperatingSystemInfo().c_str());
INFO("Binary filename: %s", getBinaryFilename());
INFO("Bundle path: %s", plugin->getBundlePath());
INFO("System directory: %s", asset::systemDir.c_str());
INFO("User directory: %s", asset::userDir.c_str());
INFO("Template patch: %s", templatePath.c_str());
INFO("System template patch: %s", factoryTemplatePath.c_str());
// Report to user if something is wrong with the installation
if (asset::systemDir.empty())
{
d_stderr2("Failed to locate Cardinal plugin bundle.\n"
"Install Cardinal with its bundle folder intact and try again.");
}
else if (! system::exists(asset::systemDir))
{
d_stderr2("System directory \"%s\" does not exist.\n"
"Make sure Cardinal was downloaded and installed correctly.", asset::systemDir.c_str());
}
INFO("Initializing plugins");
plugin::initStaticPlugins();
INFO("Initializing plugin browser DB");
app::browserInit();
#if defined(HAVE_LIBLO) && defined(HEADLESS)
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);
startThread();
#elif defined(HEADLESS)
INFO("OSC Remote control is not enabled in this build");
#endif
}
~Initializer()
{
using namespace rack;
#if defined(HAVE_LIBLO) && defined(HEADLESS)
if (oscServer != nullptr)
{
stopThread(5000);
lo_server_del_method(oscServer, nullptr, nullptr);
lo_server_free(oscServer);
oscServer = nullptr;
}
#endif
INFO("Clearing asset paths");
asset::bundlePath.clear();
asset::systemDir.clear();
asset::userDir.clear();
INFO("Destroying plugins");
plugin::destroyStaticPlugins();
INFO("Destroying colourized assets");
asset::destroy();
INFO("Destroying settings");
settings::destroy();
INFO("Destroying logger");
logger::destroy();
}
#if defined(HAVE_LIBLO) && defined(HEADLESS)
void run() override
{
INFO("OSC Thread Listening for remote commands");
while (! shouldThreadExit())
{
d_msleep(200);
while (lo_server_recv_noblock(oscServer, 0) != 0) {}
}
INFO("OSC Thread Closed");
}
static void osc_error_handler(int num, const char* msg, const char* path)
{
d_stderr("Cardinal OSC Error: code: %i, msg: \"%s\", path: \"%s\")", num, msg, path);
}
static int osc_fallback_handler(const char* const path, const char* const types, lo_arg**, int, lo_message, void*)
{
d_stderr("Cardinal OSC unhandled message \"%s\" with types \"%s\"", path, types);
return 0;
}
static int osc_hello_handler(const char*, const char*, lo_arg**, int, const lo_message m, void* const self)
{
d_stdout("osc_hello_handler()");
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");
return 0;
}
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()");
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);
const int32_t size = argv[0]->blob.size;
DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0);
const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data);
DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0);
bool ok = false;
if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin)
{
CardinalPluginContext* const context = plugin->context;
std::vector<uint8_t> data(size);
std::memcpy(data.data(), blob, size);
rack::contextSet(context);
rack::system::removeRecursively(context->patch->autosavePath);
rack::system::createDirectories(context->patch->autosavePath);
try {
rack::system::unarchiveToDirectory(data, context->patch->autosavePath);
context->patch->loadAutosave();
ok = true;
}
catch (rack::Exception& e) {
WARN("%s", e.what());
}
rack::contextSet(nullptr);
}
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");
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()");
DISTRHO_SAFE_ASSERT_RETURN(argc == 1, 0);
DISTRHO_SAFE_ASSERT_RETURN(types != nullptr && types[0] == 'b', 0);
const int32_t size = argv[0]->blob.size;
DISTRHO_SAFE_ASSERT_RETURN(size > 4, 0);
const uint8_t* const blob = (uint8_t*)(&argv[0]->blob.data);
DISTRHO_SAFE_ASSERT_RETURN(blob != nullptr, 0);
bool ok = false;
if (CardinalBasePlugin* const plugin = static_cast<Initializer*>(self)->oscPlugin)
ok = plugin->updateStateValue("screenshot", String::asBase64(blob, size).buffer());
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");
return 0;
}
#endif
};
// -----------------------------------------------------------------------------------------------------------
struct ScopedContext {
ScopedContext(const CardinalBasePlugin* const plugin)
{
@@ -479,9 +181,9 @@ public:
CardinalPlugin()
: CardinalBasePlugin(kModuleParameters + kWindowParameterCount + 1, 0, kCardinalStateCount),
#ifdef DISTRHO_OS_WASM
fInitializer(new Initializer(this)),
fInitializer(new Initializer(this, static_cast<const CardinalBaseUI*>(nullptr))),
#else
fInitializer(this),
fInitializer(this, static_cast<const CardinalBaseUI*>(nullptr)),
#endif
#if DISTRHO_PLUGIN_NUM_INPUTS != 0
fAudioBufferCopy(nullptr),
@@ -571,14 +273,14 @@ public:
context->patch->templatePath = context->patch->factoryTemplatePath;
}
#if defined(HAVE_LIBLO) && defined(HEADLESS)
#ifdef CARDINAL_INIT_OSC_THREAD
fInitializer->oscPlugin = this;
#endif
}
~CardinalPlugin() override
{
#if defined(HAVE_LIBLO) && defined(HEADLESS)
#ifdef CARDINAL_INIT_OSC_THREAD
fInitializer->oscPlugin = nullptr;
#endif


+ 105
- 6
src/CardinalUI.cpp View File

@@ -39,7 +39,6 @@
# include <ui/Label.hpp>
# include <ui/MenuOverlay.hpp>
# include <ui/SequentialLayout.hpp>
# include "CardinalCommon.hpp"
# include <emscripten/emscripten.h>
#endif

@@ -47,15 +46,27 @@
# undef DEBUG
#endif

#include <Application.hpp>
#include "Application.hpp"
#include "AsyncDialog.hpp"
#include "CardinalCommon.hpp"
#include "PluginContext.hpp"
#include "WindowParameters.hpp"

#ifndef DISTRHO_OS_WASM
# include "extra/SharedResourcePointer.hpp"
#endif

#ifndef HEADLESS
# include "extra/ScopedValueSetter.hpp"
#endif

namespace rack {
namespace app {
widget::Widget* createMenuBar(bool isStandalone);
}
namespace engine {
void Engine_setAboutToClose(Engine*);
}
namespace window {
void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui);
void WindowSetMods(Window* window, int mods);
@@ -65,7 +76,14 @@ namespace window {

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------

#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
const char* Plugin::getBundlePath() const noexcept { return nullptr; }
bool Plugin::writeMidiEvent(const MidiEvent&) noexcept { return false; }
#endif

// --------------------------------------------------------------------------------------------------------------------

#ifdef DISTRHO_OS_WASM
struct WasmWelcomeDialog : rack::widget::OpaqueWidget
@@ -268,6 +286,15 @@ static void downloadRemotePatchSucceeded(const char* const filename)
class CardinalUI : public CardinalBaseUI,
public WindowParametersCallback
{
#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#ifdef DISTRHO_OS_WASM
ScopedPointer<Initializer> fInitializer;
#else
SharedResourcePointer<Initializer> fInitializer;
#endif
std::string fAutosavePath;
#endif

rack::math::Vec lastMousePos;
WindowParameters windowParameters;
int rateLimitStep = 0;
@@ -305,8 +332,65 @@ class CardinalUI : public CardinalBaseUI,

public:
CardinalUI()
: CardinalBaseUI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT)
: CardinalBaseUI(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT),
#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#ifdef DISTRHO_OS_WASM
fInitializer(new Initializer(static_cast<const CardinalBasePlugin*>(nullptr), this)),
#else
fInitializer(static_cast<const CardinalBasePlugin*>(nullptr), this),
#endif
#endif
lastMousePos()
{
rack::contextSet(context);

#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
// create unique temporary path for this instance
try {
char uidBuf[24];
const std::string tmp = rack::system::getTempDirectory();

for (int i=1;; ++i)
{
std::snprintf(uidBuf, sizeof(uidBuf), "Cardinal.%04d", i);
const std::string trypath = rack::system::join(tmp, uidBuf);

if (! rack::system::exists(trypath))
{
if (rack::system::createDirectories(trypath))
fAutosavePath = trypath;
break;
}
}
} DISTRHO_SAFE_EXCEPTION("create unique temporary path");

const float sampleRate = getSampleRate();
rack::settings::sampleRate = sampleRate;

context->bufferSize = 128;
context->sampleRate = sampleRate;

context->engine = new rack::engine::Engine;
context->engine->setSampleRate(sampleRate);

context->history = new rack::history::State;
context->patch = new rack::patch::Manager;
context->patch->autosavePath = fAutosavePath;
context->patch->templatePath = fInitializer->templatePath;
context->patch->factoryTemplatePath = fInitializer->factoryTemplatePath;

context->event = new rack::widget::EventState;
context->scene = new rack::app::Scene;
context->event->rootWidget = context->scene;

context->window = new rack::window::Window;

context->patch->loadTemplate();
context->scene->rackScroll->reset();
// swap to factory template after first load
context->patch->templatePath = context->patch->factoryTemplatePath;
#endif

Window& window(getWindow());

window.setIgnoringKeyRepeat(true);
@@ -319,8 +403,6 @@ public:
if (scaleFactor != 1.0)
setSize(DISTRHO_UI_DEFAULT_WIDTH * scaleFactor, DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor);

rack::contextSet(context);

rack::window::WindowSetPluginUI(context->window, this);

if (rack::widget::Widget* const menuBar = context->scene->menuBar)
@@ -408,6 +490,23 @@ public:

rack::window::WindowSetPluginUI(context->window, nullptr);

#if ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
{
const ScopedContext sc(this);
context->patch->clear();

// do a little dance to prevent context scene deletion from saving to temp dir
#ifndef HEADLESS
const ScopedValueSetter<bool> svs(rack::settings::headless, true);
#endif
Engine_setAboutToClose(context->engine);
delete context;
}

if (! fAutosavePath.empty())
rack::system::removeRecursively(fAutosavePath);
#endif

rack::contextSet(nullptr);
}



+ 18
- 11
src/Makefile.cardinal.mk View File

@@ -201,8 +201,10 @@ endif
# --------------------------------------------------------------
# Setup resources

CORE_RESOURCES = patches
CORE_RESOURCES += $(subst ../Rack/res/,,$(wildcard ../Rack/res/ComponentLibrary/*.svg ../Rack/res/fonts/*.ttf))
CORE_RESOURCES = $(subst ../Rack/res/,,$(wildcard ../Rack/res/ComponentLibrary/*.svg ../Rack/res/fonts/*.ttf))
# ifneq ($(CARDINAL_VARIANT),mini)
CORE_RESOURCES += patches
# endif

LV2_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).lv2/resources/%)
VST3_RESOURCES = $(CORE_RESOURCES:%=$(TARGET_DIR)/$(NAME).vst3/Contents/Resources/%)
@@ -243,15 +245,19 @@ endif
# --------------------------------------------------------------
# mini variant UI

# ifeq ($(CARDINAL_VARIANT),mini)
# ifneq ($(HEADLESS)$(MOD_BUILD),true)
# FILES_UI = CardinalUI.cpp
# FILES_UI += glfw.cpp
# FILES_UI += Window.cpp
# EXTRA_UI_DEPENDENCIES = $(subst -headless,,$(EXTRA_DSP_DEPENDENCIES))
# EXTRA_UI_LIBS = $(subst -headless,,$(EXTRA_DSP_LIBS))
# endif
# endif
ifeq ($(CARDINAL_VARIANT),mini)
ifneq ($(HEADLESS)$(MOD_BUILD),true)
FILES_UI = CardinalUI.cpp
FILES_UI += CardinalCommon.cpp
FILES_UI += common.cpp
FILES_UI += glfw.cpp
FILES_UI += Window.cpp
EXTRA_UI_DEPENDENCIES = $(subst -headless,,$(EXTRA_DSP_DEPENDENCIES))
EXTRA_UI_LIBS = -Wl,--start-group
EXTRA_UI_LIBS += $(subst -headless,,$(EXTRA_DSP_LIBS))
EXTRA_UI_LIBS += -Wl,--end-group
endif
endif

# --------------------------------------------------------------
# Do some magic
@@ -467,6 +473,7 @@ endif

all: $(TARGETS)
lv2: $(LV2_RESOURCES)
lv2_sep: $(LV2_RESOURCES)
vst2: $(VST2_RESOURCES)
vst3: $(VST3_RESOURCES)
clap: $(CLAP_RESOURCES)


+ 0
- 3
src/PluginContext.hpp View File

@@ -189,9 +189,6 @@ public:

context->tlw = nullptr;
context->ui = nullptr;
#if !DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
delete context;
#endif
}
};
#endif


+ 19
- 0
src/extra/SharedResourcePointer.hpp View File

@@ -107,6 +107,13 @@ public:
initialise_variant<T>(variant);
}
template<class T1, class T2>
SharedResourcePointer(const T1* const v1, const T2* const v2)
: sharedObject(nullptr)
{
initialise_variant2<T1, T2>(v1, v2);
}
SharedResourcePointer (const SharedResourcePointer&)
: sharedObject(nullptr)
{
@@ -179,6 +186,18 @@ private:
sharedObject = holder.sharedInstance;
}
template<class T1, class T2>
void initialise_variant2(const T1* const v1, const T2* const v2)
{
SharedObjectHolder& holder = getSharedObjectHolder();
const SpinLock::ScopedLockType sl (holder.lock);
if (++(holder.refCount) == 1)
holder.sharedInstance = new SharedObjectType(v1, v2);
sharedObject = holder.sharedInstance;
}
// There's no need to assign to a SharedResourcePointer because every
// instance of the class is exactly the same!
SharedResourcePointer& operator= (const SharedResourcePointer&) = delete;


+ 32
- 2
src/override/Window.cpp View File

@@ -56,6 +56,7 @@
#include "Application.hpp"
#include "extra/String.hpp"
#include "../CardinalCommon.hpp"
#include "../PluginContext.hpp"
#include "../WindowParameters.hpp"

#ifndef DGL_NO_SHARED_RESOURCES
@@ -148,12 +149,14 @@ struct Window::Internal {
DGL_NAMESPACE::NanoTopLevelWidget* tlw = nullptr;
DISTRHO_NAMESPACE::WindowParameters params;
DISTRHO_NAMESPACE::WindowParametersCallback* callback = nullptr;
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
DGL_NAMESPACE::Application hiddenApp;
DGL_NAMESPACE::Window hiddenWindow;
NVGcontext* r_vg = nullptr;
NVGcontext* r_fbVg = nullptr;
NVGcontext* o_vg = nullptr;
NVGcontext* o_fbVg = nullptr;
#endif

math::Vec size = WINDOW_SIZE_MIN;

@@ -176,12 +179,16 @@ struct Window::Internal {
int fbCount = 0;

Internal()
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
: hiddenApp(false),
hiddenWindow(hiddenApp)
{
hiddenWindow.setIgnoringKeyRepeat(true);
hiddenApp.idle();
}
#else
{}
#endif
};


@@ -203,12 +210,17 @@ static int loadFallbackFont(NVGcontext* const vg)
Window::Window() {
internal = new Internal;

DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow);

// Set up NanoVG
const int nvgFlags = NVG_ANTIALIAS;

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow);
vg = nvgCreateGL(nvgFlags);
#else
vg = static_cast<CardinalPluginContext*>(APP)->tlw->getContext();
#endif
DISTRHO_SAFE_ASSERT_RETURN(vg != nullptr,);

#ifdef NANOVG_GLES2
fbVg = nvgCreateSharedGLES2(vg, nvgFlags);
#else
@@ -268,6 +280,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw)
window->internal->tlw = tlw;
window->internal->size = rack::math::Vec(tlw->getWidth(), tlw->getHeight());

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
// Set up NanoVG
window->internal->r_vg = tlw->getContext();
#ifdef NANOVG_GLES2
@@ -299,6 +312,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw)
image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(),
NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
}
#endif

// Init settings
WindowParametersRestore(window);
@@ -311,6 +325,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw)
widget::Widget::ContextDestroyEvent e;
APP->scene->onContextDestroy(e);

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
// swap contexts
window->uiFont->vg = window->internal->o_vg;
window->vg = window->internal->o_vg;
@@ -338,6 +353,7 @@ void WindowSetPluginRemote(Window* const window, NanoTopLevelWidget* const tlw)
nvgDeleteGLES2(window->internal->r_fbVg);
#else
nvgDeleteGL2(window->internal->r_fbVg);
#endif
#endif

window->internal->tlw = nullptr;
@@ -375,6 +391,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui)
window->internal->ui = ui;
window->internal->size = rack::math::Vec(ui->getWidth(), ui->getHeight());

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
// Set up NanoVG
window->internal->r_vg = ui->getContext();
#ifdef NANOVG_GLES2
@@ -406,6 +423,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui)
image.second->handle = nvgCreateImage(window->vg, image.second->ofilename.c_str(),
NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
}
#endif

// Init settings
WindowParametersRestore(window);
@@ -418,6 +436,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui)
widget::Widget::ContextDestroyEvent e;
APP->scene->onContextDestroy(e);

#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
// swap contexts
window->uiFont->vg = window->internal->o_vg;
window->vg = window->internal->o_vg;
@@ -445,6 +464,7 @@ void WindowSetPluginUI(Window* const window, DISTRHO_NAMESPACE::UI* const ui)
nvgDeleteGLES2(window->internal->r_fbVg);
#else
nvgDeleteGL2(window->internal->r_fbVg);
#endif
#endif

window->internal->tlw = nullptr;
@@ -460,9 +480,11 @@ void WindowSetMods(Window* const window, const int mods)

Window::~Window() {
{
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
DGL_NAMESPACE::Window::ScopedGraphicsContext sgc(internal->hiddenWindow);
internal->hiddenWindow.close();
internal->hiddenApp.idle();
#endif

// Fonts and Images in the cache must be deleted before the NanoVG context is deleted
internal->fontCache.clear();
@@ -470,12 +492,20 @@ Window::~Window() {

if (vg != nullptr)
{
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
#if defined NANOVG_GLES2
nvgDeleteGLES2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg);
nvgDeleteGLES2(internal->o_vg != nullptr ? internal->o_vg : vg);
#else
nvgDeleteGL2(internal->o_fbVg != nullptr ? internal->o_fbVg : fbVg);
nvgDeleteGL2(internal->o_vg != nullptr ? internal->o_vg : vg);
#endif
#else
#if defined NANOVG_GLES2
nvgDeleteGLES2(fbVg);
#else
nvgDeleteGL2(fbVg);
#endif
#endif
}
}


Loading…
Cancel
Save