I’m finished with the code review of all header files. The only things left to do before the v1 API is considered stable is
Finalize v1-gpl license exception and merge with v1 branch.
Migrate one VCV plugin and one third-party open-source plugin myself (I’ll choose one at random and send a PR) while revising https://vcvrack.com/manual/Migrate1.html.
Added the "brand" property to plugin.json manifests. The brand is used as a prefix to module names, e.g. Fundamental VCF, VCV Parametra, Vult Flux, Befaco Rampage.
Most plugins don’t need this. If the brand is not given or blank, the plugin name is used as the brand name. If you’ve made multiple plugins, you should set the brand of all your plugins to the same string, e.g. Vult. (In Vult’s case, it’s a good idea to add the word “free” to the module names to distinguish between their commercial version.)
This breaks the plugin ABI, but no API change is needed unless you want to add a brand name to your plugin. Another dev build will be available in about a week.
Why is this needed?
Here’s a table of a few plugins and their properties. (In the case of third-party plugins, I’m writing what I think the properties should be.)
Plugin slug
Plugin name
Brand
Author
Fundamental
Fundamental
VCV
VCV
VCV-Parametra
Parametra
VCV
VCV
Befaco
Befaco
Befaco
VCV
VultModulesFree
Vult Modules Free
Vult
Leonardo Laguna Ruiz
Hora-AnalogDrums
Hora Analog Drums
Hora
Hora
Note the diversity of values in the Brand column. In some cases, it’s the author name. In some cases, it’s the plugin name. In others, it’s not exactly equal to either.
But in all cases, the brand should be the most natural prefix when saying the module’s full name.
As some of you know, in order to produce a Linux plugin build that works on all Linux distros supported by Rack, you must compile against glibc 2.23 and libstdc++ 5.4.0, which is what Ubuntu 16.04 uses. This is because the ABIs of the GNU implementations of the C and C++ standard library change every few years.
Previously the only way to correctly do this is to use Ubuntu 16.04 (and thus an old version of GCC) for building plugins, either in a virtual machine, Docker container, or installed on a system.
Thanks to wheybags’ glibc_version_header, you can add the following to your plugin’s Makefile and build on any Linux distro, even bleeding edge Arch with GCC 9 if you want.
FLAGS += -include force_link_glibc_2.23.h
This works by specifying each glibc symbol version explicitly instead of defaulting to the newest versions (which might be beyond 2.23). This header is not included by default because it only works in probably ~75% of cases. Your plugin will still fail to load in Ubuntu 16.04 if you
use certain libstdc++ code. Most of the C++ stdlib is header-only but not all of it. glibc_version_header only fixes glibc symbols, not libstdc++ symbols. Furthermore, libstdc++ links to glibc and will use newer symbols unless you compile libstdc++ yourself and statically link it (which is more complicated than it’s worth).
build dependencies without passing it that flag.
use dlopen, since libdl occassionally changes its ABI but doesn’t offer versioned symbols (at least in my distro’s libdl build).
So if you use glibc_version_header, be sure to give your binary to an Ubuntu 16.04 user to test that it loads. Or test by running
objdump -p plugin.so
and looking at the Version References: section for higher versions of GLIBC and GLIBCXX.
This post is only relevant if you are building your own Linux plugin binaries. None of the above is needed for
Windows, since Mingw-w64 uses Microsoft’s MSVCRT.DLL for its C stdlib and Rack bundles libstdc++6.dll
Mac, since Rack and plugins are built with Apple LLVM’s -mmacosx-version-min=10.7, which is a fantastic feature that tells clang to link to the libc ABI used by Mac 10.7.
Source code git repos submitted to https://github.com/VCVRack/library or private ZIP files emailed to VCV, since VCV’s build service uses an Ubuntu 16.04 Docker container for Linux builds.
(The length and complexity of this post is one of the reasons why Linux is often not targeted by commercial software vendors, such as VST developers.)
Ports now encode their connectedness state in their number of channels. Inputs and outputs are now connected iff (if and only if) channels > 0 and disconnected iff channels == 0.
If you call setChannels(0), it will actually set the number of channels to 1 but will clear all voltages to 0V. In English this means “a cable is still technically connected but is carrying zero voltage as if it’s disconnected.”
If you call setChannels(channels) when the port is disconnected, your request is ignored, and the number of channels will remain at 0. In English, “I try to push N channels out of the port, but since no cable is connected, there are still technically 0 channels coming out of the port.”
Monophonic modules don’t need to change anything. Most polyphonic modules don’t need to change anything. However, consider the following code.
int channels = inputs[AUDIO_INPUT].getChannels();
for (int c = 0; c < channels; c++) {
...
outputs[AUDIO_OUTPUT].setVoltage(v, c);
}
outputs[AUDIO_OUTPUT].setChannels(channels);
If AUDIO_INPUT is disconnected, channels is 0, the for-loop is skipped, and AUDIO_OUTPUT will have 1 channel, which is set to 0V. This is very elegant behavior for such simple code for an FX processor module. But users want their filters to self-resonate when no cable is patched. In this case I would suggest limiting the minimum number of output channels to 1, e.g.
int channels = std::max(1, inputs[AUDIO_INPUT].getChannels());
Summary: If you have a polyphonic module that should produce nonzero CV/audio when your “primary” input (which defines the number of channels) is disconnected, add std::max(1, ...) to your code.
The reasoning is that people will plug poly cables into your mono module “by accident”, and summing is the least surprising, and perhaps even welcoming, result.
I should have mentioned this in the last dev build post, but because the Rack v1 ABI is not yet stable, plugin binaries are only guaranteed to work with the exact version they are compiled against. When releasing a dev build of your plugin, you must specify to users which Rack version it is compatible with.
Reminder: If you compile Rack from source, all plugins must be recompiled whenever you pull and recompile Rack, until the ABI is stabilized. Please do not post issues to GitHub until you have recompiled all Rack plugins against the current source tree or a matching SDK version.
EDIT: I have realized that it is also necessary to delete your <Rack user dir> directory after upgrading if you run these binaries in non-dev mode (without the -d flag). This forces the Fundamental plugin to be re-copied to the <Rack user dir>/plugins directory.
The VCV Plugin Manager is now called VCV Library, and the server is now live for v1. The Library client is now functional in the latest Rack source. Report any bugs with the Library client/server.
Added getVoltageSimd(), getPolyVoltageSimd(), getNormalVoltageSimd(), getNormalPolyVoltageSimd(), and setVoltageSimd() to Port. These make working with SIMD easier for polyphonic modules. Example:
for (int c = 0; c < channels; c += 4) {
float_4 in = inputs[IN_INPUT].getVoltageSimd<float_4>(c);
float_4 cv = inputs[CV_INPUT].getPolyVoltageSimd<float_4>(c);
float_4 out = in * simd::fmax(0.f, cv) / 10.f;
outputs[OUT_OUTPUT].setVoltageSimd(out, c);
}
If you have open-source plugins, please notify the Library team after your plugin is migrated to the v1 API if you have not already done so. Builds using this SDK will be made for you.