Signed-off-by: falkTX <falktx@falktx.com>tags/22.02
| @@ -12,48 +12,6 @@ | |||||
| "changelogUrl": "https://github.com/VCVRack/Rack/blob/v2/CHANGELOG.md", | "changelogUrl": "https://github.com/VCVRack/Rack/blob/v2/CHANGELOG.md", | ||||
| "description": "Necessary modules built into Cardinal", | "description": "Necessary modules built into Cardinal", | ||||
| "modules": [ | "modules": [ | ||||
| { | |||||
| "slug": "AudioInterface2", | |||||
| "name": "Audio 2", | |||||
| "description": "Sends audio and CV to/from an audio device", | |||||
| "manualUrl": "https://vcvrack.com/manual/Core#Audio", | |||||
| "tags": [ | |||||
| "External" | |||||
| ], | |||||
| "hidden": true | |||||
| }, | |||||
| { | |||||
| "slug": "AudioInterface", | |||||
| "name": "Audio 8", | |||||
| "description": "Sends audio and CV to/from an audio device", | |||||
| "manualUrl": "https://vcvrack.com/manual/Core#Audio", | |||||
| "tags": [ | |||||
| "External" | |||||
| ], | |||||
| "hidden": true | |||||
| }, | |||||
| { | |||||
| "slug": "AudioInterface16", | |||||
| "name": "Audio 16", | |||||
| "description": "Sends audio and CV to/from an audio device", | |||||
| "manualUrl": "https://vcvrack.com/manual/Core#Audio", | |||||
| "tags": [ | |||||
| "External" | |||||
| ], | |||||
| "hidden": true | |||||
| }, | |||||
| { | |||||
| "slug": "MIDIToCVInterface", | |||||
| "name": "MIDI to CV", | |||||
| "description": "Converts MIDI from an external device to CV and gates", | |||||
| "manualUrl": "https://vcvrack.com/manual/Core#MIDI-CV", | |||||
| "tags": [ | |||||
| "External", | |||||
| "MIDI", | |||||
| "Polyphonic" | |||||
| ], | |||||
| "hidden": true | |||||
| }, | |||||
| { | { | ||||
| "slug": "MIDICCToCVInterface", | "slug": "MIDICCToCVInterface", | ||||
| "name": "MIDI CC to CV", | "name": "MIDI CC to CV", | ||||
| @@ -84,18 +42,6 @@ | |||||
| "MIDI" | "MIDI" | ||||
| ] | ] | ||||
| }, | }, | ||||
| { | |||||
| "slug": "CV-MIDI", | |||||
| "name": "CV to MIDI", | |||||
| "description": "Converts CV to MIDI and sends to an external device", | |||||
| "manualUrl": "https://vcvrack.com/manual/Core#CV-MIDI", | |||||
| "tags": [ | |||||
| "External", | |||||
| "MIDI", | |||||
| "Polyphonic" | |||||
| ], | |||||
| "hidden": true | |||||
| }, | |||||
| { | { | ||||
| "slug": "CV-CC", | "slug": "CV-CC", | ||||
| "name": "CV to MIDI CC", | "name": "CV to MIDI CC", | ||||
| @@ -567,14 +567,9 @@ std::string pluginPath(const std::string& dirname); | |||||
| // core plugins | // core plugins | ||||
| namespace core { | namespace core { | ||||
| extern Model* modelAudio2; | |||||
| extern Model* modelAudio8; | |||||
| extern Model* modelAudio16; | |||||
| extern Model* modelMIDI_CV; | |||||
| extern Model* modelMIDICC_CV; | extern Model* modelMIDICC_CV; | ||||
| extern Model* modelMIDI_Gate; | extern Model* modelMIDI_Gate; | ||||
| extern Model* modelMIDIMap; | extern Model* modelMIDIMap; | ||||
| extern Model* modelCV_MIDI; | |||||
| extern Model* modelCV_MIDICC; | extern Model* modelCV_MIDICC; | ||||
| extern Model* modelGate_MIDI; | extern Model* modelGate_MIDI; | ||||
| extern Model* modelBlank; | extern Model* modelBlank; | ||||
| @@ -670,14 +665,9 @@ static void initStatic__Core() | |||||
| const StaticPluginLoader spl(p, "Core"); | const StaticPluginLoader spl(p, "Core"); | ||||
| if (spl.ok()) | if (spl.ok()) | ||||
| { | { | ||||
| p->addModel(rack::core::modelAudio2); | |||||
| p->addModel(rack::core::modelAudio8); | |||||
| p->addModel(rack::core::modelAudio16); | |||||
| p->addModel(rack::core::modelMIDI_CV); | |||||
| p->addModel(rack::core::modelMIDICC_CV); | p->addModel(rack::core::modelMIDICC_CV); | ||||
| p->addModel(rack::core::modelMIDI_Gate); | p->addModel(rack::core::modelMIDI_Gate); | ||||
| p->addModel(rack::core::modelMIDIMap); | p->addModel(rack::core::modelMIDIMap); | ||||
| p->addModel(rack::core::modelCV_MIDI); | |||||
| p->addModel(rack::core::modelCV_MIDICC); | p->addModel(rack::core::modelCV_MIDICC); | ||||
| p->addModel(rack::core::modelGate_MIDI); | p->addModel(rack::core::modelGate_MIDI); | ||||
| p->addModel(rack::core::modelBlank); | p->addModel(rack::core::modelBlank); | ||||
| @@ -102,6 +102,7 @@ RACK_FILES += custom/network.cpp | |||||
| RACK_FILES += custom/osdialog.cpp | RACK_FILES += custom/osdialog.cpp | ||||
| RACK_FILES += override/blendish.c | RACK_FILES += override/blendish.c | ||||
| RACK_FILES += override/context.cpp | RACK_FILES += override/context.cpp | ||||
| RACK_FILES += override/plugin.cpp | |||||
| RACK_FILES += override/Engine.cpp | RACK_FILES += override/Engine.cpp | ||||
| RACK_FILES += override/MenuBar.cpp | RACK_FILES += override/MenuBar.cpp | ||||
| RACK_FILES += override/Model.cpp | RACK_FILES += override/Model.cpp | ||||
| @@ -120,11 +121,15 @@ IGNORED_FILES += Rack/src/gamepad.cpp | |||||
| IGNORED_FILES += Rack/src/keyboard.cpp | IGNORED_FILES += Rack/src/keyboard.cpp | ||||
| IGNORED_FILES += Rack/src/library.cpp | IGNORED_FILES += Rack/src/library.cpp | ||||
| IGNORED_FILES += Rack/src/network.cpp | IGNORED_FILES += Rack/src/network.cpp | ||||
| IGNORED_FILES += Rack/src/plugin.cpp | |||||
| IGNORED_FILES += Rack/src/rtaudio.cpp | IGNORED_FILES += Rack/src/rtaudio.cpp | ||||
| IGNORED_FILES += Rack/src/rtmidi.cpp | IGNORED_FILES += Rack/src/rtmidi.cpp | ||||
| IGNORED_FILES += Rack/src/app/MenuBar.cpp | IGNORED_FILES += Rack/src/app/MenuBar.cpp | ||||
| IGNORED_FILES += Rack/src/app/Scene.cpp | IGNORED_FILES += Rack/src/app/Scene.cpp | ||||
| IGNORED_FILES += Rack/src/app/TipWindow.cpp | IGNORED_FILES += Rack/src/app/TipWindow.cpp | ||||
| IGNORED_FILES += Rack/src/core/Audio.cpp | |||||
| IGNORED_FILES += Rack/src/core/CV_MIDI.cpp | |||||
| IGNORED_FILES += Rack/src/core/MIDI_CV.cpp | |||||
| IGNORED_FILES += Rack/src/engine/Engine.cpp | IGNORED_FILES += Rack/src/engine/Engine.cpp | ||||
| IGNORED_FILES += Rack/src/plugin/Model.cpp | IGNORED_FILES += Rack/src/plugin/Model.cpp | ||||
| IGNORED_FILES += Rack/src/window/Window.cpp | IGNORED_FILES += Rack/src/window/Window.cpp | ||||
| @@ -5,6 +5,7 @@ set -e | |||||
| diff -U3 ../Rack/dep/oui-blendish/blendish.c blendish.c > diffs/blendish.c.diff | diff -U3 ../Rack/dep/oui-blendish/blendish.c blendish.c > diffs/blendish.c.diff | ||||
| diff -U3 ../Rack/src/common.cpp common.cpp > diffs/common.cpp.diff | diff -U3 ../Rack/src/common.cpp common.cpp > diffs/common.cpp.diff | ||||
| diff -U3 ../Rack/src/context.cpp context.cpp > diffs/context.cpp.diff | diff -U3 ../Rack/src/context.cpp context.cpp > diffs/context.cpp.diff | ||||
| diff -U3 ../Rack/src/plugin.cpp plugin.cpp > diffs/plugin.cpp.diff | |||||
| diff -U3 ../Rack/src/app/MenuBar.cpp MenuBar.cpp > diffs/MenuBar.cpp.diff | diff -U3 ../Rack/src/app/MenuBar.cpp MenuBar.cpp > diffs/MenuBar.cpp.diff | ||||
| diff -U3 ../Rack/src/app/Scene.cpp Scene.cpp > diffs/Scene.cpp.diff | diff -U3 ../Rack/src/app/Scene.cpp Scene.cpp > diffs/Scene.cpp.diff | ||||
| diff -U3 ../Rack/src/engine/Engine.cpp Engine.cpp > diffs/Engine.cpp.diff | diff -U3 ../Rack/src/engine/Engine.cpp Engine.cpp > diffs/Engine.cpp.diff | ||||
| @@ -0,0 +1,359 @@ | |||||
| --- ../Rack/src/plugin.cpp 2022-01-15 14:44:46.395281005 +0000 | |||||
| +++ plugin.cpp 2022-01-23 22:59:41.770256440 +0000 | |||||
| @@ -1,308 +1,40 @@ | |||||
| -#include <thread> | |||||
| -#include <map> | |||||
| -#include <stdexcept> | |||||
| -#include <tuple> | |||||
| - | |||||
| -#include <sys/types.h> | |||||
| -#include <sys/stat.h> | |||||
| -#include <unistd.h> | |||||
| -#include <sys/param.h> // for MAXPATHLEN | |||||
| -#include <fcntl.h> | |||||
| -#if defined ARCH_WIN | |||||
| - #include <windows.h> | |||||
| - #include <direct.h> | |||||
| -#else | |||||
| - #include <dlfcn.h> // for dlopen | |||||
| -#endif | |||||
| -#include <dirent.h> | |||||
| +/* | |||||
| + * 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. | |||||
| + */ | |||||
| + | |||||
| +/** | |||||
| + * This file is an edited version of VCVRack's plugin.cpp | |||||
| + * Copyright (C) 2016-2021 VCV. | |||||
| + * | |||||
| + * 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 (at your option) any later version. | |||||
| + */ | |||||
| -#include <osdialog.h> | |||||
| -#include <jansson.h> | |||||
| +#include <algorithm> | |||||
| +#include <map> | |||||
| #include <plugin.hpp> | |||||
| -#include <system.hpp> | |||||
| -#include <asset.hpp> | |||||
| -#include <string.hpp> | |||||
| -#include <context.hpp> | |||||
| -#include <plugin/callbacks.hpp> | |||||
| -#include <settings.hpp> | |||||
| namespace rack { | |||||
| - | |||||
| -namespace core { | |||||
| -void init(rack::plugin::Plugin* plugin); | |||||
| -} // namespace core | |||||
| - | |||||
| namespace plugin { | |||||
| -//////////////////// | |||||
| -// private API | |||||
| -//////////////////// | |||||
| - | |||||
| -/** Returns library handle */ | |||||
| -static void* loadLibrary(std::string libraryPath) { | |||||
| -#if defined ARCH_WIN | |||||
| - SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); | |||||
| - std::wstring libraryFilenameW = string::UTF8toUTF16(libraryPath); | |||||
| - HINSTANCE handle = LoadLibraryW(libraryFilenameW.c_str()); | |||||
| - SetErrorMode(0); | |||||
| - if (!handle) { | |||||
| - int error = GetLastError(); | |||||
| - throw Exception("Failed to load library %s: code %d", libraryPath.c_str(), error); | |||||
| - } | |||||
| -#else | |||||
| - // Since Rack 2, plugins on Linux/Mac link to the absolute path /tmp/Rack2/libRack.<ext> | |||||
| - // Create a symlink at /tmp/Rack2 to the system dir containting libRack. | |||||
| - std::string systemDir = system::getAbsolute(asset::systemDir); | |||||
| - std::string linkPath = "/tmp/Rack2"; | |||||
| - if (!settings::devMode) { | |||||
| - // Clean up old symbolic link in case a different edition was run earlier | |||||
| - system::remove(linkPath); | |||||
| - system::createSymbolicLink(systemDir, linkPath); | |||||
| - } | |||||
| - // Load library with dlopen | |||||
| - void* handle = NULL; | |||||
| - #if defined ARCH_LIN | |||||
| - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); | |||||
| - #elif defined ARCH_MAC | |||||
| - handle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_LOCAL); | |||||
| - #endif | |||||
| - if (!settings::devMode) { | |||||
| - system::remove(linkPath); | |||||
| - } | |||||
| - if (!handle) | |||||
| - throw Exception("Failed to load library %s: %s", libraryPath.c_str(), dlerror()); | |||||
| -#endif | |||||
| - return handle; | |||||
| -} | |||||
| - | |||||
| -typedef void (*InitCallback)(Plugin*); | |||||
| - | |||||
| -static InitCallback loadPluginCallback(Plugin* plugin) { | |||||
| - // Load plugin library | |||||
| - std::string libraryExt; | |||||
| -#if defined ARCH_LIN | |||||
| - libraryExt = "so"; | |||||
| -#elif defined ARCH_WIN | |||||
| - libraryExt = "dll"; | |||||
| -#elif ARCH_MAC | |||||
| - libraryExt = "dylib"; | |||||
| -#endif | |||||
| - std::string libraryPath = system::join(plugin->path, "plugin." + libraryExt); | |||||
| - | |||||
| - // Check file existence | |||||
| - if (!system::isFile(libraryPath)) | |||||
| - throw Exception("Plugin binary not found at %s", libraryPath.c_str()); | |||||
| - | |||||
| - // Load dynamic/shared library | |||||
| - plugin->handle = loadLibrary(libraryPath); | |||||
| - | |||||
| - // Get plugin's init() function | |||||
| - InitCallback initCallback; | |||||
| -#if defined ARCH_WIN | |||||
| - initCallback = (InitCallback) GetProcAddress((HMODULE) plugin->handle, "init"); | |||||
| -#else | |||||
| - initCallback = (InitCallback) dlsym(plugin->handle, "init"); | |||||
| -#endif | |||||
| - if (!initCallback) | |||||
| - throw Exception("Failed to read init() symbol in %s", libraryPath.c_str()); | |||||
| - | |||||
| - return initCallback; | |||||
| -} | |||||
| - | |||||
| - | |||||
| -/** If path is blank, loads Core */ | |||||
| -static Plugin* loadPlugin(std::string path) { | |||||
| - if (path == "") | |||||
| - INFO("Loading Core plugin"); | |||||
| - else | |||||
| - INFO("Loading plugin from %s", path.c_str()); | |||||
| - | |||||
| - Plugin* plugin = new Plugin; | |||||
| - try { | |||||
| - // Set plugin path | |||||
| - plugin->path = (path == "") ? asset::systemDir : path; | |||||
| - | |||||
| - // Get modified timestamp | |||||
| - if (path != "") { | |||||
| - struct stat statbuf; | |||||
| - if (!stat(path.c_str(), &statbuf)) { | |||||
| -#if defined ARCH_MAC | |||||
| - plugin->modifiedTimestamp = (double) statbuf.st_mtimespec.tv_sec + statbuf.st_mtimespec.tv_nsec * 1e-9; | |||||
| -#elif defined ARCH_WIN | |||||
| - plugin->modifiedTimestamp = (double) statbuf.st_mtime; | |||||
| -#elif defined ARCH_LIN | |||||
| - plugin->modifiedTimestamp = (double) statbuf.st_mtim.tv_sec + statbuf.st_mtim.tv_nsec * 1e-9; | |||||
| -#endif | |||||
| - } | |||||
| - } | |||||
| - | |||||
| - // Load plugin.json | |||||
| - std::string manifestFilename = (path == "") ? asset::system("Core.json") : system::join(path, "plugin.json"); | |||||
| - FILE* file = std::fopen(manifestFilename.c_str(), "r"); | |||||
| - if (!file) | |||||
| - throw Exception("Manifest file %s does not exist", manifestFilename.c_str()); | |||||
| - DEFER({std::fclose(file);}); | |||||
| - | |||||
| - json_error_t error; | |||||
| - json_t* rootJ = json_loadf(file, 0, &error); | |||||
| - if (!rootJ) | |||||
| - throw Exception("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text); | |||||
| - DEFER({json_decref(rootJ);}); | |||||
| - | |||||
| - // Call init callback | |||||
| - InitCallback initCallback; | |||||
| - if (path == "") { | |||||
| - initCallback = core::init; | |||||
| - } | |||||
| - else { | |||||
| - initCallback = loadPluginCallback(plugin); | |||||
| - } | |||||
| - initCallback(plugin); | |||||
| - | |||||
| - // Load manifest | |||||
| - plugin->fromJson(rootJ); | |||||
| - | |||||
| - // Reject plugin if slug already exists | |||||
| - Plugin* existingPlugin = getPlugin(plugin->slug); | |||||
| - if (existingPlugin) | |||||
| - throw Exception("Plugin %s is already loaded, not attempting to load it again", plugin->slug.c_str()); | |||||
| - } | |||||
| - catch (Exception& e) { | |||||
| - WARN("Could not load plugin %s: %s", path.c_str(), e.what()); | |||||
| - delete plugin; | |||||
| - return NULL; | |||||
| - } | |||||
| - | |||||
| - INFO("Loaded %s v%s", plugin->slug.c_str(), plugin->version.c_str()); | |||||
| - plugins.push_back(plugin); | |||||
| - return plugin; | |||||
| -} | |||||
| - | |||||
| - | |||||
| -static void loadPlugins(std::string path) { | |||||
| - for (std::string pluginPath : system::getEntries(path)) { | |||||
| - if (!system::isDirectory(pluginPath)) | |||||
| - continue; | |||||
| - if (!loadPlugin(pluginPath)) { | |||||
| - // Ignore bad plugins. They are reported in the log. | |||||
| - } | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -static void extractPackages(std::string path) { | |||||
| - std::string message; | |||||
| - | |||||
| - for (std::string packagePath : system::getEntries(path)) { | |||||
| - if (!system::isFile(packagePath)) | |||||
| - continue; | |||||
| - if (system::getExtension(packagePath) != ".vcvplugin") | |||||
| - continue; | |||||
| - | |||||
| - // Extract package | |||||
| - INFO("Extracting package %s", packagePath.c_str()); | |||||
| - try { | |||||
| - system::unarchiveToDirectory(packagePath, path); | |||||
| - } | |||||
| - catch (Exception& e) { | |||||
| - WARN("Plugin package %s failed to extract: %s", packagePath.c_str(), e.what()); | |||||
| - message += string::f("Could not extract plugin package %s\n", packagePath.c_str()); | |||||
| - continue; | |||||
| - } | |||||
| - // Remove package | |||||
| - system::remove(packagePath.c_str()); | |||||
| - } | |||||
| - if (!message.empty()) { | |||||
| - osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); | |||||
| - } | |||||
| -} | |||||
| - | |||||
| -//////////////////// | |||||
| -// public API | |||||
| -//////////////////// | |||||
| - | |||||
| -void init() { | |||||
| - // Don't re-initialize | |||||
| - assert(plugins.empty()); | |||||
| - | |||||
| - // Load Core | |||||
| - loadPlugin(""); | |||||
| - | |||||
| - pluginsPath = asset::user("plugins"); | |||||
| - | |||||
| - // Get user plugins directory | |||||
| - system::createDirectory(pluginsPath); | |||||
| - | |||||
| - // Extract packages and load plugins | |||||
| - extractPackages(pluginsPath); | |||||
| - loadPlugins(pluginsPath); | |||||
| - | |||||
| - // If Fundamental wasn't loaded, copy the bundled Fundamental package and load it | |||||
| - if (!settings::devMode && !getPlugin("Fundamental")) { | |||||
| - std::string fundamentalSrc = asset::system("Fundamental.vcvplugin"); | |||||
| - std::string fundamentalDir = system::join(pluginsPath, "Fundamental"); | |||||
| - if (system::isFile(fundamentalSrc)) { | |||||
| - INFO("Extracting bundled Fundamental package"); | |||||
| - try { | |||||
| - system::unarchiveToDirectory(fundamentalSrc.c_str(), pluginsPath.c_str()); | |||||
| - loadPlugin(fundamentalDir); | |||||
| - } | |||||
| - catch (Exception& e) { | |||||
| - WARN("Could not extract Fundamental package: %s", e.what()); | |||||
| - } | |||||
| - } | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -static void destroyPlugin(Plugin* plugin) { | |||||
| - void* handle = plugin->handle; | |||||
| - | |||||
| - // Call destroy() if defined in the plugin library | |||||
| - typedef void (*DestroyCallback)(); | |||||
| - DestroyCallback destroyCallback = NULL; | |||||
| - if (handle) { | |||||
| -#if defined ARCH_WIN | |||||
| - destroyCallback = (DestroyCallback) GetProcAddress((HMODULE) handle, "destroy"); | |||||
| -#else | |||||
| - destroyCallback = (DestroyCallback) dlsym(handle, "destroy"); | |||||
| -#endif | |||||
| - } | |||||
| - if (destroyCallback) { | |||||
| - try { | |||||
| - destroyCallback(); | |||||
| - } | |||||
| - catch (Exception& e) { | |||||
| - WARN("Could not destroy plugin %s", plugin->slug.c_str()); | |||||
| - } | |||||
| - } | |||||
| - | |||||
| - // We must delete the Plugin instance *before* freeing the library, because the vtables of Model subclasses are defined in the library, which are needed in the Plugin destructor. | |||||
| - delete plugin; | |||||
| - | |||||
| - // Free library handle | |||||
| - if (handle) { | |||||
| -#if defined ARCH_WIN | |||||
| - FreeLibrary((HINSTANCE) handle); | |||||
| -#else | |||||
| - dlclose(handle); | |||||
| -#endif | |||||
| - } | |||||
| -} | |||||
| - | |||||
| - | |||||
| -void destroy() { | |||||
| - for (Plugin* plugin : plugins) { | |||||
| - INFO("Destroying plugin %s", plugin->name.c_str()); | |||||
| - destroyPlugin(plugin); | |||||
| - } | |||||
| - plugins.clear(); | |||||
| -} | |||||
| - | |||||
| - | |||||
| /** Given slug => fallback slug. | |||||
| Correctly handles bidirectional fallbacks. | |||||
| To request fallback slugs to be added to this list, open a GitHub issue. | |||||
| @@ -352,6 +84,11 @@ | |||||
| */ | |||||
| using PluginModuleSlug = std::tuple<std::string, std::string>; | |||||
| static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = { | |||||
| + {{"Core", "AudioInterface2"}, {"Cardinal", "HostAudio2"}}, | |||||
| + {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}}, | |||||
| + {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}}, | |||||
| + {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}}, | |||||
| + {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}}, | |||||
| {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}}, | |||||
| {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}}, | |||||
| // {{"", ""}, {"", ""}}, | |||||
| @@ -441,7 +178,6 @@ | |||||
| } | |||||
| -std::string pluginsPath; | |||||
| std::vector<Plugin*> plugins; | |||||
| @@ -0,0 +1,185 @@ | |||||
| /* | |||||
| * 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. | |||||
| */ | |||||
| /** | |||||
| * This file is an edited version of VCVRack's plugin.cpp | |||||
| * Copyright (C) 2016-2021 VCV. | |||||
| * | |||||
| * 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 (at your option) any later version. | |||||
| */ | |||||
| #include <algorithm> | |||||
| #include <map> | |||||
| #include <plugin.hpp> | |||||
| namespace rack { | |||||
| namespace plugin { | |||||
| /** Given slug => fallback slug. | |||||
| Correctly handles bidirectional fallbacks. | |||||
| To request fallback slugs to be added to this list, open a GitHub issue. | |||||
| */ | |||||
| static const std::map<std::string, std::string> pluginSlugFallbacks = { | |||||
| {"VultModulesFree", "VultModules"}, | |||||
| {"VultModules", "VultModulesFree"}, | |||||
| {"AudibleInstrumentsPreview", "AudibleInstruments"}, | |||||
| // {"", ""}, | |||||
| }; | |||||
| Plugin* getPlugin(const std::string& pluginSlug) { | |||||
| if (pluginSlug.empty()) | |||||
| return NULL; | |||||
| auto it = std::find_if(plugins.begin(), plugins.end(), [=](Plugin* p) { | |||||
| return p->slug == pluginSlug; | |||||
| }); | |||||
| if (it != plugins.end()) | |||||
| return *it; | |||||
| return NULL; | |||||
| } | |||||
| Plugin* getPluginFallback(const std::string& pluginSlug) { | |||||
| if (pluginSlug.empty()) | |||||
| return NULL; | |||||
| // Attempt example plugin | |||||
| Plugin* p = getPlugin(pluginSlug); | |||||
| if (p) | |||||
| return p; | |||||
| // Attempt fallback plugin slug | |||||
| auto it = pluginSlugFallbacks.find(pluginSlug); | |||||
| if (it != pluginSlugFallbacks.end()) | |||||
| return getPlugin(it->second); | |||||
| return NULL; | |||||
| } | |||||
| /** Given slug => fallback slug. | |||||
| Correctly handles bidirectional fallbacks. | |||||
| To request fallback slugs to be added to this list, open a GitHub issue. | |||||
| */ | |||||
| using PluginModuleSlug = std::tuple<std::string, std::string>; | |||||
| static const std::map<PluginModuleSlug, PluginModuleSlug> moduleSlugFallbacks = { | |||||
| {{"Core", "AudioInterface2"}, {"Cardinal", "HostAudio2"}}, | |||||
| {{"Core", "AudioInterface"}, {"Cardinal", "HostAudio8"}}, | |||||
| {{"Core", "AudioInterface16"}, {"Cardinal", "HostAudio8"}}, | |||||
| {{"Core", "MIDIToCVInterface"}, {"Cardinal", "HostMIDI"}}, | |||||
| {{"Core", "CV-MIDI"}, {"Cardinal", "HostMIDI"}}, | |||||
| {{"MindMeld-ShapeMasterPro", "ShapeMasterPro"}, {"MindMeldModular", "ShapeMaster"}}, | |||||
| {{"MindMeldModular", "ShapeMaster"}, {"MindMeld-ShapeMasterPro", "ShapeMasterPro"}}, | |||||
| // {{"", ""}, {"", ""}}, | |||||
| }; | |||||
| Model* getModel(const std::string& pluginSlug, const std::string& modelSlug) { | |||||
| if (pluginSlug.empty() || modelSlug.empty()) | |||||
| return NULL; | |||||
| Plugin* p = getPlugin(pluginSlug); | |||||
| if (!p) | |||||
| return NULL; | |||||
| return p->getModel(modelSlug); | |||||
| } | |||||
| Model* getModelFallback(const std::string& pluginSlug, const std::string& modelSlug) { | |||||
| if (pluginSlug.empty() || modelSlug.empty()) | |||||
| return NULL; | |||||
| // Attempt exact plugin and model | |||||
| Model* m = getModel(pluginSlug, modelSlug); | |||||
| if (m) | |||||
| return m; | |||||
| // Attempt fallback module | |||||
| auto it = moduleSlugFallbacks.find(std::make_tuple(pluginSlug, modelSlug)); | |||||
| if (it != moduleSlugFallbacks.end()) { | |||||
| Model* m = getModel(std::get<0>(it->second), std::get<1>(it->second)); | |||||
| if (m) | |||||
| return m; | |||||
| } | |||||
| // Attempt fallback plugin | |||||
| auto it2 = pluginSlugFallbacks.find(pluginSlug); | |||||
| if (it2 != pluginSlugFallbacks.end()) { | |||||
| Model* m = getModel(it2->second, modelSlug); | |||||
| if (m) | |||||
| return m; | |||||
| } | |||||
| return NULL; | |||||
| } | |||||
| Model* modelFromJson(json_t* moduleJ) { | |||||
| // Get slugs | |||||
| json_t* pluginSlugJ = json_object_get(moduleJ, "plugin"); | |||||
| if (!pluginSlugJ) | |||||
| throw Exception("\"plugin\" property not found in module JSON"); | |||||
| std::string pluginSlug = json_string_value(pluginSlugJ); | |||||
| pluginSlug = normalizeSlug(pluginSlug); | |||||
| json_t* modelSlugJ = json_object_get(moduleJ, "model"); | |||||
| if (!modelSlugJ) | |||||
| throw Exception("\"model\" property not found in module JSON"); | |||||
| std::string modelSlug = json_string_value(modelSlugJ); | |||||
| modelSlug = normalizeSlug(modelSlug); | |||||
| // Get Model | |||||
| Model* model = getModelFallback(pluginSlug, modelSlug); | |||||
| if (!model) | |||||
| throw Exception("Could not find module %s/%s", pluginSlug.c_str(), modelSlug.c_str()); | |||||
| return model; | |||||
| } | |||||
| bool isSlugValid(const std::string& slug) { | |||||
| for (char c : slug) { | |||||
| if (!(std::isalnum(c) || c == '-' || c == '_')) | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| std::string normalizeSlug(const std::string& slug) { | |||||
| std::string s; | |||||
| for (char c : slug) { | |||||
| if (!(std::isalnum(c) || c == '-' || c == '_')) | |||||
| continue; | |||||
| s += c; | |||||
| } | |||||
| return s; | |||||
| } | |||||
| std::vector<Plugin*> plugins; | |||||
| } // namespace plugin | |||||
| } // namespace rack | |||||
| @@ -30,13 +30,6 @@ | |||||
| "params": [], | "params": [], | ||||
| "leftModuleId": 1, | "leftModuleId": 1, | ||||
| "rightModuleId": 3, | "rightModuleId": 3, | ||||
| "data": { | |||||
| "channels": 1, | |||||
| "polyMode": 0, | |||||
| "clockDivision": 24, | |||||
| "lastPitch": 8192, | |||||
| "lastMod": 0 | |||||
| }, | |||||
| "pos": [ | "pos": [ | ||||
| 5, | 5, | ||||
| 0 | 0 | ||||