Signed-off-by: falkTX <falktx@falktx.com>tags/22.07
@@ -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> |
@@ -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 | ||||
@@ -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 { | ||||
@@ -34,24 +34,38 @@ | |||||
# undef DEBUG | # undef DEBUG | ||||
#endif | #endif | ||||
#ifdef HAVE_LIBLO | |||||
# ifdef HEADLESS | |||||
# include <lo/lo.h> | |||||
# include "extra/Thread.hpp" | |||||
# endif | |||||
# include "CardinalCommon.hpp" | |||||
#if defined(HAVE_LIBLO) && defined(HEADLESS) | |||||
# include <lo/lo.h> | |||||
# include "extra/Thread.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"); | |||||
templatePath = system::join(asset::systemDir, CARDINAL_TEMPLATE_NAME); | |||||
factoryTemplatePath = system::join(asset::systemDir, CARDINAL_FACTORY_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(); | |||||
context->scene->rackScroll->reset(); | |||||
#ifdef DISTRHO_OS_WASM | |||||
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; | ||||
} | } | ||||
@@ -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/Label.hpp> | |||||
#include <ui/MenuOverlay.hpp> | |||||
#include <ui/SequentialLayout.hpp> | |||||
#include "CardinalCommon.hpp" | |||||
# include <ui/Button.hpp> | |||||
# include <ui/Label.hpp> | |||||
# include <ui/MenuOverlay.hpp> | |||||
# include <ui/SequentialLayout.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(); | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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); | |||||
?> |