diff --git a/Makefile b/Makefile index ba166565..9f1b548c 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ RACK_DIR ?= . VERSION = 1.dev -FLAGS += \ - -Iinclude \ - -Idep/include -Idep/lib/libzip/include +FLAGS += -DVERSION=$(VERSION) +FLAGS += -Iinclude +FLAGS += -Idep/include -Idep/lib/libzip/include include arch.mk diff --git a/compile.mk b/compile.mk index 397fc0cc..42c45335 100644 --- a/compile.mk +++ b/compile.mk @@ -2,15 +2,10 @@ ifndef RACK_DIR $(error RACK_DIR is not defined) endif -ifndef VERSION -$(error VERSION is not defined) -endif - include $(RACK_DIR)/arch.mk OBJCOPY ?= objcopy -FLAGS += -DVERSION=$(VERSION) # Generate dependency files alongside the object files FLAGS += -MMD -MP FLAGS += -g diff --git a/include/engine/Param.hpp b/include/engine/Param.hpp index 6a1c66aa..b973942c 100644 --- a/include/engine/Param.hpp +++ b/include/engine/Param.hpp @@ -20,7 +20,7 @@ struct Param { std::string label; std::string unit; - void setup(float minValue, float maxValue, float defaultValue, std::string label = "", std::string unit = "", int displayPrecision = 2) { + void setup(float minValue, float maxValue, float defaultValue, std::string label = "", std::string unit = "", int displayPrecision = 2, float displayBase = 0.f, float displayMultiplier = 1.f) { this->value = defaultValue; this->minValue = minValue; this->maxValue = maxValue; @@ -28,6 +28,8 @@ struct Param { this->label = label; this->unit = unit; this->displayPrecision = displayPrecision; + this->displayBase = displayBase; + this->displayMultiplier = displayMultiplier; } json_t *toJson(); diff --git a/include/helpers.hpp b/include/helpers.hpp index a751e05c..aea4aea7 100644 --- a/include/helpers.hpp +++ b/include/helpers.hpp @@ -12,7 +12,7 @@ namespace rack { template -Model *createModel(std::string author, std::string slug, std::string name, Tags... tags) { +Model *createModel(std::string slug) { struct TModel : Model { Module *createModule() override { TModule *o = new TModule; @@ -32,10 +32,7 @@ Model *createModel(std::string author, std::string slug, std::string name, Tags. }; Model *o = new TModel; - o->author = author; o->slug = slug; - o->name = name; - o->tags = {tags...}; return o; } @@ -46,25 +43,28 @@ TWidget *createWidget(math::Vec pos) { return o; } +template +TWidget *createWidgetCentered(math::Vec pos) { + TWidget *o = new TWidget; + o->box.pos = pos.minus(o->box.size.div(2));; + return o; +} + template -TParamWidget *createParam(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { +TParamWidget *createParam(math::Vec pos, Module *module, int paramId) { TParamWidget *o = new TParamWidget; o->box.pos = pos; o->quantity->module = module; o->quantity->paramId = paramId; - o->setLimits(minValue, maxValue); - o->setDefaultValue(defaultValue); return o; } template -TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { +TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId) { TParamWidget *o = new TParamWidget; o->box.pos = pos.minus(o->box.size.div(2)); o->quantity->module = module; o->quantity->paramId = paramId; - o->setLimits(minValue, maxValue); - o->setDefaultValue(defaultValue); return o; } diff --git a/include/network.hpp b/include/network.hpp index b6735b0b..c50badd4 100644 --- a/include/network.hpp +++ b/include/network.hpp @@ -19,11 +19,11 @@ Caller must json_decref(). */ json_t *requestJson(Method method, std::string url, json_t *dataJ); /** Returns true if downloaded successfully */ -bool requestDownload(std::string url, std::string filename, float *progress); +bool requestDownload(std::string url, const std::string &filename, float *progress); /** URL-encodes `s` */ -std::string encodeUrl(std::string s); +std::string encodeUrl(const std::string &s); /** Computes the SHA256 of the file at `filename` */ -std::string computeSHA256File(std::string filename); +std::string computeSHA256File(const std::string &filename); } // namespace network diff --git a/include/plugin.hpp b/include/plugin.hpp index fe8e360c..705d534d 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -2,6 +2,7 @@ #include "common.hpp" #include "plugin/Plugin.hpp" #include "plugin/Model.hpp" +#include #include @@ -19,6 +20,7 @@ void cancelDownload(); bool isLoggedIn(); Plugin *getPlugin(std::string pluginSlug); Model *getModel(std::string pluginSlug, std::string modelSlug); +std::string getAllowedTag(std::string tag); extern std::list plugins; @@ -27,6 +29,7 @@ extern bool isDownloading; extern float downloadProgress; extern std::string downloadName; extern std::string loginStatus; +extern const std::vector allowedTags; } // namespace plugin diff --git a/include/plugin/Model.hpp b/include/plugin/Model.hpp index 433571c2..c31a6f92 100644 --- a/include/plugin/Model.hpp +++ b/include/plugin/Model.hpp @@ -1,7 +1,7 @@ #pragma once #include "common.hpp" #include "plugin/Plugin.hpp" -#include "tags.hpp" +#include #include @@ -14,19 +14,17 @@ struct Module; struct Model { Plugin *plugin = NULL; + /** An identifier for the model, e.g. "VCO". Used for saving patches. The model slug must be unique in your plugin, but it doesn't need to be unique among different plugins. */ std::string slug; /** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ std::string name; - /** The author name of the module. - This might be different than the plugin slug. For example, if you create multiple plugins but want them to be branded similarly, you may use the same author in multiple plugins. - You may even have multiple authors in one plugin, although this property will be moved to Plugin for Rack 1.0. - */ - std::string author; - /** List of tags representing the function(s) of the module (optional) */ - std::list tags; + /** A one-line summary of the module's purpose */ + std::string description; + /** List of tags representing the function(s) of the module */ + std::list tags; virtual ~Model() {} /** Creates a headless Module */ @@ -35,6 +33,8 @@ struct Model { virtual ModuleWidget *createModuleWidget() { return NULL; } /** Creates a ModuleWidget with no Module, useful for previews */ virtual ModuleWidget *createModuleWidgetNull() { return NULL; } + + void fromJson(json_t *rootJ); }; diff --git a/include/plugin/Plugin.hpp b/include/plugin/Plugin.hpp index c14565c7..b1e0d57e 100644 --- a/include/plugin/Plugin.hpp +++ b/include/plugin/Plugin.hpp @@ -1,5 +1,6 @@ #pragma once #include "common.hpp" +#include #include @@ -22,19 +23,26 @@ struct Plugin { To guarantee uniqueness, it is a good idea to prefix the slug by your "company name" if available, e.g. "MyCompany-MyPlugin" */ std::string slug; - /** The version of your plugin Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266 Do not include the "v" in "v1.0" for example. */ std::string version; - - /** Deprecated, do not use. */ - std::string website; - std::string manual; + /** Human readable name for your plugin, e.g. "Voltage Controlled Oscillator" */ + std::string name; + std::string author; + std::string license; + std::string authorEmail; + std::string pluginUrl; + std::string authorUrl; + std::string manualUrl; + std::string sourceUrl; + std::string donateUrl; virtual ~Plugin(); void addModel(Model *model); + Model *getModel(std::string slug); + void fromJson(json_t *rootJ); }; diff --git a/include/string.hpp b/include/string.hpp index 3a7781b6..14a4cfd0 100644 --- a/include/string.hpp +++ b/include/string.hpp @@ -6,23 +6,25 @@ namespace rack { namespace string { -/** Converts a printf format string and optional arguments into a std::string */ +/** Converts a `printf()` format string and optional arguments into a std::string +Remember that "%s" must reference a `char *`, so use `.c_str()` for `std::string`s. +*/ std::string f(const char *format, ...); /** Replaces all characters to lowercase letters */ std::string lowercase(std::string s); /** Replaces all characters to uppercase letters */ std::string uppercase(std::string s); /** Truncates and adds "..." to a string, not exceeding `len` characters */ -std::string ellipsize(std::string s, size_t len); -bool startsWith(std::string str, std::string prefix); -bool endsWith(std::string str, std::string suffix); +std::string ellipsize(const std::string &s, size_t len); +bool startsWith(const std::string &str, const std::string &prefix); +bool endsWith(const std::string &str, const std::string &suffix); /** Extracts portions of a path */ -std::string directory(std::string path); -std::string filename(std::string path); +std::string directory(const std::string &path); +std::string filename(const std::string &path); /** Extracts the portion of a path without the extension */ -std::string basename(std::string path); +std::string basename(const std::string &path); /** Extracts the extension of a path */ -std::string extension(std::string path); +std::string extension(const std::string &path); struct CaseInsensitiveCompare { bool operator()(const std::string &a, const std::string &b) const { diff --git a/include/system.hpp b/include/system.hpp index d1b9c331..1378826d 100644 --- a/include/system.hpp +++ b/include/system.hpp @@ -7,17 +7,17 @@ namespace rack { namespace system { -std::list listEntries(std::string path); -bool isFile(std::string path); -bool isDirectory(std::string path); -void copyFile(std::string srcPath, std::string destPath); -void createDirectory(std::string path); +std::list listEntries(const std::string &path); +bool isFile(const std::string &path); +bool isDirectory(const std::string &path); +void copyFile(const std::string &srcPath, const std::string &destPath); +void createDirectory(const std::string &path); /** Opens a URL, also happens to work with PDFs and folders. Shell injection is possible, so make sure the URL is trusted or hard coded. May block, so open in a new thread. */ -void openBrowser(std::string url); +void openBrowser(const std::string &url); } // namespace system diff --git a/include/tags.hpp b/include/tags.hpp deleted file mode 100644 index 993d5ccd..00000000 --- a/include/tags.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include "common.hpp" - - -namespace rack { - - -/** Describes the type(s) of each module -To see comments, turn word wrap on. I'm using inline comments so I can automatically sort the list when more tags are added. -*/ -enum ModelTag { - NO_TAG, // Don't use this in `Model::create(...)`. Instead, just omit the tags entirely. - AMPLIFIER_TAG, - ARPEGGIATOR_TAG, - ATTENUATOR_TAG, - BLANK_TAG, - CHORUS_TAG, - CLOCK_MODULATOR_TAG, // Clock dividers, multipliers, etc. - CLOCK_TAG, - COMPRESSOR_TAG, - CONTROLLER_TAG, // Use only if the artist "performs" with this module. Knobs are not sufficient. Examples: on-screen keyboard, XY pad. - DELAY_TAG, - DIGITAL_TAG, - DISTORTION_TAG, - DRUM_TAG, - DUAL_TAG, // The core functionality times two. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Dual module. - DYNAMICS_TAG, - EFFECT_TAG, - ENVELOPE_FOLLOWER_TAG, - ENVELOPE_GENERATOR_TAG, - EQUALIZER_TAG, - EXTERNAL_TAG, - FILTER_TAG, - FLANGER_TAG, - FUNCTION_GENERATOR_TAG, - GRANULAR_TAG, - LFO_TAG, - LIMITER_TAG, - LOGIC_TAG, - LOW_PASS_GATE_TAG, - MIDI_TAG, - MIXER_TAG, - MULTIPLE_TAG, - NOISE_TAG, - OSCILLATOR_TAG, - PANNING_TAG, - PHASER_TAG, - PHYSICAL_MODELING_TAG, - QUAD_TAG, // The core functionality times four. If multiple channels are a requirement for the module to exist (ring modulator, mixer, etc), it is not a Quad module. - QUANTIZER_TAG, - RANDOM_TAG, - RECORDING_TAG, - REVERB_TAG, - RING_MODULATOR_TAG, - SAMPLE_AND_HOLD_TAG, - SAMPLER_TAG, - SEQUENCER_TAG, - SLEW_LIMITER_TAG, - SWITCH_TAG, - SYNTH_VOICE_TAG, // A synth voice must have an envelope built-in. - TUNER_TAG, - UTILITY_TAG, // Serves only extremely basic functions, like inverting, max, min, multiplying by 2, etc. - VISUAL_TAG, - VOCODER_TAG, - WAVESHAPER_TAG, - NUM_TAGS -}; - - -void tagsInit(); - -extern std::string gTagNames[NUM_TAGS]; - - -} // namespace rack diff --git a/plugin.mk b/plugin.mk index 2aa2c130..9d024fae 100644 --- a/plugin.mk +++ b/plugin.mk @@ -2,13 +2,13 @@ ifndef RACK_DIR $(error RACK_DIR is not defined) endif -ifndef SLUG -$(error SLUG is not defined) -endif +SLUG := $(shell jq ".slug" plugin.json) +VERSION := $(shell jq ".version" plugin.json) STRIP ?= strip -FLAGS += -DSLUG=$(SLUG) +DISTRIBUTABLES += plugin.json + FLAGS += -fPIC FLAGS += -I$(RACK_DIR)/include -I$(RACK_DIR)/dep/include diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index c86f3883..fba200d0 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -282,4 +282,4 @@ struct AudioInterfaceWidget : ModuleWidget { }; -Model *modelAudioInterface = createModel("Core", "AudioInterface", "Audio", EXTERNAL_TAG); \ No newline at end of file +Model *modelAudioInterface = createModel("AudioInterface"); \ No newline at end of file diff --git a/src/Core/Blank.cpp b/src/Core/Blank.cpp index b8d40cd5..7f57ed74 100644 --- a/src/Core/Blank.cpp +++ b/src/Core/Blank.cpp @@ -121,4 +121,4 @@ struct BlankWidget : ModuleWidget { }; -Model *modelBlank = createModel("Core", "Blank", "Blank", BLANK_TAG); +Model *modelBlank = createModel("Blank"); diff --git a/src/Core/MIDICCToCVInterface.cpp b/src/Core/MIDICCToCVInterface.cpp index 5038a65e..5f51df1f 100644 --- a/src/Core/MIDICCToCVInterface.cpp +++ b/src/Core/MIDICCToCVInterface.cpp @@ -216,4 +216,4 @@ struct MIDICCToCVInterfaceWidget : ModuleWidget { }; -Model *modelMIDICCToCVInterface = createModel("Core", "MIDICCToCVInterface", "MIDI-CC", MIDI_TAG, EXTERNAL_TAG); +Model *modelMIDICCToCVInterface = createModel("MIDICCToCVInterface"); diff --git a/src/Core/MIDIToCVInterface.cpp b/src/Core/MIDIToCVInterface.cpp index 2521e317..a7f2395d 100644 --- a/src/Core/MIDIToCVInterface.cpp +++ b/src/Core/MIDIToCVInterface.cpp @@ -328,4 +328,4 @@ struct MIDIToCVInterfaceWidget : ModuleWidget { }; -Model *modelMIDIToCVInterface = createModel("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG); +Model *modelMIDIToCVInterface = createModel("MIDIToCVInterface"); diff --git a/src/Core/MIDITriggerToCVInterface.cpp b/src/Core/MIDITriggerToCVInterface.cpp index 60c66961..31fc173b 100644 --- a/src/Core/MIDITriggerToCVInterface.cpp +++ b/src/Core/MIDITriggerToCVInterface.cpp @@ -251,4 +251,4 @@ struct MIDITriggerToCVInterfaceWidget : ModuleWidget { }; -Model *modelMIDITriggerToCVInterface = createModel("Core", "MIDITriggerToCVInterface", "MIDI-Trig", MIDI_TAG, EXTERNAL_TAG); +Model *modelMIDITriggerToCVInterface = createModel("MIDITriggerToCVInterface"); diff --git a/src/Core/Notes.cpp b/src/Core/Notes.cpp index 55bb7c7c..ae50e5f5 100644 --- a/src/Core/Notes.cpp +++ b/src/Core/Notes.cpp @@ -41,4 +41,4 @@ struct NotesWidget : ModuleWidget { }; -Model *modelNotes = createModel("Core", "Notes", "Notes", BLANK_TAG); +Model *modelNotes = createModel("Notes"); diff --git a/src/Core/QuadMIDIToCVInterface.cpp b/src/Core/QuadMIDIToCVInterface.cpp index 54a95b82..e5905019 100644 --- a/src/Core/QuadMIDIToCVInterface.cpp +++ b/src/Core/QuadMIDIToCVInterface.cpp @@ -366,5 +366,5 @@ struct QuadMIDIToCVInterfaceWidget : ModuleWidget { }; -Model *modelQuadMIDIToCVInterface = createModel("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG); +Model *modelQuadMIDIToCVInterface = createModel("QuadMIDIToCVInterface"); diff --git a/src/app/ModuleBrowser.cpp b/src/app/ModuleBrowser.cpp index a7a6010f..6a5c1843 100644 --- a/src/app/ModuleBrowser.cpp +++ b/src/app/ModuleBrowser.cpp @@ -22,7 +22,7 @@ namespace rack { static std::set sFavoriteModels; static std::string sAuthorFilter; -static ModelTag sTagFilter = NO_TAG; +static std::string sTagFilter; @@ -38,14 +38,17 @@ static bool isModelMatch(Model *model, std::string search) { std::string s; s += model->plugin->slug; s += " "; - s += model->author; + s += model->plugin->author; s += " "; s += model->name; s += " "; s += model->slug; - for (ModelTag tag : model->tags) { - s += " "; - s += gTagNames[tag]; + for (std::string tag : model->tags) { + std::string allowedTag = plugin::getAllowedTag(tag); + if (!allowedTag.empty()) { + s += " "; + s += allowedTag; + } } return isMatch(s, search); } @@ -185,16 +188,16 @@ struct AuthorItem : BrowserListItem { struct TagItem : BrowserListItem { - ModelTag tag; + std::string tag; - void setTag(ModelTag tag) { + void setTag(std::string tag) { clearChildren(); this->tag = tag; Label *tagLabel = createWidget