New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin API Updates Thread #258

Open
AndrewBelt opened this Issue Oct 18, 2017 · 37 comments

Comments

Projects
None yet
1 participant
@AndrewBelt
Copy link
Member

AndrewBelt commented Oct 18, 2017

Attention plugin developers: Subscribe to this thread using the button in the sidebar to receive notifications when the Rack API changes or a discussion about a change is being held.

This thread is locked to prevent unneeded noise. If you have a question or comment, create an issue, and if I think all plugin developers should participate, I will link it from here.

Starting with...

Rack 0.4 API

@VCVRack VCVRack locked and limited conversation to collaborators Oct 18, 2017

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 18, 2017

gSampleRate replaced with getter function engineGetSampleRate() in 9dbadfb

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 18, 2017

Use the C++ override keyword to explicitly state that you are overriding a method. Otherwise you will get build warnings. 275204c

@AndrewBelt AndrewBelt changed the title Plugin Developer Thread Plugin API Updates Thread Oct 19, 2017

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 19, 2017

Starting now, this is how you should version your plugins: #266

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 19, 2017

Module::initialize() is hard-deprecated. The new name is Module::reset(), since the function is not called upon initialization but when the user requests to reset the state of the module. #253

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 21, 2017

Discussion about optionally splitting plugin.dll/dylib/so into plugins for each module within the same folder: e.g. VCO.dll, VCF.dll, ADSR.dll, etc. #277

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 23, 2017

fde8877 breaks the relationship between plugins and manufacturers. You can now have multiple plugins with the same manufacturer, or even multiple manufacturers with one plugin. The 1 plugin per folder in plugins/ rule still holds.

See https://github.com/VCVRack/Tutorial/blob/master/src/Tutorial.cpp for an example of how to handle this change.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 25, 2017

Removed Davies1900hSmallBlackKnob and Davies1900hSmallBlackSnapKnob in 265cd9b. Use RoundSmallBlackKnob and RoundSmallBlackSnapKnob instead.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 26, 2017

Light and ValueLight are replaced with LightWidget and ColorLightWidget. Light is now the name for a struct just like Output, Input, and Param and is used for passing data from the engine to the GUI. Module now has a built-in lights vector which can be resized by the Module() constructor. You can create a ModuleLightWidget easily with createLight() in rack.hpp.

See https://github.com/VCVRack/Tutorial/blob/master/src/MyModule.cpp for an example.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Oct 29, 2017

#337 changes the Rack SVG DPI from 96 to 75. This allows existing Eurorack modules to be ported without resizing their panel graphics from the 5.06" height standard.

If your panels or components are saved with pixel units, no change is needed. If the units are set to inches or millimeters and appear to be incorrectly scaled with the latest source, open each SVG file in Inkscape (other software may work but not tested), open Document Properties (Shift+Ctrl+D), under the Orientation section set Units to "px" (not Display Units), and Save.

As a reminder, you may now use floating point values when placing components.

addParam(createParam<RoundSmallBlackKnob>(Vec(45.308, 47.753), module, 0, -1.0, 1.0, 0.0));

Now, if you prefer to uses inches or millimeters in these positions, you may use the new in2px() or mm2px() convenience functions.

addParam(createParam<RoundSmallBlackKnob>(in2px(Vec(0.6041, 0.6367)), module, 0, -1.0, 1.0, 0.0));
addParam(createParam<RoundSmallBlackKnob>(mm2px(Vec(15.34431, 16.17235)), module, 0, -1.0, 1.0, 0.0));
@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Nov 1, 2017

The Widget event framework has been refactored in 8e251c0. If your code uses on*() event functions, you must update them to use the new function signatures defined here. https://github.com/VCVRack/Rack/blob/master/include/widgets.hpp#L125-L154

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Nov 3, 2017

LargeLight, MediumLight, SmallLight, and TinyLight have been slightly adjusted in size to follow the 5mm, 3mm, 2mm, and 1mm standard for LEDs. If your modules use these, you may need to subtract ~1 px from their position to re-align them. Remember that you can use floating point positions like Vec(45.5, 47.2).

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Nov 11, 2017

In fc29d98, createModel() has a new signature which is incompatible with the old version, so you'll need to change your init() code.

createModel(std::string manufacturer, std::string slug, std::string name, Tags... tags);

You may add multiple tags on each module, which can be found at https://github.com/VCVRack/Rack/blob/master/include/plugin.hpp#L9-L54

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Nov 22, 2017

