How to hack zzub plugins
This manual is outdated. You'd rather want to write dsp scripts instead. Check lunar dsp scripting for more info.
This is a short manual on how to write zzub plugins. It is not complete at all, and will be extended over time. If this is the first time you have ever written a plugin, this guide might currently not be helpful at all. If you are experienced in writing machines for Jeskola Buzz, getting a grip on zzub plugins should be easy.
I suggest that you use sources from SVN to write new plugins, as the plugin API is constantly being changed and improved, and you want to keep track of that. However you can as well start with the last release sources. I do not recommend building your plugins from binary releases, as the build system is usually not included. The build system is important to set up a quick working routine.
First, make sure you have built aldrin and libzzub as described here.
The source tree is structured like this (the binary release follows roughly the same order, with a few directories omitted):
| Path | Description |
| /bin | starter scripts which load and run the application package |
| /doc | user and development documentation and generator scripts |
| /include | public include files for writing plugins |
| /lib | zzub library binaries, zzub plugin binaries and other dependencies |
| /share/aldrin | aldrin python package, this is the gui only |
| /share/applications | desktop related information for gnome and kde |
| /share/icons | application icons in various resolutions |
| /share/zzub | files required by libzzub, such as an alias list and a blacklist for plugins |
| /src | C/C++ source code |
| /src/libzzub | libzzub source code |
| /src/plugins | root folder for all zzub plugins |
| /tools | scripts which help with building and updating wrapper modules |
Set up a new plugin project
Lets say you wanted to create a new effect plugin, "zzub halfamp". All it should do is lowering the input volume by 50% (-6dB).
First, create a new directory for the new plugin sources to reside in, /src/plugins/zzub halfamp. Put a new source file there, main.cpp.
main.cpp should roughly look like this:
// zzub halfamp // Copyright (C) 2006 Your Name (your@email.org) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include <zzub/signature.h> #include <zzub/plugin.h> const zzub::parameter parameter_dummy = // parameter 0 { zzub::parameter_type_byte, // type "Dummy", "Example dummy parameter", // description 1, // minimum value 128, // maximum value 255, // no value zzub::parameter_flag_state, // flag 64 // initial value }; const zzub::attribute attribute_dummy = // attribute 0 { "Dummy Attribute", 0, 1, 0 }; const zzub::parameter *parameters[] = { // global ¶meter_dummy, }; const zzub::attribute *attributes[] = { &attribute_dummy, }; #pragma pack(1) struct gvals // local store for global values { unsigned char dummy; }; struct avals // local store for attribute values { int dummy; }; #pragma pack() struct halfamp : zzub::plugin { halfamp(); virtual ~halfamp(); virtual void process_events() {}; virtual void init(zzub::instream * pi); virtual bool process_stereo(float *pin, float *pout, int numsamples, int mode); virtual void command(int i) {}; virtual void save(zzub::outstream * po) {}; virtual const char * describe_value(int param, int value); // ::zzub::plugin methods virtual void destroy() { delete this; } virtual void stop() {} virtual void attributes_changed() {} virtual void set_track_count(int) {} virtual void mute_track(int) {} virtual bool is_track_muted(int) const { return false; } virtual void midi_note(int, int, int) {} virtual void event(unsigned int) {} virtual const zzub::envelope_info** get_envelope_infos() { return 0; } virtual bool play_wave(int, int, float) { return false; } virtual void stop_wave() {} virtual int get_wave_envelope_play_position(int) { return -1; } virtual const char* describe_param(int) { return 0; } virtual bool set_instrument(const char*) { return false; } virtual void get_sub_menu(int, zzub::outstream*) {} virtual void add_input(const char*) {} virtual void delete_input(const char*) {} virtual void rename_input(const char*, const char*) {} virtual void input(float*, int, float) {} virtual void midi_control_change(int, int, int) {} virtual bool handle_input(int, int, int) { return false; } gvals gval; avals aval; }; halfamp::halfamp() { global_values = &gval; attributes = (int *)&aval; } halfamp::~halfamp() { } void halfamp::init(zzub::instream * pi) { } bool halfamp::process_stereo(float *pin, float *pout, int numsamples, int mode) { if( (mode&zzub::process_mode_write)==0 ) return mode&zzub::process_mode_read?true:false; for (int i = 0; i < numsamples; ++i) { *pout++ = *pin++ * 0.5; *pout++ = *pin++ * 0.5; } return true; } const char * halfamp::describe_value(int param, int value) { static char txt[30]; switch(param) { case 0: // dummy parameter sprintf(txt, "%i", value); break; default: return NULL; } return txt; } zzub::plugin * create_plugin(const zzub::info *) { return new halfamp(); } const zzub::info halfamp_plugin_info = { zzub::plugin_type_effect, // type zzub::version, // zzub version 0, // flags 0, // minimum number of tracks 0, // maximum number of tracks 19, // number of global parameters 0, // number of track parameters parameters, // pointer to parameters (global, then track) 1, // number of attributes attributes, // pointer to attributes "zzub halfamp", // name "HalfAmp", // short name "Your Name And Email Here", // author NULL, NULL, &create_plugin, "@yourdomain.org/yourpluginuri;1", // don't copy this. ZZUB_SIGNATURE, }; const zzub::info *g_infos[] = { &halfamp_plugin_info, 0, }; const zzub::info ** zzub_get_infos() { return g_infos; }
Then, create a new file named SConscript in your plugin directory, which looks roughly like that:
Import('pluginenv', 'build_plugin') build_plugin(pluginenv, 'zzub halfamp', [ 'main.cpp', ])
Now your plugin is ready to be built. Change to the root directory and build the project again:
# scons
Your plugin should now be built. You can start aldrin, and it should show up in the 'Unsorted' submenu of your router context menu.
Troubleshooting
In case you have trouble porting your plugin, here are a few hints to look out for:
- There is a zzubify.py script in the tools directory which can do basic conversion of buzzmachine code.
- zzub plugins mix samples in the range -1..1, where Buzz machines mix in the range -32767 to 32768. If you do any scaling related to these boundaries, your plugin might sound way too loud and distorted in Aldrin, or parameters seem to have no effect.
- zzub plugins mix stereo only. There are no mono buffers. Input and output buffers of the process_stereo method are interleaved stereo buffers.
- If your plugin was using MDK, remove the zzub::plugin_flag_does_input_mixing.
Deploy your plugin
With Aldrin, we want to make sure that all plugin sources are available to the community, so all plugins can be ported to as many platforms as possible. You can most possibly not just deploy your binaries, as they won't work on anyone elses system - only with the zzub binaries you built yourself.
To organize the community work, we have a community plugin tree which contains all plugins written by Aldrin users. If you enter your sources into the plugin tree, your plugin will be included with the next source release, and even be made available on all platforms it runs with. We also make sure that your plugin keeps building, even if we make changes to the interface api - this way, the community will be able to enjoy an exceptionally stable Aldrin.
To get your plugin included in the tree, join us at #aldrin on irc.freenode.net, or send your packed sources (or an url with a description) to <contact at leonard-ritter + com>. Be sure that they contain an appropriate GPL licence header as outlined above.
