Browse Source

Add a few sections to Migrate2.

pull/46/merge
Andrew Belt 3 years ago
parent
commit
6a6201a53a
1 changed files with 107 additions and 25 deletions
  1. +107
    -25
      Migrate2.md

+ 107
- 25
Migrate2.md View File

@@ -1,10 +1,9 @@
# Migrating v1 Plugins to v2
The API of VCV Rack 2 has been designed to be nearly backward-compatible with v1 plugins, so updating your plugin to v2 likely only involves a version update and a recompile.

The API of VCV Rack v2 has been designed to be nearly backward-compatible with v1 plugins, so updating your plugin to v2 likely only involves a version update and a recompile.

## Update plugin version to v2

The major version number (`vMAJOR.MINOR.REVISION`) of your plugin must match the major version of Rack, so change the version number in your plugin's `plugin.json` manifest to `2`.
### 1.1) Update `plugin.json` version string to v2
The major version number (`MAJOR.MINOR.REVISION`) of your plugin must match the major version of Rack, so change the version number in your plugin's `plugin.json` manifest to `2`.
For example, change
```json
{
@@ -21,49 +20,132 @@ to
```
If you wish, you may keep the minor and revision numbers unchanged, e.g. `2.2.3`.

Download the latest [Rack v2 SDK](https://vcvrack.com/downloads/).
Download the latest [Rack 2 SDK](https://vcvrack.com/downloads/).
Clean and compile the plugin.
```bash
export RACK_SDK=/path/to/Rack-SDK
make clean
make dist
```
If your plugin compiles successfully, you are ready to test and release.
Or, you may take advantage of new v2 features below.

If your plugin compiles successfully, you can skip ahead to [Potential runtime bugs](#Potential-runtime-bugs).
If not, read on to solve potential errors or warnings.


### 1.2) `ParamWidget::reset()` and `ParamWidget::randomize()` has been removed
If you would like to disable resetting or randomization of a particular parameter when the user resets/randomizes the module, set these properties in your `Module` constructor.
```cpp
getParamQuantity(MY_PARAM)->resetEnabled = false;
getParamQuantity(MY_PARAM)->randomizeEnabled = false;
```

Alternatively, you can override the following methods in your `Module` class to implement custom behavior for *all* parameters and internal state.
```cpp
void onReset(const ResetEvent& e) override {
params[MY_PARAM].setValue(0.f);
// ...

// Call super method if you wish to include default behavior
// Module::onReset(e);
}

void onRandomize(const RandomizeEvent& e) override {
params[MY_PARAM].setValue(random::uniform());
// ...

// Call super method if you wish to include default behavior
// Module::onRandomize(e);
}
```


### 1.3) `ParamWidget::paramQuantity` has been replaced by `getParamQuantity()`
If your `ParamWidget` methods use the `paramQuantity` member variable, replace it with the getter method, or add this line to the top of your method.
```cpp
ParamQuantity* paramQuantity = getParamQuantity();
```


### 1.4) `Window::loadSvg()` has been deprecated and moved to `Svg::load()`
Search and replace:
```bash
perl -p -i -e 's/APP->window->loadSvg\b/Svg::load/g' src/*.{cpp,hpp}
```

## Potential bugs

### Avoid keeping `Font` and `Image` references across multiple frames
## 2) Potential runtime bugs
You might encounter these issues while testing.

*Rack for DAWs* uses a different OpenGL context each time the plugin editor window is closed and reopened.
This means that if you load a `Font` or `Image` in a widget's constructor with `font = APP->window->loadFont(...)` or `image = APP->window->loadImage(...)`, its OpenGL reference will be invalid if the window is reopened later.

### 2.1) Don't store `Font` and `Image` references across multiple frames
The DAW plugin version of Rack uses a different OpenGL context each time the plugin editor window is closed and reopened.
This means that if you load and store a `Font` or `Image` in a widget's constructor with `font = APP->window->loadFont(...)` or `image = APP->window->loadImage(...)`, its OpenGL reference will be invalid if the window is reopened later.

Instead, save only its path, and fetch the font/image each frame in `draw()`. Example:
```cpp
std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
if (font) {
nvgFontFaceId(args.vg, font->handle);
...
void draw(const DrawArgs& args) override {
std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
if (font) {
nvgFontFaceId(args.vg, font->handle);
...
}
}

```
`loadFont()` and `loadImage()` caches the font/image by its path, so this operation can be efficiently called in `draw()` every frame.

## Optional v2 API features

### SVG load function
`Window::loadSvg()` has been moved to `Svg::load()`. Search and replace:
```bash
perl -i -pe 's/APP->window->loadSvg\b/Svg::load/g' src/*
## 3) New optional v2 API features
While not required, these new API features in Rack 2 can enhance the usability of your modules.


### 3.2) Add Port and Light labels
Add names to your ports and lights which appear in tooltips.

For example, in your `Module` constructor:
```cpp
configInput(PITCH_INPUT, "1V/oct pitch");
configOutput(SIN_OUTPUT, "Sine");
configLight(PHASE_LIGHT, "Phase");
```


### 3.3) Replace `configParam()` with `configButton()` or `configSwitch()` as applicable
For momentary buttons and multi-state switches, displaying a real-valued parameter to the user doesn't make much sense.

Instead, use `configButton()` to hide the text field in its context menu, or `configSwitch()` to offer a list of choices.
```cpp
configButton(TAP_PARAM);
configSwitch(SYNC_PARAM, "Sync mode", {"Soft", "Hard"});
```

### Port labels

TOOD
### 3.4) Add bypass routes
If your module is bypassed by the user (via the context menu or key command), you can route certain inputs directly to outputs, bypassing all processing.
This would make sense for a filter or reverb, or even a clock divider or quantizer, but it would not make sense for a VCO, since it generates a signal rather than processes one.

For example, in your `Module` constructor:
```cpp
configBypass(LEFT_INPUT, LEFT_OUTPUT);
configBypass(RIGHT_INPUT, RIGHT_OUTPUT);
```

Alternatively, override `Module::processBypass()` to implement custom bypass behavior.

### Light labels

TODO
### 3.5) Store large data in the module's patch storage directory
Instead of serializing large (>100 KB) buffers in `toJson/fromJson()` methods which could lag the UI, read/write to the directory returned by `createPatchStorageDirectory()` and `getPatchStorageDirectory()`.

### TODO
Example:
```cpp
void onAdd(const AddEvent& e) override {
std::string path = system::join(createPatchStorageDirectory(), "wavetable.wav");
// Read file...
}

void onSave(const SaveEvent& e) override {
std::string path = system::join(createPatchStorageDirectory(), "wavetable.wav");
// Write file...
}
```
Note: You cannot call these methods in the `Module` constructor since it is not added to the Engine at that point.

Loading…
Cancel
Save