| @@ -0,0 +1,31 @@ | |||
| # astyle settings file | |||
| # Requires Artistic Style 3.1 | |||
| # http://astyle.sourceforge.net/ | |||
| # Usage: | |||
| # astyle --suffix=none --options=.astylerc filename.cpp | |||
| # or recursively | |||
| # astyle --suffix=none --options=.astylerc -r 'include/*' 'src/*' | |||
| # or using find | |||
| # find src include -type f | xargs astyle --suffix=none --options=.astylerc | |||
| style=java | |||
| indent=tab=2 | |||
| attach-closing-while | |||
| indent-switches | |||
| indent-preproc-block | |||
| indent-preproc-define | |||
| indent-col1-comments | |||
| pad-oper | |||
| pad-comma | |||
| pad-header | |||
| unpad-paren | |||
| align-pointer=type | |||
| align-reference=type | |||
| break-closing-braces | |||
| break-one-line-headers | |||
| attach-return-type | |||
| attach-return-type-decl | |||
| keep-one-line-statements | |||
| close-templates | |||
| @@ -1,26 +1,28 @@ | |||
| ### Pre-implementation | |||
| Before authoring a code contribution, open an issue explaining your proposal in detail. | |||
| Remember that code is easy to write, but inventing a valid design is usually the crux. | |||
| Expect the proposal to be rejected if you do not consider 100% of use-cases and corner cases. | |||
| ### Code style and quality | |||
| Be courteous and try to match the code style. | |||
| I won't define the exact style here, so just follow the existing code. | |||
| Use C++11 style, not >=C++14 or C. | |||
| This means using the `std::` namespace for stdlib symbols, `std::string` instead of `char*`, `//` comments and `/** */` docstrings, etc. | |||
| Avoid over-engineered things like `iostream`, `std::array`, `std::unique_ptr`, etc when simpler alternatives exist. | |||
| Write maintainable code that will last >4 years. | |||
| No feature/bug is urgent enough to write dirty/temporary implementations. | |||
| I have no need to withdraw technical debt. | |||
| ### Legal | |||
| Because a proprietary fork of VCV Rack is planned by VCV (*Rack for DAWs*), in order for your patch to be accepted, you must make a declaration stating that: | |||
| - you are the sole author of your patch. | |||
| - your patch is released under the [CC0](https://creativecommons.org/publicdomain/zero/1.0/) license. | |||
| - or ask for a copyright reassignment form if you prefer this instead. | |||
| VCV Rack is [open-source](https://opensource.org/osd) but not [open-contribution](https://opensource.guide/how-to-contribute/). | |||
| I am unable to accept free code contributions to Rack for the following reasons. | |||
| - **Quality.** | |||
| Most contributions to open-source projects typically only contain code, but writing code is just a small percentage of the effort required to maintain a large software project. | |||
| Additional tasks for fully supporting a feature include | |||
| - debating the best design before any code is written | |||
| - considering all use cases and corner cases of the implementation | |||
| - generalizability to allow other features to be built on top if needed | |||
| - testing across all supported operating systems and hardware | |||
| - dedication to support the feature for >4 years | |||
| - **Time.** | |||
| In the past, free code contributions have cost far more time to review, iterate, fix, and test than writing the implementation from scratch. | |||
| There have been exceptions to this, but they are rare. | |||
| - **Legal.** | |||
| A proprietary fork of VCV Rack is planned (see [*Rack for DAWs*](https://vcvrack.com/manual/FAQ.html#is-vcv-rack-available-as-a-vst-au-aax-plugin-for-daws)), so VCV must own all GPLv3 code. | |||
| To accept a contribution, all authors of the contribution need to either | |||
| - declare the patch under the [CC0](https://creativecommons.org/publicdomain/zero/1.0/) license. | |||
| - complete a copyright reassignment form. | |||
| - perform the work under a paid agreement. | |||
| Except in exceptional circumstances, contributions are only accepted as paid work under detailed guidelines. | |||
| However there are several areas you may contribute Rack project. | |||
| - [creating high-quality issues](https://vcvrack.com/manual/FAQ.html#i-found-a-bug) | |||
| - responding to [issues](https://github.com/VCVRack/Rack/issues) and answering questions in the [VCV communities](https://vcvrack.com/manual/Communities.html) | |||
| - [developing and maintaining your own plugins](https://vcvrack.com/manual/PluginDevelopmentTutorial.html) | |||
| - contributing to Rack's open-source dependencies, like [GLFW](https://www.glfw.org/), [nanovg](https://github.com/memononen/nanovg), [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/), and [RtMidi](https://www.music.mcgill.ca/~gary/rtmidi/). | |||
| @@ -5,10 +5,11 @@ about: Bugs, build errors, compatibility/stability issues | |||
| --- | |||
| <!-- | |||
| Search existing issues before posting a new one to avoid duplicates. | |||
| To file a bug report, fill out the form below. | |||
| Use a descriptive title that best explains the bug in one sentence. | |||
| Attach screenshots if the bug is visual. | |||
| Attach your `<Rack user folder>/log.txt` file if Rack is crashing. | |||
| If Rack crashed, attach/upload (don't copy/paste) your `<Rack user folder>/log.txt` file. | |||
| Surround terminal output with three tildes | |||
| ``` | |||
| like this. | |||
| @@ -5,6 +5,7 @@ about: Feature ideas, design proposals | |||
| --- | |||
| <!-- | |||
| Search existing issues before posting a new one to avoid duplicates. | |||
| To request a feature, fill out the form below. | |||
| Use a descriptive title that best explains the feature request in one sentence. | |||
| --> | |||
| @@ -2,6 +2,18 @@ | |||
| In this document, Mod is Ctrl on Windows/Linux and Cmd on Mac. | |||
| ### 1.1.4 (2019-08-22) | |||
| - Fix parameter smoothing of MIDI-Map. | |||
| - Sort modules within plugin in the Module Browser according to plugin rather than alphabetically. | |||
| - Fix bug where knobs sometimes jump while dragging. | |||
| - Reimplement CPU meter to measure thread runtime, not real time. | |||
| - Fix crashes when deleting/duplicating modules while dragging modules/cables in certain cases. | |||
| - API | |||
| - Add `dsp::BiquadFilter`. | |||
| - Add `dsp/approx.hpp` with approximate math functions. | |||
| - Add `simd::tan()`, `atan()`, and `atan2()`. | |||
| - Add `string::toBase64()` and `fromBase64()`. | |||
| ### 1.1.3 (2019-07-23) | |||
| - Include root certificate bundle for libcurl on all OS's. | |||
| - Revert to OpenSSL from Schannel on Windows. | |||
| @@ -1,7 +1,7 @@ | |||
| { | |||
| "slug": "Core", | |||
| "name": "Core", | |||
| "version": "1.1.3", | |||
| "version": "1.1.4", | |||
| "license": "GPL-3.0-only", | |||
| "author": "VCV", | |||
| "brand": "VCV", | |||
| @@ -1,6 +1,6 @@ | |||
| RACK_DIR ?= . | |||
| # VERSION := 1.dev.$(shell git rev-parse --short HEAD) | |||
| VERSION := 1.1.3 | |||
| VERSION := 1.1.4 | |||
| FLAGS += -DVERSION=$(VERSION) | |||
| FLAGS += -Iinclude -Idep/include | |||
| @@ -200,7 +200,7 @@ def create_module(slug, panel_filename=None, source_filename=None): | |||
| module_manifest['slug'] = slug | |||
| module_manifest['name'] = input_default("Module name", slug) | |||
| module_manifest['description'] = input_default("One-line description (optional)") | |||
| tags = input_default("Tags (comma-separated, case-insensitive, see https://github.com/VCVRack/Rack/blob/v1/src/plugin.cpp#L511-L571 for list)") | |||
| tags = input_default("Tags (comma-separated, case-insensitive, see https://github.com/VCVRack/Rack/blob/v1/src/tag.cpp for list)") | |||
| tags = tags.split(",") | |||
| tags = [tag.strip() for tag in tags] | |||
| if len(tags) == 1 and tags[0] == "": | |||
| @@ -19,24 +19,36 @@ struct Quantity { | |||
| /** Returns the value | |||
| Override this to return the state of your subclass. | |||
| */ | |||
| virtual float getValue() {return 0.f;} | |||
| virtual float getValue() { | |||
| return 0.f; | |||
| } | |||
| /** Returns the minimum allowed value */ | |||
| virtual float getMinValue() {return 0.f;} | |||
| virtual float getMinValue() { | |||
| return 0.f; | |||
| } | |||
| /** Returns the maximum allowed value */ | |||
| virtual float getMaxValue() {return 1.f;} | |||
| virtual float getMaxValue() { | |||
| return 1.f; | |||
| } | |||
| /** Returns the default value, for resetting */ | |||
| virtual float getDefaultValue() {return 0.f;} | |||
| virtual float getDefaultValue() { | |||
| return 0.f; | |||
| } | |||
| /** Returns the value, possibly transformed for displaying | |||
| Useful for logarithmic scaling, multiplying by 100 for percentages, etc. | |||
| */ | |||
| virtual float getDisplayValue() {return getValue();} | |||
| virtual float getDisplayValue() { | |||
| return getValue(); | |||
| } | |||
| /** Inversely transforms the display value and sets the value */ | |||
| virtual void setDisplayValue(float displayValue) {setValue(displayValue);} | |||
| virtual void setDisplayValue(float displayValue) { | |||
| setValue(displayValue); | |||
| } | |||
| /** The number of total decimal places for generating the display value string | |||
| */ | |||
| @@ -48,12 +60,16 @@ struct Quantity { | |||
| virtual void setDisplayValueString(std::string s); | |||
| /** The name of the quantity */ | |||
| virtual std::string getLabel() {return "";} | |||
| virtual std::string getLabel() { | |||
| return ""; | |||
| } | |||
| /** The unit abbreviation of the quantity | |||
| Include an initial space character if you want a space after the number, e.g. "440 Hz". This allows space-less units, like "100%". | |||
| */ | |||
| virtual std::string getUnit() {return "";} | |||
| virtual std::string getUnit() { | |||
| return ""; | |||
| } | |||
| /** Returns a string representation of the quantity */ | |||
| virtual std::string getString(); | |||
| @@ -6,12 +6,12 @@ namespace rack { | |||
| namespace history { | |||
| struct State; | |||
| struct State; | |||
| } // namespace history | |||
| namespace engine { | |||
| struct Engine; | |||
| struct Engine; | |||
| } // namespace engine | |||
| @@ -20,23 +20,23 @@ struct PatchManager; | |||
| namespace event { | |||
| struct State; | |||
| struct State; | |||
| } // namespace event | |||
| namespace app { | |||
| struct Scene; | |||
| struct Scene; | |||
| } // namespace app | |||
| /** Contains the application state */ | |||
| struct App { | |||
| event::State *event = NULL; | |||
| app::Scene *scene = NULL; | |||
| engine::Engine *engine = NULL; | |||
| Window *window = NULL; | |||
| history::State *history = NULL; | |||
| PatchManager *patch = NULL; | |||
| event::State* event = NULL; | |||
| app::Scene* scene = NULL; | |||
| engine::Engine* engine = NULL; | |||
| Window* window = NULL; | |||
| history::State* history = NULL; | |||
| PatchManager* patch = NULL; | |||
| void init(); | |||
| ~App(); | |||
| @@ -46,7 +46,7 @@ struct App { | |||
| void appInit(); | |||
| void appDestroy(); | |||
| /** Returns the global App pointer */ | |||
| App *appGet(); | |||
| App* appGet(); | |||
| /** Accesses the global App pointer */ | |||
| #define APP appGet() | |||
| @@ -9,14 +9,14 @@ namespace app { | |||
| struct AudioWidget : LedDisplay { | |||
| LedDisplayChoice *driverChoice; | |||
| LedDisplaySeparator *driverSeparator; | |||
| LedDisplayChoice *deviceChoice; | |||
| LedDisplaySeparator *deviceSeparator; | |||
| LedDisplayChoice *sampleRateChoice; | |||
| LedDisplaySeparator *sampleRateSeparator; | |||
| LedDisplayChoice *bufferSizeChoice; | |||
| void setAudioPort(audio::Port *port); | |||
| LedDisplayChoice* driverChoice; | |||
| LedDisplaySeparator* driverSeparator; | |||
| LedDisplayChoice* deviceChoice; | |||
| LedDisplaySeparator* deviceSeparator; | |||
| LedDisplayChoice* sampleRateChoice; | |||
| LedDisplaySeparator* sampleRateSeparator; | |||
| LedDisplayChoice* bufferSizeChoice; | |||
| void setAudioPort(audio::Port* port); | |||
| }; | |||
| @@ -12,25 +12,25 @@ namespace app { | |||
| struct CableWidget : widget::OpaqueWidget { | |||
| PortWidget *outputPort = NULL; | |||
| PortWidget *inputPort = NULL; | |||
| PortWidget *hoveredOutputPort = NULL; | |||
| PortWidget *hoveredInputPort = NULL; | |||
| PortWidget* outputPort = NULL; | |||
| PortWidget* inputPort = NULL; | |||
| PortWidget* hoveredOutputPort = NULL; | |||
| PortWidget* hoveredInputPort = NULL; | |||
| /** Owned. */ | |||
| engine::Cable *cable; | |||
| engine::Cable* cable; | |||
| NVGcolor color; | |||
| CableWidget(); | |||
| ~CableWidget(); | |||
| bool isComplete(); | |||
| void setOutput(PortWidget *outputPort); | |||
| void setInput(PortWidget *inputPort); | |||
| void setOutput(PortWidget* outputPort); | |||
| void setInput(PortWidget* inputPort); | |||
| math::Vec getOutputPos(); | |||
| math::Vec getInputPos(); | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| void draw(const DrawArgs &args) override; | |||
| void drawPlugs(const DrawArgs &args); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| void draw(const DrawArgs& args) override; | |||
| void drawPlugs(const DrawArgs& args); | |||
| }; | |||
| @@ -12,7 +12,7 @@ struct CircularShadow : widget::TransparentWidget { | |||
| float opacity; | |||
| CircularShadow(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -20,11 +20,11 @@ struct Knob : ParamWidget { | |||
| /** Drag horizontally instead of vertically */ | |||
| bool horizontal = false; | |||
| void onHover(const event::Hover &e) override; | |||
| void onButton(const event::Button &e) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDragMove(const event::DragMove &e) override; | |||
| void onHover(const event::Hover& e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void reset() override; | |||
| void randomize() override; | |||
| }; | |||
| @@ -10,12 +10,12 @@ namespace app { | |||
| struct LedDisplay : widget::OpaqueWidget { | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| struct LedDisplaySeparator : widget::Widget { | |||
| LedDisplaySeparator(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| struct LedDisplayChoice : widget::OpaqueWidget { | |||
| @@ -25,8 +25,8 @@ struct LedDisplayChoice : widget::OpaqueWidget { | |||
| NVGcolor color; | |||
| NVGcolor bgColor; | |||
| LedDisplayChoice(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onButton(const event::Button &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onButton(const event::Button& e) override; | |||
| }; | |||
| struct LedDisplayTextField : ui::TextField { | |||
| @@ -34,7 +34,7 @@ struct LedDisplayTextField : ui::TextField { | |||
| math::Vec textOffset; | |||
| NVGcolor color; | |||
| LedDisplayTextField(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| int getTextPosition(math::Vec mousePos) override; | |||
| }; | |||
| @@ -12,9 +12,9 @@ struct LightWidget : widget::TransparentWidget { | |||
| NVGcolor color = nvgRGBA(0, 0, 0, 0); | |||
| NVGcolor borderColor = nvgRGBA(0, 0, 0, 0); | |||
| void draw(const DrawArgs &args) override; | |||
| virtual void drawLight(const DrawArgs &args); | |||
| virtual void drawHalo(const DrawArgs &args); | |||
| void draw(const DrawArgs& args) override; | |||
| virtual void drawLight(const DrawArgs& args); | |||
| virtual void drawHalo(const DrawArgs& args); | |||
| }; | |||
| @@ -8,11 +8,11 @@ namespace app { | |||
| struct MenuBar : widget::OpaqueWidget { | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| MenuBar *createMenuBar(); | |||
| MenuBar* createMenuBar(); | |||
| } // namespace app | |||
| @@ -9,12 +9,12 @@ namespace app { | |||
| struct MidiWidget : LedDisplay { | |||
| LedDisplayChoice *driverChoice; | |||
| LedDisplaySeparator *driverSeparator; | |||
| LedDisplayChoice *deviceChoice; | |||
| LedDisplaySeparator *deviceSeparator; | |||
| LedDisplayChoice *channelChoice; | |||
| void setMidiPort(midi::Port *port); | |||
| LedDisplayChoice* driverChoice; | |||
| LedDisplaySeparator* driverSeparator; | |||
| LedDisplayChoice* deviceChoice; | |||
| LedDisplaySeparator* deviceSeparator; | |||
| LedDisplayChoice* channelChoice; | |||
| void setMidiPort(midi::Port* port); | |||
| }; | |||
| @@ -7,7 +7,7 @@ namespace rack { | |||
| namespace app { | |||
| widget::Widget *moduleBrowserCreate(); | |||
| widget::Widget* moduleBrowserCreate(); | |||
| } // namespace app | |||
| @@ -12,7 +12,7 @@ namespace app { | |||
| Will access firstLightId, firstLightId + 1, etc. for each added color | |||
| */ | |||
| struct ModuleLightWidget : MultiLightWidget { | |||
| engine::Module *module = NULL; | |||
| engine::Module* module = NULL; | |||
| int firstLightId; | |||
| void step() override; | |||
| @@ -14,11 +14,11 @@ namespace app { | |||
| /** Manages an engine::Module in the rack. */ | |||
| struct ModuleWidget : widget::OpaqueWidget { | |||
| plugin::Model *model = NULL; | |||
| plugin::Model* model = NULL; | |||
| /** Owned. */ | |||
| engine::Module *module = NULL; | |||
| engine::Module* module = NULL; | |||
| widget::Widget *panel = NULL; | |||
| widget::Widget* panel = NULL; | |||
| /** Note that the indexes of these vectors do not necessarily correspond with the indexes of `Module::params` etc. | |||
| */ | |||
| std::vector<ParamWidget*> params; | |||
| @@ -29,39 +29,39 @@ struct ModuleWidget : widget::OpaqueWidget { | |||
| math::Vec oldPos; | |||
| ModuleWidget(); | |||
| DEPRECATED ModuleWidget(engine::Module *module) : ModuleWidget() { | |||
| DEPRECATED ModuleWidget(engine::Module* module) : ModuleWidget() { | |||
| setModule(module); | |||
| } | |||
| ~ModuleWidget(); | |||
| void draw(const DrawArgs &args) override; | |||
| void drawShadow(const DrawArgs &args); | |||
| void draw(const DrawArgs& args) override; | |||
| void drawShadow(const DrawArgs& args); | |||
| void onButton(const event::Button &e) override; | |||
| void onHoverKey(const event::HoverKey &e) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDragMove(const event::DragMove &e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onHoverKey(const event::HoverKey& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| /** Associates this ModuleWidget with the Module | |||
| Transfers ownership | |||
| */ | |||
| void setModule(engine::Module *module); | |||
| void setModule(engine::Module* module); | |||
| void setPanel(std::shared_ptr<Svg> svg); | |||
| /** Convenience functions for adding special widgets (calls addChild()) */ | |||
| void addParam(ParamWidget *param); | |||
| void addOutput(PortWidget *output); | |||
| void addInput(PortWidget *input); | |||
| ParamWidget *getParam(int paramId); | |||
| PortWidget *getOutput(int outputId); | |||
| PortWidget *getInput(int inputId); | |||
| void addParam(ParamWidget* param); | |||
| void addOutput(PortWidget* output); | |||
| void addInput(PortWidget* input); | |||
| ParamWidget* getParam(int paramId); | |||
| PortWidget* getOutput(int outputId); | |||
| PortWidget* getInput(int inputId); | |||
| /** Overriding these is deprecated. | |||
| Use Module::dataToJson() and dataFromJson() instead | |||
| */ | |||
| virtual json_t *toJson(); | |||
| virtual void fromJson(json_t *rootJ); | |||
| virtual json_t* toJson(); | |||
| virtual void fromJson(json_t* rootJ); | |||
| /** Serializes/unserializes the module state */ | |||
| void copyClipboard(); | |||
| @@ -93,7 +93,7 @@ struct ModuleWidget : widget::OpaqueWidget { | |||
| /** Override to add context menu entries to your subclass. | |||
| It is recommended to add a blank ui::MenuEntry first for spacing. | |||
| */ | |||
| virtual void appendContextMenu(ui::Menu *menu) {} | |||
| virtual void appendContextMenu(ui::Menu* menu) {} | |||
| }; | |||
| @@ -14,7 +14,7 @@ struct MultiLightWidget : LightWidget { | |||
| void addBaseColor(NVGcolor baseColor); | |||
| /** Sets the color to a linear combination of the baseColors with the given weights */ | |||
| void setBrightnesses(const std::vector<float> &brightnesses); | |||
| void setBrightnesses(const std::vector<float>& brightnesses); | |||
| }; | |||
| @@ -12,20 +12,20 @@ namespace app { | |||
| /** Manages an engine::Param on a ModuleWidget. */ | |||
| struct ParamWidget : widget::OpaqueWidget { | |||
| engine::ParamQuantity *paramQuantity = NULL; | |||
| engine::ParamQuantity* paramQuantity = NULL; | |||
| float dirtyValue = NAN; | |||
| ui::Tooltip *tooltip = NULL; | |||
| ui::Tooltip* tooltip = NULL; | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onButton(const event::Button &e) override; | |||
| void onDoubleClick(const event::DoubleClick &e) override; | |||
| void onEnter(const event::Enter &e) override; | |||
| void onLeave(const event::Leave &e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onDoubleClick(const event::DoubleClick& e) override; | |||
| void onEnter(const event::Enter& e) override; | |||
| void onLeave(const event::Leave& e) override; | |||
| /** For legacy patch loading */ | |||
| void fromJson(json_t *rootJ); | |||
| void fromJson(json_t* rootJ); | |||
| void createContextMenu(); | |||
| void resetAction(); | |||
| virtual void reset() {} | |||
| @@ -11,7 +11,7 @@ namespace app { | |||
| /** Manages an engine::Port on a ModuleWidget. */ | |||
| struct PortWidget : widget::OpaqueWidget { | |||
| engine::Module *module = NULL; | |||
| engine::Module* module = NULL; | |||
| int portId; | |||
| bool hovered = false; | |||
| @@ -20,22 +20,22 @@ struct PortWidget : widget::OpaqueWidget { | |||
| INPUT | |||
| }; | |||
| Type type; | |||
| MultiLightWidget *plugLight; | |||
| MultiLightWidget* plugLight; | |||
| PortWidget(); | |||
| ~PortWidget(); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void onButton(const event::Button &e) override; | |||
| void onEnter(const event::Enter &e) override; | |||
| void onLeave(const event::Leave &e) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDragDrop(const event::DragDrop &e) override; | |||
| void onDragEnter(const event::DragEnter &e) override; | |||
| void onDragLeave(const event::DragLeave &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onEnter(const event::Enter& e) override; | |||
| void onLeave(const event::Leave& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragDrop(const event::DragDrop& e) override; | |||
| void onDragEnter(const event::DragEnter& e) override; | |||
| void onDragLeave(const event::DragLeave& e) override; | |||
| }; | |||
| @@ -11,7 +11,7 @@ struct RackRail : widget::TransparentWidget { | |||
| std::shared_ptr<Svg> busBoardSvg; | |||
| RackRail(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -10,17 +10,17 @@ namespace app { | |||
| struct RackScrollWidget : ui::ScrollWidget { | |||
| widget::ZoomWidget *zoomWidget; | |||
| RackWidget *rackWidget; | |||
| widget::ZoomWidget* zoomWidget; | |||
| RackWidget* rackWidget; | |||
| /** The pivot point for zooming */ | |||
| math::Vec zoomPos; | |||
| math::Vec oldOffset; | |||
| RackScrollWidget(); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void onHoverKey(const event::HoverKey &e) override; | |||
| void onHoverScroll(const event::HoverScroll &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onHoverKey(const event::HoverKey& e) override; | |||
| void onHoverScroll(const event::HoverScroll& e) override; | |||
| void reset(); | |||
| }; | |||
| @@ -16,13 +16,13 @@ namespace app { | |||
| /** Container for ModuleWidget and CableWidget. */ | |||
| struct RackWidget : widget::OpaqueWidget { | |||
| widget::Widget *moduleContainer; | |||
| widget::Widget *cableContainer; | |||
| CableWidget *incompleteCable = NULL; | |||
| widget::FramebufferWidget *railFb; | |||
| widget::Widget* moduleContainer; | |||
| widget::Widget* cableContainer; | |||
| CableWidget* incompleteCable = NULL; | |||
| widget::FramebufferWidget* railFb; | |||
| /** The last mouse position in the RackWidget */ | |||
| math::Vec mousePos; | |||
| ParamWidget *touchedParam = NULL; | |||
| ParamWidget* touchedParam = NULL; | |||
| std::map<int, math::Vec> moduleDragPositions; | |||
| int nextCableColorId = 0; | |||
| @@ -30,17 +30,17 @@ struct RackWidget : widget::OpaqueWidget { | |||
| ~RackWidget(); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onHover(const event::Hover &e) override; | |||
| void onHoverKey(const event::HoverKey &e) override; | |||
| void onDragHover(const event::DragHover &e) override; | |||
| void onButton(const event::Button &e) override; | |||
| void onHover(const event::Hover& e) override; | |||
| void onHoverKey(const event::HoverKey& e) override; | |||
| void onDragHover(const event::DragHover& e) override; | |||
| void onButton(const event::Button& e) override; | |||
| /** Completely clear the rack's modules and cables */ | |||
| void clear(); | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| void pastePresetClipboardAction(); | |||
| // Module methods | |||
| @@ -48,39 +48,39 @@ struct RackWidget : widget::OpaqueWidget { | |||
| /** Adds a module and adds it to the Engine | |||
| Ownership rules work like add/removeChild() | |||
| */ | |||
| void addModule(ModuleWidget *mw); | |||
| void addModuleAtMouse(ModuleWidget *mw); | |||
| void addModule(ModuleWidget* mw); | |||
| void addModuleAtMouse(ModuleWidget* mw); | |||
| /** Removes the module and transfers ownership to the caller */ | |||
| void removeModule(ModuleWidget *mw); | |||
| void removeModule(ModuleWidget* mw); | |||
| /** Sets a module's box if non-colliding. Returns true if set */ | |||
| bool requestModulePos(ModuleWidget *mw, math::Vec pos); | |||
| bool requestModulePos(ModuleWidget* mw, math::Vec pos); | |||
| /** Moves a module to the closest non-colliding position */ | |||
| void setModulePosNearest(ModuleWidget *mw, math::Vec pos); | |||
| void setModulePosForce(ModuleWidget *mw, math::Vec pos); | |||
| ModuleWidget *getModule(int moduleId); | |||
| void setModulePosNearest(ModuleWidget* mw, math::Vec pos); | |||
| void setModulePosForce(ModuleWidget* mw, math::Vec pos); | |||
| ModuleWidget* getModule(int moduleId); | |||
| bool isEmpty(); | |||
| void updateModuleDragPositions(); | |||
| history::ComplexAction *getModuleDragAction(); | |||
| history::ComplexAction* getModuleDragAction(); | |||
| // Cable methods | |||
| void clearCables(); | |||
| void clearCablesAction(); | |||
| /** Removes all complete cables connected to the port */ | |||
| void clearCablesOnPort(PortWidget *port); | |||
| void clearCablesOnPort(PortWidget* port); | |||
| /** Adds a complete cable and adds it to the Engine. | |||
| Ownership rules work like add/removeChild() | |||
| */ | |||
| void addCable(CableWidget *w); | |||
| void removeCable(CableWidget *w); | |||
| void addCable(CableWidget* w); | |||
| void removeCable(CableWidget* w); | |||
| /** Takes ownership of `w` and adds it as a child if it isn't already */ | |||
| void setIncompleteCable(CableWidget *w); | |||
| CableWidget *releaseIncompleteCable(); | |||
| void setIncompleteCable(CableWidget* w); | |||
| CableWidget* releaseIncompleteCable(); | |||
| /** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack */ | |||
| CableWidget *getTopCable(PortWidget *port); | |||
| CableWidget *getCable(int cableId); | |||
| CableWidget* getTopCable(PortWidget* port); | |||
| CableWidget* getCable(int cableId); | |||
| /** Returns all cables attached to port, complete or not */ | |||
| std::list<CableWidget*> getCablesOnPort(PortWidget *port); | |||
| std::list<CableWidget*> getCablesOnPort(PortWidget* port); | |||
| }; | |||
| @@ -12,10 +12,10 @@ namespace app { | |||
| struct Scene : widget::OpaqueWidget { | |||
| // Convenience variables for accessing important widgets | |||
| RackScrollWidget *rackScroll; | |||
| RackWidget *rack; | |||
| MenuBar *menuBar; | |||
| widget::Widget *moduleBrowser; | |||
| RackScrollWidget* rackScroll; | |||
| RackWidget* rack; | |||
| MenuBar* menuBar; | |||
| widget::Widget* moduleBrowser; | |||
| double lastAutosaveTime = 0.0; | |||
| @@ -27,9 +27,9 @@ struct Scene : widget::OpaqueWidget { | |||
| Scene(); | |||
| ~Scene(); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void onHoverKey(const event::HoverKey &e) override; | |||
| void onPathDrop(const event::PathDrop &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onHoverKey(const event::HoverKey& e) override; | |||
| void onPathDrop(const event::PathDrop& e) override; | |||
| void runCheckVersion(); | |||
| }; | |||
| @@ -9,10 +9,10 @@ namespace app { | |||
| struct SliderKnob : Knob { | |||
| // Bypass Knob's circular hitbox detection | |||
| void onHover(const event::Hover &e) override { | |||
| void onHover(const event::Hover& e) override { | |||
| ParamWidget::onHover(e); | |||
| } | |||
| void onButton(const event::Button &e) override { | |||
| void onButton(const event::Button& e) override { | |||
| ParamWidget::onButton(e); | |||
| } | |||
| }; | |||
| @@ -11,16 +11,16 @@ namespace app { | |||
| struct SvgButton : widget::OpaqueWidget { | |||
| widget::FramebufferWidget *fb; | |||
| CircularShadow *shadow; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fb; | |||
| CircularShadow* shadow; | |||
| widget::SvgWidget* sw; | |||
| std::vector<std::shared_ptr<Svg>> frames; | |||
| SvgButton(); | |||
| void addFrame(std::shared_ptr<Svg> svg); | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDragDrop(const event::DragDrop &e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragDrop(const event::DragDrop& e) override; | |||
| }; | |||
| @@ -13,18 +13,20 @@ namespace app { | |||
| /** A knob which rotates an SVG and caches it in a framebuffer */ | |||
| struct SvgKnob : Knob { | |||
| widget::FramebufferWidget *fb; | |||
| CircularShadow *shadow; | |||
| widget::TransformWidget *tw; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fb; | |||
| CircularShadow* shadow; | |||
| widget::TransformWidget* tw; | |||
| widget::SvgWidget* sw; | |||
| /** Angles in radians */ | |||
| float minAngle = 0.f; | |||
| float maxAngle = M_PI; | |||
| SvgKnob(); | |||
| void setSvg(std::shared_ptr<Svg> svg); | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) {setSvg(svg);} | |||
| void onChange(const event::Change &e) override; | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) { | |||
| setSvg(svg); | |||
| } | |||
| void onChange(const event::Change& e) override; | |||
| }; | |||
| @@ -11,7 +11,7 @@ namespace app { | |||
| struct PanelBorder : widget::TransparentWidget { | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -11,13 +11,15 @@ namespace app { | |||
| struct SvgPort : PortWidget { | |||
| widget::FramebufferWidget *fb; | |||
| CircularShadow *shadow; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fb; | |||
| CircularShadow* shadow; | |||
| widget::SvgWidget* sw; | |||
| SvgPort(); | |||
| void setSvg(std::shared_ptr<Svg> svg); | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) {setSvg(svg);} | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) { | |||
| setSvg(svg); | |||
| } | |||
| }; | |||
| @@ -11,8 +11,8 @@ namespace app { | |||
| /** If you don't add these to your ModuleWidget, they will fall out of the rack... */ | |||
| struct SvgScrew : widget::Widget { | |||
| widget::FramebufferWidget *fb; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fb; | |||
| widget::SvgWidget* sw; | |||
| SvgScrew(); | |||
| void setSvg(std::shared_ptr<Svg> svg); | |||
| @@ -13,19 +13,23 @@ namespace app { | |||
| Can be used for horizontal or vertical linear faders. | |||
| */ | |||
| struct SvgSlider : app::SliderKnob { | |||
| widget::FramebufferWidget *fb; | |||
| widget::SvgWidget *background; | |||
| widget::SvgWidget *handle; | |||
| widget::FramebufferWidget* fb; | |||
| widget::SvgWidget* background; | |||
| widget::SvgWidget* handle; | |||
| /** Intermediate positions will be interpolated between these positions */ | |||
| math::Vec minHandlePos, maxHandlePos; | |||
| SvgSlider(); | |||
| void setBackgroundSvg(std::shared_ptr<Svg> svg); | |||
| void setHandleSvg(std::shared_ptr<Svg> svg); | |||
| void onChange(const event::Change &e) override; | |||
| void onChange(const event::Change& e) override; | |||
| DEPRECATED void setBackgroundSVG(std::shared_ptr<Svg> svg) {setBackgroundSvg(svg);} | |||
| DEPRECATED void setHandleSVG(std::shared_ptr<Svg> svg) {setBackgroundSvg(svg);} | |||
| DEPRECATED void setBackgroundSVG(std::shared_ptr<Svg> svg) { | |||
| setBackgroundSvg(svg); | |||
| } | |||
| DEPRECATED void setHandleSVG(std::shared_ptr<Svg> svg) { | |||
| setBackgroundSvg(svg); | |||
| } | |||
| DEPRECATED void setSVGs(std::shared_ptr<Svg> backgroundSvg, std::shared_ptr<Svg> handleSvg) { | |||
| setBackgroundSvg(backgroundSvg); | |||
| setHandleSvg(handleSvg); | |||
| @@ -12,15 +12,15 @@ namespace app { | |||
| /** A ParamWidget with multiple frames corresponding to its value */ | |||
| struct SvgSwitch : Switch { | |||
| widget::FramebufferWidget *fb; | |||
| CircularShadow *shadow; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fb; | |||
| CircularShadow* shadow; | |||
| widget::SvgWidget* sw; | |||
| std::vector<std::shared_ptr<Svg>> frames; | |||
| SvgSwitch(); | |||
| /** Adds an SVG file to represent the next switch position */ | |||
| void addFrame(std::shared_ptr<Svg> svg); | |||
| void onChange(const event::Change &e) override; | |||
| void onChange(const event::Change& e) override; | |||
| }; | |||
| @@ -20,9 +20,9 @@ struct Switch : ParamWidget { | |||
| bool momentaryReleased = false; | |||
| void step() override; | |||
| void onDoubleClick(const event::DoubleClick &e) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDoubleClick(const event::DoubleClick& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void reset() override; | |||
| void randomize() override; | |||
| }; | |||
| @@ -6,7 +6,7 @@ namespace rack { | |||
| namespace plugin { | |||
| struct Plugin; | |||
| struct Plugin; | |||
| } // namespace plugin | |||
| @@ -19,7 +19,7 @@ std::string system(std::string filename); | |||
| /** Returns the path of a user resource. Can read and write files to this location. */ | |||
| std::string user(std::string filename); | |||
| /** Returns the path of a resource in the plugin's folder. Should only read files from this location. */ | |||
| std::string plugin(plugin::Plugin *plugin, std::string filename); | |||
| std::string plugin(plugin::Plugin* plugin, std::string filename); | |||
| // Set these before calling init() to override the default paths | |||
| @@ -4,7 +4,7 @@ | |||
| #pragma GCC diagnostic push | |||
| #ifndef __clang__ | |||
| #pragma GCC diagnostic ignored "-Wsuggest-override" | |||
| #pragma GCC diagnostic ignored "-Wsuggest-override" | |||
| #endif | |||
| #include <RtAudio.h> | |||
| #pragma GCC diagnostic pop | |||
| @@ -28,7 +28,7 @@ struct Port { | |||
| int blockSize = 256; | |||
| int numOutputs = 0; | |||
| int numInputs = 0; | |||
| RtAudio *rtAudio = NULL; | |||
| RtAudio* rtAudio = NULL; | |||
| /** Cached */ | |||
| RtAudio::DeviceInfo deviceInfo; | |||
| @@ -40,7 +40,7 @@ struct Port { | |||
| void setDriverId(int driverId); | |||
| int getDeviceCount(); | |||
| bool getDeviceInfo(int deviceId, RtAudio::DeviceInfo *deviceInfo); | |||
| bool getDeviceInfo(int deviceId, RtAudio::DeviceInfo* deviceInfo); | |||
| /** Returns the number of inputs or outputs, whichever is greater */ | |||
| int getDeviceChannels(int deviceId); | |||
| std::string getDeviceName(int deviceId); | |||
| @@ -58,12 +58,12 @@ struct Port { | |||
| void openStream(); | |||
| void closeStream(); | |||
| virtual void processStream(const float *input, float *output, int frames) {} | |||
| virtual void processStream(const float* input, float* output, int frames) {} | |||
| virtual void onCloseStream() {} | |||
| virtual void onOpenStream() {} | |||
| virtual void onChannelsChange() {} | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| }; | |||
| @@ -8,8 +8,8 @@ namespace rack { | |||
| void bridgeInit(); | |||
| void bridgeDestroy(); | |||
| void bridgeAudioSubscribe(int channel, audio::Port *port); | |||
| void bridgeAudioUnsubscribe(int channel, audio::Port *port); | |||
| void bridgeAudioSubscribe(int channel, audio::Port* port); | |||
| void bridgeAudioUnsubscribe(int channel, audio::Port* port); | |||
| } // namespace rack | |||
| @@ -120,13 +120,13 @@ Example: | |||
| Foo *foo = construct<Foo>(&Foo::greeting, "Hello world", &Foo::legs, 2); | |||
| */ | |||
| template <typename T> | |||
| T *construct() { | |||
| T* construct() { | |||
| return new T; | |||
| } | |||
| template <typename T, typename F, typename V, typename... Args> | |||
| T *construct(F f, V v, Args... args) { | |||
| T *o = construct<T>(args...); | |||
| T* construct(F f, V v, Args... args) { | |||
| T* o = construct<T>(args...); | |||
| o->*f = v; | |||
| return o; | |||
| } | |||
| @@ -144,7 +144,9 @@ template <typename F> | |||
| struct DeferWrapper { | |||
| F f; | |||
| DeferWrapper(F f) : f(f) {} | |||
| ~DeferWrapper() { f(); } | |||
| ~DeferWrapper() { | |||
| f(); | |||
| } | |||
| }; | |||
| template <typename F> | |||
| @@ -157,7 +159,7 @@ DeferWrapper<F> deferWrapper(F f) { | |||
| /** An exception meant to be shown to the user */ | |||
| struct UserException : std::runtime_error { | |||
| UserException(const std::string &msg) : std::runtime_error(msg) {} | |||
| UserException(const std::string& msg) : std::runtime_error(msg) {} | |||
| }; | |||
| @@ -45,8 +45,8 @@ static const NVGcolor SCHEME_DARK_GRAY = nvgRGB(0x17, 0x17, 0x17); | |||
| struct RoundKnob : app::SvgKnob { | |||
| RoundKnob() { | |||
| minAngle = -0.83*M_PI; | |||
| maxAngle = 0.83*M_PI; | |||
| minAngle = -0.83 * M_PI; | |||
| maxAngle = 0.83 * M_PI; | |||
| } | |||
| }; | |||
| @@ -83,8 +83,8 @@ struct RoundBlackSnapKnob : RoundBlackKnob { | |||
| struct Davies1900hKnob : app::SvgKnob { | |||
| Davies1900hKnob() { | |||
| minAngle = -0.83*M_PI; | |||
| maxAngle = 0.83*M_PI; | |||
| minAngle = -0.83 * M_PI; | |||
| maxAngle = 0.83 * M_PI; | |||
| } | |||
| }; | |||
| @@ -127,8 +127,8 @@ struct Davies1900hLargeRedKnob : Davies1900hKnob { | |||
| struct Rogan : app::SvgKnob { | |||
| Rogan() { | |||
| minAngle = -0.83*M_PI; | |||
| maxAngle = 0.83*M_PI; | |||
| minAngle = -0.83 * M_PI; | |||
| maxAngle = 0.83 * M_PI; | |||
| } | |||
| }; | |||
| @@ -297,12 +297,12 @@ struct Rogan1PWhite : Rogan { | |||
| struct SynthTechAlco : app::SvgKnob { | |||
| SynthTechAlco() { | |||
| minAngle = -0.82*M_PI; | |||
| maxAngle = 0.82*M_PI; | |||
| minAngle = -0.82 * M_PI; | |||
| maxAngle = 0.82 * M_PI; | |||
| setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco.svg"))); | |||
| // Add cap | |||
| widget::FramebufferWidget *capFb = new widget::FramebufferWidget; | |||
| widget::SvgWidget *cap = new widget::SvgWidget; | |||
| widget::FramebufferWidget* capFb = new widget::FramebufferWidget; | |||
| widget::SvgWidget* cap = new widget::SvgWidget; | |||
| cap->setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/SynthTechAlco_cap.svg"))); | |||
| capFb->addChild(cap); | |||
| addChild(capFb); | |||
| @@ -311,16 +311,16 @@ struct SynthTechAlco : app::SvgKnob { | |||
| struct Trimpot : app::SvgKnob { | |||
| Trimpot() { | |||
| minAngle = -0.75*M_PI; | |||
| maxAngle = 0.75*M_PI; | |||
| minAngle = -0.75 * M_PI; | |||
| maxAngle = 0.75 * M_PI; | |||
| setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/Trimpot.svg"))); | |||
| } | |||
| }; | |||
| struct BefacoBigKnob : app::SvgKnob { | |||
| BefacoBigKnob() { | |||
| minAngle = -0.75*M_PI; | |||
| maxAngle = 0.75*M_PI; | |||
| minAngle = -0.75 * M_PI; | |||
| maxAngle = 0.75 * M_PI; | |||
| setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/BefacoBigKnob.svg"))); | |||
| } | |||
| }; | |||
| @@ -333,8 +333,8 @@ struct BefacoBigSnapKnob : BefacoBigKnob { | |||
| struct BefacoTinyKnob : app::SvgKnob { | |||
| BefacoTinyKnob() { | |||
| minAngle = -0.75*M_PI; | |||
| maxAngle = 0.75*M_PI; | |||
| minAngle = -0.75 * M_PI; | |||
| maxAngle = 0.75 * M_PI; | |||
| setSvg(APP->window->loadSvg(asset::system("res/ComponentLibrary/BefacoTinyKnob.svg"))); | |||
| } | |||
| }; | |||
| @@ -0,0 +1,70 @@ | |||
| #pragma once | |||
| #include <dsp/common.hpp> | |||
| namespace rack { | |||
| namespace dsp { | |||
| /* | |||
| In this header, function names are divided into two or more parts, separated by "_". | |||
| The functionality is the first part, and the approximation methods are the following parts. | |||
| Glossary: | |||
| https://en.wikipedia.org/wiki/Taylor_series | |||
| https://en.wikipedia.org/wiki/Chebyshev_polynomials | |||
| https://en.wikipedia.org/wiki/Pad%C3%A9_approximant | |||
| https://en.wikipedia.org/wiki/Horner%27s_method | |||
| https://en.wikipedia.org/wiki/Estrin%27s_scheme | |||
| https://en.wikipedia.org/wiki/CORDIC | |||
| */ | |||
| /** Returns 2^floor(x), assuming that x >= 0. | |||
| If `xf` is non-NULL, it is set to the fractional part of x. | |||
| */ | |||
| template <typename T> | |||
| T approxExp2Floor(T x, T* xf); | |||
| template <> | |||
| inline simd::float_4 approxExp2Floor(simd::float_4 x, simd::float_4* xf) { | |||
| simd::int32_4 xi = x; | |||
| if (xf) | |||
| *xf = x - simd::float_4(xi); | |||
| // Set float exponent directly | |||
| // https://stackoverflow.com/a/57454528/272642 | |||
| simd::int32_4 y = (xi + 127) << 23; | |||
| return simd::float_4::cast(y); | |||
| } | |||
| template <> | |||
| inline float approxExp2Floor(float x, float* xf) { | |||
| int xi = x; | |||
| if (xf) | |||
| *xf = x - xi; | |||
| return 1 << xi; | |||
| } | |||
| /** Returns 2^x, assuming that x >= 0. | |||
| Maximum 0.00024% error. | |||
| For float, roughly 3x faster than `std::pow(2.f, x)`. | |||
| For float_4, roughly 2x faster than `simd::pow(2.f, x)`. | |||
| If negative powers are needed, you may use a lower bound and rescale. | |||
| approxExp2(x + 20) / 1048576 | |||
| */ | |||
| template <typename T> | |||
| T approxExp2_taylor5(T x) { | |||
| // Use bit-shifting for integer part of x. | |||
| T y = approxExp2Floor(x, &x); | |||
| // 5th order expansion of 2^x around 0.4752 in Horner form. | |||
| // The center is chosen so that the endpoints of [0, 1] have equal error, creating no discontinuity at integers. | |||
| y *= T(0.9999976457798443) + x * (T(0.6931766804601935) + x * (T(0.2400729486415728) + x * (T(0.05592817518644387) + x * (T(0.008966320633544) + x * T(0.001853512473884202))))); | |||
| return y; | |||
| } | |||
| } // namespace dsp | |||
| } // namespace rack | |||
| @@ -53,23 +53,23 @@ T dbToAmplitude(T db) { | |||
| template <typename T> | |||
| T quadraticBipolar(T x) { | |||
| return simd::sgn(x) * (x*x); | |||
| return simd::sgn(x) * (x * x); | |||
| } | |||
| template <typename T> | |||
| T cubic(T x) { | |||
| return x*x*x; | |||
| return x * x * x; | |||
| } | |||
| template <typename T> | |||
| T quarticBipolar(T x) { | |||
| return simd::sgn(x) * (x*x*x*x); | |||
| return simd::sgn(x) * (x * x * x * x); | |||
| } | |||
| template <typename T> | |||
| T quintic(T x) { | |||
| // optimal with -fassociative-math | |||
| return x*x*x*x*x; | |||
| return x * x * x * x * x; | |||
| } | |||
| template <typename T> | |||
| @@ -23,7 +23,27 @@ struct BooleanTrigger { | |||
| /** Turns HIGH when value reaches 1.f, turns LOW when value reaches 0.f. */ | |||
| struct SchmittTrigger { | |||
| template <typename T = float> | |||
| struct TSchmittTrigger { | |||
| T state; | |||
| TSchmittTrigger() { | |||
| reset(); | |||
| } | |||
| void reset() { | |||
| state = T::mask(); | |||
| } | |||
| T process(T in) { | |||
| T on = (in >= 1.f); | |||
| T off = (in <= 0.f); | |||
| T triggered = ~state & on; | |||
| state = on | (state & ~off); | |||
| return triggered; | |||
| } | |||
| }; | |||
| template <> | |||
| struct TSchmittTrigger<float> { | |||
| bool state = true; | |||
| void reset() { | |||
| @@ -60,24 +80,7 @@ struct SchmittTrigger { | |||
| } | |||
| }; | |||
| template <typename T> | |||
| struct TSchmittTrigger { | |||
| T state; | |||
| TSchmittTrigger() { | |||
| reset(); | |||
| } | |||
| void reset() { | |||
| state = T::mask(); | |||
| } | |||
| T process(T in) { | |||
| T on = (in >= 1.f); | |||
| T off = (in <= 0.f); | |||
| T triggered = ~state & on; | |||
| state = on | (state & ~off); | |||
| return triggered; | |||
| } | |||
| }; | |||
| typedef TSchmittTrigger<> SchmittTrigger; | |||
| /** When triggered, holds a high value for a specified time before going low again */ | |||
| @@ -13,7 +13,7 @@ Wrapper for [PFFFT](https://bitbucket.org/jpommier/pffft/) | |||
| Buffers must be aligned to 16-byte boundaries. new[] and malloc() do this for you. | |||
| */ | |||
| struct RealFFT { | |||
| PFFFT_Setup *setup; | |||
| PFFFT_Setup* setup; | |||
| int length; | |||
| RealFFT(size_t length) { | |||
| @@ -31,7 +31,7 @@ struct RealFFT { | |||
| Output is arbitrarily ordered for performance reasons. | |||
| However, this ordering is consistent, so element-wise multiplication with line up with other results, and the inverse FFT will return a correctly ordered result. | |||
| */ | |||
| void rfftUnordered(const float *input, float *output) { | |||
| void rfftUnordered(const float* input, float* output) { | |||
| pffft_transform(setup, input, output, NULL, PFFFT_FORWARD); | |||
| } | |||
| @@ -39,7 +39,7 @@ struct RealFFT { | |||
| Input is `2*length` elements. Output is `length` elements. | |||
| Scaling is such that IRFFT(RFFT(x)) = N*x. | |||
| */ | |||
| void irfftUnordered(const float *input, float *output) { | |||
| void irfftUnordered(const float* input, float* output) { | |||
| pffft_transform(setup, input, output, NULL, PFFFT_BACKWARD); | |||
| } | |||
| @@ -54,17 +54,17 @@ struct RealFFT { | |||
| output[length - 2] = real(F(n/2 - 1)) | |||
| output[length - 1] = imag(F(n/2 - 1)) | |||
| */ | |||
| void rfft(const float *input, float *output) { | |||
| void rfft(const float* input, float* output) { | |||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_FORWARD); | |||
| } | |||
| void irfft(const float *input, float *output) { | |||
| void irfft(const float* input, float* output) { | |||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); | |||
| } | |||
| /** Scales the RFFT so that `scale(IFFT(FFT(x))) = x`. | |||
| */ | |||
| void scale(float *x) { | |||
| void scale(float* x) { | |||
| float a = 1.f / length; | |||
| for (int i = 0; i < length; i++) { | |||
| x[i] *= a; | |||
| @@ -77,7 +77,7 @@ struct RealFFT { | |||
| `length` must be a multiple of 16. | |||
| */ | |||
| struct ComplexFFT { | |||
| PFFFT_Setup *setup; | |||
| PFFFT_Setup* setup; | |||
| int length; | |||
| ComplexFFT(size_t length) { | |||
| @@ -93,7 +93,7 @@ struct ComplexFFT { | |||
| Input and output must be aligned using the above align*() functions. | |||
| Input is `2*length` elements. Output is `2*length` elements. | |||
| */ | |||
| void fftUnordered(const float *input, float *output) { | |||
| void fftUnordered(const float* input, float* output) { | |||
| pffft_transform(setup, input, output, NULL, PFFFT_FORWARD); | |||
| } | |||
| @@ -101,23 +101,23 @@ struct ComplexFFT { | |||
| Input is `2*length` elements. Output is `2*length` elements. | |||
| Scaling is such that FFT(IFFT(x)) = N*x. | |||
| */ | |||
| void ifftUnordered(const float *input, float *output) { | |||
| void ifftUnordered(const float* input, float* output) { | |||
| pffft_transform(setup, input, output, NULL, PFFFT_BACKWARD); | |||
| } | |||
| void fft(const float *input, float *output) { | |||
| void fft(const float* input, float* output) { | |||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_FORWARD); | |||
| } | |||
| void ifft(const float *input, float *output) { | |||
| void ifft(const float* input, float* output) { | |||
| pffft_transform_ordered(setup, input, output, NULL, PFFFT_BACKWARD); | |||
| } | |||
| void scale(float *x) { | |||
| void scale(float* x) { | |||
| float a = 1.f / length; | |||
| for (int i = 0; i < length; i++) { | |||
| x[2*i+0] *= a; | |||
| x[2*i+1] *= a; | |||
| x[2 * i + 0] *= a; | |||
| x[2 * i + 1] *= a; | |||
| } | |||
| } | |||
| }; | |||
| @@ -68,6 +68,10 @@ struct TExponentialFilter { | |||
| this->lambda = lambda; | |||
| } | |||
| void setTau(T tau) { | |||
| this->lambda = 1 / tau; | |||
| } | |||
| T process(T deltaTime, T in) { | |||
| T y = out + (in - out) * lambda * deltaTime; | |||
| // If no change was made between the old and new output, assume T granularity is too small and snap output to input | |||
| @@ -98,6 +102,10 @@ struct TPeakFilter { | |||
| this->lambda = lambda; | |||
| } | |||
| void setTau(T tau) { | |||
| this->lambda = 1 / tau; | |||
| } | |||
| T process(T deltaTime, T in) { | |||
| T y = out + (in - out) * lambda * deltaTime; | |||
| out = simd::fmax(y, in); | |||
| @@ -174,5 +182,207 @@ struct TExponentialSlewLimiter { | |||
| typedef TExponentialSlewLimiter<> ExponentialSlewLimiter; | |||
| template <typename T = float> | |||
| struct TBiquadFilter { | |||
| /** input state */ | |||
| T x[2]; | |||
| /** output state */ | |||
| T y[2]; | |||
| /** transfer function numerator coefficients: b_0, b_1, b_2 */ | |||
| float b[3]; | |||
| /** transfer function denominator coefficients: a_1, a_2 | |||
| a_0 is fixed to 1. | |||
| */ | |||
| float a[2]; | |||
| enum Type { | |||
| LOWPASS_1POLE, | |||
| HIGHPASS_1POLE, | |||
| LOWPASS, | |||
| HIGHPASS, | |||
| LOWSHELF, | |||
| HIGHSHELF, | |||
| BANDPASS, | |||
| PEAK, | |||
| NOTCH, | |||
| NUM_TYPES | |||
| }; | |||
| TBiquadFilter() { | |||
| reset(); | |||
| setParameters(LOWPASS, 0.f, 0.f, 1.f); | |||
| } | |||
| void reset() { | |||
| std::memset(x, 0, sizeof(x)); | |||
| std::memset(y, 0, sizeof(y)); | |||
| } | |||
| T process(T in) { | |||
| // Advance IIR | |||
| T out = b[0] * in + b[1] * x[0] + b[2] * x[1] | |||
| - a[0] * y[0] - a[1] * y[1]; | |||
| // Push input | |||
| x[1] = x[0]; | |||
| x[0] = in; | |||
| // Push output | |||
| y[1] = y[0]; | |||
| y[0] = out; | |||
| return out; | |||
| } | |||
| /** Calculates and sets the biquad transfer function coefficients. | |||
| f: normalized frequency (cutoff frequency / sample rate), must be less than 0.5 | |||
| Q: quality factor | |||
| V: gain | |||
| */ | |||
| void setParameters(Type type, float f, float Q, float V) { | |||
| float K = std::tan(M_PI * f); | |||
| switch (type) { | |||
| case LOWPASS_1POLE: { | |||
| a[0] = -std::exp(-2.f * M_PI * f); | |||
| a[1] = 0.f; | |||
| b[0] = 1.f + a[0]; | |||
| b[1] = 0.f; | |||
| b[2] = 0.f; | |||
| } break; | |||
| case HIGHPASS_1POLE: { | |||
| a[0] = std::exp(-2.f * M_PI * (0.5f - f)); | |||
| a[1] = 0.f; | |||
| b[0] = 1.f - a[0]; | |||
| b[1] = 0.f; | |||
| b[2] = 0.f; | |||
| } break; | |||
| case LOWPASS: { | |||
| float norm = 1.f / (1.f + K / Q + K * K); | |||
| b[0] = K * K * norm; | |||
| b[1] = 2.f * b[0]; | |||
| b[2] = b[0]; | |||
| a[0] = 2.f * (K * K - 1.f) * norm; | |||
| a[1] = (1.f - K / Q + K * K) * norm; | |||
| } break; | |||
| case HIGHPASS: { | |||
| float norm = 1.f / (1.f + K / Q + K * K); | |||
| b[0] = norm; | |||
| b[1] = -2.f * b[0]; | |||
| b[2] = b[0]; | |||
| a[0] = 2.f * (K * K - 1.f) * norm; | |||
| a[1] = (1.f - K / Q + K * K) * norm; | |||
| } break; | |||
| case LOWSHELF: { | |||
| float sqrtV = std::sqrt(V); | |||
| if (V >= 1.f) { | |||
| float norm = 1.f / (1.f + M_SQRT2 * K + K * K); | |||
| b[0] = (1.f + M_SQRT2 * sqrtV * K + V * K * K) * norm; | |||
| b[1] = 2.f * (V * K * K - 1.f) * norm; | |||
| b[2] = (1.f - M_SQRT2 * sqrtV * K + V * K * K) * norm; | |||
| a[0] = 2.f * (K * K - 1.f) * norm; | |||
| a[1] = (1.f - M_SQRT2 * K + K * K) * norm; | |||
| } | |||
| else { | |||
| float norm = 1.f / (1.f + M_SQRT2 / sqrtV * K + K * K / V); | |||
| b[0] = (1.f + M_SQRT2 * K + K * K) * norm; | |||
| b[1] = 2.f * (K * K - 1) * norm; | |||
| b[2] = (1.f - M_SQRT2 * K + K * K) * norm; | |||
| a[0] = 2.f * (K * K / V - 1.f) * norm; | |||
| a[1] = (1.f - M_SQRT2 / sqrtV * K + K * K / V) * norm; | |||
| } | |||
| } break; | |||
| case HIGHSHELF: { | |||
| float sqrtV = std::sqrt(V); | |||
| if (V >= 1.f) { | |||
| float norm = 1.f / (1.f + M_SQRT2 * K + K * K); | |||
| b[0] = (V + M_SQRT2 * sqrtV * K + K * K) * norm; | |||
| b[1] = 2.f * (K * K - V) * norm; | |||
| b[2] = (V - M_SQRT2 * sqrtV * K + K * K) * norm; | |||
| a[0] = 2.f * (K * K - 1.f) * norm; | |||
| a[1] = (1.f - M_SQRT2 * K + K * K) * norm; | |||
| } | |||
| else { | |||
| float norm = 1.f / (1.f / V + M_SQRT2 / sqrtV * K + K * K); | |||
| b[0] = (1.f + M_SQRT2 * K + K * K) * norm; | |||
| b[1] = 2.f * (K * K - 1.f) * norm; | |||
| b[2] = (1.f - M_SQRT2 * K + K * K) * norm; | |||
| a[0] = 2.f * (K * K - 1.f / V) * norm; | |||
| a[1] = (1.f / V - M_SQRT2 / sqrtV * K + K * K) * norm; | |||
| } | |||
| } break; | |||
| case BANDPASS: { | |||
| float norm = 1.f / (1.f + K / Q + K * K); | |||
| b[0] = K / Q * norm; | |||
| b[1] = 0.f; | |||
| b[2] = -b[0]; | |||
| a[0] = 2.f * (K * K - 1.f) * norm; | |||
| a[1] = (1.f - K / Q + K * K) * norm; | |||
| } break; | |||
| case PEAK: { | |||
| if (V >= 1.f) { | |||
| float norm = 1.f / (1.f + K / Q + K * K); | |||
| b[0] = (1.f + K / Q * V + K * K) * norm; | |||
| b[1] = 2.f * (K * K - 1.f) * norm; | |||
| b[2] = (1.f - K / Q * V + K * K) * norm; | |||
| a[0] = b[1]; | |||
| a[1] = (1.f - K / Q + K * K) * norm; | |||
| } | |||
| else { | |||
| float norm = 1.f / (1.f + K / Q / V + K * K); | |||
| b[0] = (1.f + K / Q + K * K) * norm; | |||
| b[1] = 2.f * (K * K - 1.f) * norm; | |||
| b[2] = (1.f - K / Q + K * K) * norm; | |||
| a[0] = b[1]; | |||
| a[1] = (1.f - K / Q / V + K * K) * norm; | |||
| } | |||
| } break; | |||
| case NOTCH: { | |||
| float norm = 1.f / (1.f + K / Q + K * K); | |||
| b[0] = (1.f + K * K) * norm; | |||
| b[1] = 2.f * (K * K - 1.f) * norm; | |||
| b[2] = b[0]; | |||
| a[0] = b[1]; | |||
| a[1] = (1.f - K / Q + K * K) * norm; | |||
| } break; | |||
| default: break; | |||
| } | |||
| } | |||
| void copyParameters(const TBiquadFilter<T>& from) { | |||
| b[0] = from.b[0]; | |||
| b[1] = from.b[1]; | |||
| b[2] = from.b[2]; | |||
| a[0] = from.a[0]; | |||
| a[1] = from.a[1]; | |||
| } | |||
| /** Computes the gain of a particular frequency | |||
| f: normalized frequency | |||
| */ | |||
| float getFrequencyResponse(float f) { | |||
| // Compute sum(a_k e^(-i k f)) | |||
| std::complex<float> bsum = b[0]; | |||
| std::complex<float> asum = 1.f; | |||
| for (int i = 1; i < 3; i++) { | |||
| float p = 2 * M_PI * -i * f; | |||
| std::complex<float> e(std::cos(p), std::sin(p)); | |||
| bsum += b[i] * e; | |||
| asum += a[i - 1] * e; | |||
| } | |||
| return std::abs(bsum / asum); | |||
| } | |||
| }; | |||
| typedef TBiquadFilter<> BiquadFilter; | |||
| } // namespace dsp | |||
| } // namespace rack | |||
| @@ -8,7 +8,7 @@ namespace dsp { | |||
| /** Performs a direct sum convolution */ | |||
| inline float convolveNaive(const float *in, const float *kernel, int len) { | |||
| inline float convolveNaive(const float* in, const float* kernel, int len) { | |||
| float y = 0.f; | |||
| for (int i = 0; i < len; i++) { | |||
| y += in[len - 1 - i] * kernel[i]; | |||
| @@ -17,7 +17,7 @@ inline float convolveNaive(const float *in, const float *kernel, int len) { | |||
| } | |||
| /** Computes the impulse response of a boxcar lowpass filter */ | |||
| inline void boxcarLowpassIR(float *out, int len, float cutoff = 0.5f) { | |||
| inline void boxcarLowpassIR(float* out, int len, float cutoff = 0.5f) { | |||
| for (int i = 0; i < len; i++) { | |||
| float t = i - (len - 1) / 2.f; | |||
| out[i] = 2 * cutoff * sinc(2 * cutoff * t); | |||
| @@ -28,23 +28,23 @@ inline void boxcarLowpassIR(float *out, int len, float cutoff = 0.5f) { | |||
| struct RealTimeConvolver { | |||
| // `kernelBlocks` number of contiguous FFT blocks of size `blockSize` | |||
| // indexed by [i * blockSize*2 + j] | |||
| float *kernelFfts = NULL; | |||
| float *inputFfts = NULL; | |||
| float *outputTail = NULL; | |||
| float *tmpBlock = NULL; | |||
| float* kernelFfts = NULL; | |||
| float* inputFfts = NULL; | |||
| float* outputTail = NULL; | |||
| float* tmpBlock = NULL; | |||
| size_t blockSize; | |||
| size_t kernelBlocks = 0; | |||
| size_t inputPos = 0; | |||
| PFFFT_Setup *pffft; | |||
| PFFFT_Setup* pffft; | |||
| /** `blockSize` is the size of each FFT block. It should be >=32 and a power of 2. */ | |||
| RealTimeConvolver(size_t blockSize) { | |||
| this->blockSize = blockSize; | |||
| pffft = pffft_new_setup(blockSize*2, PFFFT_REAL); | |||
| pffft = pffft_new_setup(blockSize * 2, PFFFT_REAL); | |||
| outputTail = new float[blockSize]; | |||
| std::memset(outputTail, 0, blockSize * sizeof(float)); | |||
| tmpBlock = new float[blockSize*2]; | |||
| std::memset(tmpBlock, 0, blockSize*2 * sizeof(float)); | |||
| tmpBlock = new float[blockSize * 2]; | |||
| std::memset(tmpBlock, 0, blockSize * 2 * sizeof(float)); | |||
| } | |||
| ~RealTimeConvolver() { | |||
| @@ -54,7 +54,7 @@ struct RealTimeConvolver { | |||
| pffft_destroy_setup(pffft); | |||
| } | |||
| void setKernel(const float *kernel, size_t length) { | |||
| void setKernel(const float* kernel, size_t length) { | |||
| // Clear existing kernel | |||
| if (kernelFfts) { | |||
| pffft_aligned_free(kernelFfts); | |||
| @@ -72,17 +72,17 @@ struct RealTimeConvolver { | |||
| kernelBlocks = (length - 1) / blockSize + 1; | |||
| // Allocate blocks | |||
| kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks); | |||
| inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize*2 * kernelBlocks); | |||
| std::memset(inputFfts, 0, sizeof(float) * blockSize*2 * kernelBlocks); | |||
| kernelFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks); | |||
| inputFfts = (float*) pffft_aligned_malloc(sizeof(float) * blockSize * 2 * kernelBlocks); | |||
| std::memset(inputFfts, 0, sizeof(float) * blockSize * 2 * kernelBlocks); | |||
| for (size_t i = 0; i < kernelBlocks; i++) { | |||
| // Pad each block with zeros | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize*2); | |||
| size_t len = std::min((int) blockSize, (int) (length - i*blockSize)); | |||
| std::memcpy(tmpBlock, &kernel[i*blockSize], sizeof(float)*len); | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2); | |||
| size_t len = std::min((int) blockSize, (int)(length - i * blockSize)); | |||
| std::memcpy(tmpBlock, &kernel[i * blockSize], sizeof(float)*len); | |||
| // Compute fft | |||
| pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize*2 * i], NULL, PFFFT_FORWARD); | |||
| pffft_transform(pffft, tmpBlock, &kernelFfts[blockSize * 2 * i], NULL, PFFFT_FORWARD); | |||
| } | |||
| } | |||
| } | |||
| @@ -90,7 +90,7 @@ struct RealTimeConvolver { | |||
| /** Applies reverb to input | |||
| input and output must be of size `blockSize` | |||
| */ | |||
| void processBlock(const float *input, float *output) { | |||
| void processBlock(const float* input, float* output) { | |||
| if (kernelBlocks == 0) { | |||
| std::memset(output, 0, sizeof(float) * blockSize); | |||
| return; | |||
| @@ -99,17 +99,17 @@ struct RealTimeConvolver { | |||
| // Step input position | |||
| inputPos = (inputPos + 1) % kernelBlocks; | |||
| // Pad block with zeros | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize*2); | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2); | |||
| std::memcpy(tmpBlock, input, sizeof(float) * blockSize); | |||
| // Compute input fft | |||
| pffft_transform(pffft, tmpBlock, &inputFfts[blockSize*2 * inputPos], NULL, PFFFT_FORWARD); | |||
| pffft_transform(pffft, tmpBlock, &inputFfts[blockSize * 2 * inputPos], NULL, PFFFT_FORWARD); | |||
| // Create output fft | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize*2); | |||
| std::memset(tmpBlock, 0, sizeof(float) * blockSize * 2); | |||
| // convolve input fft by kernel fft | |||
| // Note: This is the CPU bottleneck loop | |||
| for (size_t i = 0; i < kernelBlocks; i++) { | |||
| size_t pos = (inputPos - i + kernelBlocks) % kernelBlocks; | |||
| pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize*2 * i], &inputFfts[blockSize*2 * pos], tmpBlock, 1.f); | |||
| pffft_zconvolve_accumulate(pffft, &kernelFfts[blockSize * 2 * i], &inputFfts[blockSize * 2 * pos], tmpBlock, 1.f); | |||
| } | |||
| // Compute output | |||
| pffft_transform(pffft, tmpBlock, tmpBlock, NULL, PFFFT_BACKWARD); | |||
| @@ -118,7 +118,7 @@ struct RealTimeConvolver { | |||
| tmpBlock[i] += outputTail[i]; | |||
| } | |||
| // Copy output block to output | |||
| float scale = 1.f / (blockSize*2); | |||
| float scale = 1.f / (blockSize * 2); | |||
| for (size_t i = 0; i < blockSize; i++) { | |||
| // Scale based on FFT | |||
| output[i] = tmpBlock[i] * scale; | |||
| @@ -12,7 +12,7 @@ o: oversample factor | |||
| output: must be length `2 * z * o`. | |||
| https://www.cs.cmu.edu/~eli/papers/icmc01-hardsync.pdf | |||
| */ | |||
| void minBlepImpulse(int z, int o, float *output); | |||
| void minBlepImpulse(int z, int o, float* output); | |||
| template <int Z, int O, typename T = float> | |||
| @@ -15,7 +15,7 @@ namespace dsp { | |||
| /** Resamples by a fixed rational factor. */ | |||
| template <int MAX_CHANNELS> | |||
| struct SampleRateConverter { | |||
| SpeexResamplerState *st = NULL; | |||
| SpeexResamplerState* st = NULL; | |||
| int channels = MAX_CHANNELS; | |||
| int quality = SPEEX_RESAMPLER_QUALITY_DEFAULT; | |||
| int inRate = 44100; | |||
| @@ -73,7 +73,7 @@ struct SampleRateConverter { | |||
| } | |||
| /** `in` and `out` are interlaced with the number of channels */ | |||
| void process(const Frame<MAX_CHANNELS> *in, int *inFrames, Frame<MAX_CHANNELS> *out, int *outFrames) { | |||
| void process(const Frame<MAX_CHANNELS>* in, int* inFrames, Frame<MAX_CHANNELS>* out, int* outFrames) { | |||
| assert(in); | |||
| assert(inFrames); | |||
| assert(out); | |||
| @@ -105,13 +105,13 @@ struct SampleRateConverter { | |||
| /** Downsamples by an integer factor. */ | |||
| template <int OVERSAMPLE, int QUALITY, typename T = float> | |||
| struct Decimator { | |||
| T inBuffer[OVERSAMPLE*QUALITY]; | |||
| float kernel[OVERSAMPLE*QUALITY]; | |||
| T inBuffer[OVERSAMPLE * QUALITY]; | |||
| float kernel[OVERSAMPLE * QUALITY]; | |||
| int inIndex; | |||
| Decimator(float cutoff = 0.9f) { | |||
| boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY); | |||
| boxcarLowpassIR(kernel, OVERSAMPLE * QUALITY, cutoff * 0.5f / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE * QUALITY); | |||
| reset(); | |||
| } | |||
| void reset() { | |||
| @@ -119,17 +119,17 @@ struct Decimator { | |||
| std::memset(inBuffer, 0, sizeof(inBuffer)); | |||
| } | |||
| /** `in` must be length OVERSAMPLE */ | |||
| T process(T *in) { | |||
| T process(T* in) { | |||
| // Copy input to buffer | |||
| std::memcpy(&inBuffer[inIndex], in, OVERSAMPLE*sizeof(T)); | |||
| std::memcpy(&inBuffer[inIndex], in, OVERSAMPLE * sizeof(T)); | |||
| // Advance index | |||
| inIndex += OVERSAMPLE; | |||
| inIndex %= OVERSAMPLE*QUALITY; | |||
| inIndex %= OVERSAMPLE * QUALITY; | |||
| // Perform naive convolution | |||
| T out = 0.f; | |||
| for (int i = 0; i < OVERSAMPLE*QUALITY; i++) { | |||
| for (int i = 0; i < OVERSAMPLE * QUALITY; i++) { | |||
| int index = inIndex - 1 - i; | |||
| index = (index + OVERSAMPLE*QUALITY) % (OVERSAMPLE*QUALITY); | |||
| index = (index + OVERSAMPLE * QUALITY) % (OVERSAMPLE * QUALITY); | |||
| out += kernel[i] * inBuffer[index]; | |||
| } | |||
| return out; | |||
| @@ -141,12 +141,12 @@ struct Decimator { | |||
| template <int OVERSAMPLE, int QUALITY> | |||
| struct Upsampler { | |||
| float inBuffer[QUALITY]; | |||
| float kernel[OVERSAMPLE*QUALITY]; | |||
| float kernel[OVERSAMPLE * QUALITY]; | |||
| int inIndex; | |||
| Upsampler(float cutoff = 0.9f) { | |||
| boxcarLowpassIR(kernel, OVERSAMPLE*QUALITY, cutoff * 0.5f / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE*QUALITY); | |||
| boxcarLowpassIR(kernel, OVERSAMPLE * QUALITY, cutoff * 0.5f / OVERSAMPLE); | |||
| blackmanHarrisWindow(kernel, OVERSAMPLE * QUALITY); | |||
| reset(); | |||
| } | |||
| void reset() { | |||
| @@ -154,7 +154,7 @@ struct Upsampler { | |||
| std::memset(inBuffer, 0, sizeof(inBuffer)); | |||
| } | |||
| /** `out` must be length OVERSAMPLE */ | |||
| void process(float in, float *out) { | |||
| void process(float in, float* out) { | |||
| // Zero-stuff input buffer | |||
| inBuffer[inIndex] = OVERSAMPLE * in; | |||
| // Advance index | |||
| @@ -18,13 +18,13 @@ struct RingBuffer { | |||
| size_t end = 0; | |||
| size_t mask(size_t i) const { | |||
| return i & (S - 1); | |||
| return i & (S - 1); | |||
| } | |||
| void push(T t) { | |||
| size_t i = mask(end++); | |||
| data[i] = t; | |||
| } | |||
| void pushBuffer(const T *t, int n) { | |||
| void pushBuffer(const T* t, int n) { | |||
| size_t i = mask(end); | |||
| size_t e1 = i + n; | |||
| size_t e2 = (e1 < S) ? e1 : S; | |||
| @@ -37,7 +37,7 @@ struct RingBuffer { | |||
| T shift() { | |||
| return data[mask(start++)]; | |||
| } | |||
| void shiftBuffer(T *t, size_t n) { | |||
| void shiftBuffer(T* t, size_t n) { | |||
| size_t i = mask(start); | |||
| size_t s1 = i + n; | |||
| size_t s2 = (s1 < S) ? s1 : S; | |||
| @@ -70,7 +70,7 @@ Thread-safe for single producers and consumers? | |||
| */ | |||
| template <typename T, size_t S> | |||
| struct DoubleRingBuffer { | |||
| T data[S*2]; | |||
| T data[S * 2]; | |||
| size_t start = 0; | |||
| size_t end = 0; | |||
| @@ -104,7 +104,7 @@ struct DoubleRingBuffer { | |||
| If any data is appended, you must call endIncr afterwards. | |||
| Pointer is invalidated when any other method is called. | |||
| */ | |||
| T *endData() { | |||
| T* endData() { | |||
| return &data[mask(end)]; | |||
| } | |||
| void endIncr(size_t n) { | |||
| @@ -123,7 +123,7 @@ struct DoubleRingBuffer { | |||
| /** Returns a pointer to S consecutive elements for consumption | |||
| If any data is consumed, call startIncr afterwards. | |||
| */ | |||
| const T *startData() const { | |||
| const T* startData() const { | |||
| return &data[mask(start)]; | |||
| } | |||
| void startIncr(size_t n) { | |||
| @@ -174,7 +174,7 @@ struct AppleRingBuffer { | |||
| } | |||
| /** Returns a pointer to S consecutive elements for appending, requesting to append n elements. | |||
| */ | |||
| T *endData(size_t n) { | |||
| T* endData(size_t n) { | |||
| if (end + n > N) { | |||
| returnBuffer(); | |||
| } | |||
| @@ -189,7 +189,7 @@ struct AppleRingBuffer { | |||
| /** Returns a pointer to S consecutive elements for consumption | |||
| If any data is consumed, call startIncr afterwards. | |||
| */ | |||
| const T *startData() const { | |||
| const T* startData() const { | |||
| return &data[start]; | |||
| } | |||
| void startIncr(size_t n) { | |||
| @@ -16,7 +16,7 @@ inline T hann(T p) { | |||
| } | |||
| /** Multiplies the Hann window by a signal `x` of length `len` in-place. */ | |||
| inline void hannWindow(float *x, int len) { | |||
| inline void hannWindow(float* x, int len) { | |||
| for (int i = 0; i < len; i++) { | |||
| x[i] *= hann(float(i) / (len - 1)); | |||
| } | |||
| @@ -29,12 +29,12 @@ A typical alpha value is 0.16. | |||
| template <typename T> | |||
| inline T blackman(T alpha, T p) { | |||
| return | |||
| + (1 - alpha) / 2 | |||
| - T(1) / 2 * simd::cos(2 * T(M_PI) * p) | |||
| + alpha / 2 * simd::cos(4 * T(M_PI) * p); | |||
| + (1 - alpha) / 2 | |||
| - T(1) / 2 * simd::cos(2 * T(M_PI) * p) | |||
| + alpha / 2 * simd::cos(4 * T(M_PI) * p); | |||
| } | |||
| inline void blackmanWindow(float alpha, float *x, int len) { | |||
| inline void blackmanWindow(float alpha, float* x, int len) { | |||
| for (int i = 0; i < len; i++) { | |||
| x[i] *= blackman(alpha, float(i) / (len - 1)); | |||
| } | |||
| @@ -47,13 +47,13 @@ https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Nuttall_window | |||
| template <typename T> | |||
| inline T blackmanNuttall(T p) { | |||
| return | |||
| + T(0.3635819) | |||
| - T(0.4891775) * simd::cos(2 * T(M_PI) * p) | |||
| + T(0.1365995) * simd::cos(4 * T(M_PI) * p) | |||
| - T(0.0106411) * simd::cos(6 * T(M_PI) * p); | |||
| + T(0.3635819) | |||
| - T(0.4891775) * simd::cos(2 * T(M_PI) * p) | |||
| + T(0.1365995) * simd::cos(4 * T(M_PI) * p) | |||
| - T(0.0106411) * simd::cos(6 * T(M_PI) * p); | |||
| } | |||
| inline void blackmanNuttallWindow(float *x, int len) { | |||
| inline void blackmanNuttallWindow(float* x, int len) { | |||
| for (int i = 0; i < len; i++) { | |||
| x[i] *= blackmanNuttall(float(i) / (len - 1)); | |||
| } | |||
| @@ -65,13 +65,13 @@ https://en.wikipedia.org/wiki/Window_function#Blackman%E2%80%93Harris_window | |||
| template <typename T> | |||
| inline T blackmanHarris(T p) { | |||
| return | |||
| + T(0.35875) | |||
| - T(0.48829) * simd::cos(2 * T(M_PI) * p) | |||
| + T(0.14128) * simd::cos(4 * T(M_PI) * p) | |||
| - T(0.01168) * simd::cos(6 * T(M_PI) * p); | |||
| + T(0.35875) | |||
| - T(0.48829) * simd::cos(2 * T(M_PI) * p) | |||
| + T(0.14128) * simd::cos(4 * T(M_PI) * p) | |||
| - T(0.01168) * simd::cos(6 * T(M_PI) * p); | |||
| } | |||
| inline void blackmanHarrisWindow(float *x, int len) { | |||
| inline void blackmanHarrisWindow(float* x, int len) { | |||
| for (int i = 0; i < len; i++) { | |||
| x[i] *= blackmanHarris(float(i) / (len - 1)); | |||
| } | |||
| @@ -9,9 +9,9 @@ namespace engine { | |||
| struct Cable { | |||
| int id = -1; | |||
| Module *outputModule = NULL; | |||
| Module* outputModule = NULL; | |||
| int outputId; | |||
| Module *inputModule = NULL; | |||
| Module* inputModule = NULL; | |||
| int inputId; | |||
| }; | |||
| @@ -12,7 +12,7 @@ namespace engine { | |||
| struct Engine { | |||
| struct Internal; | |||
| Internal *internal; | |||
| Internal* internal; | |||
| Engine(); | |||
| ~Engine(); | |||
| @@ -37,12 +37,12 @@ struct Engine { | |||
| If the module ID is -1, an ID is automatically assigned. | |||
| Does not transfer pointer ownership. | |||
| */ | |||
| void addModule(Module *module); | |||
| void removeModule(Module *module); | |||
| Module *getModule(int moduleId); | |||
| void resetModule(Module *module); | |||
| void randomizeModule(Module *module); | |||
| void bypassModule(Module *module, bool bypass); | |||
| void addModule(Module* module); | |||
| void removeModule(Module* module); | |||
| Module* getModule(int moduleId); | |||
| void resetModule(Module* module); | |||
| void randomizeModule(Module* module); | |||
| void bypassModule(Module* module, bool bypass); | |||
| // Cables | |||
| /** Adds a cable to the rack engine. | |||
| @@ -50,26 +50,26 @@ struct Engine { | |||
| If the cable ID is -1, an ID is automatically assigned. | |||
| Does not transfer pointer ownership. | |||
| */ | |||
| void addCable(Cable *cable); | |||
| void removeCable(Cable *cable); | |||
| void addCable(Cable* cable); | |||
| void removeCable(Cable* cable); | |||
| // Params | |||
| void setParam(Module *module, int paramId, float value); | |||
| float getParam(Module *module, int paramId); | |||
| void setSmoothParam(Module *module, int paramId, float value); | |||
| float getSmoothParam(Module *module, int paramId); | |||
| void setParam(Module* module, int paramId, float value); | |||
| float getParam(Module* module, int paramId); | |||
| void setSmoothParam(Module* module, int paramId, float value); | |||
| float getSmoothParam(Module* module, int paramId); | |||
| // ParamHandles | |||
| void addParamHandle(ParamHandle *paramHandle); | |||
| void removeParamHandle(ParamHandle *paramHandle); | |||
| void addParamHandle(ParamHandle* paramHandle); | |||
| void removeParamHandle(ParamHandle* paramHandle); | |||
| /** Returns the unique ParamHandle for the given paramId */ | |||
| ParamHandle *getParamHandle(int moduleId, int paramId); | |||
| ParamHandle* getParamHandle(int moduleId, int paramId); | |||
| /** Use getParamHandle(int, int) instead. */ | |||
| DEPRECATED ParamHandle *getParamHandle(Module *module, int paramId); | |||
| DEPRECATED ParamHandle* getParamHandle(Module* module, int paramId); | |||
| /** Sets the ParamHandle IDs and module pointer. | |||
| If `overwrite` is true and another ParamHandle points to the same param, unsets that one and replaces it with the given handle. | |||
| */ | |||
| void updateParamHandle(ParamHandle *paramHandle, int moduleId, int paramId, bool overwrite = true); | |||
| void updateParamHandle(ParamHandle* paramHandle, int moduleId, int paramId, bool overwrite = true); | |||
| }; | |||
| @@ -14,7 +14,7 @@ namespace rack { | |||
| namespace plugin { | |||
| struct Model; | |||
| struct Model; | |||
| } | |||
| @@ -23,7 +23,7 @@ namespace engine { | |||
| /** DSP processor instance for your module. */ | |||
| struct Module { | |||
| plugin::Model *model = NULL; /** Unique ID for referring to the module in the engine. | |||
| plugin::Model* model = NULL; /** Unique ID for referring to the module in the engine. | |||
| Assigned when added to the engine. | |||
| */ | |||
| int id = -1; | |||
| @@ -42,7 +42,7 @@ struct Module { | |||
| /** ID of the expander module, or -1 if nonexistent. */ | |||
| int moduleId = -1; | |||
| /** Pointer to the expander Module, or NULL if nonexistent. */ | |||
| Module *module = NULL; | |||
| Module* module = NULL; | |||
| /** Double buffer for receiving messages from the expander module. | |||
| If you intend to receive messages from an expander, allocate both message buffers with identical blocks of memory (arrays, structs, etc). | |||
| Remember to free the buffer in the Module destructor. | |||
| @@ -63,8 +63,8 @@ struct Module { | |||
| You may choose for your Module to instead write to its own message buffer for consumption by other modules, i.e. the expander "pulls" rather than this module "pushing". | |||
| As long as this convention is followed by the other module, this is fine. | |||
| */ | |||
| void *producerMessage = NULL; | |||
| void *consumerMessage = NULL; | |||
| void* producerMessage = NULL; | |||
| void* consumerMessage = NULL; | |||
| bool messageFlipRequested = false; | |||
| }; | |||
| @@ -97,10 +97,10 @@ struct Module { | |||
| if (paramQuantities[paramId]) | |||
| delete paramQuantities[paramId]; | |||
| Param *p = ¶ms[paramId]; | |||
| Param* p = ¶ms[paramId]; | |||
| p->value = defaultValue; | |||
| ParamQuantity *q = new TParamQuantity; | |||
| ParamQuantity* q = new TParamQuantity; | |||
| q->module = this; | |||
| q->paramId = paramId; | |||
| q->minValue = minValue; | |||
| @@ -125,7 +125,7 @@ struct Module { | |||
| /** Advances the module by one audio sample. | |||
| Override this method to read Inputs and Params and to write Outputs and Lights. | |||
| */ | |||
| virtual void process(const ProcessArgs &args) { | |||
| virtual void process(const ProcessArgs& args) { | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | |||
| step(); | |||
| @@ -145,12 +145,14 @@ struct Module { | |||
| /** Called when the Module is removed from the Engine */ | |||
| virtual void onRemove() {} | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| /** Override to store extra internal data in the "data" property of the module's JSON object. */ | |||
| virtual json_t *dataToJson() { return NULL; } | |||
| virtual void dataFromJson(json_t *root) {} | |||
| virtual json_t* dataToJson() { | |||
| return NULL; | |||
| } | |||
| virtual void dataFromJson(json_t* root) {} | |||
| }; | |||
| @@ -16,7 +16,7 @@ struct ParamHandle { | |||
| */ | |||
| int moduleId = -1; | |||
| int paramId = 0; | |||
| Module *module = NULL; | |||
| Module* module = NULL; | |||
| std::string text; | |||
| NVGcolor color; | |||
| @@ -12,7 +12,7 @@ struct Module; | |||
| /** A Quantity that wraps an engine::Param. */ | |||
| struct ParamQuantity : Quantity { | |||
| Module *module = NULL; | |||
| Module* module = NULL; | |||
| int paramId = 0; | |||
| /** The minimum allowed value. */ | |||
| @@ -38,7 +38,7 @@ struct ParamQuantity : Quantity { | |||
| /** An optional one-sentence description of the parameter. */ | |||
| std::string description; | |||
| Param *getParam(); | |||
| Param* getParam(); | |||
| /** Request to the engine to smoothly set the value */ | |||
| void setSmoothValue(float smoothValue); | |||
| float getSmoothValue(); | |||
| @@ -62,12 +62,12 @@ struct alignas(32) Port { | |||
| /** Returns a pointer to the array of voltages beginning with firstChannel. | |||
| The pointer can be used for reading and writing. | |||
| */ | |||
| float *getVoltages(int firstChannel = 0) { | |||
| float* getVoltages(int firstChannel = 0) { | |||
| return &voltages[firstChannel]; | |||
| } | |||
| /** Copies the port's voltages to an array of size at least `channels`. */ | |||
| void readVoltages(float *v) { | |||
| void readVoltages(float* v) { | |||
| for (int c = 0; c < channels; c++) { | |||
| v[c] = voltages[c]; | |||
| } | |||
| @@ -76,7 +76,7 @@ struct alignas(32) Port { | |||
| /** Copies an array of size at least `channels` to the port's voltages. | |||
| Remember to set the number of channels *before* calling this method. | |||
| */ | |||
| void writeVoltages(const float *v) { | |||
| void writeVoltages(const float* v) { | |||
| for (int c = 0; c < channels; c++) { | |||
| voltages[c] = v[c]; | |||
| } | |||
| @@ -105,7 +105,7 @@ struct alignas(32) Port { | |||
| template <typename T> | |||
| T getPolyVoltageSimd(int firstChannel) { | |||
| return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel); | |||
| return isMonophonic() ? getVoltage(0) : getVoltageSimd<T>(firstChannel); | |||
| } | |||
| template <typename T> | |||
| @@ -31,7 +31,7 @@ namespace rack { | |||
| namespace widget { | |||
| struct Widget; | |||
| struct Widget; | |||
| } | |||
| @@ -47,46 +47,52 @@ struct Context { | |||
| /** Whether the event has been consumed by an event handler and no more handlers should consume the event. */ | |||
| bool consumed = false; | |||
| /** The widget that responded to the event. */ | |||
| widget::Widget *target = NULL; | |||
| widget::Widget* target = NULL; | |||
| }; | |||
| /** Base class for all events. */ | |||
| struct Base { | |||
| Context *context = NULL; | |||
| Context* context = NULL; | |||
| /** Prevents the event from being handled by more Widgets. | |||
| */ | |||
| void stopPropagating() const { | |||
| if (!context) return; | |||
| if (!context) | |||
| return; | |||
| context->propagating = false; | |||
| } | |||
| bool isPropagating() const { | |||
| if (!context) return true; | |||
| if (!context) | |||
| return true; | |||
| return context->propagating; | |||
| } | |||
| /** Tells the event handler that a particular Widget consumed the event. | |||
| You usually want to stop propagation as well, so call consume() instead. | |||
| */ | |||
| void setTarget(widget::Widget *w) const { | |||
| if (!context) return; | |||
| void setTarget(widget::Widget* w) const { | |||
| if (!context) | |||
| return; | |||
| context->target = w; | |||
| } | |||
| widget::Widget *getTarget() const { | |||
| if (!context) return NULL; | |||
| widget::Widget* getTarget() const { | |||
| if (!context) | |||
| return NULL; | |||
| return context->target; | |||
| } | |||
| /** Sets the target Widget and stops propagating. | |||
| A NULL Widget may be passed to consume but not set a target. | |||
| */ | |||
| void consume(widget::Widget *w) const { | |||
| if (!context) return; | |||
| void consume(widget::Widget* w) const { | |||
| if (!context) | |||
| return; | |||
| context->propagating = false; | |||
| context->consumed = true; | |||
| context->target = w; | |||
| } | |||
| bool isConsumed() const { | |||
| if (!context) return false; | |||
| if (!context) | |||
| return false; | |||
| return context->consumed; | |||
| } | |||
| }; | |||
| @@ -256,7 +262,7 @@ Consume this event to allow DragEnter and DragLeave to occur. | |||
| */ | |||
| struct DragHover : DragBase, PositionBase { | |||
| /** The dragged widget */ | |||
| widget::Widget *origin = NULL; | |||
| widget::Widget* origin = NULL; | |||
| /** Change in mouse position since the last frame. Can be zero. */ | |||
| math::Vec mouseDelta; | |||
| }; | |||
| @@ -268,7 +274,7 @@ The target sets `draggedWidget`, which allows DragLeave to occur. | |||
| */ | |||
| struct DragEnter : DragBase { | |||
| /** The dragged widget */ | |||
| widget::Widget *origin = NULL; | |||
| widget::Widget* origin = NULL; | |||
| }; | |||
| @@ -277,7 +283,7 @@ Must consume the DragHover event (when the Widget is entered) to receive this ev | |||
| */ | |||
| struct DragLeave : DragBase { | |||
| /** The dragged widget */ | |||
| widget::Widget *origin = NULL; | |||
| widget::Widget* origin = NULL; | |||
| }; | |||
| @@ -286,7 +292,7 @@ Must consume the Button event (on release) to receive this event. | |||
| */ | |||
| struct DragDrop : DragBase { | |||
| /** The dragged widget */ | |||
| widget::Widget *origin = NULL; | |||
| widget::Widget* origin = NULL; | |||
| }; | |||
| @@ -294,10 +300,10 @@ struct DragDrop : DragBase { | |||
| Recurses. | |||
| */ | |||
| struct PathDrop : Base, PositionBase { | |||
| PathDrop(const std::vector<std::string> &paths) : paths(paths) {} | |||
| PathDrop(const std::vector<std::string>& paths) : paths(paths) {} | |||
| /** List of file paths in the dropped selection */ | |||
| const std::vector<std::string> &paths; | |||
| const std::vector<std::string>& paths; | |||
| }; | |||
| @@ -361,32 +367,42 @@ struct Hide : Base { | |||
| struct State { | |||
| widget::Widget *rootWidget = NULL; | |||
| widget::Widget* rootWidget = NULL; | |||
| /** State widgets | |||
| Don't set these directly unless you know what you're doing. Use the set*() methods instead. | |||
| */ | |||
| widget::Widget *hoveredWidget = NULL; | |||
| widget::Widget *draggedWidget = NULL; | |||
| widget::Widget* hoveredWidget = NULL; | |||
| widget::Widget* draggedWidget = NULL; | |||
| int dragButton = 0; | |||
| widget::Widget *dragHoveredWidget = NULL; | |||
| widget::Widget *selectedWidget = NULL; | |||
| widget::Widget* dragHoveredWidget = NULL; | |||
| widget::Widget* selectedWidget = NULL; | |||
| /** For double-clicking */ | |||
| double lastClickTime = -INFINITY; | |||
| widget::Widget *lastClickedWidget = NULL; | |||
| widget::Widget* lastClickedWidget = NULL; | |||
| std::set<int> heldKeys; | |||
| widget::Widget *getRootWidget() {return rootWidget;} | |||
| widget::Widget *getHoveredWidget() {return hoveredWidget;} | |||
| widget::Widget *getDraggedWidget() {return draggedWidget;} | |||
| widget::Widget *getDragHoveredWidget() {return dragHoveredWidget;} | |||
| widget::Widget *getSelectedWidget() {return selectedWidget;} | |||
| widget::Widget* getRootWidget() { | |||
| return rootWidget; | |||
| } | |||
| widget::Widget* getHoveredWidget() { | |||
| return hoveredWidget; | |||
| } | |||
| widget::Widget* getDraggedWidget() { | |||
| return draggedWidget; | |||
| } | |||
| widget::Widget* getDragHoveredWidget() { | |||
| return dragHoveredWidget; | |||
| } | |||
| widget::Widget* getSelectedWidget() { | |||
| return selectedWidget; | |||
| } | |||
| void setHovered(widget::Widget *w); | |||
| void setDragged(widget::Widget *w, int button); | |||
| void setDragHovered(widget::Widget *w); | |||
| void setSelected(widget::Widget *w); | |||
| void setHovered(widget::Widget* w); | |||
| void setDragged(widget::Widget* w, int button); | |||
| void setDragHovered(widget::Widget* w); | |||
| void setSelected(widget::Widget* w); | |||
| /** Prepares a widget for deletion */ | |||
| void finalizeWidget(widget::Widget *w); | |||
| void finalizeWidget(widget::Widget* w); | |||
| bool handleButton(math::Vec pos, int button, int action, int mods); | |||
| bool handleHover(math::Vec pos, math::Vec mouseDelta); | |||
| @@ -394,7 +410,7 @@ struct State { | |||
| bool handleScroll(math::Vec pos, math::Vec scrollDelta); | |||
| bool handleText(math::Vec pos, int codepoint); | |||
| bool handleKey(math::Vec pos, int key, int scancode, int action, int mods); | |||
| bool handleDrop(math::Vec pos, const std::vector<std::string> &paths); | |||
| bool handleDrop(math::Vec pos, const std::vector<std::string>& paths); | |||
| bool handleZoom(); | |||
| }; | |||
| @@ -501,11 +501,11 @@ __asm__(".symver __progname_full,__progname_full@GLIBC_2.2.5"); | |||
| __asm__(".symver __pthread_cleanup_routine,__pthread_cleanup_routine@GLIBC_2.3.3"); | |||
| __asm__(".symver __pthread_getspecific,__pthread_getspecific@GLIBC_2.2.5"); | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver __pthread_key_create,__pthread_key_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver __pthread_key_create,__pthread_key_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| __asm__(".symver __pthread_mutex_destroy,__pthread_mutex_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver __pthread_mutex_init,__pthread_mutex_init@GLIBC_2.2.5"); | |||
| @@ -544,7 +544,7 @@ __asm__(".symver __realpath_chk,__realpath_chk@GLIBC_2.4"); | |||
| __asm__(".symver __recv_chk,__recv_chk@GLIBC_2.4"); | |||
| __asm__(".symver __recvfrom_chk,__recvfrom_chk@GLIBC_2.4"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver __register_atfork,__register_atfork@GLIBC_2.3.2"); | |||
| __asm__(".symver __register_atfork,__register_atfork@GLIBC_2.3.2"); | |||
| #endif | |||
| __asm__(".symver __remainder_finite,__remainder_finite@GLIBC_2.15"); | |||
| __asm__(".symver __remainderf_finite,__remainderf_finite@GLIBC_2.15"); | |||
| @@ -2009,47 +2009,47 @@ __asm__(".symver pselect,pselect@GLIBC_2.2.5"); | |||
| __asm__(".symver psiginfo,psiginfo@GLIBC_2.10"); | |||
| __asm__(".symver psignal,psignal@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_destroy,pthread_attr_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_destroy,pthread_attr_destroy@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_getaffinity_np,pthread_attr_getaffinity_np@GLIBC_2.3.4"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_getdetachstate,pthread_attr_getdetachstate@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getdetachstate,pthread_attr_getdetachstate@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_getguardsize,pthread_attr_getguardsize@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_getinheritsched,pthread_attr_getinheritsched@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getinheritsched,pthread_attr_getinheritsched@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_getschedparam,pthread_attr_getschedparam@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getschedparam,pthread_attr_getschedparam@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_getschedpolicy,pthread_attr_getschedpolicy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getschedpolicy,pthread_attr_getschedpolicy@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_getscope,pthread_attr_getscope@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getscope,pthread_attr_getscope@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_getstack,pthread_attr_getstack@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getstackaddr,pthread_attr_getstackaddr@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_getstacksize,pthread_attr_getstacksize@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_init,pthread_attr_init@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_init,pthread_attr_init@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_setaffinity_np,pthread_attr_setaffinity_np@GLIBC_2.3.4"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_setdetachstate,pthread_attr_setdetachstate@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setdetachstate,pthread_attr_setdetachstate@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_setguardsize,pthread_attr_setguardsize@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_setinheritsched,pthread_attr_setinheritsched@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setinheritsched,pthread_attr_setinheritsched@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_setschedparam,pthread_attr_setschedparam@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setschedparam,pthread_attr_setschedparam@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_setschedpolicy,pthread_attr_setschedpolicy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setschedpolicy,pthread_attr_setschedpolicy@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_attr_setscope,pthread_attr_setscope@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setscope,pthread_attr_setscope@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_attr_setstack,pthread_attr_setstack@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_attr_setstackaddr,pthread_attr_setstackaddr@GLIBC_2.2.5"); | |||
| @@ -2063,44 +2063,44 @@ __asm__(".symver pthread_barrierattr_init,pthread_barrierattr_init@GLIBC_2.2.5") | |||
| __asm__(".symver pthread_barrierattr_setpshared,pthread_barrierattr_setpshared@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_cancel,pthread_cancel@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_cond_broadcast,pthread_cond_broadcast@GLIBC_2.3.2"); | |||
| __asm__(".symver pthread_cond_broadcast,pthread_cond_broadcast@GLIBC_2.3.2"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_cond_destroy,pthread_cond_destroy@GLIBC_2.3.2"); | |||
| __asm__(".symver pthread_cond_destroy,pthread_cond_destroy@GLIBC_2.3.2"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_cond_init,pthread_cond_init@GLIBC_2.3.2"); | |||
| __asm__(".symver pthread_cond_init,pthread_cond_init@GLIBC_2.3.2"); | |||
| #endif | |||
| __asm__(".symver pthread_cond_signal,pthread_cond_signal@GLIBC_2.3.2"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_cond_timedwait,pthread_cond_timedwait@GLIBC_2.3.2"); | |||
| __asm__(".symver pthread_cond_timedwait,pthread_cond_timedwait@GLIBC_2.3.2"); | |||
| #endif | |||
| __asm__(".symver pthread_cond_wait,pthread_cond_wait@GLIBC_2.3.2"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_condattr_destroy,pthread_condattr_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_condattr_destroy,pthread_condattr_destroy@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_condattr_getclock,pthread_condattr_getclock@GLIBC_2.3.3"); | |||
| __asm__(".symver pthread_condattr_getpshared,pthread_condattr_getpshared@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_condattr_init,pthread_condattr_init@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_condattr_init,pthread_condattr_init@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_condattr_setclock,pthread_condattr_setclock@GLIBC_2.3.3"); | |||
| __asm__(".symver pthread_condattr_setpshared,pthread_condattr_setpshared@GLIBC_2.2.5"); | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_create,pthread_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_create,pthread_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_detach,pthread_detach@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_detach,pthread_detach@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_equal,pthread_equal@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_equal,pthread_equal@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_exit,pthread_exit@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_exit,pthread_exit@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_getaffinity_np,pthread_getaffinity_np@GLIBC_2.3.4"); | |||
| __asm__(".symver pthread_getattr_default_np,pthread_getattr_default_np@GLIBC_2.18"); | |||
| @@ -2109,50 +2109,50 @@ __asm__(".symver pthread_getconcurrency,pthread_getconcurrency@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_getcpuclockid,pthread_getcpuclockid@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_getname_np,pthread_getname_np@GLIBC_2.12"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_getschedparam,pthread_getschedparam@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_getschedparam,pthread_getschedparam@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_getspecific,pthread_getspecific@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_getspecific,pthread_getspecific@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_join,pthread_join@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_join,pthread_join@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_key_create,pthread_key_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_key_create,pthread_key_create@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_key_delete,pthread_key_delete@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_key_delete,pthread_key_delete@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| __asm__(".symver pthread_kill,pthread_kill@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_consistent,pthread_mutex_consistent@GLIBC_2.12"); | |||
| __asm__(".symver pthread_mutex_consistent_np,pthread_mutex_consistent_np@GLIBC_2.4"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_mutex_destroy,pthread_mutex_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_destroy,pthread_mutex_destroy@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_mutex_getprioceiling,pthread_mutex_getprioceiling@GLIBC_2.4"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_mutex_init,pthread_mutex_init@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_init,pthread_mutex_init@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_mutex_lock,pthread_mutex_lock@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_lock,pthread_mutex_lock@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_mutex_setprioceiling,pthread_mutex_setprioceiling@GLIBC_2.4"); | |||
| __asm__(".symver pthread_mutex_timedlock,pthread_mutex_timedlock@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_trylock,pthread_mutex_trylock@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_mutex_unlock,pthread_mutex_unlock@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutex_unlock,pthread_mutex_unlock@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_mutexattr_destroy,pthread_mutexattr_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_mutexattr_getkind_np,pthread_mutexattr_getkind_np@GLIBC_2.2.5"); | |||
| @@ -2171,11 +2171,11 @@ __asm__(".symver pthread_mutexattr_setrobust,pthread_mutexattr_setrobust@GLIBC_2 | |||
| __asm__(".symver pthread_mutexattr_setrobust_np,pthread_mutexattr_setrobust_np@GLIBC_2.4"); | |||
| __asm__(".symver pthread_mutexattr_settype,pthread_mutexattr_settype@GLIBC_2.2.5"); | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_once,pthread_once@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_once,pthread_once@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| __asm__(".symver pthread_rwlock_destroy,pthread_rwlock_destroy@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_rwlock_init,pthread_rwlock_init@GLIBC_2.2.5"); | |||
| @@ -2193,28 +2193,28 @@ __asm__(".symver pthread_rwlockattr_init,pthread_rwlockattr_init@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_rwlockattr_setkind_np,pthread_rwlockattr_setkind_np@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_rwlockattr_setpshared,pthread_rwlockattr_setpshared@GLIBC_2.2.5"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_self,pthread_self@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_self,pthread_self@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_setaffinity_np,pthread_setaffinity_np@GLIBC_2.3.4"); | |||
| __asm__(".symver pthread_setattr_default_np,pthread_setattr_default_np@GLIBC_2.18"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_setcancelstate,pthread_setcancelstate@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_setcancelstate,pthread_setcancelstate@GLIBC_2.2.5"); | |||
| #endif | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_setcanceltype,pthread_setcanceltype@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_setcanceltype,pthread_setcanceltype@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_setconcurrency,pthread_setconcurrency@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_setname_np,pthread_setname_np@GLIBC_2.12"); | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_setschedparam,pthread_setschedparam@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_setschedparam,pthread_setschedparam@GLIBC_2.2.5"); | |||
| #endif | |||
| __asm__(".symver pthread_setschedprio,pthread_setschedprio@GLIBC_2.3.4"); | |||
| #ifndef _GLIBCXX_SHARED | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_setspecific,pthread_setspecific@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #ifndef IN_LIBGCC2 | |||
| #ifdef _REENTRANT | |||
| __asm__(".symver pthread_setspecific,pthread_setspecific@GLIBC_2.2.5"); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| __asm__(".symver pthread_sigmask,pthread_sigmask@GLIBC_2.2.5"); | |||
| __asm__(".symver pthread_sigqueue,pthread_sigqueue@GLIBC_2.11"); | |||
| @@ -16,50 +16,50 @@ | |||
| namespace rack { | |||
| template <class TModule, class TModuleWidget, typename... Tags> | |||
| plugin::Model *createModel(const std::string &slug) { | |||
| template <class TModule, class TModuleWidget> | |||
| plugin::Model* createModel(const std::string& slug) { | |||
| struct TModel : plugin::Model { | |||
| engine::Module *createModule() override { | |||
| engine::Module *m = new TModule; | |||
| engine::Module* createModule() override { | |||
| engine::Module* m = new TModule; | |||
| m->model = this; | |||
| return m; | |||
| } | |||
| app::ModuleWidget *createModuleWidget() override { | |||
| TModule *m = new TModule; | |||
| app::ModuleWidget* createModuleWidget() override { | |||
| TModule* m = new TModule; | |||
| m->engine::Module::model = this; | |||
| app::ModuleWidget *mw = new TModuleWidget(m); | |||
| app::ModuleWidget* mw = new TModuleWidget(m); | |||
| mw->model = this; | |||
| return mw; | |||
| } | |||
| app::ModuleWidget *createModuleWidgetNull() override { | |||
| app::ModuleWidget *mw = new TModuleWidget(NULL); | |||
| app::ModuleWidget* createModuleWidgetNull() override { | |||
| app::ModuleWidget* mw = new TModuleWidget(NULL); | |||
| mw->model = this; | |||
| return mw; | |||
| } | |||
| }; | |||
| plugin::Model *o = new TModel; | |||
| plugin::Model* o = new TModel; | |||
| o->slug = slug; | |||
| return o; | |||
| } | |||
| template <class TWidget> | |||
| TWidget *createWidget(math::Vec pos) { | |||
| TWidget *o = new TWidget; | |||
| TWidget* createWidget(math::Vec pos) { | |||
| TWidget* o = new TWidget; | |||
| o->box.pos = pos; | |||
| return o; | |||
| } | |||
| template <class TWidget> | |||
| TWidget *createWidgetCentered(math::Vec pos) { | |||
| TWidget *o = createWidget<TWidget>(pos); | |||
| TWidget* createWidgetCentered(math::Vec pos) { | |||
| TWidget* o = createWidget<TWidget>(pos); | |||
| o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
| return o; | |||
| } | |||
| template <class TParamWidget> | |||
| TParamWidget *createParam(math::Vec pos, engine::Module *module, int paramId) { | |||
| TParamWidget *o = new TParamWidget; | |||
| TParamWidget* createParam(math::Vec pos, engine::Module* module, int paramId) { | |||
| TParamWidget* o = new TParamWidget; | |||
| o->box.pos = pos; | |||
| if (module) { | |||
| o->paramQuantity = module->paramQuantities[paramId]; | |||
| @@ -68,15 +68,15 @@ TParamWidget *createParam(math::Vec pos, engine::Module *module, int paramId) { | |||
| } | |||
| template <class TParamWidget> | |||
| TParamWidget *createParamCentered(math::Vec pos, engine::Module *module, int paramId) { | |||
| TParamWidget *o = createParam<TParamWidget>(pos, module, paramId); | |||
| TParamWidget* createParamCentered(math::Vec pos, engine::Module* module, int paramId) { | |||
| TParamWidget* o = createParam<TParamWidget>(pos, module, paramId); | |||
| o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
| return o; | |||
| } | |||
| template <class TPortWidget> | |||
| TPortWidget *createInput(math::Vec pos, engine::Module *module, int inputId) { | |||
| TPortWidget *o = new TPortWidget; | |||
| TPortWidget* createInput(math::Vec pos, engine::Module* module, int inputId) { | |||
| TPortWidget* o = new TPortWidget; | |||
| o->box.pos = pos; | |||
| o->module = module; | |||
| o->type = app::PortWidget::INPUT; | |||
| @@ -85,15 +85,15 @@ TPortWidget *createInput(math::Vec pos, engine::Module *module, int inputId) { | |||
| } | |||
| template <class TPortWidget> | |||
| TPortWidget *createInputCentered(math::Vec pos, engine::Module *module, int inputId) { | |||
| TPortWidget *o = createInput<TPortWidget>(pos, module, inputId); | |||
| TPortWidget* createInputCentered(math::Vec pos, engine::Module* module, int inputId) { | |||
| TPortWidget* o = createInput<TPortWidget>(pos, module, inputId); | |||
| o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
| return o; | |||
| } | |||
| template <class TPortWidget> | |||
| TPortWidget *createOutput(math::Vec pos, engine::Module *module, int outputId) { | |||
| TPortWidget *o = new TPortWidget; | |||
| TPortWidget* createOutput(math::Vec pos, engine::Module* module, int outputId) { | |||
| TPortWidget* o = new TPortWidget; | |||
| o->box.pos = pos; | |||
| o->module = module; | |||
| o->type = app::PortWidget::OUTPUT; | |||
| @@ -102,15 +102,15 @@ TPortWidget *createOutput(math::Vec pos, engine::Module *module, int outputId) { | |||
| } | |||
| template <class TPortWidget> | |||
| TPortWidget *createOutputCentered(math::Vec pos, engine::Module *module, int outputId) { | |||
| TPortWidget *o = createOutput<TPortWidget>(pos, module, outputId); | |||
| TPortWidget* createOutputCentered(math::Vec pos, engine::Module* module, int outputId) { | |||
| TPortWidget* o = createOutput<TPortWidget>(pos, module, outputId); | |||
| o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
| return o; | |||
| } | |||
| template <class TModuleLightWidget> | |||
| TModuleLightWidget *createLight(math::Vec pos, engine::Module *module, int firstLightId) { | |||
| TModuleLightWidget *o = new TModuleLightWidget; | |||
| TModuleLightWidget* createLight(math::Vec pos, engine::Module* module, int firstLightId) { | |||
| TModuleLightWidget* o = new TModuleLightWidget; | |||
| o->box.pos = pos; | |||
| o->module = module; | |||
| o->firstLightId = firstLightId; | |||
| @@ -118,33 +118,33 @@ TModuleLightWidget *createLight(math::Vec pos, engine::Module *module, int first | |||
| } | |||
| template <class TModuleLightWidget> | |||
| TModuleLightWidget *createLightCentered(math::Vec pos, engine::Module *module, int firstLightId) { | |||
| TModuleLightWidget *o = createLight<TModuleLightWidget>(pos, module, firstLightId); | |||
| TModuleLightWidget* createLightCentered(math::Vec pos, engine::Module* module, int firstLightId) { | |||
| TModuleLightWidget* o = createLight<TModuleLightWidget>(pos, module, firstLightId); | |||
| o->box.pos = o->box.pos.minus(o->box.size.div(2)); | |||
| return o; | |||
| } | |||
| template <class TMenuLabel = ui::MenuLabel> | |||
| TMenuLabel *createMenuLabel(std::string text) { | |||
| TMenuLabel *o = new TMenuLabel; | |||
| TMenuLabel * createMenuLabel(std::string text) { | |||
| TMenuLabel* o = new TMenuLabel; | |||
| o->text = text; | |||
| return o; | |||
| } | |||
| template <class TMenuItem = ui::MenuItem> | |||
| TMenuItem *createMenuItem(std::string text, std::string rightText = "") { | |||
| TMenuItem *o = new TMenuItem; | |||
| TMenuItem * createMenuItem(std::string text, std::string rightText = "") { | |||
| TMenuItem* o = new TMenuItem; | |||
| o->text = text; | |||
| o->rightText = rightText; | |||
| return o; | |||
| } | |||
| template <class TMenu = ui::Menu> | |||
| TMenu *createMenu() { | |||
| TMenu *o = new TMenu; | |||
| TMenu * createMenu() { | |||
| TMenu* o = new TMenu; | |||
| o->box.pos = APP->window->mousePos; | |||
| ui::MenuOverlay *menuOverlay = new ui::MenuOverlay; | |||
| ui::MenuOverlay* menuOverlay = new ui::MenuOverlay; | |||
| menuOverlay->addChild(o); | |||
| APP->scene->addChild(menuOverlay); | |||
| @@ -11,8 +11,8 @@ namespace rack { | |||
| namespace app { | |||
| struct ModuleWidget; | |||
| struct CableWidget; | |||
| struct ModuleWidget; | |||
| struct CableWidget; | |||
| } // namespace app | |||
| @@ -52,7 +52,7 @@ struct ComplexAction : Action { | |||
| ~ComplexAction(); | |||
| void undo() override; | |||
| void redo() override; | |||
| void push(Action *action); | |||
| void push(Action* action); | |||
| bool isEmpty(); | |||
| }; | |||
| @@ -66,14 +66,14 @@ struct ModuleAction : Action { | |||
| struct ModuleAdd : ModuleAction { | |||
| plugin::Model *model; | |||
| plugin::Model* model; | |||
| math::Vec pos; | |||
| json_t *moduleJ; | |||
| json_t* moduleJ; | |||
| ModuleAdd() { | |||
| name = "add module"; | |||
| } | |||
| ~ModuleAdd(); | |||
| void setModule(app::ModuleWidget *mw); | |||
| void setModule(app::ModuleWidget* mw); | |||
| void undo() override; | |||
| void redo() override; | |||
| }; | |||
| @@ -108,8 +108,8 @@ struct ModuleBypass : ModuleAction { | |||
| struct ModuleChange : ModuleAction { | |||
| json_t *oldModuleJ; | |||
| json_t *newModuleJ; | |||
| json_t* oldModuleJ; | |||
| json_t* newModuleJ; | |||
| ModuleChange() { | |||
| name = "change module"; | |||
| } | |||
| @@ -138,7 +138,7 @@ struct CableAdd : Action { | |||
| int inputModuleId; | |||
| int inputId; | |||
| NVGcolor color; | |||
| void setCable(app::CableWidget *cw); | |||
| void setCable(app::CableWidget* cw); | |||
| void undo() override; | |||
| void redo() override; | |||
| CableAdd() { | |||
| @@ -163,7 +163,7 @@ struct State { | |||
| State(); | |||
| ~State(); | |||
| void clear(); | |||
| void push(Action *action); | |||
| void push(Action* action); | |||
| void undo(); | |||
| void redo(); | |||
| bool canUndo(); | |||
| @@ -35,7 +35,7 @@ void destroy(); | |||
| /** Do not use this function directly. Use the macros above. | |||
| Thread-safe, meaning messages cannot overlap each other in the log. | |||
| */ | |||
| void log(Level level, const char *filename, int line, const char *format, ...); | |||
| void log(Level level, const char* filename, int line, const char* format, ...); | |||
| } // namespace logger | |||
| @@ -1,5 +1,6 @@ | |||
| #pragma once | |||
| #include <common.hpp> | |||
| #include <complex> | |||
| #include <algorithm> // for std::min, max | |||
| @@ -63,7 +64,7 @@ inline int eucDiv(int a, int b) { | |||
| return div; | |||
| } | |||
| inline void eucDivMod(int a, int b, int *div, int *mod) { | |||
| inline void eucDivMod(int a, int b, int* div, int* mod) { | |||
| *div = a / b; | |||
| *mod = a % b; | |||
| if (*mod < 0) { | |||
| @@ -148,10 +149,10 @@ inline float crossfade(float a, float b, float p) { | |||
| /** Linearly interpolates an array `p` with index `x`. | |||
| Assumes that the array at `p` is of length at least `floor(x) + 1`. | |||
| */ | |||
| inline float interpolateLinear(const float *p, float x) { | |||
| inline float interpolateLinear(const float* p, float x) { | |||
| int xi = x; | |||
| float xf = x - xi; | |||
| return crossfade(p[xi], p[xi+1], xf); | |||
| return crossfade(p[xi], p[xi + 1], xf); | |||
| } | |||
| /** Complex multiplication `c = a * b`. | |||
| @@ -160,7 +161,7 @@ Example: | |||
| cmultf(ar, ai, br, bi, &ar, &ai); | |||
| */ | |||
| inline void complexMult(float ar, float ai, float br, float bi, float *cr, float *ci) { | |||
| inline void complexMult(float ar, float ai, float br, float bi, float* cr, float* ci) { | |||
| *cr = ar * br - ai * bi; | |||
| *ci = ar * bi + ai * br; | |||
| } | |||
| @@ -279,17 +280,17 @@ struct Rect { | |||
| /** Returns whether this Rect contains an entire point, inclusive on the top/left, non-inclusive on the bottom/right. */ | |||
| bool isContaining(Vec v) const { | |||
| return pos.x <= v.x && v.x < pos.x + size.x | |||
| && pos.y <= v.y && v.y < pos.y + size.y; | |||
| && pos.y <= v.y && v.y < pos.y + size.y; | |||
| } | |||
| /** Returns whether this Rect contains an entire Rect. */ | |||
| bool isContaining(Rect r) const { | |||
| return pos.x <= r.pos.x && r.pos.x + r.size.x <= pos.x + size.x | |||
| && pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y; | |||
| && pos.y <= r.pos.y && r.pos.y + r.size.y <= pos.y + size.y; | |||
| } | |||
| /** Returns whether this Rect overlaps with another Rect. */ | |||
| bool isIntersecting(Rect r) const { | |||
| return (pos.x + size.x > r.pos.x && r.pos.x + r.size.x > pos.x) | |||
| && (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y); | |||
| && (pos.y + size.y > r.pos.y && r.pos.y + r.size.y > pos.y); | |||
| } | |||
| bool isEqual(Rect r) const { | |||
| return pos.isEqual(r.pos) && size.isEqual(r.size); | |||
| @@ -364,22 +365,28 @@ struct Rect { | |||
| return r; | |||
| } | |||
| DEPRECATED bool contains(Vec v) const {return isContaining(v);} | |||
| DEPRECATED bool contains(Rect r) const {return isContaining(r);} | |||
| DEPRECATED bool intersects(Rect r) const {return isIntersecting(r);} | |||
| DEPRECATED bool contains(Vec v) const { | |||
| return isContaining(v); | |||
| } | |||
| DEPRECATED bool contains(Rect r) const { | |||
| return isContaining(r); | |||
| } | |||
| DEPRECATED bool intersects(Rect r) const { | |||
| return isIntersecting(r); | |||
| } | |||
| }; | |||
| inline Vec Vec::clamp(Rect bound) const { | |||
| return Vec( | |||
| math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
| math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
| math::clamp(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
| math::clamp(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
| } | |||
| inline Vec Vec::clampSafe(Rect bound) const { | |||
| return Vec( | |||
| math::clampSafe(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
| math::clampSafe(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
| math::clampSafe(x, bound.pos.x, bound.pos.x + bound.size.x), | |||
| math::clampSafe(y, bound.pos.y, bound.pos.y + bound.size.y)); | |||
| } | |||
| @@ -59,17 +59,31 @@ struct Output; | |||
| struct Driver { | |||
| virtual ~Driver() {} | |||
| virtual std::string getName() {return "";} | |||
| virtual std::string getName() { | |||
| return ""; | |||
| } | |||
| virtual std::vector<int> getInputDeviceIds() {return {};} | |||
| virtual std::string getInputDeviceName(int deviceId) {return "";} | |||
| virtual InputDevice *subscribeInput(int deviceId, Input *input) {return NULL;} | |||
| virtual void unsubscribeInput(int deviceId, Input *input) {} | |||
| virtual std::vector<int> getInputDeviceIds() { | |||
| return {}; | |||
| } | |||
| virtual std::string getInputDeviceName(int deviceId) { | |||
| return ""; | |||
| } | |||
| virtual InputDevice* subscribeInput(int deviceId, Input* input) { | |||
| return NULL; | |||
| } | |||
| virtual void unsubscribeInput(int deviceId, Input* input) {} | |||
| virtual std::vector<int> getOutputDeviceIds() {return {};} | |||
| virtual std::string getOutputDeviceName(int deviceId) {return "";} | |||
| virtual OutputDevice *subscribeOutput(int deviceId, Output *output) {return NULL;} | |||
| virtual void unsubscribeOutput(int deviceId, Output *output) {} | |||
| virtual std::vector<int> getOutputDeviceIds() { | |||
| return {}; | |||
| } | |||
| virtual std::string getOutputDeviceName(int deviceId) { | |||
| return ""; | |||
| } | |||
| virtual OutputDevice* subscribeOutput(int deviceId, Output* output) { | |||
| return NULL; | |||
| } | |||
| virtual void unsubscribeOutput(int deviceId, Output* output) {} | |||
| }; | |||
| //////////////////// | |||
| @@ -82,15 +96,15 @@ struct Device { | |||
| struct InputDevice : Device { | |||
| std::set<Input*> subscribed; | |||
| void subscribe(Input *input); | |||
| void unsubscribe(Input *input); | |||
| void subscribe(Input* input); | |||
| void unsubscribe(Input* input); | |||
| void onMessage(Message message); | |||
| }; | |||
| struct OutputDevice : Device { | |||
| std::set<Output*> subscribed; | |||
| void subscribe(Output *input); | |||
| void unsubscribe(Output *input); | |||
| void subscribe(Output* input); | |||
| void unsubscribe(Output* input); | |||
| virtual void sendMessage(Message message) {} | |||
| }; | |||
| @@ -108,7 +122,7 @@ struct Port { | |||
| */ | |||
| int channel = -1; | |||
| /** Not owned */ | |||
| Driver *driver = NULL; | |||
| Driver* driver = NULL; | |||
| /** Remember to call setDriverId(-1) in subclass destructors. */ | |||
| virtual ~Port() {} | |||
| @@ -125,14 +139,14 @@ struct Port { | |||
| std::string getChannelName(int channel); | |||
| void setChannel(int channel); | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| }; | |||
| struct Input : Port { | |||
| /** Not owned */ | |||
| InputDevice *inputDevice = NULL; | |||
| InputDevice* inputDevice = NULL; | |||
| Input(); | |||
| ~Input(); | |||
| @@ -152,13 +166,13 @@ struct InputQueue : Input { | |||
| std::queue<Message> queue; | |||
| void onMessage(Message message) override; | |||
| /** If a Message is available, writes `message` and return true */ | |||
| bool shift(Message *message); | |||
| bool shift(Message* message); | |||
| }; | |||
| struct Output : Port { | |||
| /** Not owned */ | |||
| OutputDevice *outputDevice = NULL; | |||
| OutputDevice* outputDevice = NULL; | |||
| Output(); | |||
| ~Output(); | |||
| @@ -176,7 +190,7 @@ struct Output : Port { | |||
| void init(); | |||
| void destroy(); | |||
| /** Registers a new MIDI driver. Takes pointer ownership. */ | |||
| void addDriver(int driverId, Driver *driver); | |||
| void addDriver(int driverId, Driver* driver); | |||
| } // namespace midi | |||
| @@ -21,13 +21,13 @@ enum Method { | |||
| /** Requests a JSON API URL over HTTP(S), using the data as the query (GET) or the body (POST, etc) | |||
| Caller must json_decref(). | |||
| */ | |||
| json_t *requestJson(Method method, std::string url, json_t *dataJ); | |||
| json_t* requestJson(Method method, std::string url, json_t* dataJ); | |||
| /** Returns true if downloaded successfully */ | |||
| bool requestDownload(std::string url, const std::string &filename, float *progress); | |||
| bool requestDownload(std::string url, const std::string& filename, float* progress); | |||
| /** URL-encodes `s` */ | |||
| std::string encodeUrl(const std::string &s); | |||
| std::string encodeUrl(const std::string& s); | |||
| /** Gets the path portion of the URL. */ | |||
| std::string urlPath(const std::string &url); | |||
| std::string urlPath(const std::string& url); | |||
| } // namespace network | |||
| @@ -30,8 +30,8 @@ struct PatchManager { | |||
| /** Disconnects all cables */ | |||
| void disconnectDialog(); | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| bool isLegacy(int level); | |||
| }; | |||
| @@ -3,7 +3,6 @@ | |||
| #include <plugin/Plugin.hpp> | |||
| #include <plugin/Model.hpp> | |||
| #include <vector> | |||
| #include <set> | |||
| namespace rack { | |||
| @@ -25,24 +24,22 @@ struct Update { | |||
| void init(); | |||
| void destroy(); | |||
| void logIn(const std::string &email, const std::string &password); | |||
| void logIn(const std::string& email, const std::string& password); | |||
| void logOut(); | |||
| bool isLoggedIn(); | |||
| void queryUpdates(); | |||
| bool hasUpdates(); | |||
| void syncUpdate(Update *update); | |||
| void syncUpdate(Update* update); | |||
| void syncUpdates(); | |||
| bool isSyncing(); | |||
| Plugin *getPlugin(const std::string &pluginSlug); | |||
| Model *getModel(const std::string &pluginSlug, const std::string &modelSlug); | |||
| std::string normalizeTag(const std::string &tag); | |||
| Plugin* getPlugin(const std::string& pluginSlug); | |||
| Model* getModel(const std::string& pluginSlug, const std::string& modelSlug); | |||
| /** Checks that the slug contains only alphanumeric characters, "-", and "_" */ | |||
| bool isSlugValid(const std::string &slug); | |||
| bool isSlugValid(const std::string& slug); | |||
| /** Returns a string containing only the valid slug characters. */ | |||
| std::string normalizeSlug(const std::string &slug); | |||
| std::string normalizeSlug(const std::string& slug); | |||
| extern const std::set<std::string> allowedTags; | |||
| extern std::vector<Plugin*> plugins; | |||
| extern std::string loginStatus; | |||
| @@ -9,12 +9,12 @@ namespace rack { | |||
| namespace app { | |||
| struct ModuleWidget; | |||
| struct ModuleWidget; | |||
| } // namespace app | |||
| namespace engine { | |||
| struct Module; | |||
| struct Module; | |||
| } // namespace engine | |||
| @@ -22,7 +22,7 @@ namespace plugin { | |||
| struct Model { | |||
| Plugin *plugin = NULL; | |||
| Plugin* plugin = NULL; | |||
| std::vector<std::string> presetPaths; | |||
| /** Must be unique. Used for saving patches. Never change this after releasing your module. | |||
| @@ -31,20 +31,28 @@ struct Model { | |||
| std::string slug; | |||
| /** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ | |||
| std::string name; | |||
| /** List of tags representing the function(s) of the module */ | |||
| std::vector<std::string> tags; | |||
| /** List of tag IDs representing the function(s) of the module. | |||
| Tag IDs are not part of the ABI and may change at any time. | |||
| */ | |||
| std::vector<int> tags; | |||
| /** A one-line summary of the module's purpose */ | |||
| std::string description; | |||
| virtual ~Model() {} | |||
| /** Creates a headless Module */ | |||
| virtual engine::Module *createModule() { return NULL; } | |||
| virtual engine::Module* createModule() { | |||
| return NULL; | |||
| } | |||
| /** Creates a ModuleWidget with a Module attached */ | |||
| virtual app::ModuleWidget *createModuleWidget() { return NULL; } | |||
| virtual app::ModuleWidget* createModuleWidget() { | |||
| return NULL; | |||
| } | |||
| /** Creates a ModuleWidget with no Module, useful for previews */ | |||
| virtual app::ModuleWidget *createModuleWidgetNull() { return NULL; } | |||
| virtual app::ModuleWidget* createModuleWidgetNull() { | |||
| return NULL; | |||
| } | |||
| void fromJson(json_t *rootJ); | |||
| void fromJson(json_t* rootJ); | |||
| }; | |||
| @@ -18,7 +18,7 @@ struct Plugin { | |||
| /** The file path to the plugin's directory */ | |||
| std::string path; | |||
| /** OS-dependent library handle */ | |||
| void *handle = NULL; | |||
| void* handle = NULL; | |||
| /** Must be unique. Used for saving patches. Never change this after releasing your plugin. | |||
| To guarantee uniqueness, it is a good idea to prefix the slug by your "company name" if available, e.g. "MyCompany-MyPlugin" | |||
| @@ -63,9 +63,9 @@ struct Plugin { | |||
| double modifiedTimestamp = -INFINITY; | |||
| ~Plugin(); | |||
| void addModel(Model *model); | |||
| Model *getModel(std::string slug); | |||
| void fromJson(json_t *rootJ); | |||
| void addModel(Model* model); | |||
| Model* getModel(std::string slug); | |||
| void fromJson(json_t* rootJ); | |||
| }; | |||
| @@ -6,4 +6,4 @@ | |||
| You must implement this in your plugin | |||
| */ | |||
| extern "C" | |||
| void init(rack::plugin::Plugin *plugin); | |||
| void init(rack::plugin::Plugin* plugin); | |||
| @@ -91,6 +91,7 @@ | |||
| #include <dsp/ringbuffer.hpp> | |||
| #include <dsp/vumeter.hpp> | |||
| #include <dsp/window.hpp> | |||
| #include <dsp/approx.hpp> | |||
| #include <simd/vector.hpp> | |||
| #include <simd/functions.hpp> | |||
| @@ -101,16 +102,16 @@ namespace rack { | |||
| /** Define this macro before including this header to prevent common namespaces from being included in the main `rack::` namespace. */ | |||
| #ifndef RACK_FLATTEN_NAMESPACES | |||
| // Import some namespaces for convenience | |||
| using namespace logger; | |||
| using namespace math; | |||
| using namespace widget; | |||
| using namespace ui; | |||
| using namespace app; | |||
| using plugin::Plugin; | |||
| using plugin::Model; | |||
| using namespace engine; | |||
| using namespace componentlibrary; | |||
| // Import some namespaces for convenience | |||
| using namespace logger; | |||
| using namespace math; | |||
| using namespace widget; | |||
| using namespace ui; | |||
| using namespace app; | |||
| using plugin::Plugin; | |||
| using plugin::Model; | |||
| using namespace engine; | |||
| using namespace componentlibrary; | |||
| #endif | |||
| @@ -17,35 +17,91 @@ namespace rack { | |||
| using namespace math; | |||
| DEPRECATED inline int min(int a, int b) {return std::min(a, b);} | |||
| DEPRECATED inline int max(int a, int b) {return std::max(a, b);} | |||
| DEPRECATED inline int eucmod(int a, int base) {return eucMod(a, base);} | |||
| DEPRECATED inline bool ispow2(int n) {return isPow2(n);} | |||
| DEPRECATED inline int clamp2(int x, int a, int b) {return clampSafe(x, a, b);} | |||
| DEPRECATED inline float min(float a, float b) {return std::min(a, b);} | |||
| DEPRECATED inline float max(float a, float b) {return std::max(a, b);} | |||
| DEPRECATED inline float eucmod(float a, float base) {return eucMod(a, base);} | |||
| DEPRECATED inline float clamp2(float x, float a, float b) {return clampSafe(x, a, b);} | |||
| DEPRECATED inline int mini(int a, int b) {return std::min(a, b);} | |||
| DEPRECATED inline int maxi(int a, int b) {return std::max(a, b);} | |||
| DEPRECATED inline int clampi(int x, int min, int max) {return math::clamp(x, min, max);} | |||
| DEPRECATED inline int absi(int a) {return std::fabs(a);} | |||
| DEPRECATED inline int eucmodi(int a, int base) {return eucMod(a, base);} | |||
| DEPRECATED inline int log2i(int n) {return math::log2(n);} | |||
| DEPRECATED inline bool ispow2i(int n) {return isPow2(n);} | |||
| DEPRECATED inline float absf(float x) {return std::fabs(x);} | |||
| DEPRECATED inline float sgnf(float x) {return sgn(x);} | |||
| DEPRECATED inline float eucmodf(float a, float base) {return eucMod(a, base);} | |||
| DEPRECATED inline bool nearf(float a, float b, float epsilon = 1.0e-6f) {return math::isNear(a, b, epsilon);} | |||
| DEPRECATED inline float clampf(float x, float min, float max) {return math::clamp(x, min, max);} | |||
| DEPRECATED inline float clamp2f(float x, float min, float max) {return clampSafe(x, min, max);} | |||
| DEPRECATED inline float chopf(float x, float eps) {return chop(x, eps);} | |||
| DEPRECATED inline float rescalef(float x, float a, float b, float yMin, float yMax) {return math::rescale(x, a, b, yMin, yMax);} | |||
| DEPRECATED inline float crossf(float a, float b, float frac) {return crossfade(a, b, frac);} | |||
| DEPRECATED inline float interpf(const float *p, float x) {return interpolateLinear(p, x);} | |||
| DEPRECATED inline void complexMult(float *cr, float *ci, float ar, float ai, float br, float bi) {complexMult(ar, ai, br, bi, cr, ci);} | |||
| DEPRECATED inline void cmultf(float *cr, float *ci, float ar, float ai, float br, float bi) {return complexMult(ar, ai, br, bi, cr, ci);} | |||
| DEPRECATED inline int min(int a, int b) { | |||
| return std::min(a, b); | |||
| } | |||
| DEPRECATED inline int max(int a, int b) { | |||
| return std::max(a, b); | |||
| } | |||
| DEPRECATED inline int eucmod(int a, int base) { | |||
| return eucMod(a, base); | |||
| } | |||
| DEPRECATED inline bool ispow2(int n) { | |||
| return isPow2(n); | |||
| } | |||
| DEPRECATED inline int clamp2(int x, int a, int b) { | |||
| return clampSafe(x, a, b); | |||
| } | |||
| DEPRECATED inline float min(float a, float b) { | |||
| return std::min(a, b); | |||
| } | |||
| DEPRECATED inline float max(float a, float b) { | |||
| return std::max(a, b); | |||
| } | |||
| DEPRECATED inline float eucmod(float a, float base) { | |||
| return eucMod(a, base); | |||
| } | |||
| DEPRECATED inline float clamp2(float x, float a, float b) { | |||
| return clampSafe(x, a, b); | |||
| } | |||
| DEPRECATED inline int mini(int a, int b) { | |||
| return std::min(a, b); | |||
| } | |||
| DEPRECATED inline int maxi(int a, int b) { | |||
| return std::max(a, b); | |||
| } | |||
| DEPRECATED inline int clampi(int x, int min, int max) { | |||
| return math::clamp(x, min, max); | |||
| } | |||
| DEPRECATED inline int absi(int a) { | |||
| return std::fabs(a); | |||
| } | |||
| DEPRECATED inline int eucmodi(int a, int base) { | |||
| return eucMod(a, base); | |||
| } | |||
| DEPRECATED inline int log2i(int n) { | |||
| return math::log2(n); | |||
| } | |||
| DEPRECATED inline bool ispow2i(int n) { | |||
| return isPow2(n); | |||
| } | |||
| DEPRECATED inline float absf(float x) { | |||
| return std::fabs(x); | |||
| } | |||
| DEPRECATED inline float sgnf(float x) { | |||
| return sgn(x); | |||
| } | |||
| DEPRECATED inline float eucmodf(float a, float base) { | |||
| return eucMod(a, base); | |||
| } | |||
| DEPRECATED inline bool nearf(float a, float b, float epsilon = 1.0e-6f) { | |||
| return math::isNear(a, b, epsilon); | |||
| } | |||
| DEPRECATED inline float clampf(float x, float min, float max) { | |||
| return math::clamp(x, min, max); | |||
| } | |||
| DEPRECATED inline float clamp2f(float x, float min, float max) { | |||
| return clampSafe(x, min, max); | |||
| } | |||
| DEPRECATED inline float chopf(float x, float eps) { | |||
| return chop(x, eps); | |||
| } | |||
| DEPRECATED inline float rescalef(float x, float a, float b, float yMin, float yMax) { | |||
| return math::rescale(x, a, b, yMin, yMax); | |||
| } | |||
| DEPRECATED inline float crossf(float a, float b, float frac) { | |||
| return crossfade(a, b, frac); | |||
| } | |||
| DEPRECATED inline float interpf(const float* p, float x) { | |||
| return interpolateLinear(p, x); | |||
| } | |||
| DEPRECATED inline void complexMult(float* cr, float* ci, float ar, float ai, float br, float bi) { | |||
| complexMult(ar, ai, br, bi, cr, ci); | |||
| } | |||
| DEPRECATED inline void cmultf(float* cr, float* ci, float ar, float ai, float br, float bi) { | |||
| return complexMult(ar, ai, br, bi, cr, ci); | |||
| } | |||
| //////////////////// | |||
| // string | |||
| @@ -57,11 +113,21 @@ DEPRECATED inline void cmultf(float *cr, float *ci, float ar, float ai, float br | |||
| // random | |||
| //////////////////// | |||
| DEPRECATED inline uint32_t randomu32() {return random::u32();} | |||
| DEPRECATED inline uint64_t randomu64() {return random::u64();} | |||
| DEPRECATED inline float randomUniform() {return random::uniform();} | |||
| DEPRECATED inline float randomNormal() {return random::normal();} | |||
| DEPRECATED inline float randomf() {return random::uniform();} | |||
| DEPRECATED inline uint32_t randomu32() { | |||
| return random::u32(); | |||
| } | |||
| DEPRECATED inline uint64_t randomu64() { | |||
| return random::u64(); | |||
| } | |||
| DEPRECATED inline float randomUniform() { | |||
| return random::uniform(); | |||
| } | |||
| DEPRECATED inline float randomNormal() { | |||
| return random::normal(); | |||
| } | |||
| DEPRECATED inline float randomf() { | |||
| return random::uniform(); | |||
| } | |||
| //////////////////// | |||
| // logger | |||
| @@ -77,23 +143,47 @@ DEPRECATED inline float randomf() {return random::uniform();} | |||
| // asset | |||
| //////////////////// | |||
| DEPRECATED inline std::string assetGlobal(std::string filename) {return asset::system(filename);} | |||
| DEPRECATED inline std::string assetLocal(std::string filename) {return asset::user(filename);} | |||
| DEPRECATED inline std::string assetPlugin(Plugin *plugin, std::string filename) {return asset::plugin(plugin, filename);} | |||
| DEPRECATED inline std::string assetGlobal(std::string filename) { | |||
| return asset::system(filename); | |||
| } | |||
| DEPRECATED inline std::string assetLocal(std::string filename) { | |||
| return asset::user(filename); | |||
| } | |||
| DEPRECATED inline std::string assetPlugin(Plugin* plugin, std::string filename) { | |||
| return asset::plugin(plugin, filename); | |||
| } | |||
| //////////////////// | |||
| // color | |||
| //////////////////// | |||
| DEPRECATED inline NVGcolor colorClip(NVGcolor a) {return color::clamp(a);} | |||
| DEPRECATED inline NVGcolor colorMinus(NVGcolor a, NVGcolor b) {return color::minus(a, b);} | |||
| DEPRECATED inline NVGcolor colorPlus(NVGcolor a, NVGcolor b) {return color::plus(a, b);} | |||
| DEPRECATED inline NVGcolor colorMult(NVGcolor a, NVGcolor b) {return color::mult(a, b);} | |||
| DEPRECATED inline NVGcolor colorMult(NVGcolor a, float x) {return color::mult(a, x);} | |||
| DEPRECATED inline NVGcolor colorScreen(NVGcolor a, NVGcolor b) {return color::screen(a, b);} | |||
| DEPRECATED inline NVGcolor colorAlpha(NVGcolor a, float alpha) {return color::alpha(a, alpha);} | |||
| DEPRECATED inline NVGcolor colorFromHexString(std::string s) {return color::fromHexString(s);} | |||
| DEPRECATED inline std::string colorToHexString(NVGcolor c) {return color::toHexString(c);} | |||
| DEPRECATED inline NVGcolor colorClip(NVGcolor a) { | |||
| return color::clamp(a); | |||
| } | |||
| DEPRECATED inline NVGcolor colorMinus(NVGcolor a, NVGcolor b) { | |||
| return color::minus(a, b); | |||
| } | |||
| DEPRECATED inline NVGcolor colorPlus(NVGcolor a, NVGcolor b) { | |||
| return color::plus(a, b); | |||
| } | |||
| DEPRECATED inline NVGcolor colorMult(NVGcolor a, NVGcolor b) { | |||
| return color::mult(a, b); | |||
| } | |||
| DEPRECATED inline NVGcolor colorMult(NVGcolor a, float x) { | |||
| return color::mult(a, x); | |||
| } | |||
| DEPRECATED inline NVGcolor colorScreen(NVGcolor a, NVGcolor b) { | |||
| return color::screen(a, b); | |||
| } | |||
| DEPRECATED inline NVGcolor colorAlpha(NVGcolor a, float alpha) { | |||
| return color::alpha(a, alpha); | |||
| } | |||
| DEPRECATED inline NVGcolor colorFromHexString(std::string s) { | |||
| return color::fromHexString(s); | |||
| } | |||
| DEPRECATED inline std::string colorToHexString(NVGcolor c) { | |||
| return color::toHexString(c); | |||
| } | |||
| //////////////////// | |||
| // componentlibrary | |||
| @@ -118,14 +208,14 @@ DEPRECATED static const NVGcolor COLOR_DARK_PANEL = componentlibrary::SCHEME_DAR | |||
| /** Use createWidget() instead */ | |||
| template <class TScrew> | |||
| DEPRECATED TScrew *createScrew(math::Vec pos) { | |||
| DEPRECATED TScrew* createScrew(math::Vec pos) { | |||
| return createWidget<TScrew>(pos); | |||
| } | |||
| /** Use createParam(pos, module, paramId) and set the Param properties in your Module constructor */ | |||
| template <class TParamWidget> | |||
| DEPRECATED TParamWidget *createParam(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
| TParamWidget *o = createParam<TParamWidget>(pos, module, paramId); | |||
| DEPRECATED TParamWidget* createParam(math::Vec pos, Module* module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
| TParamWidget* o = createParam<TParamWidget>(pos, module, paramId); | |||
| if (module) { | |||
| module->configParam(paramId, minValue, maxValue, defaultValue); | |||
| } | |||
| @@ -133,8 +223,8 @@ DEPRECATED TParamWidget *createParam(math::Vec pos, Module *module, int paramId, | |||
| } | |||
| template <class TParamWidget> | |||
| DEPRECATED TParamWidget *createParamCentered(math::Vec pos, Module *module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
| TParamWidget *o = createParamCentered<TParamWidget>(pos, module, paramId); | |||
| DEPRECATED TParamWidget* createParamCentered(math::Vec pos, Module* module, int paramId, float minValue, float maxValue, float defaultValue) { | |||
| TParamWidget* o = createParamCentered<TParamWidget>(pos, module, paramId); | |||
| if (module) { | |||
| module->configParam(paramId, minValue, maxValue, defaultValue); | |||
| } | |||
| @@ -143,8 +233,8 @@ DEPRECATED TParamWidget *createParamCentered(math::Vec pos, Module *module, int | |||
| /** Use createInput() and createOutput() without the `type` variable */ | |||
| template <class TPortWidget> | |||
| DEPRECATED TPortWidget *createPort(math::Vec pos, PortWidget::Type type, Module *module, int inputId) { | |||
| TPortWidget *o = new TPortWidget; | |||
| DEPRECATED TPortWidget* createPort(math::Vec pos, PortWidget::Type type, Module* module, int inputId) { | |||
| TPortWidget* o = new TPortWidget; | |||
| o->box.pos = pos; | |||
| o->type = type; | |||
| o->module = module; | |||
| @@ -42,10 +42,10 @@ extern bool skipLoadOnLaunch; | |||
| extern std::string patchPath; | |||
| extern std::vector<NVGcolor> cableColors; | |||
| json_t *toJson(); | |||
| void fromJson(json_t *rootJ); | |||
| void save(const std::string &path); | |||
| void load(const std::string &path); | |||
| json_t* toJson(); | |||
| void fromJson(json_t* rootJ); | |||
| void save(const std::string& path); | |||
| void load(const std::string& path); | |||
| } // namespace settings | |||
| @@ -1,14 +1,53 @@ | |||
| #pragma once | |||
| #include <simd/vector.hpp> | |||
| #include <simd/sse_mathfun.h> | |||
| #include <simd/sse_mathfun_extension.h> | |||
| #include <common.hpp> | |||
| #include <math.hpp> | |||
| #include <cmath> | |||
| namespace rack { | |||
| namespace simd { | |||
| // Nonstandard functions | |||
| inline float ifelse(bool cond, float a, float b) { | |||
| return cond ? a : b; | |||
| } | |||
| /** Given a mask, returns a if mask is 0xffffffff per element, b if mask is 0x00000000 */ | |||
| inline float_4 ifelse(float_4 mask, float_4 a, float_4 b) { | |||
| return (a & mask) | andnot(mask, b); | |||
| } | |||
| /** Returns the approximate reciprocal square root. | |||
| Much faster than `1/sqrt(x)`. | |||
| */ | |||
| inline float_4 rsqrt(float_4 x) { | |||
| return float_4(_mm_rsqrt_ps(x.v)); | |||
| } | |||
| /** Returns the approximate reciprocal. | |||
| Much faster than `1/x`. | |||
| */ | |||
| inline float_4 rcp(float_4 x) { | |||
| return float_4(_mm_rcp_ps(x.v)); | |||
| } | |||
| /** Given a mask `a`, returns a vector with each element either 0's or 1's depending on the mask bit. | |||
| */ | |||
| template <typename T> | |||
| T movemaskInverse(int a); | |||
| template <> | |||
| inline float_4 movemaskInverse<float_4>(int x) { | |||
| __m128i msk8421 = _mm_set_epi32(8, 4, 2, 1); | |||
| __m128i x_bc = _mm_set1_epi32(x); | |||
| __m128i t = _mm_and_si128(x_bc, msk8421); | |||
| return float_4(_mm_castsi128_ps(_mm_cmpeq_epi32(x_bc, t))); | |||
| } | |||
| // Standard math functions from std:: | |||
| /* Import std:: math functions into the simd namespace so you can use `sin(T)` etc in templated functions and get both the scalar and vector versions. | |||
| @@ -75,40 +114,66 @@ inline float_4 cos(float_4 x) { | |||
| return float_4(sse_mathfun_cos_ps(x.v)); | |||
| } | |||
| using std::tan; | |||
| inline float_4 tan(float_4 x) { | |||
| return float_4(sse_mathfun_tan_ps(x.v)); | |||
| } | |||
| using std::atan; | |||
| inline float_4 atan(float_4 x) { | |||
| return float_4(sse_mathfun_atan_ps(x.v)); | |||
| } | |||
| using std::atan2; | |||
| inline float_4 atan2(float_4 x, float_4 y) { | |||
| return float_4(sse_mathfun_atan2_ps(x.v, y.v)); | |||
| } | |||
| using std::trunc; | |||
| inline float_4 trunc(float_4 a) { | |||
| return float_4(_mm_cvtepi32_ps(_mm_cvttps_epi32(a.v))); | |||
| } | |||
| using std::floor; | |||
| inline float_4 floor(float_4 a) { | |||
| return float_4(sse_mathfun_floor_ps(a.v)); | |||
| float_4 b = trunc(a); | |||
| b -= (b > a) & 1.f; | |||
| return b; | |||
| } | |||
| using std::ceil; | |||
| inline float_4 ceil(float_4 a) { | |||
| return float_4(sse_mathfun_ceil_ps(a.v)); | |||
| float_4 b = trunc(a); | |||
| b += (b < a) & 1.f; | |||
| return b; | |||
| } | |||
| using std::round; | |||
| inline float_4 round(float_4 a) { | |||
| return float_4(sse_mathfun_round_ps(a.v)); | |||
| a += ifelse(a < 0, -0.5f, 0.5f); | |||
| float_4 b = trunc(a); | |||
| return b; | |||
| } | |||
| using std::fmod; | |||
| inline float_4 fmod(float_4 a, float_4 b) { | |||
| return float_4(sse_mathfun_fmod_ps(a.v, b.v)); | |||
| return a - trunc(a / b) * b; | |||
| } | |||
| using std::fabs; | |||
| inline float_4 fabs(float_4 a) { | |||
| return float_4(sse_mathfun_fabs_ps(a.v)); | |||
| } | |||
| using std::trunc; | |||
| inline float_4 trunc(float_4 a) { | |||
| return float_4(sse_mathfun_trunc_ps(a.v)); | |||
| // Sign bit | |||
| int32_4 mask = ~0x80000000; | |||
| return a & float_4::cast(mask); | |||
| } | |||
| using std::pow; | |||
| @@ -133,31 +198,6 @@ T pow(T a, int b) { | |||
| return p; | |||
| } | |||
| // Nonstandard functions | |||
| inline float ifelse(bool cond, float a, float b) { | |||
| return cond ? a : b; | |||
| } | |||
| /** Given a mask, returns a if mask is 0xffffffff per element, b if mask is 0x00000000 */ | |||
| inline float_4 ifelse(float_4 mask, float_4 a, float_4 b) { | |||
| return (a & mask) | andnot(mask, b); | |||
| } | |||
| /** Returns the approximate reciprocal square root. | |||
| Much faster than `1/sqrt(x)`. | |||
| */ | |||
| inline float_4 rsqrt(float_4 x) { | |||
| return float_4(_mm_rsqrt_ps(x.v)); | |||
| } | |||
| /** Returns the approximate reciprocal. | |||
| Much faster than `1/x`. | |||
| */ | |||
| inline float_4 rcp(float_4 x) { | |||
| return float_4(_mm_rcp_ps(x.v)); | |||
| } | |||
| // From math.hpp | |||
| using math::clamp; | |||
| @@ -186,18 +226,6 @@ inline float_4 sgn(float_4 x) { | |||
| return signbit | (nonzero & 1.f); | |||
| } | |||
| /** Given a mask `a`, returns a vector with each element either 0's or 1's depending on the mask bit. */ | |||
| template <typename T> | |||
| inline T movemaskInverse(int a); | |||
| template <> | |||
| inline float_4 movemaskInverse<float_4>(int x) { | |||
| __m128i msk8421 = _mm_set_epi32(8, 4, 2, 1); | |||
| __m128i x_bc = _mm_set1_epi32(x); | |||
| __m128i t = _mm_and_si128(x_bc, msk8421); | |||
| return float_4(_mm_castsi128_ps(_mm_cmpeq_epi32(x_bc, t))); | |||
| } | |||
| } // namespace simd | |||
| } // namespace rack | |||
| @@ -1,4 +1,3 @@ | |||
| #pragma once | |||
| /* Modified version of http://gruntthepeon.free.fr/ssemath/ for VCV Rack. | |||
| The following changes were made. | |||
| @@ -6,7 +5,7 @@ The following changes were made. | |||
| - Make all functions inline since this is a header file. | |||
| - Remove non-SSE2 code, since Rack assumes SSE2 CPUs. | |||
| - Move `const static` variables to function variables for clarity. See https://stackoverflow.com/a/52139901/272642 for explanation of why the performance is not worse. | |||
| - Change header file to <emmintrin.h> since we're using SSE2 intrinsics. | |||
| - Change header file to <pmmintrin.h> since we're using SSE2 intrinsics. | |||
| - Prefix functions with `sse_mathfun_`. | |||
| - Add floor, ceil, fmod. | |||
| @@ -43,8 +42,7 @@ This derived source file is released under the zlib license. | |||
| (this is the zlib license) | |||
| */ | |||
| #pragma once | |||
| #include <pmmintrin.h> | |||
| @@ -376,7 +374,7 @@ inline __m128 sse_mathfun_cos_ps(__m128 x) { // any x | |||
| /* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them.. | |||
| it is almost as fast, and gives you a free cosine with your sine */ | |||
| inline void sse_mathfun_sincos_ps(__m128 x, __m128 *s, __m128 *c) { | |||
| inline void sse_mathfun_sincos_ps(__m128 x, __m128* s, __m128* c) { | |||
| __m128 xmm1, xmm2, xmm3 = _mm_setzero_ps(), sign_bit_sin, y; | |||
| __m128i emm0, emm2, emm4; | |||
| sign_bit_sin = x; | |||
| @@ -470,45 +468,3 @@ inline void sse_mathfun_sincos_ps(__m128 x, __m128 *s, __m128 *c) { | |||
| *s = _mm_xor_ps(xmm1, sign_bit_sin); | |||
| *c = _mm_xor_ps(xmm2, sign_bit_cos); | |||
| } | |||
| inline __m128 sse_mathfun_trunc_ps(__m128 a) { | |||
| return _mm_cvtepi32_ps(_mm_cvttps_epi32(a)); | |||
| } | |||
| inline __m128 sse_mathfun_floor_ps(__m128 a) { | |||
| __m128 b = sse_mathfun_trunc_ps(a); | |||
| // If b > a, subtract 1 fom b | |||
| b = _mm_sub_ps(b, _mm_and_ps(_mm_cmpgt_ps(b, a), sse_mathfun_one_ps())); | |||
| return b; | |||
| } | |||
| inline __m128 sse_mathfun_ceil_ps(__m128 a) { | |||
| __m128 b = sse_mathfun_trunc_ps(a); | |||
| // If b < a, add 1 to b | |||
| b = _mm_add_ps(b, _mm_and_ps(_mm_cmplt_ps(b, a), sse_mathfun_one_ps())); | |||
| return b; | |||
| } | |||
| inline __m128 sse_mathfun_round_ps(__m128 a) { | |||
| // TODO Incorrect for -0.5, -1.5, etc. | |||
| return sse_mathfun_floor_ps(_mm_add_ps(a, _mm_set_ps1(0.5f))); | |||
| } | |||
| inline __m128 sse_mathfun_fmod_ps(__m128 a, __m128 b) { | |||
| __m128 c = _mm_div_ps(a, b); | |||
| c = sse_mathfun_trunc_ps(c); | |||
| c = _mm_mul_ps(c, b); | |||
| return _mm_sub_ps(a, c); | |||
| } | |||
| inline __m128 sse_mathfun_fabs_ps(__m128 a) { | |||
| __m128i minus1 = _mm_set1_epi32(-1); | |||
| __m128 abs_mask = _mm_castsi128_ps(_mm_srli_epi32(minus1, 1)); | |||
| return _mm_and_ps(abs_mask, a); | |||
| } | |||
| @@ -0,0 +1,295 @@ | |||
| /* Modified version of https://github.com/to-miz/sse_mathfun_extension for VCV Rack. | |||
| The following changes were made. | |||
| - Make functions inline. | |||
| - Change global constants to function-scope. | |||
| This derived source file is released under the zlib license. | |||
| */ | |||
| /* | |||
| sse_mathfun_extension.h - zlib license | |||
| Written by Tolga Mizrak 2016 | |||
| Extension of sse_mathfun.h, which is written by Julien Pommier | |||
| Based on the corresponding algorithms of the cephes math library | |||
| This is written as an extension to sse_mathfun.h instead of modifying it, just because I didn't want | |||
| to maintain a modified version of the original library. This way switching to a newer version of the | |||
| library won't be a hassle. | |||
| Note that non SSE2 implementations of tan_ps, atan_ps, cot_ps and atan2_ps are not implemented yet. | |||
| As such, currently you need to #define USE_SSE2 to compile. | |||
| With tan_ps, cot_ps you get good precision on input ranges that are further away from the domain | |||
| borders (-PI/2, PI/2 for tan and 0, 1 for cot). See the results on the deviations for these | |||
| functions on my machine: | |||
| checking tan on [-0.25*Pi, 0.25*Pi] | |||
| max deviation from tanf(x): 1.19209e-07 at 0.250000006957*Pi, max deviation from cephes_tan(x): | |||
| 5.96046e-08 | |||
| ->> precision OK for the tan_ps <<- | |||
| checking tan on [-0.49*Pi, 0.49*Pi] | |||
| max deviation from tanf(x): 3.8147e-06 at -0.490000009841*Pi, max deviation from cephes_tan(x): | |||
| 9.53674e-07 | |||
| ->> precision OK for the tan_ps <<- | |||
| checking cot on [0.2*Pi, 0.7*Pi] | |||
| max deviation from cotf(x): 1.19209e-07 at 0.204303119606*Pi, max deviation from cephes_cot(x): | |||
| 1.19209e-07 | |||
| ->> precision OK for the cot_ps <<- | |||
| checking cot on [0.01*Pi, 0.99*Pi] | |||
| max deviation from cotf(x): 3.8147e-06 at 0.987876517942*Pi, max deviation from cephes_cot(x): | |||
| 9.53674e-07 | |||
| ->> precision OK for the cot_ps <<- | |||
| With atan_ps and atan2_ps you get pretty good precision, atan_ps max deviation is < 2e-7 and | |||
| atan2_ps max deviation is < 2.5e-7 | |||
| */ | |||
| /* Copyright (C) 2016 Tolga Mizrak | |||
| This software is provided 'as-is', without any express or implied | |||
| warranty. In no event will the authors be held liable for any damages | |||
| arising from the use of this software. | |||
| Permission is granted to anyone to use this software for any purpose, | |||
| including commercial applications, and to alter it and redistribute it | |||
| freely, subject to the following restrictions: | |||
| 1. The origin of this software must not be misrepresented; you must not | |||
| claim that you wrote the original software. If you use this software | |||
| in a product, an acknowledgment in the product documentation would be | |||
| appreciated but is not required. | |||
| 2. Altered source versions must be plainly marked as such, and must not be | |||
| misrepresented as being the original software. | |||
| 3. This notice may not be removed or altered from any source distribution. | |||
| (this is the zlib license) | |||
| */ | |||
| #pragma once | |||
| #include "sse_mathfun.h" | |||
| inline __m128 sse_mathfun_tancot_ps(__m128 x, int cotFlag) { | |||
| __m128 p0 = _mm_set_ps1(9.38540185543E-3); | |||
| __m128 p1 = _mm_set_ps1(3.11992232697E-3); | |||
| __m128 p2 = _mm_set_ps1(2.44301354525E-2); | |||
| __m128 p3 = _mm_set_ps1(5.34112807005E-2); | |||
| __m128 p4 = _mm_set_ps1(1.33387994085E-1); | |||
| __m128 p5 = _mm_set_ps1(3.33331568548E-1); | |||
| __m128 xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y; | |||
| __m128i emm2; | |||
| sign_bit = x; | |||
| /* take the absolute value */ | |||
| __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); | |||
| __m128 inv_sign_mask = _mm_castsi128_ps(_mm_set1_epi32(~0x80000000)); | |||
| x = _mm_and_ps(x, inv_sign_mask); | |||
| /* extract the sign bit (upper one) */ | |||
| sign_bit = _mm_and_ps(sign_bit, sign_mask); | |||
| /* scale by 4/Pi */ | |||
| __m128 cephes_FOPI = _mm_set_ps1(1.27323954473516); | |||
| y = _mm_mul_ps(x, cephes_FOPI); | |||
| /* store the integer part of y in mm0 */ | |||
| emm2 = _mm_cvttps_epi32(y); | |||
| /* j=(j+1) & (~1) (see the cephes sources) */ | |||
| emm2 = _mm_add_epi32(emm2, _mm_set1_epi32(1)); | |||
| emm2 = _mm_and_si128(emm2, _mm_set1_epi32(~1)); | |||
| y = _mm_cvtepi32_ps(emm2); | |||
| emm2 = _mm_and_si128(emm2, _mm_set1_epi32(2)); | |||
| emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128()); | |||
| __m128 poly_mask = _mm_castsi128_ps(emm2); | |||
| /* The magic pass: "Extended precision modular arithmetic" | |||
| x = ((x - y * DP1) - y * DP2) - y * DP3; */ | |||
| __m128 minus_cephes_DP1 = _mm_set_ps1(-0.78515625); | |||
| __m128 minus_cephes_DP2 = _mm_set_ps1(-2.4187564849853515625e-4); | |||
| __m128 minus_cephes_DP3 = _mm_set_ps1(-3.77489497744594108e-8); | |||
| xmm1 = minus_cephes_DP1; | |||
| xmm2 = minus_cephes_DP2; | |||
| xmm3 = minus_cephes_DP3; | |||
| xmm1 = _mm_mul_ps(y, xmm1); | |||
| xmm2 = _mm_mul_ps(y, xmm2); | |||
| xmm3 = _mm_mul_ps(y, xmm3); | |||
| __m128 z = _mm_add_ps(x, xmm1); | |||
| z = _mm_add_ps(z, xmm2); | |||
| z = _mm_add_ps(z, xmm3); | |||
| __m128 zz = _mm_mul_ps(z, z); | |||
| y = p0; | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_add_ps(y, p1); | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_add_ps(y, p2); | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_add_ps(y, p3); | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_add_ps(y, p4); | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_add_ps(y, p5); | |||
| y = _mm_mul_ps(y, zz); | |||
| y = _mm_mul_ps(y, z); | |||
| y = _mm_add_ps(y, z); | |||
| __m128 y2; | |||
| if (cotFlag) { | |||
| y2 = _mm_xor_ps(y, sign_mask); | |||
| /* y = _mm_rcp_ps(y); */ | |||
| /* using _mm_rcp_ps here loses on way too much precision, better to do a div */ | |||
| y = _mm_div_ps(_mm_set_ps1(1.f), y); | |||
| } | |||
| else { | |||
| /* y2 = _mm_rcp_ps(y); */ | |||
| /* using _mm_rcp_ps here loses on way too much precision, better to do a div */ | |||
| y2 = _mm_div_ps(_mm_set_ps1(1.f), y); | |||
| y2 = _mm_xor_ps(y2, sign_mask); | |||
| } | |||
| /* select the correct result from the two polynoms */ | |||
| xmm3 = poly_mask; | |||
| y = _mm_and_ps(xmm3, y); | |||
| y2 = _mm_andnot_ps(xmm3, y2); | |||
| y = _mm_or_ps(y, y2); | |||
| /* update the sign */ | |||
| y = _mm_xor_ps(y, sign_bit); | |||
| return y; | |||
| } | |||
| inline __m128 sse_mathfun_tan_ps(__m128 x) { | |||
| return sse_mathfun_tancot_ps(x, 0); | |||
| } | |||
| inline __m128 sse_mathfun_cot_ps(__m128 x) { | |||
| return sse_mathfun_tancot_ps(x, 1); | |||
| } | |||
| inline __m128 sse_mathfun_atan_ps(__m128 x) { | |||
| __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); | |||
| __m128 inv_sign_mask = _mm_castsi128_ps(_mm_set1_epi32(~0x80000000)); | |||
| __m128 atanrange_hi = _mm_set_ps1(2.414213562373095); | |||
| __m128 atanrange_lo = _mm_set_ps1(0.4142135623730950); | |||
| __m128 cephes_PIO2F = _mm_set_ps1(1.5707963267948966192); | |||
| __m128 cephes_PIO4F = _mm_set_ps1(0.7853981633974483096); | |||
| __m128 atancof_p0 = _mm_set_ps1(8.05374449538e-2); | |||
| __m128 atancof_p1 = _mm_set_ps1(1.38776856032E-1); | |||
| __m128 atancof_p2 = _mm_set_ps1(1.99777106478E-1); | |||
| __m128 atancof_p3 = _mm_set_ps1(3.33329491539E-1); | |||
| __m128 sign_bit, y; | |||
| sign_bit = x; | |||
| /* take the absolute value */ | |||
| x = _mm_and_ps(x, inv_sign_mask); | |||
| /* extract the sign bit (upper one) */ | |||
| sign_bit = _mm_and_ps(sign_bit, sign_mask); | |||
| /* range reduction, init x and y depending on range */ | |||
| /* x > 2.414213562373095 */ | |||
| __m128 cmp0 = _mm_cmpgt_ps(x, atanrange_hi); | |||
| /* x > 0.4142135623730950 */ | |||
| __m128 cmp1 = _mm_cmpgt_ps(x, atanrange_lo); | |||
| /* x > 0.4142135623730950 && !(x > 2.414213562373095) */ | |||
| __m128 cmp2 = _mm_andnot_ps(cmp0, cmp1); | |||
| /* -(1.0/x) */ | |||
| __m128 y0 = _mm_and_ps(cmp0, cephes_PIO2F); | |||
| __m128 x0 = _mm_div_ps(_mm_set_ps1(1.f), x); | |||
| x0 = _mm_xor_ps(x0, sign_mask); | |||
| __m128 y1 = _mm_and_ps(cmp2, cephes_PIO4F); | |||
| /* (x-1.0)/(x+1.0) */ | |||
| __m128 x1_o = _mm_sub_ps(x, _mm_set_ps1(1.f)); | |||
| __m128 x1_u = _mm_add_ps(x, _mm_set_ps1(1.f)); | |||
| __m128 x1 = _mm_div_ps(x1_o, x1_u); | |||
| __m128 x2 = _mm_and_ps(cmp2, x1); | |||
| x0 = _mm_and_ps(cmp0, x0); | |||
| x2 = _mm_or_ps(x2, x0); | |||
| cmp1 = _mm_or_ps(cmp0, cmp2); | |||
| x2 = _mm_and_ps(cmp1, x2); | |||
| x = _mm_andnot_ps(cmp1, x); | |||
| x = _mm_or_ps(x2, x); | |||
| y = _mm_or_ps(y0, y1); | |||
| __m128 zz = _mm_mul_ps(x, x); | |||
| __m128 acc = atancof_p0; | |||
| acc = _mm_mul_ps(acc, zz); | |||
| acc = _mm_sub_ps(acc, atancof_p1); | |||
| acc = _mm_mul_ps(acc, zz); | |||
| acc = _mm_add_ps(acc, atancof_p2); | |||
| acc = _mm_mul_ps(acc, zz); | |||
| acc = _mm_sub_ps(acc, atancof_p3); | |||
| acc = _mm_mul_ps(acc, zz); | |||
| acc = _mm_mul_ps(acc, x); | |||
| acc = _mm_add_ps(acc, x); | |||
| y = _mm_add_ps(y, acc); | |||
| /* update the sign */ | |||
| y = _mm_xor_ps(y, sign_bit); | |||
| return y; | |||
| } | |||
| inline __m128 sse_mathfun_atan2_ps(__m128 y, __m128 x) { | |||
| __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); | |||
| __m128 x_eq_0 = _mm_cmpeq_ps(x, _mm_setzero_ps()); | |||
| __m128 x_gt_0 = _mm_cmpgt_ps(x, _mm_setzero_ps()); | |||
| __m128 x_le_0 = _mm_cmple_ps(x, _mm_setzero_ps()); | |||
| __m128 y_eq_0 = _mm_cmpeq_ps(y, _mm_setzero_ps()); | |||
| __m128 x_lt_0 = _mm_cmplt_ps(x, _mm_setzero_ps()); | |||
| __m128 y_lt_0 = _mm_cmplt_ps(y, _mm_setzero_ps()); | |||
| __m128 cephes_PIF = _mm_set_ps1(3.141592653589793238); | |||
| __m128 cephes_PIO2F = _mm_set_ps1(1.5707963267948966192); | |||
| __m128 zero_mask = _mm_and_ps(x_eq_0, y_eq_0); | |||
| __m128 zero_mask_other_case = _mm_and_ps(y_eq_0, x_gt_0); | |||
| zero_mask = _mm_or_ps(zero_mask, zero_mask_other_case); | |||
| __m128 pio2_mask = _mm_andnot_ps(y_eq_0, x_eq_0); | |||
| __m128 pio2_mask_sign = _mm_and_ps(y_lt_0, sign_mask); | |||
| __m128 pio2_result = cephes_PIO2F; | |||
| pio2_result = _mm_xor_ps(pio2_result, pio2_mask_sign); | |||
| pio2_result = _mm_and_ps(pio2_mask, pio2_result); | |||
| __m128 pi_mask = _mm_and_ps(y_eq_0, x_le_0); | |||
| __m128 pi = cephes_PIF; | |||
| __m128 pi_result = _mm_and_ps(pi_mask, pi); | |||
| __m128 swap_sign_mask_offset = _mm_and_ps(x_lt_0, y_lt_0); | |||
| swap_sign_mask_offset = _mm_and_ps(swap_sign_mask_offset, sign_mask); | |||
| __m128 offset0 = _mm_setzero_ps(); | |||
| __m128 offset1 = cephes_PIF; | |||
| offset1 = _mm_xor_ps(offset1, swap_sign_mask_offset); | |||
| __m128 offset = _mm_andnot_ps(x_lt_0, offset0); | |||
| offset = _mm_and_ps(x_lt_0, offset1); | |||
| __m128 arg = _mm_div_ps(y, x); | |||
| __m128 atan_result = sse_mathfun_atan_ps(arg); | |||
| atan_result = _mm_add_ps(atan_result, offset); | |||
| /* select between zero_result, pio2_result and atan_result */ | |||
| __m128 result = _mm_andnot_ps(zero_mask, pio2_result); | |||
| atan_result = _mm_andnot_ps(pio2_mask, atan_result); | |||
| atan_result = _mm_andnot_ps(pio2_mask, atan_result); | |||
| result = _mm_or_ps(result, atan_result); | |||
| result = _mm_or_ps(result, pi_result); | |||
| return result; | |||
| } | |||
| @@ -63,13 +63,13 @@ struct Vector<float, 4> { | |||
| /** Returns a vector with all 1 bits. */ | |||
| static Vector mask() { | |||
| return _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); | |||
| return _mm_castsi128_ps(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); | |||
| } | |||
| /** Reads an array of 4 values. | |||
| On little-endian machines (e.g. x86), the order is reversed, so `x[0]` corresponds to `vector.s[3]`. | |||
| */ | |||
| static Vector load(const float *x) { | |||
| static Vector load(const float* x) { | |||
| /* | |||
| My benchmarks show that _mm_loadu_ps() performs equally as fast as _mm_load_ps() when data is actually aligned. | |||
| This post seems to agree. https://stackoverflow.com/a/20265193/272642 | |||
| @@ -81,12 +81,16 @@ struct Vector<float, 4> { | |||
| /** Writes an array of 4 values. | |||
| On little-endian machines (e.g. x86), the order is reversed, so `x[0]` corresponds to `vector.s[3]`. | |||
| */ | |||
| void store(float *x) { | |||
| void store(float* x) { | |||
| _mm_storeu_ps(x, v); | |||
| } | |||
| float &operator[](int i) {return s[i];} | |||
| const float &operator[](int i) const {return s[i];} | |||
| float& operator[](int i) { | |||
| return s[i]; | |||
| } | |||
| const float& operator[](int i) const { | |||
| return s[i]; | |||
| } | |||
| // Conversions | |||
| Vector(Vector<int32_t, 4> a); | |||
| @@ -119,18 +123,22 @@ struct Vector<int32_t, 4> { | |||
| static Vector mask() { | |||
| return Vector(_mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); | |||
| } | |||
| static Vector load(const int32_t *x) { | |||
| static Vector load(const int32_t* x) { | |||
| // HACK | |||
| // Use _mm_loadu_si128() because GCC doesn't support _mm_loadu_si32() | |||
| return Vector(_mm_loadu_si128((__m128i*) x)); | |||
| } | |||
| void store(int32_t *x) { | |||
| void store(int32_t* x) { | |||
| // HACK | |||
| // Use _mm_storeu_si128() because GCC doesn't support _mm_storeu_si32() | |||
| _mm_storeu_si128((__m128i*) x, v); | |||
| } | |||
| int32_t &operator[](int i) {return s[i];} | |||
| const int32_t &operator[](int i) const {return s[i];} | |||
| int32_t& operator[](int i) { | |||
| return s[i]; | |||
| } | |||
| const int32_t& operator[](int i) const { | |||
| return s[i]; | |||
| } | |||
| Vector(Vector<float, 4> a); | |||
| static Vector cast(Vector<float, 4> a); | |||
| }; | |||
| @@ -144,7 +152,7 @@ inline Vector<float, 4>::Vector(Vector<int32_t, 4> a) { | |||
| } | |||
| inline Vector<int32_t, 4>::Vector(Vector<float, 4> a) { | |||
| v = _mm_cvtps_epi32(a.v); | |||
| v = _mm_cvttps_epi32(a.v); | |||
| } | |||
| inline Vector<float, 4> Vector<float, 4>::cast(Vector<int32_t, 4> a) { | |||
| @@ -160,21 +168,21 @@ inline Vector<int32_t, 4> Vector<int32_t, 4>::cast(Vector<float, 4> a) { | |||
| /** `~a & b` */ | |||
| inline Vector<float, 4> andnot(const Vector<float, 4> &a, const Vector<float, 4> &b) { | |||
| inline Vector<float, 4> andnot(const Vector<float, 4>& a, const Vector<float, 4>& b) { | |||
| return Vector<float, 4>(_mm_andnot_ps(a.v, b.v)); | |||
| } | |||
| /** Returns an integer with each bit corresponding to the most significant bit of each element. | |||
| For example, `movemask(float_4::mask())` returns 0xf. | |||
| */ | |||
| inline int movemask(const Vector<float, 4> &a) { | |||
| inline int movemask(const Vector<float, 4>& a) { | |||
| return _mm_movemask_ps(a.v); | |||
| } | |||
| /** Returns an integer with each bit corresponding to the most significant bit of each byte. | |||
| For example, `movemask(int32_4::mask())` returns 0xffff. | |||
| */ | |||
| inline int movemask(const Vector<int32_t, 4> &a) { | |||
| inline int movemask(const Vector<int32_t, 4>& a) { | |||
| return _mm_movemask_epi8(a.v); | |||
| } | |||
| @@ -250,7 +258,7 @@ DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator==, _mm_cmpeq_ps) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator==, _mm_cmpeq_epi32) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>=, _mm_cmpge_ps) | |||
| inline Vector<int32_t, 4> operator>=(const Vector<int32_t, 4> &a, const Vector<int32_t, 4> &b) { | |||
| inline Vector<int32_t, 4> operator>=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) { | |||
| return Vector<int32_t, 4>(_mm_cmpgt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask(); | |||
| } | |||
| @@ -258,7 +266,7 @@ DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator>, _mm_cmpgt_ps) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator>, _mm_cmpgt_epi32) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<=, _mm_cmple_ps) | |||
| inline Vector<int32_t, 4> operator<=(const Vector<int32_t, 4> &a, const Vector<int32_t, 4> &b) { | |||
| inline Vector<int32_t, 4> operator<=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) { | |||
| return Vector<int32_t, 4>(_mm_cmplt_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask(); | |||
| } | |||
| @@ -266,78 +274,88 @@ DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator<, _mm_cmplt_ps) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(int32_t, 4, operator<, _mm_cmplt_epi32) | |||
| DECLARE_VECTOR_OPERATOR_INFIX(float, 4, operator!=, _mm_cmpneq_ps) | |||
| inline Vector<int32_t, 4> operator!=(const Vector<int32_t, 4> &a, const Vector<int32_t, 4> &b) { | |||
| inline Vector<int32_t, 4> operator!=(const Vector<int32_t, 4>& a, const Vector<int32_t, 4>& b) { | |||
| return Vector<int32_t, 4>(_mm_cmpeq_epi32(a.v, b.v)) ^ Vector<int32_t, 4>::mask(); | |||
| } | |||
| /** `+a` */ | |||
| inline Vector<float, 4> operator+(const Vector<float, 4> &a) { | |||
| inline Vector<float, 4> operator+(const Vector<float, 4>& a) { | |||
| return a; | |||
| } | |||
| inline Vector<int32_t, 4> operator+(const Vector<int32_t, 4> &a) { | |||
| inline Vector<int32_t, 4> operator+(const Vector<int32_t, 4>& a) { | |||
| return a; | |||
| } | |||
| /** `-a` */ | |||
| inline Vector<float, 4> operator-(const Vector<float, 4> &a) { | |||
| inline Vector<float, 4> operator-(const Vector<float, 4>& a) { | |||
| return 0.f - a; | |||
| } | |||
| inline Vector<int32_t, 4> operator-(const Vector<int32_t, 4> &a) { | |||
| inline Vector<int32_t, 4> operator-(const Vector<int32_t, 4>& a) { | |||
| return 0 - a; | |||
| } | |||
| /** `++a` */ | |||
| inline Vector<float, 4> &operator++(Vector<float, 4> &a) { | |||
| inline Vector<float, 4>& operator++(Vector<float, 4>& a) { | |||
| a += 1.f; | |||
| return a; | |||
| } | |||
| inline Vector<int32_t, 4> &operator++(Vector<int32_t, 4> &a) { | |||
| inline Vector<int32_t, 4>& operator++(Vector<int32_t, 4>& a) { | |||
| a += 1; | |||
| return a; | |||
| } | |||
| /** `--a` */ | |||
| inline Vector<float, 4> &operator--(Vector<float, 4> &a) { | |||
| inline Vector<float, 4>& operator--(Vector<float, 4>& a) { | |||
| a -= 1.f; | |||
| return a; | |||
| } | |||
| inline Vector<int32_t, 4> &operator--(Vector<int32_t, 4> &a) { | |||
| inline Vector<int32_t, 4>& operator--(Vector<int32_t, 4>& a) { | |||
| a -= 1; | |||
| return a; | |||
| } | |||
| /** `a++` */ | |||
| inline Vector<float, 4> operator++(Vector<float, 4> &a, int) { | |||
| inline Vector<float, 4> operator++(Vector<float, 4>& a, int) { | |||
| Vector<float, 4> b = a; | |||
| ++a; | |||
| return b; | |||
| } | |||
| inline Vector<int32_t, 4> operator++(Vector<int32_t, 4> &a, int) { | |||
| inline Vector<int32_t, 4> operator++(Vector<int32_t, 4>& a, int) { | |||
| Vector<int32_t, 4> b = a; | |||
| ++a; | |||
| return b; | |||
| } | |||
| /** `a--` */ | |||
| inline Vector<float, 4> operator--(Vector<float, 4> &a, int) { | |||
| inline Vector<float, 4> operator--(Vector<float, 4>& a, int) { | |||
| Vector<float, 4> b = a; | |||
| --a; | |||
| return b; | |||
| } | |||
| inline Vector<int32_t, 4> operator--(Vector<int32_t, 4> &a, int) { | |||
| inline Vector<int32_t, 4> operator--(Vector<int32_t, 4>& a, int) { | |||
| Vector<int32_t, 4> b = a; | |||
| --a; | |||
| return b; | |||
| } | |||
| /** `~a` */ | |||
| inline Vector<float, 4> operator~(const Vector<float, 4> &a) { | |||
| inline Vector<float, 4> operator~(const Vector<float, 4>& a) { | |||
| return a ^ Vector<float, 4>::mask(); | |||
| } | |||
| inline Vector<int32_t, 4> operator~(const Vector<int32_t, 4> &a) { | |||
| inline Vector<int32_t, 4> operator~(const Vector<int32_t, 4>& a) { | |||
| return a ^ Vector<int32_t, 4>::mask(); | |||
| } | |||
| /** `a << b` */ | |||
| inline Vector<int32_t, 4> operator<<(const Vector<int32_t, 4>& a, const int& b) { | |||
| return Vector<int32_t, 4>(_mm_slli_epi32(a.v, b)); | |||
| } | |||
| /** `a >> b` */ | |||
| inline Vector<int32_t, 4> operator>>(const Vector<int32_t, 4>& a, const int& b) { | |||
| return Vector<int32_t, 4>(_mm_srli_epi32(a.v, b)); | |||
| } | |||
| // Typedefs | |||
| @@ -11,55 +11,65 @@ namespace string { | |||
| /** Converts a UTF-16/32 string (depending on the size of wchar_t) to a UTF-8 string. */ | |||
| std::string fromWstring(const std::wstring &s); | |||
| std::wstring toWstring(const std::string &s); | |||
| std::string fromWstring(const std::wstring& s); | |||
| std::wstring toWstring(const std::string& s); | |||
| /** 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, otherwise you might get binary garbage. | |||
| */ | |||
| std::string f(const char *format, ...); | |||
| std::string f(const char* format, ...); | |||
| /** Replaces all characters to lowercase letters */ | |||
| std::string lowercase(const std::string &s); | |||
| std::string lowercase(const std::string& s); | |||
| /** Replaces all characters to uppercase letters */ | |||
| std::string uppercase(const std::string &s); | |||
| std::string uppercase(const std::string& s); | |||
| /** Removes whitespace from beginning and end of string. */ | |||
| std::string trim(const std::string &s); | |||
| std::string trim(const std::string& s); | |||
| /** Truncates and adds "..." to a string, not exceeding `len` characters */ | |||
| std::string ellipsize(const std::string &s, size_t len); | |||
| std::string ellipsizePrefix(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); | |||
| std::string ellipsize(const std::string& s, size_t len); | |||
| std::string ellipsizePrefix(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 the directory of the path. | |||
| Example: directory("dir/file.txt") // "dir" | |||
| Calls POSIX dirname(). | |||
| */ | |||
| std::string directory(const std::string &path); | |||
| std::string directory(const std::string& path); | |||
| /** Extracts the filename of the path. | |||
| Example: directory("dir/file.txt") // "file.txt" | |||
| Calls POSIX basename(). | |||
| */ | |||
| std::string filename(const std::string &path); | |||
| std::string filename(const std::string& path); | |||
| /** Extracts the portion of a filename without the extension. | |||
| Example: filenameBase("file.txt") // "file" | |||
| Note: Only works on filenames. Call filename(path) to get the filename of the path. | |||
| */ | |||
| std::string filenameBase(const std::string &filename); | |||
| std::string filenameBase(const std::string& filename); | |||
| /** Extracts the extension of a filename. | |||
| Example: filenameExtension("file.txt") // "txt" | |||
| Note: Only works on filenames. Call filename(path) to get the filename of the path. | |||
| */ | |||
| std::string filenameExtension(const std::string &filename); | |||
| std::string filenameExtension(const std::string& filename); | |||
| /** Returns the canonicalized absolute path pointed to by `path`, following symlinks. | |||
| Returns "" if the symbol is not found. | |||
| */ | |||
| std::string absolutePath(const std::string &path); | |||
| std::string absolutePath(const std::string& path); | |||
| /** Scores how well a query matches a string. | |||
| A score of 0 means no match. | |||
| The score is arbitrary and is only meaningful for sorting. | |||
| */ | |||
| float fuzzyScore(const std::string &s, const std::string &query); | |||
| float fuzzyScore(const std::string& s, const std::string& query); | |||
| /** Converts a byte array to a Base64-encoded string. | |||
| https://en.wikipedia.org/wiki/Base64 | |||
| */ | |||
| std::string toBase64(const uint8_t* data, size_t len); | |||
| /** Converts a Base64-encoded string to a byte array. | |||
| `outLen` is set to the length of the byte array. | |||
| If non-NULL, caller must delete[] the result. | |||
| */ | |||
| uint8_t* fromBase64(const std::string& str, size_t* outLen); | |||
| struct CaseInsensitiveCompare { | |||
| bool operator()(const std::string &a, const std::string &b) const { | |||
| bool operator()(const std::string& a, const std::string& b) const { | |||
| return lowercase(a) < lowercase(b); | |||
| } | |||
| }; | |||
| @@ -7,7 +7,7 @@ | |||
| namespace rack { | |||
| void svgDraw(NVGcontext *vg, NSVGimage *svg); | |||
| void svgDraw(NVGcontext* vg, NSVGimage* svg); | |||
| } // namespace rack | |||
| @@ -12,38 +12,40 @@ namespace system { | |||
| /** Returns a list of all entries (directories, files, symbols) in a directory. */ | |||
| std::list<std::string> getEntries(const std::string &path); | |||
| std::list<std::string> getEntries(const std::string& path); | |||
| /** Returns whether the given path is a file. */ | |||
| bool isFile(const std::string &path); | |||
| bool isFile(const std::string& path); | |||
| /** Returns whether the given path is a directory. */ | |||
| bool isDirectory(const std::string &path); | |||
| bool isDirectory(const std::string& path); | |||
| /** Moves a file. */ | |||
| void moveFile(const std::string &srcPath, const std::string &destPath); | |||
| void moveFile(const std::string& srcPath, const std::string& destPath); | |||
| /** Copies a file. */ | |||
| void copyFile(const std::string &srcPath, const std::string &destPath); | |||
| void copyFile(const std::string& srcPath, const std::string& destPath); | |||
| /** Creates a directory. | |||
| The parent directory must exist. | |||
| */ | |||
| void createDirectory(const std::string &path); | |||
| void createDirectory(const std::string& path); | |||
| /** Returns the number of logical simultaneous multithreading (SMT) (e.g. Intel Hyperthreaded) threads on the CPU. */ | |||
| int getLogicalCoreCount(); | |||
| /** Sets a name of the current thread for debuggers and OS-specific process viewers. */ | |||
| void setThreadName(const std::string &name); | |||
| void setThreadName(const std::string& name); | |||
| /** Sets the current thread to be high-priority. */ | |||
| void setThreadRealTime(bool realTime); | |||
| /** Returns the number of seconds the current thread has been active. */ | |||
| double getThreadTime(); | |||
| /** Returns the caller's human-readable stack trace with "\n"-separated lines. */ | |||
| std::string getStackTrace(); | |||
| /** 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(const std::string &url); | |||
| void openBrowser(const std::string& url); | |||
| /** Opens Windows Explorer, Finder, etc at the folder location. */ | |||
| void openFolder(const std::string &path); | |||
| void openFolder(const std::string& path); | |||
| /** Runs an executable without blocking. | |||
| The launched process will continue running if the current process is closed. | |||
| */ | |||
| void runProcessDetached(const std::string &path); | |||
| void runProcessDetached(const std::string& path); | |||
| std::string getOperatingSystemInfo(); | |||
| @@ -0,0 +1,28 @@ | |||
| #pragma once | |||
| #include <common.hpp> | |||
| #include <vector> | |||
| namespace rack { | |||
| namespace tag { | |||
| /** List of tags and their aliases. | |||
| The first alias of each tag `tag[tagId][0]` is the "canonical" alias. | |||
| It is guaranteed to exist and should be used as the human-readable form. | |||
| This list is manually alphabetized by the canonical alias. | |||
| */ | |||
| extern const std::vector<std::vector<std::string>> tagAliases; | |||
| /** Searches for a tag ID. | |||
| Searches tag aliases. | |||
| Case-insensitive. | |||
| Returns -1 if not found. | |||
| */ | |||
| int findId(const std::string& tag); | |||
| } // namespace tag | |||
| } // namespace rack | |||
| @@ -11,13 +11,13 @@ namespace ui { | |||
| struct Button : widget::OpaqueWidget { | |||
| std::string text; | |||
| /** Not owned. Tracks the pressed state of the button.*/ | |||
| Quantity *quantity = NULL; | |||
| Quantity* quantity = NULL; | |||
| Button(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDragDrop(const event::DragDrop &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDragDrop(const event::DragDrop& e) override; | |||
| }; | |||
| @@ -8,7 +8,7 @@ namespace ui { | |||
| struct ChoiceButton : Button { | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -10,12 +10,14 @@ namespace ui { | |||
| struct IconButton : Button { | |||
| widget::FramebufferWidget *fw; | |||
| widget::SvgWidget *sw; | |||
| widget::FramebufferWidget* fw; | |||
| widget::SvgWidget* sw; | |||
| IconButton(); | |||
| void setSvg(std::shared_ptr<Svg> svg); | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) {setSvg(svg);} | |||
| DEPRECATED void setSVG(std::shared_ptr<Svg> svg) { | |||
| setSvg(svg); | |||
| } | |||
| }; | |||
| @@ -20,7 +20,7 @@ struct Label : widget::Widget { | |||
| Alignment alignment = LEFT_ALIGNMENT; | |||
| Label(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -9,17 +9,17 @@ namespace ui { | |||
| struct Menu : widget::OpaqueWidget { | |||
| Menu *parentMenu = NULL; | |||
| Menu *childMenu = NULL; | |||
| Menu* parentMenu = NULL; | |||
| Menu* childMenu = NULL; | |||
| /** The entry which created the child menu */ | |||
| MenuEntry *activeEntry = NULL; | |||
| MenuEntry* activeEntry = NULL; | |||
| Menu(); | |||
| ~Menu(); | |||
| void setChildMenu(Menu *menu); | |||
| void setChildMenu(Menu* menu); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void onHoverScroll(const event::HoverScroll &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onHoverScroll(const event::HoverScroll& e) override; | |||
| }; | |||
| @@ -15,12 +15,14 @@ struct MenuItem : MenuEntry { | |||
| bool disabled = false; | |||
| bool active = false; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void step() override; | |||
| void onEnter(const event::Enter &e) override; | |||
| void onDragDrop(const event::DragDrop &e) override; | |||
| void onEnter(const event::Enter& e) override; | |||
| void onDragDrop(const event::DragDrop& e) override; | |||
| void doAction(); | |||
| virtual Menu *createChildMenu() {return NULL;} | |||
| virtual Menu* createChildMenu() { | |||
| return NULL; | |||
| } | |||
| }; | |||
| @@ -10,7 +10,7 @@ namespace ui { | |||
| struct MenuLabel : MenuEntry { | |||
| std::string text; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void step() override; | |||
| }; | |||
| @@ -10,8 +10,8 @@ namespace ui { | |||
| /** Deletes itself from parent when clicked */ | |||
| struct MenuOverlay : widget::OpaqueWidget { | |||
| void step() override; | |||
| void onButton(const event::Button &e) override; | |||
| void onHoverKey(const event::HoverKey &e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onHoverKey(const event::HoverKey& e) override; | |||
| }; | |||
| @@ -9,7 +9,7 @@ namespace ui { | |||
| struct MenuSeparator : MenuEntry { | |||
| MenuSeparator(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -8,7 +8,7 @@ namespace ui { | |||
| struct PasswordField : TextField { | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -10,10 +10,10 @@ namespace ui { | |||
| struct ProgressBar : widget::Widget { | |||
| /** Not owned. Stores the progress value and label. */ | |||
| Quantity *quantity = NULL; | |||
| Quantity* quantity = NULL; | |||
| ProgressBar(); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -12,11 +12,11 @@ namespace ui { | |||
| struct RadioButton : widget::OpaqueWidget { | |||
| /** Not owned. */ | |||
| Quantity *quantity = NULL; | |||
| Quantity* quantity = NULL; | |||
| RadioButton(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onDragDrop(const event::DragDrop &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onDragDrop(const event::DragDrop& e) override; | |||
| }; | |||
| @@ -18,10 +18,10 @@ struct ScrollBar : widget::OpaqueWidget { | |||
| float size = 0.0; | |||
| ScrollBar(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragMove(const event::DragMove &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| }; | |||
| @@ -10,19 +10,19 @@ namespace ui { | |||
| /** Handles a container with ScrollBar */ | |||
| struct ScrollWidget : widget::OpaqueWidget { | |||
| widget::Widget *container; | |||
| ScrollBar *horizontalScrollBar; | |||
| ScrollBar *verticalScrollBar; | |||
| widget::Widget* container; | |||
| ScrollBar* horizontalScrollBar; | |||
| ScrollBar* verticalScrollBar; | |||
| math::Vec offset; | |||
| ScrollWidget(); | |||
| void scrollTo(math::Rect r); | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void step() override; | |||
| void onButton(const event::Button &e) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragMove(const event::DragMove &e) override; | |||
| void onHoverScroll(const event::HoverScroll &e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void onHoverScroll(const event::HoverScroll& e) override; | |||
| }; | |||
| @@ -11,14 +11,14 @@ namespace ui { | |||
| struct Slider : widget::OpaqueWidget { | |||
| /** Not owned. */ | |||
| Quantity *quantity = NULL; | |||
| Quantity* quantity = NULL; | |||
| Slider(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onDragStart(const event::DragStart &e) override; | |||
| void onDragMove(const event::DragMove &e) override; | |||
| void onDragEnd(const event::DragEnd &e) override; | |||
| void onDoubleClick(const event::DoubleClick &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onDragStart(const event::DragStart& e) override; | |||
| void onDragMove(const event::DragMove& e) override; | |||
| void onDragEnd(const event::DragEnd& e) override; | |||
| void onDoubleClick(const event::DoubleClick& e) override; | |||
| }; | |||
| @@ -20,11 +20,11 @@ struct TextField : widget::OpaqueWidget { | |||
| int selection = 0; | |||
| TextField(); | |||
| void draw(const DrawArgs &args) override; | |||
| void onDragHover(const event::DragHover &e) override; | |||
| void onButton(const event::Button &e) override; | |||
| void onSelectText(const event::SelectText &e) override; | |||
| void onSelectKey(const event::SelectKey &e) override; | |||
| void draw(const DrawArgs& args) override; | |||
| void onDragHover(const event::DragHover& e) override; | |||
| void onButton(const event::Button& e) override; | |||
| void onSelectText(const event::SelectText& e) override; | |||
| void onSelectKey(const event::SelectKey& e) override; | |||
| /** Inserts text at the cursor, replacing the selection if necessary */ | |||
| void insertText(std::string text); | |||
| @@ -11,7 +11,7 @@ struct Tooltip : widget::Widget { | |||
| std::string text; | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| }; | |||
| @@ -15,7 +15,7 @@ struct FramebufferWidget : Widget { | |||
| bool dirty = true; | |||
| bool bypass = false; | |||
| float oversample = 1.0; | |||
| NVGLUframebuffer *fb = NULL; | |||
| NVGLUframebuffer* fb = NULL; | |||
| /** Scale relative to the world */ | |||
| math::Vec scale; | |||
| /** Offset in world coordinates */ | |||
| @@ -34,7 +34,7 @@ struct FramebufferWidget : Widget { | |||
| FramebufferWidget(); | |||
| ~FramebufferWidget(); | |||
| void step() override; | |||
| void draw(const DrawArgs &args) override; | |||
| void draw(const DrawArgs& args) override; | |||
| virtual void drawFramebuffer(); | |||
| int getImageHandle(); | |||
| }; | |||