Rack 0.5 API

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Dec 8, 2017

SVGSlider has been renamed to SVGFader in 199f99b (to differentiate from the Slider widget, which is entirely different).

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Dec 10, 2017

Renamed Module::reset() to onReset() and Module::randomize() to onRandomize() with 36cd050.
Also added onCreate() and onDelete() when user explicitly creates (or clones) or deletes the module.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jan 5, 2018

Brainstorming on ideas to mitigate the pain of upgrading the API to v0.6 #590. Feel free to chime in.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jan 14, 2018

The patch format will change so that indices for params and input/outputIds for wires will refer to Module::params/inputs/outputs indices instead of ModuleWidget::params/inputs/outputs indices. Old patches will be detected and loaded using a legacy patch loader, so the end-users will not see a result. This does not affect the plugin API unless you're doing weird things with param/input/output indices, but it is best that you know. See #240

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 3, 2018

Functions in util/math.hpp have been renamed. See the deprecated functions and their new alternatives.
Additionally, some functions like quadraticBipolar() from math.hpp have been moved to dsp/functions.hpp, so you will need to include this header if you use them.

It now uses function overloading on different types, so you must be more explicit about the types of their arguments. For example,

clamp(x / 10.0, 0.0, 1.0);

is called with types

clamp(double, double, double);

so it cannot decide whether clamp(int, int, int) or clamp(float, float, float) should be used. Use this instead.

clamp(x / 10.0f, 0.0f, 1.0f);

or more generally, get in the (good) habit of using float literals (1.0f) instead of double literals (1.0) in your DSP code, since they are usually faster because they are guaranteed to not cast to double and back.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 9, 2018

SchmittTrigger::setThresholds(float low, float high) has been removed, and the thresholds are now fixed at 0 and 1. Instead, rescale your input if needed with trigger.process(rescale(in, low, high, 0.f, 1.f)).

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 9, 2018

The helpers in rack.hpp are deprecated. Use the new create() helpers discussed in #595. This idiom is a more consistent method for creating Widget subclasses, and is easily extendable to create your own. Literal replacements:

createScrew -> Widget::create
createParam -> ParamWidget::create
createInput -> Port::create<TPort>(..., Port::INPUT, ...)
createOutput -> Port::create<TPort>(..., Port::OUTPUT, ...)
createLight -> ModuleLightWidget::create

Here are some sed scripts. Potentially destructive, so backup (or use version control) before running in your src/ directory.

sed -i 's/createScrew/Widget::create/' *.cpp
sed -i 's/createParam/ParamWidget::create/' *.cpp
sed -i -E 's/createInput<(.*?)>\((.*?), module/Port::create<\1>(\2, Port::INPUT, module/' *.cpp
sed -i -E 's/createOutput<(.*?)>\((.*?), module/Port::create<\1>(\2, Port::OUTPUT, module/' *.cpp
sed -i 's/createLight/ModuleLightWidget::create/' *.cpp
@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 13, 2018

It's time for all plugin developers to update their Makefiles. The Makefile system is now stable and will likely not change in the future.
Follow the Template plugin as an example. You don't need the comments, so for most purposes, something like the Fundamental Makefile is fine. Be sure to include

  • SLUG and VERSION at the top of your Makefile.
  • the DISTRIBUTABLES list with all files you wish to include in your ZIP package.
  • The RACK_DIR variable, so people can build your plugins in locations outside Rack's plugin/ directory.
  • Omit the dist: target if you still have that from previous versions, as that has been included for you in plugin.mk.
@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 14, 2018

Last but not least, the API has changed in 2ff1ee5 to allow for separation of the Module and ModuleWidget classes. This will allow for things in the future like Rack headless (no GUI) mode and module previews in the GUI without instantiating the DSP engine of each previewed module. Unfortunately it requires some substantial changes for plugins.

This should be the last API change for the Rack 0.6 release. The ABI may change, so plugin binaries compiled now might not work in Rack 0.6, but if you update your plugins' source code, a simple recompile will be all that is needed to make them work in Rack 0.6 after its release.

1

In the Template.hpp file (usually renamed to YourPlugin.hpp), copy and paste each ModuleWidget struct

struct MyModuleWidget : ModuleWidget {
	MyModuleWidget();
};

into the .cpp file of each module, right above the MyModuleWidget::MyModuleWidget() constructor definition, and add an argument to the constructor.

	MyModuleWidget(MyModule *module);

