Allow wasm fetch patchstorage thigns, set system factory template

Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
falkTX 2022-07-09 00:17:07 +01:00
parent e389ca7469
commit 3af971534c
No known key found for this signature in database
GPG key ID: CDBAA37ABC74FBA0
7 changed files with 248 additions and 38 deletions

View file

@ -17,6 +17,9 @@
#pragma once #pragma once
#ifndef __EMSCRIPTEN__
#define GHC_OS_DETECTED #define GHC_OS_DETECTED
#define GHC_OS_LINUX #define GHC_OS_LINUX
#endif
#include_next <ghc/filesystem.hpp> #include_next <ghc/filesystem.hpp>

View file

@ -100,6 +100,10 @@ std::string getSpecialPath(const SpecialPath type)
} }
#endif #endif
#ifdef DISTRHO_OS_WASM
char* patchStorageSlug = nullptr;
#endif
std::string homeDir() std::string homeDir()
{ {
# ifdef ARCH_WIN # ifdef ARCH_WIN

View file

@ -25,6 +25,10 @@
# define REMOTE_HOST_PORT "2228" # define REMOTE_HOST_PORT "2228"
#endif #endif
#ifdef DISTRHO_OS_WASM
# define CARDINAL_IMPORTED_TEMPLATE_FILENAME "/imported.vcv"
#endif
extern const std::string CARDINAL_VERSION; extern const std::string CARDINAL_VERSION;
namespace rack { namespace rack {
@ -53,6 +57,10 @@ enum SpecialPath {
std::string getSpecialPath(SpecialPath type); std::string getSpecialPath(SpecialPath type);
#endif #endif
#ifdef DISTRHO_OS_WASM
extern char* patchStorageSlug;
#endif
} // namespace rack } // namespace rack
namespace patchUtils { namespace patchUtils {

View file

@ -34,24 +34,38 @@
# undef DEBUG # undef DEBUG
#endif #endif
#ifdef HAVE_LIBLO #if defined(HAVE_LIBLO) && defined(HEADLESS)
# ifdef HEADLESS # include <lo/lo.h>
# include <lo/lo.h> # include "extra/Thread.hpp"
# include "extra/Thread.hpp"
# endif
# include "CardinalCommon.hpp"
#endif #endif
#include <list> #include <list>
#include "CardinalCommon.hpp"
#include "DistrhoPluginUtils.hpp" #include "DistrhoPluginUtils.hpp"
#include "PluginContext.hpp" #include "PluginContext.hpp"
#include "extra/Base64.hpp" #include "extra/Base64.hpp"
#ifndef DISTRHO_OS_WASM #ifdef DISTRHO_OS_WASM
# include <emscripten/emscripten.h>
#else
# include "extra/SharedResourcePointer.hpp" # include "extra/SharedResourcePointer.hpp"
#endif #endif
#if CARDINAL_VARIANT_FX
# define CARDINAL_FACTORY_TEMPLATE_NAME "template-fx.vcv"
#elif CARDINAL_VARIANT_SYNTH
# define CARDINAL_FACTORY_TEMPLATE_NAME "template-synth.vcv"
#else
# define CARDINAL_FACTORY_TEMPLATE_NAME "template.vcv"
#endif
#ifdef DISTRHO_OS_WASM
# define CARDINAL_TEMPLATE_NAME "template-wasm.vcv"
#else
# define CARDINAL_TEMPLATE_NAME CARDINAL_FACTORY_TEMPLATE_NAME
#endif
static const constexpr uint kCardinalStateBaseCount = 3; // patch, screenshot, comment static const constexpr uint kCardinalStateBaseCount = 3; // patch, screenshot, comment
#ifndef HEADLESS #ifndef HEADLESS
@ -63,14 +77,6 @@ static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount + 2; /
static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount; static const constexpr uint kCardinalStateCount = kCardinalStateBaseCount;
#endif #endif
#if CARDINAL_VARIANT_FX
# define CARDINAL_TEMPLATE_NAME "template-fx.vcv"
#elif CARDINAL_VARIANT_SYNTH
# define CARDINAL_TEMPLATE_NAME "template-synth.vcv"
#else
# define CARDINAL_TEMPLATE_NAME "template.vcv"
#endif
namespace rack { namespace rack {
namespace engine { namespace engine {
void Engine_setAboutToClose(Engine*); void Engine_setAboutToClose(Engine*);
@ -97,6 +103,21 @@ bool d_isDiffHigherThanLimit(const T& v1, const T& v2, const T& limit)
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
#ifdef DISTRHO_OS_WASM
EM_JS(char*, getPatchStorageSlug, (), {
var searchParams = new URLSearchParams(window.location.search);
var patch = searchParams.get('patchstorage');
if (!patch)
return null;
var length = lengthBytesUTF8(patch) + 1;
var str = _malloc(length);
stringToUTF8(patch, str, length);
return str;
});
#endif
// -----------------------------------------------------------------------------------------------------------
struct Initializer struct Initializer
#if defined(HAVE_LIBLO) && defined(HEADLESS) #if defined(HAVE_LIBLO) && defined(HEADLESS)
: public Thread : public Thread
@ -107,6 +128,7 @@ struct Initializer
CardinalBasePlugin* oscPlugin = nullptr; CardinalBasePlugin* oscPlugin = nullptr;
#endif #endif
std::string templatePath; std::string templatePath;
std::string factoryTemplatePath;
Initializer(const CardinalBasePlugin* const plugin) Initializer(const CardinalBasePlugin* const plugin)
{ {
@ -158,6 +180,7 @@ struct Initializer
asset::bundlePath = system::join(resourcePath, "PluginManifests"); asset::bundlePath = system::join(resourcePath, "PluginManifests");
asset::systemDir = resourcePath; asset::systemDir = resourcePath;
templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME); templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME);
factoryTemplatePath = system::join(asset::systemDir, CARDINAL_FACTORY_TEMPLATE_NAME);
} }
} }
@ -170,6 +193,7 @@ struct Initializer
if (system::exists(system::join(asset::systemDir, "res"))) if (system::exists(system::join(asset::systemDir, "res")))
{ {
templatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR CARDINAL_TEMPLATE_NAME; templatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR CARDINAL_TEMPLATE_NAME;
factoryTemplatePath = CARDINAL_PLUGIN_SOURCE_DIR DISTRHO_OS_SEP_STR CARDINAL_FACTORY_TEMPLATE_NAME;
} }
// If source code dir does not exist use install target prefix as system dir // If source code dir does not exist use install target prefix as system dir
else else
@ -187,17 +211,20 @@ struct Initializer
asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal"; asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal";
#endif #endif
if (! asset::systemDir.empty()) asset::bundlePath = system::join(asset::systemDir, "PluginManifests");
{ templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME);
asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); factoryTemplatePath = system::join(asset::systemDir, CARDINAL_FACTORY_TEMPLATE_NAME);
templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME);
}
} }
} }
asset::userDir = asset::systemDir; asset::userDir = asset::systemDir;
} }
#ifdef DISTRHO_OS_WASM
if ((patchStorageSlug = getPatchStorageSlug()) != nullptr)
templatePath = CARDINAL_IMPORTED_TEMPLATE_FILENAME;
#endif
// Log environment // Log environment
INFO("%s %s v%s", APP_NAME.c_str(), APP_EDITION.c_str(), APP_VERSION.c_str()); INFO("%s %s v%s", APP_NAME.c_str(), APP_EDITION.c_str(), APP_VERSION.c_str());
INFO("%s", system::getOperatingSystemInfo().c_str()); INFO("%s", system::getOperatingSystemInfo().c_str());
@ -206,6 +233,7 @@ struct Initializer
INFO("System directory: %s", asset::systemDir.c_str()); INFO("System directory: %s", asset::systemDir.c_str());
INFO("User directory: %s", asset::userDir.c_str()); INFO("User directory: %s", asset::userDir.c_str());
INFO("Template patch: %s", templatePath.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 // Report to user if something is wrong with the installation
if (asset::systemDir.empty()) if (asset::systemDir.empty())
@ -564,6 +592,7 @@ public:
context->patch = new rack::patch::Manager; context->patch = new rack::patch::Manager;
context->patch->autosavePath = fAutosavePath; context->patch->autosavePath = fAutosavePath;
context->patch->templatePath = fInitializer->templatePath; context->patch->templatePath = fInitializer->templatePath;
context->patch->factoryTemplatePath = fInitializer->factoryTemplatePath;
context->event = new rack::widget::EventState; context->event = new rack::widget::EventState;
context->scene = new rack::app::Scene; context->scene = new rack::app::Scene;
@ -572,28 +601,33 @@ public:
if (! isDummyInstance()) if (! isDummyInstance())
context->window = new rack::window::Window; context->window = new rack::window::Window;
context->patch->loadTemplate(); #ifdef DISTRHO_OS_WASM
context->scene->rackScroll->reset(); if (rack::patchStorageSlug == nullptr)
#endif
{
context->patch->loadTemplate();
context->scene->rackScroll->reset();
}
#if defined(HAVE_LIBLO) && defined(HEADLESS) #if defined(HAVE_LIBLO) && defined(HEADLESS)
fInitializer->oscPlugin = this; fInitializer->oscPlugin = this;
#endif #endif
} }
~CardinalPlugin() override ~CardinalPlugin() override
{ {
#if defined(HAVE_LIBLO) && defined(HEADLESS) #if defined(HAVE_LIBLO) && defined(HEADLESS)
fInitializer->oscPlugin = nullptr; fInitializer->oscPlugin = nullptr;
#endif #endif
{ {
const ScopedContext sc(this); const ScopedContext sc(this);
context->patch->clear(); context->patch->clear();
// do a little dance to prevent context scene deletion from saving to temp dir // do a little dance to prevent context scene deletion from saving to temp dir
#ifndef HEADLESS #ifndef HEADLESS
const ScopedValueSetter<bool> svs(rack::settings::headless, true); const ScopedValueSetter<bool> svs(rack::settings::headless, true);
#endif #endif
Engine_setAboutToClose(context->engine); Engine_setAboutToClose(context->engine);
delete context; delete context;
} }

View file

@ -31,11 +31,12 @@
#include <window/Window.hpp> #include <window/Window.hpp>
#ifdef DISTRHO_OS_WASM #ifdef DISTRHO_OS_WASM
#include <ui/Button.hpp> # include <ui/Button.hpp>
#include <ui/Label.hpp> # include <ui/Label.hpp>
#include <ui/MenuOverlay.hpp> # include <ui/MenuOverlay.hpp>
#include <ui/SequentialLayout.hpp> # include <ui/SequentialLayout.hpp>
#include "CardinalCommon.hpp" # include "CardinalCommon.hpp"
# include <emscripten/emscripten.h>
#endif #endif
#ifdef NDEBUG #ifdef NDEBUG
@ -189,6 +190,104 @@ struct WasmWelcomeDialog : rack::widget::OpaqueWidget
Widget::draw(args); Widget::draw(args);
} }
}; };
struct WasmPatchStorageLoadingDialog : rack::widget::OpaqueWidget
{
static const constexpr float margin = 10;
rack::ui::MenuOverlay* overlay;
WasmPatchStorageLoadingDialog()
{
using rack::ui::Label;
using rack::ui::MenuOverlay;
using rack::ui::SequentialLayout;
box.size = rack::math::Vec(300, 50);
SequentialLayout* const layout = new SequentialLayout;
layout->box.pos = rack::math::Vec(0, 0);
layout->box.size = box.size;
layout->orientation = SequentialLayout::VERTICAL_ORIENTATION;
layout->margin = rack::math::Vec(margin, margin);
layout->spacing = rack::math::Vec(margin, margin);
layout->wrap = false;
addChild(layout);
Label* const label = new Label;
label->box.size.x = box.size.x - 2*margin;
label->box.size.y = box.size.y - 2*margin - 40;
label->fontSize = 16;
label->text = "Load patch from PatchStorage...\n";
layout->addChild(label);
overlay = new MenuOverlay;
overlay->bgColor = nvgRGBAf(0, 0, 0, 0.33);
overlay->addChild(this);
APP->scene->addChild(overlay);
}
void step() override
{
OpaqueWidget::step();
box.pos = parent->box.size.minus(box.size).div(2).round();
}
void draw(const DrawArgs& args) override
{
bndMenuBackground(args.vg, 0.0, 0.0, box.size.x, box.size.y, 0);
Widget::draw(args);
}
};
static void downloadPatchStorageFailed(const char* const filename)
{
d_stdout("downloadPatchStorageFailed %s", filename);
CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui);
if (ui->psDialog != nullptr)
{
ui->psDialog->overlay->requestDelete();
asyncDialog::create("Failed to fetch patch from PatchStorage");
}
using namespace rack;
context->patch->templatePath = system::join(asset::systemDir, "template-synth.vcv"); // FIXME
context->patch->loadTemplate();
context->scene->rackScroll->reset();
}
static void downloadPatchStorageSucceeded(const char* const filename)
{
d_stdout("downloadPatchStorageSucceeded %s | %s", filename, APP->patch->templatePath.c_str());
CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
CardinalBaseUI* const ui = static_cast<CardinalBaseUI*>(context->ui);
ui->psDialog->overlay->requestDelete();
ui->psDialog = nullptr;
if (FILE* f = fopen(filename, "r"))
{
uint8_t buf[8] = {};
fread(buf, 8, 1, f);
d_stdout("read patch %x %x %x %x %x %x %x %x",
buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);
fclose(f);
}
try {
context->patch->load(CARDINAL_IMPORTED_TEMPLATE_FILENAME);
} catch (rack::Exception& e) {
const std::string message = rack::string::f("Could not load patch: %s", e.what());
asyncDialog::create(message.c_str());
return;
}
context->scene->rackScroll->reset();
context->patch->path = "";
context->history->setSaved();
}
#endif #endif
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
@ -288,7 +387,21 @@ public:
} }
#ifdef DISTRHO_OS_WASM #ifdef DISTRHO_OS_WASM
new WasmWelcomeDialog(); if (rack::patchStorageSlug != nullptr)
{
std::string url("/patchstorage.php?slug=");
url += rack::patchStorageSlug;
std::free(rack::patchStorageSlug);
rack::patchStorageSlug = nullptr;
psDialog = new WasmPatchStorageLoadingDialog();
emscripten_async_wget(url.c_str(), context->patch->templatePath.c_str(),
downloadPatchStorageSucceeded, downloadPatchStorageFailed);
}
else
{
new WasmWelcomeDialog();
}
#endif #endif
context->window->step(); context->window->step();

