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):

PathDescription
/binstarter scripts which load and run the application package
/docuser and development documentation and generator scripts
/includepublic include files for writing plugins
/libzzub library binaries, zzub plugin binaries and other dependencies
/share/aldrinaldrin python package, this is the gui only
/share/applicationsdesktop related information for gnome and kde
/share/iconsapplication icons in various resolutions
/share/zzubfiles required by libzzub, such as an alias list and a blacklist for plugins
/srcC/C++ source code
/src/libzzublibzzub source code
/src/pluginsroot folder for all zzub plugins
/toolsscripts 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
        &parameter_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.