Next Previous Contents

3. Writing LADSPA Plugins

3.1 Initial Organization

The first thing you must do is figure out the specifics of how you want your plugins to be organized.

3.2 Writing the plugin handle structure

It is usually easiest to write a struct corresponding to the handle of a plugin descriptor.

The structure may however you want; however, it is necessary to store somewhere the various buffer pointers the host has connected to us (via connect_port).

So here is a typically example, from the ladspa.tgz package, filter.c:


typedef struct {

  LADSPA_Data m_fSampleRate;

  /* filter-specific data deleted. */

  /* Ports:
     ------ */

  LADSPA_Data * m_pfCutoff;
  LADSPA_Data * m_pfInput;
  LADSPA_Data * m_pfOutput;

} SimpleFilter;

3.3 Writing ladspa_descriptor

Once you have decided this, you must write the ladspa_descriptor function. It takes an integer for the plugin index, and returns the LADSPA_Descriptor. It's exact implementation is up to the plugin, but here is a fairly generic code outline:


static LADSPA_Descriptor* the_descriptor = NULL;

LADSPA_Descriptor* ladspa_descriptor(unsigned long index)
{
  if (index != 0)
    return NULL;
  if (the_descriptor != NULL)
    return the_descriptor;
  the_descriptor = (LADSPA_Descriptor*) calloc(sizeof(LADSPA_Descriptor), 1);
  if (the_descriptor == NULL)
    return NULL;

  ... You must fill in the various fields of the_descriptor ...

  return the_descriptor;
}

void _fini()
{
  if (the_descriptor != NULL)
    {
      free(the_descriptor);
      the_descriptor = NULL;
    }
}

3.4 Writing the virtual functions.

The data fields are fairly easy to fill. We describe the function pointers in more detail.

Writing instantiate and activate

These are the functions which allocate and clear the plugin handle.

instantiate

This must call malloc to allocate space for a plugin handle.

activate

This should clear the state of the plugin. For example, it is important that calling deactivate, then activate should clear all the buffers.

A trivial connect_port implementation

Here is the simple filter connect_port implemenation:


void
connectPortToSimpleFilter(LADSPA_Handle Instance,
                          unsigned long Port,
                          LADSPA_Data * DataLocation) {

  SimpleFilter * psFilter;

  psFilter = (SimpleFilter *)Instance;

  switch (Port) {
  case SF_CUTOFF:
    psFilter->m_pfCutoff = DataLocation;
    break;
  case SF_INPUT:
    psFilter->m_pfInput = DataLocation;
    break;
  case SF_OUTPUT:
    psFilter->m_pfOutput = DataLocation;
    break;
  }
}

Notes on the run function.

The run (and its run_adding cousin) functions both take unsigned long SampleCount as an argument, which is both the number of audio samples available as input to each input-audio data port, and the number of samples to produce for each output-audio data port.

Of course, control port always have just a single LADSPA_Data as input or output.

By default run must operate if the input and output buffers are at the same address. However, if your plugin cannot conveniently do that, just set LADSPA_PROPERTY_INPLACE_BROKEN in the Properties field.

In many cases, you are going to add the plugins output to another buffer after regaining (ie you are mixing). If the descriptor defines run_adding you may use that for some possible speed benefit. If the descriptor's run_adding function is NULL you must be able to use the run function instead.

Cleaning up

You may wish to implement the deactivate function to undo any memory allocation done in activate but it is often unnecessary, and is usually left NULL.

But you must implement cleanup to deallocate the plugin handle and the buffers it uses.

If you have data associated with a plugin type (for example a large lookup table), you will probably need to free it with the _fini function, which is called for the plugin library just before it is unloaded.

3.5 Building LADSPA plugins

The process of building plugins is extremely nonportable, but we give commands to compile a LADSPA plugin using the GNU tools.

If your plugin is a simple C plugin, try using commands like:


    gcc -fPIC -DPIC -Wall -O2 -c -o my_plugin.o my_plugin.c
    ld -shared -o my_plugin.so my_plugin.o

If your plugin uses an external library, you must add that to the ld line:


    ld -shared -o my_plugin.so my_plugin.o -lmy_library

The plugin will not load unless the library has been found.

If your plugin uses C++, it is far easier to use g++ to find the various library that are needed:


    g++ -fPIC -DPIC -shared -nostartfiles -o my_plugin.so my_plugin.cc

Otherwise non-C++ hosts won't have the standard c++ libraries automatically loaded for them.

If you use external libraries, add them on with -l, as above.

More on external libraries

Shared libraries are searched for according to the environment variable LD_LIBRARY_PATH, then they use the system's shared library table, which is generated by ldconfig.

In some rare cases, you may wish to support the loading of multiple versions of the library which define different functions and interfaces.

You have basically two choices:


Next Previous Contents