View file

@ -122,10 +122,6 @@ void handleHostParameterDrag(const CardinalPluginContext* pcontext, uint index,
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
struct CardinalAudioDevice;
struct CardinalMidiInputDevice;
struct CardinalMidiOutputDevice;
CardinalPluginContext* getRackContextFromPlugin(void* ptr); CardinalPluginContext* getRackContextFromPlugin(void* ptr);
class CardinalBasePlugin : public Plugin { class CardinalBasePlugin : public Plugin {
@ -139,12 +135,18 @@ public:
}; };
#ifndef HEADLESS #ifndef HEADLESS
struct WasmPatchStorageLoadingDialog;
class CardinalBaseUI : public UI { class CardinalBaseUI : public UI {
public: public:
CardinalPluginContext* const context; CardinalPluginContext* const context;
bool saving; bool saving;
bool savingUncompressed; bool savingUncompressed;
#ifdef DISTRHO_OS_WASM
WasmPatchStorageLoadingDialog* psDialog;
#endif
// for 3rd party modules // for 3rd party modules
std::function<void(char* path)> filebrowseraction; std::function<void(char* path)> filebrowseraction;
FileBrowserHandle filebrowserhandle; FileBrowserHandle filebrowserhandle;
@ -154,6 +156,9 @@ public:
context(getRackContextFromPlugin(getPluginInstancePointer())), context(getRackContextFromPlugin(getPluginInstancePointer())),
saving(false), saving(false),
savingUncompressed(false), savingUncompressed(false),
#ifdef DISTRHO_OS_WASM
psDialog(nullptr),
#endif
filebrowseraction(), filebrowseraction(),
filebrowserhandle(nullptr) filebrowserhandle(nullptr)
{ {

View file

@ -0,0 +1,43 @@
<?php
$slug = filter_input(INPUT_GET, 'slug', FILTER_SANITIZE_ENCODED);
if (!$slug) { http_response_code(404); die(); }
$api = 'https://patchstorage.com/api/alpha';
$search = file_get_contents($api.'/patches?platform=7834&slug='.$slug);
if (!$search) { http_response_code(404); die(); }
$searchJ = json_decode($search, true);
if (!$searchJ) { http_response_code(404); die(); }
if (!$searchJ[0]) { http_response_code(404); die(); }
$patchId = $searchJ[0]['id'];
if (!$patchId) { http_response_code(404); die(); }
$patchDetails = file_get_contents($api.'/patches/'.$patchId);
if (!$patchDetails) { http_response_code(404); die(); }
$patchDetailsJ = json_decode($patchDetails, true);
if (!$patchDetailsJ) { http_response_code(404); die(); }
$patchFiles = $patchDetailsJ['files'];
if (!$patchFiles) { http_response_code(404); die(); }
$patchFileDetails = $patchFiles[0];
if (!$patchFileDetails) { http_response_code(404); die(); }
if (!$patchFileDetails['filename']) { http_response_code(404); die(); }
if (!$patchFileDetails['url']) { http_response_code(404); die(); }
$contents = file_get_contents($patchFileDetails['url']);
if (!$contents) { http_response_code(404); die(); }
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$patchFileDetails['filename'].'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($contents));
flush();
die($contents);
?>