/* * DISTRHO Cardinal Plugin * Copyright (C) 2021-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the LICENSE file. */ #include "rack.hpp" #include "plugin.hpp" #include "DistrhoUtils.hpp" // Cardinal (built-in) #include "Cardinal/src/plugin.hpp" // Fundamental #include "Fundamental/src/plugin.hpp" // Aria extern Model* modelSpleet; extern Model* modelSwerge; // AudibleInstruments #include "AudibleInstruments/src/plugin.hpp" // BogaudioModules - integrate theme/skin support #include #include #include #include #include #define private public #include "BogaudioModules/src/skins.hpp" #undef private // BogaudioModules extern Model* modelAD; extern Model* modelBogaudioLFO; extern Model* modelBogaudioNoise; extern Model* modelBogaudioVCA; extern Model* modelBogaudioVCF; extern Model* modelBogaudioVCO; extern Model* modelOffset; extern Model* modelSampleHold; extern Model* modelSwitch; extern Model* modelSwitch18; extern Model* modelUnison; // MockbaModular #include "MockbaModular/src/plugin.hpp" #include "MockbaModular/src/MockbaModular.hpp" #undef min #define saveBack ignoreMockbaModular1 #define loadBack ignoreMockbaModular2 #include "MockbaModular/src/MockbaModular.cpp" #undef saveBack #undef loadBack std::string loadBack(int) { return "res/Empty_gray.svg"; } // surgext #include "surgext/src/SurgeXT.h" void surgext_rack_initialize(); void surgext_rack_update_theme(); // ValleyAudio #include "ValleyAudio/src/Valley.hpp" // known terminal modules std::vector hostTerminalModels; // plugin instances Plugin* pluginInstance__Cardinal; Plugin* pluginInstance__Fundamental; Plugin* pluginInstance__Aria; Plugin* pluginInstance__AudibleInstruments; Plugin* pluginInstance__BogaudioModules; Plugin* pluginInstance__MockbaModular; Plugin* pluginInstance__surgext; Plugin* pluginInstance__ValleyAudio; namespace rack { namespace asset { std::string pluginManifest(const std::string& dirname); std::string pluginPath(const std::string& dirname); } namespace plugin { struct StaticPluginLoader { Plugin* const plugin; FILE* file; json_t* rootJ; StaticPluginLoader(Plugin* const p, const char* const name) : plugin(p), file(nullptr), rootJ(nullptr) { #ifdef DEBUG DEBUG("Loading plugin module %s", name); #endif p->path = asset::pluginPath(name); const std::string manifestFilename = asset::pluginManifest(name); if ((file = std::fopen(manifestFilename.c_str(), "r")) == nullptr) { d_stderr2("Manifest file %s does not exist", manifestFilename.c_str()); return; } json_error_t error; if ((rootJ = json_loadf(file, 0, &error)) == nullptr) { d_stderr2("JSON parsing error at %s %d:%d %s", manifestFilename.c_str(), error.line, error.column, error.text); return; } // force ABI, we use static plugins so this doesnt matter as long as it builds json_t* const version = json_string((APP_VERSION_MAJOR + ".0").c_str()); json_object_set(rootJ, "version", version); json_decref(version); // Load manifest p->fromJson(rootJ); // Reject plugin if slug already exists if (Plugin* const existingPlugin = getPlugin(p->slug)) throw Exception("Plugin %s is already loaded, not attempting to load it again", p->slug.c_str()); } ~StaticPluginLoader() { if (rootJ != nullptr) { // Load modules manifest json_t* const modulesJ = json_object_get(rootJ, "modules"); plugin->modulesFromJson(modulesJ); json_decref(rootJ); plugins.push_back(plugin); } if (file != nullptr) std::fclose(file); } bool ok() const noexcept { return rootJ != nullptr; } void removeModule(const char* const slugToRemove) const noexcept { json_t* const modules = json_object_get(rootJ, "modules"); DISTRHO_SAFE_ASSERT_RETURN(modules != nullptr,); size_t i; json_t* v; json_array_foreach(modules, i, v) { if (json_t* const slug = json_object_get(v, "slug")) { if (const char* const value = json_string_value(slug)) { if (std::strcmp(value, slugToRemove) == 0) { json_array_remove(modules, i); break; } } } } } }; static void initStatic__Cardinal() { Plugin* const p = new Plugin; pluginInstance__Cardinal = p; const StaticPluginLoader spl(p, "Cardinal"); if (spl.ok()) { p->addModel(modelHostAudio2); p->addModel(modelHostCV); p->addModel(modelHostMIDI); p->addModel(modelHostMIDICC); p->addModel(modelHostMIDIGate); p->addModel(modelHostMIDIMap); p->addModel(modelHostParameters); p->addModel(modelHostParametersMap); p->addModel(modelHostTime); p->addModel(modelTextEditor); /* TODO #ifdef HAVE_FFTW3F p->addModel(modelAudioToCVPitch); #else */ spl.removeModule("AudioToCVPitch"); /* #endif */ spl.removeModule("AIDA-X"); spl.removeModule("AudioFile"); spl.removeModule("Blank"); spl.removeModule("Carla"); spl.removeModule("ExpanderInputMIDI"); spl.removeModule("ExpanderOutputMIDI"); spl.removeModule("HostAudio8"); spl.removeModule("Ildaeil"); spl.removeModule("MPV"); spl.removeModule("SassyScope"); spl.removeModule("glBars"); hostTerminalModels = { modelHostAudio2, modelHostCV, modelHostMIDI, modelHostMIDICC, modelHostMIDIGate, modelHostMIDIMap, modelHostParameters, modelHostParametersMap, modelHostTime, }; } } static void initStatic__Fundamental() { Plugin* const p = new Plugin; pluginInstance__Fundamental = p; const StaticPluginLoader spl(p, "Fundamental"); if (spl.ok()) { p->addModel(modelADSR); p->addModel(modelLFO); p->addModel(modelMerge); p->addModel(modelMidSide); p->addModel(modelNoise); p->addModel(modelQuantizer); p->addModel(modelRandom); p->addModel(modelScope); p->addModel(modelSplit); p->addModel(modelSum); p->addModel(modelVCA_1); p->addModel(modelVCF); p->addModel(modelVCMixer); p->addModel(modelVCO); spl.removeModule("8vert"); spl.removeModule("Delay"); spl.removeModule("LFO2"); spl.removeModule("Mixer"); spl.removeModule("Mutes"); spl.removeModule("Octave"); spl.removeModule("Pulses"); spl.removeModule("SEQ3"); spl.removeModule("SequentialSwitch1"); spl.removeModule("SequentialSwitch2"); spl.removeModule("VCA"); spl.removeModule("VCO2"); } } static void initStatic__Aria() { Plugin* const p = new Plugin; pluginInstance__Aria = p; const StaticPluginLoader spl(p, "AriaModules"); if (spl.ok()) { p->addModel(modelSpleet); p->addModel(modelSwerge); spl.removeModule("Aleister"); spl.removeModule("Arcane"); spl.removeModule("Atout"); spl.removeModule("Blank"); spl.removeModule("Darius"); spl.removeModule("Grabby"); spl.removeModule("Pokies4"); spl.removeModule("Psychopump"); spl.removeModule("Q"); spl.removeModule("Qqqq"); spl.removeModule("Quack"); spl.removeModule("Quale"); spl.removeModule("Rotatoes4"); spl.removeModule("Smerge"); spl.removeModule("Solomon16"); spl.removeModule("Solomon4"); spl.removeModule("Solomon8"); spl.removeModule("Splirge"); spl.removeModule("Splort"); spl.removeModule("Undular"); } } static void initStatic__AudibleInstruments() { Plugin* const p = new Plugin; pluginInstance__AudibleInstruments = p; const StaticPluginLoader spl(p, "AudibleInstruments"); if (spl.ok()) { p->addModel(modelPlaits); spl.removeModule("Blinds"); spl.removeModule("Braids"); spl.removeModule("Branches"); spl.removeModule("Clouds"); spl.removeModule("Elements"); spl.removeModule("Frames"); spl.removeModule("Kinks"); spl.removeModule("Links"); spl.removeModule("Marbles"); spl.removeModule("Rings"); spl.removeModule("Ripples"); spl.removeModule("Shades"); spl.removeModule("Shelves"); spl.removeModule("Stages"); spl.removeModule("Streams"); spl.removeModule("Tides"); spl.removeModule("Tides2"); spl.removeModule("Veils"); spl.removeModule("Warps"); } } static void initStatic__BogaudioModules() { Plugin* const p = new Plugin; pluginInstance__BogaudioModules = p; const StaticPluginLoader spl(p, "BogaudioModules"); if (spl.ok()) { // Make sure to use match Cardinal theme Skins& skins(Skins::skins()); skins._default = settings::preferDarkPanels ? "dark" : "light"; p->addModel(modelAD); p->addModel(modelBogaudioLFO); p->addModel(modelBogaudioNoise); p->addModel(modelBogaudioVCA); p->addModel(modelBogaudioVCF); p->addModel(modelBogaudioVCO); p->addModel(modelOffset); p->addModel(modelSampleHold); p->addModel(modelSwitch); p->addModel(modelSwitch18); p->addModel(modelUnison); // cat plugins/BogaudioModules/plugin.json | jq -r .modules[].slug - | sort spl.removeModule("Bogaudio-Additator"); spl.removeModule("Bogaudio-AddrSeq"); spl.removeModule("Bogaudio-AddrSeqX"); spl.removeModule("Bogaudio-ADSR"); spl.removeModule("Bogaudio-AMRM"); spl.removeModule("Bogaudio-Analyzer"); spl.removeModule("Bogaudio-AnalyzerXL"); spl.removeModule("Bogaudio-Arp"); spl.removeModule("Bogaudio-ASR"); spl.removeModule("Bogaudio-Assign"); spl.removeModule("Bogaudio-Blank3"); spl.removeModule("Bogaudio-Blank6"); spl.removeModule("Bogaudio-Bool"); spl.removeModule("Bogaudio-Chirp"); spl.removeModule("Bogaudio-Clpr"); spl.removeModule("Bogaudio-Cmp"); spl.removeModule("Bogaudio-CmpDist"); spl.removeModule("Bogaudio-CVD"); spl.removeModule("Bogaudio-DADSRH"); spl.removeModule("Bogaudio-DADSRHPlus"); spl.removeModule("Bogaudio-Detune"); spl.removeModule("Bogaudio-DGate"); spl.removeModule("Bogaudio-Edge"); spl.removeModule("Bogaudio-EightFO"); spl.removeModule("Bogaudio-EightOne"); spl.removeModule("Bogaudio-EQ"); spl.removeModule("Bogaudio-EQS"); spl.removeModule("Bogaudio-FFB"); spl.removeModule("Bogaudio-FlipFlop"); spl.removeModule("Bogaudio-FMOp"); spl.removeModule("Bogaudio-Follow"); spl.removeModule("Bogaudio-FourFO"); spl.removeModule("Bogaudio-FourMan"); spl.removeModule("Bogaudio-Inv"); spl.removeModule("Bogaudio-Lgsw"); spl.removeModule("Bogaudio-LLFO"); spl.removeModule("Bogaudio-LLPG"); spl.removeModule("Bogaudio-Lmtr"); spl.removeModule("Bogaudio-LPG"); spl.removeModule("Bogaudio-LVCF"); spl.removeModule("Bogaudio-LVCO"); spl.removeModule("Bogaudio-Manual"); spl.removeModule("Bogaudio-Matrix18"); spl.removeModule("Bogaudio-Matrix44"); spl.removeModule("Bogaudio-Matrix44Cvm"); spl.removeModule("Bogaudio-Matrix81"); spl.removeModule("Bogaudio-Matrix88"); spl.removeModule("Bogaudio-Matrix88Cv"); spl.removeModule("Bogaudio-Matrix88M"); spl.removeModule("Bogaudio-MegaGate"); spl.removeModule("Bogaudio-Mix1"); spl.removeModule("Bogaudio-Mix2"); spl.removeModule("Bogaudio-Mix4"); spl.removeModule("Bogaudio-Mix4x"); spl.removeModule("Bogaudio-Mix8"); spl.removeModule("Bogaudio-Mix8x"); spl.removeModule("Bogaudio-Mono"); spl.removeModule("Bogaudio-Mult"); spl.removeModule("Bogaudio-Mumix"); spl.removeModule("Bogaudio-Mute8"); spl.removeModule("Bogaudio-Nsgt"); spl.removeModule("Bogaudio-OneEight"); spl.removeModule("Bogaudio-Pan"); spl.removeModule("Bogaudio-PEQ"); spl.removeModule("Bogaudio-PEQ14"); spl.removeModule("Bogaudio-PEQ14XF"); spl.removeModule("Bogaudio-PEQ6"); spl.removeModule("Bogaudio-PEQ6XF"); spl.removeModule("Bogaudio-Pgmr"); spl.removeModule("Bogaudio-PgmrX"); spl.removeModule("Bogaudio-PolyCon"); spl.removeModule("Bogaudio-PolyCon8"); spl.removeModule("Bogaudio-PolyMult"); spl.removeModule("Bogaudio-PolyOff16"); spl.removeModule("Bogaudio-PolyOff8"); spl.removeModule("Bogaudio-Pressor"); spl.removeModule("Bogaudio-Pulse"); spl.removeModule("Bogaudio-Ranalyzer"); spl.removeModule("Bogaudio-Reftone"); spl.removeModule("Bogaudio-RGate"); spl.removeModule("Bogaudio-Shaper"); spl.removeModule("Bogaudio-ShaperPlus"); spl.removeModule("Bogaudio-Sine"); spl.removeModule("Bogaudio-Slew"); spl.removeModule("Bogaudio-Stack"); spl.removeModule("Bogaudio-Sums"); spl.removeModule("Bogaudio-Switch1616"); spl.removeModule("Bogaudio-Switch44"); spl.removeModule("Bogaudio-Switch81"); spl.removeModule("Bogaudio-Switch88"); spl.removeModule("Bogaudio-UMix"); spl.removeModule("Bogaudio-VCAmp"); spl.removeModule("Bogaudio-VCM"); spl.removeModule("Bogaudio-Velo"); spl.removeModule("Bogaudio-Vish"); spl.removeModule("Bogaudio-VU"); spl.removeModule("Bogaudio-Walk"); spl.removeModule("Bogaudio-Walk2"); spl.removeModule("Bogaudio-XCO"); spl.removeModule("Bogaudio-XFade"); } } static void initStatic__MockbaModular() { Plugin* const p = new Plugin; pluginInstance__MockbaModular = p; const StaticPluginLoader spl(p, "MockbaModular"); if (spl.ok()) { p->addModel(modelCZOsc); p->addModel(modelFiltah); p->addModel(modelMaugOsc); p->addModel(modelMixah); p->addModel(modelPannah); p->addModel(modelReVoltah); p->addModel(modelShapah); spl.removeModule("Blank"); spl.removeModule("Comparator"); spl.removeModule("Countah"); spl.removeModule("CZDblSine"); spl.removeModule("CZPulse"); spl.removeModule("CZReso1"); spl.removeModule("CZReso2"); spl.removeModule("CZReso3"); spl.removeModule("CZSaw"); spl.removeModule("CZSawPulse"); spl.removeModule("CZSquare"); spl.removeModule("Dividah"); spl.removeModule("DualBUFFER"); spl.removeModule("DualNOT"); spl.removeModule("DualOR"); spl.removeModule("DualNOR"); spl.removeModule("DualAND"); spl.removeModule("DualNAND"); spl.removeModule("DualXOR"); spl.removeModule("DualXNOR"); spl.removeModule("Feidah"); spl.removeModule("FeidahS"); spl.removeModule("Holdah"); spl.removeModule("MaugSaw"); spl.removeModule("MaugSaw2"); spl.removeModule("MaugShark"); spl.removeModule("MaugSquare"); spl.removeModule("MaugSquare2"); spl.removeModule("MaugSquare3"); spl.removeModule("MaugTriangle"); spl.removeModule("Mixah3"); spl.removeModule("PSelectah"); spl.removeModule("Selectah"); spl.removeModule("UDPClockMaster"); spl.removeModule("UDPClockSlave"); } } static void initStatic__surgext() { Plugin* const p = new Plugin; pluginInstance__surgext = p; const StaticPluginLoader spl(p, "surgext"); if (spl.ok()) { p->addModel(modelVCOModern); p->addModel(modelVCOSine); /* p->addModel(modelVCOAlias); p->addModel(modelVCOClassic); p->addModel(modelVCOFM2); p->addModel(modelVCOFM3); p->addModel(modelVCOSHNoise); p->addModel(modelVCOString); p->addModel(modelVCOTwist); p->addModel(modelVCOWavetable); p->addModel(modelVCOWindow); */ spl.removeModule("SurgeXTOSCAlias"); spl.removeModule("SurgeXTOSCClassic"); spl.removeModule("SurgeXTOSCFM2"); spl.removeModule("SurgeXTOSCFM3"); spl.removeModule("SurgeXTOSCSHNoise"); spl.removeModule("SurgeXTOSCString"); spl.removeModule("SurgeXTOSCTwist"); spl.removeModule("SurgeXTOSCWavetable"); spl.removeModule("SurgeXTOSCWindow"); // Add the ported ones p->addModel(modelSurgeLFO); p->addModel(modelSurgeMixer); p->addModel(modelSurgeMixerSlider); p->addModel(modelSurgeModMatrix); p->addModel(modelSurgeWaveshaper); /* p->addModel(modelSurgeDelay); p->addModel(modelSurgeDelayLineByFreq); p->addModel(modelSurgeDelayLineByFreqExpanded); p->addModel(modelSurgeDigitalRingMods); p->addModel(modelSurgeVCF); */ spl.removeModule("SurgeXTDelay"); spl.removeModule("SurgeXTDelayLineByFreq"); spl.removeModule("SurgeXTDelayLineByFreqExpanded"); spl.removeModule("SurgeXTDigitalRingMod"); spl.removeModule("SurgeXTVCF"); p->addModel(modelFXNimbus); spl.removeModule("SurgeXTFXBonsai"); spl.removeModule("SurgeXTFXChorus"); spl.removeModule("SurgeXTFXChow"); spl.removeModule("SurgeXTFXCombulator"); spl.removeModule("SurgeXTDigitalRingMod"); spl.removeModule("SurgeXTFXDistortion"); spl.removeModule("SurgeXTFXExciter"); spl.removeModule("SurgeXTFXEnsemble"); spl.removeModule("SurgeXTFXFlanger"); spl.removeModule("SurgeXTFXFrequencyShifter"); spl.removeModule("SurgeXTFXNeuron"); spl.removeModule("SurgeXTFXPhaser"); spl.removeModule("SurgeXTFXResonator"); spl.removeModule("SurgeXTFXReverb"); spl.removeModule("SurgeXTFXReverb2"); spl.removeModule("SurgeXTFXRingMod"); spl.removeModule("SurgeXTFXRotarySpeaker"); spl.removeModule("SurgeXTFXSpringReverb"); spl.removeModule("SurgeXTFXTreeMonster"); spl.removeModule("SurgeXTFXVocoder"); /* p->addModel(modelEGxVCA); p->addModel(modelQuadAD); p->addModel(modelQuadLFO); p->addModel(modelUnisonHelper); p->addModel(modelUnisonHelperCVExpander); */ spl.removeModule("SurgeXTEGxVCA"); spl.removeModule("SurgeXTQuadAD"); spl.removeModule("SurgeXTQuadLFO"); spl.removeModule("SurgeXTUnisonHelper"); spl.removeModule("SurgeXTUnisonHelperCVExpander"); surgext_rack_initialize(); } } /* static void initStatic__ValleyAudio() { Plugin* const p = new Plugin; pluginInstance__ValleyAudio = p; const StaticPluginLoader spl(p, "ValleyAudio"); if (spl.ok()) { p->addModel(modelDexter); p->addModel(modelInterzone); spl.removeModule("Amalgam"); spl.removeModule("Feline"); spl.removeModule("Plateau"); spl.removeModule("Terrorform"); spl.removeModule("Topograph"); spl.removeModule("uGraph"); } } */ void initStaticPlugins() { initStatic__Cardinal(); initStatic__Fundamental(); initStatic__Aria(); initStatic__AudibleInstruments(); initStatic__BogaudioModules(); initStatic__MockbaModular(); initStatic__surgext(); /* initStatic__ValleyAudio(); */ } void destroyStaticPlugins() { for (Plugin* p : plugins) delete p; plugins.clear(); } void updateStaticPluginsDarkMode() { const bool darkMode = settings::preferDarkPanels; // bogaudio { Skins& skins(Skins::skins()); skins._default = darkMode ? "dark" : "light"; std::lock_guard lock(skins._defaultSkinListenersLock); for (auto listener : skins._defaultSkinListeners) { listener->defaultSkinChanged(skins._default); } } // surgext { surgext_rack_update_theme(); } } } }