You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Migrate2.md 6.2KB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. # Migrating v1 Plugins to v2
  2. *This document is a work-in-progress and will be finalized when the Rack 2 ABI is declared stable.*
  3. The API of VCV Rack 2 has been designed to be nearly backward-compatible with v1 plugins.
  4. This means that 90% of plugins will only require a version update and a recompile (a 1-line edit, 15 seconds of work).
  5. For the other 10% of plugins using advanced or unstable API features, updating to v2 is easy and involves following a few search-and-replace steps.
  6. Additionally, Rack 2 includes several new optional enhancements that plugin developers can use to improve usability of their plugins in [section 3](#3-New-optional-v2-API-features).
  7. ### 1.1) Update `plugin.json` version string to v2
  8. 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`.
  9. For example, change
  10. ```json
  11. {
  12. "slug": "Fundamental",
  13. "version": "1.2.3",
  14. ...
  15. ```
  16. to
  17. ```json
  18. {
  19. "slug": "Fundamental",
  20. "version": "2.0.0",
  21. ...
  22. ```
  23. If you wish, you may keep the minor and revision numbers unchanged, e.g. `2.2.3`.
  24. Download the latest [Rack 2 SDK](https://vcvrack.com/downloads/).
  25. Clean and compile the plugin.
  26. ```bash
  27. export RACK_DIR=/path/to/Rack-SDK
  28. make clean
  29. make dist
  30. ```
  31. If your plugin compiles successfully, you can skip ahead to [Potential runtime bugs](#2-Potential-runtime-bugs).
  32. If not, read on to solve potential errors or warnings.
  33. ### 1.2) `ParamWidget::paramQuantity` has been replaced by `getParamQuantity()`
  34. 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.
  35. ```cpp
  36. ParamQuantity* paramQuantity = getParamQuantity();
  37. ```
  38. ### 1.3) `ParamWidget::reset()` and `ParamWidget::randomize()` has been removed
  39. 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.
  40. ```cpp
  41. getParamQuantity(MY_PARAM)->resetEnabled = false;
  42. getParamQuantity(MY_PARAM)->randomizeEnabled = false;
  43. ```
  44. Alternatively, you can override the following methods in your `Module` class to implement custom behavior for *all* parameters and internal state.
  45. ```cpp
  46. void onReset(const ResetEvent& e) override {
  47. params[MY_PARAM].setValue(0.f);
  48. // ...
  49. // Call super method if you wish to include default behavior
  50. // Module::onReset(e);
  51. }
  52. void onRandomize(const RandomizeEvent& e) override {
  53. params[MY_PARAM].setValue(random::uniform());
  54. // ...
  55. // Call super method if you wish to include default behavior
  56. // Module::onRandomize(e);
  57. }
  58. ```
  59. If your plugin still has build errors, open a thread in the [VCV development forum](https://community.vcvrack.com/c/development/8) or contact [VCV support](https://vcvrack.com/support) to describe your build error.
  60. ## 2) Potential runtime bugs
  61. You might encounter these issues while testing.
  62. ### 2.1) Don't store `Font` and `Image` references across multiple frames
  63. The DAW plugin version of Rack uses a different OpenGL context each time the plugin editor window is closed and reopened.
  64. 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.
  65. Instead, save only its path, and fetch the font/image each frame in `draw()`. Example:
  66. ```cpp
  67. void draw(const DrawArgs& args) override {
  68. std::shared_ptr<Font> font = APP->window->loadFont(fontPath);
  69. if (font) {
  70. nvgFontFaceId(args.vg, font->handle);
  71. ...
  72. }
  73. }
  74. ```
  75. `loadFont()` and `loadImage()` caches the font/image by its path, so this operation can be efficiently called in `draw()` every frame.
  76. ## 3) New optional v2 API features
  77. While not required, these new API features in Rack 2 can enhance the usability of your modules.
  78. ### 3.1) Add Port and Light labels
  79. Add names to your ports and lights which appear in tooltips.
  80. For example, in your `Module` constructor:
  81. ```cpp
  82. configInput(PITCH_INPUT, "1V/oct pitch");
  83. configOutput(SIN_OUTPUT, "Sine");
  84. configLight(PHASE_LIGHT, "Phase");
  85. ```
  86. ### 3.2) Replace `configParam()` with `configButton()` or `configSwitch()` as applicable
  87. For momentary buttons and multi-state switches, displaying a real-valued parameter to the user doesn't make much sense.
  88. Instead, use `configButton()` to hide the text field in its context menu, or `configSwitch()` to offer a list of choices.
  89. ```cpp
  90. configButton(TAP_PARAM);
  91. configSwitch(SYNC_PARAM, 0, 1, 0, "Sync mode", {"Soft", "Hard"});
  92. ```
  93. ### 3.3) Add bypass routes
  94. 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.
  95. 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.
  96. For example, in your `Module` constructor:
  97. ```cpp
  98. configBypass(LEFT_INPUT, LEFT_OUTPUT);
  99. configBypass(RIGHT_INPUT, RIGHT_OUTPUT);
  100. ```
  101. Alternatively, override `Module::processBypass()` to implement custom bypass behavior.
  102. ### 3.4) Store large data in the module's patch storage directory
  103. 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()`.
  104. Example:
  105. ```cpp
  106. void onAdd(const AddEvent& e) override {
  107. std::string path = system::join(createPatchStorageDirectory(), "wavetable.wav");
  108. // Read file...
  109. }
  110. void onSave(const SaveEvent& e) override {
  111. std::string path = system::join(createPatchStorageDirectory(), "wavetable.wav");
  112. // Write file...
  113. }
  114. ```
  115. Note: You cannot call these methods in the `Module` constructor since it is not added to the Engine at that point.
  116. ### 3.5) Draw custom light-like widgets at full brightness
  117. <img src="images/dark-scope.png" class="float-right">
  118. Rack 2 allows users to decrease the brightness of the rack, leaving lights at full brightness.
  119. If your custom widget should emit light, set the global tint to white in its `draw()` method.
  120. ```cpp
  121. void draw(const DrawArgs& args) override {
  122. nvgGlobalTint(args.vg, color::WHITE);
  123. ...
  124. }
  125. ```