Developing Generator Plugins
If you are just getting started, A "Small" Guide on Making An Effect for Buzz is a good place to learn about compiler settings and the generic structure of a Buzz MachineInterface instance.
In general, a synthesizer consists of the following parts:
- a set of voices
- a set of parameters
- a voice allocator
- a voice mixer
- a bunch of built in global control signals (LFOs, Envelopes)
- possibly some effects (like global filter/chorus/flanger/reverb built in to the synth)
A single voice generally consists of:
- a soundsource (oscillator, wavetable lookup, physical model)
- a set of voice-state variables (what note it is playing, current phase, physical model properties)
- a set of local control signals (LFOs, Envelopes)
- possibly some local effects (most common is a multimode filter with an envelope attached)
- take care that 1 sample can be active with the same note on multiple voices.. for synths this would have been an awesome time to recycle the voice if retriggered, but for sample material this is probably not a good idea.
Notes on dealing with tracks/voices/polyphony
- polyphony modes (legato/retrigger/independent)
- always have more voices than tracks in the pattern (and take care of external midi notes coming in.. they might need even more tracks)
- when ending a note, do not directly flag the voice as "available" if its still in its release.
- when out of voices, recycle the oldest/softest voice (or skip the new note, although users will think its broken that way)
External input / special
With external inputs -> take care of buffer underruns.. The most succesful external-input-based generators have been linked to a special wave-driver that guarantees new buffers before the machine gets to do its work();
Now how to do all this in Buzz
Buzz expects its plugins to be DLLs put in to the right folders (you are screwed if you put your Generator plugin in the Effects folder). It also expects the DLL to export a set of static data describing the name and parameters of the generator. Finally.. it expects the DLL to export a function that creates a new instance of a local subclass of the MachineInterface.
MachineInterface.h provides a handy DLL_EXPORTS macro to export your class & data to the outside world.
(warning, this list is not complete yet.. these are only the vague memories out of Zephods mind)
- buzz starts
- buzz scans plugin folders
- buzz loads all dlls to grab their data (not sure if it also unloads here? needs a check..)
- user creates a plugin instance
- plugin is loaded (if it was unloaded at all)
- dllmain runs in plugin
- mi::mi() is visited (or the constructor of your name)
- mi::Init() is visited after all parameters are set up and the audio driver works
- mi::Work() is called as long as the audiostream is running. Buzz will want up to 256 new samples generated in to a buffer here. These calls are interleaved by
- mi::Tick() every time the pattern editor moves 1 row down. This is also the place where you should check your local parameter definition for any changes.
- somewhere randomly inbetween, there is also a call to respond to midi messages.
- user destroys plugin or kills buzz
- mi::~mi() is visited
- dllmain runs to detach
Up to recently, all this was happening on 1 core, in a single thread (I think.. not sure about the midi). Recently Buzz moved to multicore, so some care might have to be taken when responding to midi or counting cpu-cycles for benchmarking. (rdtsc is erratic between different cores)