Back in the header file, replace the struct with a global variable declaration

extern Model *modelMyModule;

renaming MyModule of course.

2

In the .cpp file for each module, change the code from

MyModuleWidget::MyModuleWidget() {
	MyModule *module = new MyModule();
	setModule(module);

to

MyModuleWidget::MyModuleWidget(MyModule *module) : ModuleWidget(module) {

The Module is now constructed internally by Rack and passed to your MyModuleWidget constructor.

3

In the Template.cpp file (usually renamed to YourPlugin.cpp), move

	p->addModel(createModel<MyModuleWidget>("Template", "MyModule", "My Module", OSCILLATOR_TAG));

to the bottom of each respective module source file, and change it to

Model *modelMyModule = Model::create<MyModule, MyModuleWidget>(
	"Template", "MyModule", "My Module", OSCILLATOR_TAG);

Note: For blank panels with no Module, simply use the base class Module in place of MyModule.

Finally, returning to Template.cpp, add

	p->addModel(modelMyModule);

in the place of the moved line for each module.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 17, 2018

Note: It is recommended to clone a clean copy of Rack master instead of "pulling out the carpet" from beneath your Rack v0.5 branch, but if you wish to do so, make sure to git checkout master, git pull, and git submodule update --init --recursive in the Rack/ directory, and then make clean in the Rack/ and Rack/dep/ directories before rebuilding.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 19, 2018

Plans for 1.0: While the 0.5-to-0.6 upgrade is laborous, 0.6-to-1.0 will have very little changes, because I have implemented all API changes I had planned for 1.0 into the 0.6 API. Currently I see no reason to change the API in 1.0 from its current state, and although new changes will certainly come up in the estimated 5 months from now---they may be just a few simple one-line fixes.

  • Plugin Makefiles are completely stable, would be surprised if they change in the next 10 years
  • init() may change, but the function is so minimal now, there's not much to change
  • If you are using deprecated functions, variables, and methods, they will be removed in 1.0.
  • The DSP headers will be expanded with new stock filters, FFT, and advanced math, but existing classes are somewhat stable. At worst, you may need to change the names your #includes if I reorganize.
  • ParamWidgets may be given names/labels/units/scales, which will be optional, and I will not drop support for current Knob::create-like functions.
@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 24, 2018

Fixed a bug in the LightWidget color algorithm, so now lights draw with the exact requested color. The LightWidget API has changed slightly but not in a way that will affect most (if not all) plugins. The color has changed, so compile your plugin against the latest Rack master code and test whether the lights look as intended. If your plugin doesn't compile, open an issue and I'll take a look at your specific issue.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Feb 26, 2018

Discussion: What is the best way to make turn the official Rack distribution into the "Rack SDK", so plugin developers no longer need to build Rack from source? #754

EDIT: Has been resolved. Do the normal git pull, git submodule update --init --recursive, make dep for now. The Rack-SDK package will be available after Rack 0.6.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Mar 4, 2018

Best practices: Instead of inheriting from Widget directly, inherit from VirtualWidget to guarantee that all classes in the widget hierarchy use virtual inheritance, i.e. only one copy of Widget's member variables are used by all instances.

If none of your classes inherit Widget directly in your plugins, you can ignore this post.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Mar 13, 2018

I have removed support for Knobs that rescale to their box.size. If you are changing box.size in a constructor of a subclass of Knob, you will have to remove it and rescale the SVG document (don't forget to re-wrap the page boundary as well.)

If you are using RoundBlackKnobs, make sure you are using the correct sizes, since I have corrected a mistake in the naming.

  • RoundHugeBlackKnob: 19mm
  • RoundLargeBlackKnob: 12.7mm
  • RoundBlackKnob: 10mm
  • RoundSmallBlackKnob: 8mm

From the looks of it, replace RoundBlackKnob -> RoundLargeBlackKnob, RoundSmallBlackKnob -> RoundBlackKnob, and RoundSmallBlackSnapKnob -> RoundBlackSnapKnob.

Recompile your plugin and check that the knobs line up. Open an issue for help.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Mar 14, 2018

Shadows have been added to SVGKnob and SVGPort.

If you don't like the appearance, you can change it in your SVGKnob or SVGPort subclass's constructor after calling setSVG(). (Shadows will not work if you fail to call this method.) The default values are

shadow->blurRadius = 0.0;
shadow->opacity = 0.15;
shadow->box.pos = Vec(0.0, box.size.y * 0.1);

If you set the opacity to 0.0, the shadow draw routine will be skipped.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Mar 21, 2018

A few people have asked me about recommended practices for Git branches and versions for their plugins. I suggest having the branches

  • master for latest work which compiles against Rack master
  • 0.5 for 0.5-compatible API. No reason to maintain after Rack 0.6 releases
  • 0.6 which should branch from master after Rack 0.6 releases

After Rack 0.6 releases, I suggest resetting your REVISION number in MAJOR.MINOR.REVISION to 0. Example:

  • 0.5.11, 0.5.12, ...
  • 0.6.0, 0.6.1, etc.

Remember that the REVISION number has nothing to do with the Rack REVISION.

These are just my opinions if you don't have your own. Feel free to do your own thing.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Mar 26, 2018

Rack 0.6 API

The Rack 0.6 API and ABI are now stable. Today I'm also releasing Rack SDK, which is a new way to compile modules from source without needing to compile Rack itself. You can skip the Building section of the Rack README and proceed to the Building plugins section. Plugins built with the 0.6.* SDK will work with official releases of Rack 0.6.* and source builds from Rack's v0.6 branch.

https://vcvrack.com/downloads/Rack-SDK-0.6.0.zip

Use by running

RACK_DIR=<Rack SDK path> make dist

in your plugin's directory.

Open-source plugins in https://github.com/VCVRack/community/tree/v2/repos do not need to supply builds. The Build Team VCVRack/library#353 is responsible for that. Closed-source plugins need to supply their own builds, and plugins sold through the VCV Store need to send updated plugins to me.

Plugin developers should no longer submit PRs to the community repository. Simply create an issue in the repository with the title equal to your plugin's slug with a description of your desired changes in the issue body, and the Library Team VCVRack/library#352 will make the requested changes.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Apr 16, 2018

To submit plugins and update information and builds in the Plugin Manager, please see https://github.com/VCVRack/community#for-plugin-developers for instructions to notify the VCV Community. This is easier than the old method of sending a pull request.

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jun 17, 2018

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jun 18, 2018

Embedded resources: It's recommended to use external resource files for non-code such as DSP lookup tables, but sometimes it's necessary to store them in the binary itself (e.g. due to licensing issues). Now you can easily include resources in the binary by declaring the file in your Makefile and referencing it from your source code.
https://github.com/VCVRack/Rack/blob/v0.6/include/util/common.hpp#L58-L69

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jun 22, 2018

Combined release and development modes: Since b6b6ec8, there is no make RELEASE=1 flag. Instead, the mode is determined at runtime. Run ./Rack or double-click the binary to launch in release mode. Run ./Rack -d or make run to launch in development mode. A quick comparison:

Release mode:

  • Login toolbar and Plugin Manager enabled
  • Version checking enabled
  • Rack local directory at Documents/Rack/ on Mac, My Documents/Rack/ on Windows, ~/.Rack/ on Linux
  • Logs to <Rack local directory>/log.txt

Development mode:

  • Login toolbar and Plugin Manager disabled
  • Version checking disabled
  • Rack local directory is your current working directory
  • Logs (in color) to the terminal

@AndrewBelt AndrewBelt changed the title Plugin API Updates Thread Rack Development Blog Jun 22, 2018

@AndrewBelt AndrewBelt changed the title Rack Development Blog Plugin API Updates Thread Jun 22, 2018

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Jul 15, 2018

@AndrewBelt

This comment has been minimized.

Copy link
Member

AndrewBelt commented Sep 13, 2018

I am deprecating json_real_value() in Rack plugins to avoid an issue when users process VCV patch files with external scripts. Use json_number_value() instead to convert json_t* values to double. The following scenario will result in unintended behavior.

  • A plugin uses json_real() to serialize the double 42.0. The JSON encoder emits 42.0.
  • A user uses a script to decode (JSON.parse), process, and encode (JSON.stringify). The JSON encoder emits 42, which is a valid representation under the JSON standard.
  • The plugin uses json_real_value() to parse 42. Jansson's internal value system interprets it as an integer instead of a real number, so the function will return 0. Bad!

The following plugins use json_real_value(). A simple search-and-replace and update will solve this issue.

AnimatedCircuits_Noises
AS-Seqs-n-Tools
Bidoo
Blamsoft-XFXWave
Geodesics
Hora-Processors
ML_modules_QU
ML_modules_seq
NYSTHI
Ohmer
trowaSoft
VultModulesFree
VultModules
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.