VST2 plugins in Go

Hello there! I would like to announce the thing nobody asked for: now you can write VST2 plugins in go!

What is VST2?

VST2 is the audio plugin interface designed by Steinberg. It’s the most popular audio plugin format. Why did I build a go VST2 library? Well, I really like go and I think it would be cool to enable this capability for my favourite language. But mostly I did it for fun.

Special thanks to @emicklei for asking me if it was possible!

Demo

Ok, how about a (de)gain plugin? It’s almost like a classic example gain plugin, but gain value only scales 0 to 1. Because I was too excited to spend time on proper scale.

Code

// +build plugin

package main

import (
	"pipelined.dev/audio/vst2"
)

func init() {
	vst2.PluginAllocator = func(h vst2.Host) (vst2.Plugin, vst2.Dispatcher) {
		gain := vst2.Parameter{
			Name:  "Gain",
			Unit:  "db",
			Value: 1,
		}
		channels := 2
		return vst2.Plugin{
			InputChannels:  channels,
			OutputChannels: channels,
			Parameters: []*vst2.Parameter{
				&gain,
			},
			ProcessDoubleFunc: func(in, out vst2.DoubleBuffer) {
				for c := 0; c < channels; c++ {
					for i := 0; i < in.Frames; i++ {
						out.Channel(c)[i] = in.Channel(c)[i] * float64(gain.Value)
					}
				}
			},
		}, vst2.Dispatcher{}
	}
}

func main() {}

A couple of important moments here:

Build

VST2 plugins have different formats in the modern operating systems:

To build plugin on Linux/Windows you can just run the following command:

go build --buildmode c-shared --tags plugin

It’ll produce an output file that is ready to use as-is. In theory. I only tested OS X. OOPS.

To build on OS X you need to do a few more things:

  1. Run go build command to build Mach-O dylib:

    go build --buildmode c-archive --tags plugin -o demoplugin.a
    
  2. Compile Mach-O bundle:

    clang -bundle -o demoplugin -all_load demoplugin.a
    
  3. Create a VST bundle. To do that you need to create a specific folder structure and place there Mach-O bundle along with Info.plist file with plugin metadata. Or you can use a script from Rust vst2 library (it’s a nice project, don’t hesitate to star it) that can be found here. There is an issue to add a bundler command to automate this step.

Does it work?

Yes! But not every host detects the plugin. Reaper does that successfully, but not Ableton.

On top of that:

Implementing VST3

VST3 was considered, but it’ll require much more effort to implement go wrapper for it. API surface is much bigger which would require much more dances with CGO. On top of that, when I started this project (4 years ago) VST3 wasn’t even supported by Ableton (it was added in version 10). As of now, I don’t see much value in implementing it.

Was it worth it?

Of course! It was very exciting to see plugin built with Go working in the DAW.

Give it a try. Not all opcodes and callbacks are implemented, but API is there and should be expanding with new dispatch/callback functions. Pull requests are very welcome!