Subject: [linux-audio-dev] A Plugin API
From: Richard W.E. Furse (richard_AT_muse.demon.co.uk)
Date: ma helmi 28 2000 - 10:04:27 EST
Got annoyed about the whole plugin thing and wrote a prototype API this
morning. As suggested in an earlier message, I believe this plugin API
should be a subset rather than a superset of the logical functionality of
systems in use at the moment. This is partly an attempt to provoke
discussion but starting from something I'm pretty sure will work in its own
right.
It's a strongly float-based approach using dynamic libraries which I think
will be highly compatible with most of the C/C++ code/ideas out there
(Quasimodo, Csound, MuCoS, aRts, MN etc) although all programs would of
course need to write a loader and wrapper. And yes, it's really really
simplistic and doesn't cover events or MIDI. The reason for this is that I
don't think we've agreed on a 'right way' to do this or even that a 'right
way' might exist and in the meantime we're stopping inexperienced audio
programmers getting their hands dirty writing conventional plugins.
I include the API below (in header file form with internal documentation).
Apologies for the largish posting (though I think we can cope in this day
and age...).
-- Richard
/* ladspa.h - Copyright 2000 Richard W.E. Furse */
#ifndef LADSPA_INCLUDED
#define LADSPA_INCLUDED
/***********************************************************************
******/
/* Overview:
--------- */
/* The concept of the 'unit generator' was developed by Max Matthews
at Bell Labs in 1960 as part of the Music III software synthesis
package. See "The Technology of Computer Music", M. Matthews,
Cambridge Massachusetts, MIT Press 1969.
There is a large number of synthesis packages in use or development
on the Linux platform at this time. This API ('The Linux Audio
Developer's Simple Plugin API') attempts to give programmers the
ability to write simple unit generators in C and link them
dynamically ('plug') into a range of these packages ('hosts'). Unit
generators will be organised into dynamically loaded libraries
('plugins'). It should be possible for any host and any plugin to
communicate completely through this interface.
This API is deliberately short and simple. To achieve compatibility
with a range of promising Linux sound synthesis packages it
attempts to find the 'lowest common denominator' in their logical
behaviour. Having said this, certain limiting decisions are
implicit, notably the use of floats for all data transfer and
absence of a parameterised 'initialisation' phase.
Plugins are expected to use the 'control rate' thinking implicit in
the Music N languages (including Csound). Unit generators have
'ports' that are inputs or outputs for audio or control data and
the plugin is 'run' for a 'frame' corresponding to a short time
interval measured in samples. Audio data is communicated using
arrays of floats, allowing a frame of audio to be processed by the
unit generator in a single block. Control data is communicated
using simple float values. Control data may only change once per
frame and so is not suitable for the transfer of many types of
data, however this provides a significant performance benefit. The
unit generator may assume that all its inputs and outputs are bound
before it is asked to run a frame.
This API contains very limited error-handling at this time. */
/***********************************************************************
******/
/* Unit Generator Properties:
-------------------------- */
/* Unit generators may have special properties. These are ORed
together. */
typedef int LADSPA_UGProperties;
/* Indicates that the unit generator has a real-time dependency and so
its output not be cached of subject to serious latency. */
#define LADSPA_UG_PROPERTY_REALTIME 1
#define LADSPA_IS_UG_REALTIME(x) ((x) && LADSPA_UG_PROPERTY_REALTIME)
/***********************************************************************
******/
/* Unit Generator Ports:
--------------------- */
/* Unit generators have 'ports' that are inputs or outputs of audio or
data. Ports can communicate arrays of floats (for audio-rate
inputs/outputs) or single floats (for control-rate
input/outputs). This information is encapsulated in the
LADSPA_UGPortDescriptor type which is assembled by ORing properties
together. By default a port is an audio-rate output. */
typedef int LADSPA_UGPortDescriptor;
/* Indicates that the port is an input rather than an output. */
#define LADSPA_UG_PORT_INPUT 1
/* Indicates that the port is a control-rate (single float) port
rather than an audio-rate (float array) port. */
#define LADSPA_UG_PORT_CONTROL 2
#define LADSPA_IS_UG_PORT_INPUT(x) ((x) && LADSPA_UG_PORT_INPUT)
#define LADSPA_IS_UG_PORT_OUTPUT(x) (!((x) && LADSPA_UG_PORT_INPUT))
#define LADSPA_IS_UG_PORT_CONTROL(x) ((x) && LADSPA_UG_PORT_CONTROL)
#define LADSPA_IS_UG_PORT_AUDIO(x) (!((x) && LADSPA_UG_PORT_CONTROL))
/***********************************************************************
******/
/* Unit Generator Handles:
----------------------- */
/* This unit generator handle indicates access to a particular
instance of the unit generator concerned. It is valid to compare
this to NULL. */
typedef void * LADSPA_UGHandle;
/***********************************************************************
******/
/* Descriptor for a Type of Unit Generator:
---------------------------------------- */
/* This structure is used to describe a type of unit generator. It
provides a number of functions to examine the type, instantiate it,
link it to buffers and workspaces and to run it. */
struct LADSPA_UGDescriptor {
/* This indicates a number of properties of the unit generator. */
LADSPA_UGProperties m_iProperties;
/* This member points to the name of the unit generator
(e.g. "Sine Oscillator"). */
const char * m_pcUnitGeneratorName;
/* This indicates the number of ports (input AND output) present on
the unit generator. */
int m_iPortCount;
/* This member indicates an array of port descriptor. Valid indexes
numbers vary from 0 to m_iPortCount-1. */
LADSPA_UGPortDescriptor * m_piPortDescriptors;
/* This member is a function pointer that takes a port number as an
input and returns a short null-terminated string (e.g. "frequency
(Hz)") describing the port. The string returned should be
finished with (but not freed) before calling this method again in
case the memory space is reused by the plugin. */
const char * (*m_pfGetPortName)(const int iPort);
/* This member is a function pointer that instantiates a unit
generator. A handle is returned, indicating the new function. The
instantiation function accepts a (float) sample rate as a
parameter. Returns NULL if instantiation fails. */
LADSPA_UGHandle (*m_pfInstantiate)(const float fSampleRate);
/* This member is a function pointer that connects a port on an
instantiated unit generator to a memory location at which a frame
of data for the port will be read/written. The data location is
expected to be a block of floats for audio rate ports or a single
float for control rate ports. Memory issues will be managed by
the host. The unit generator should read/write the data at these
locations every frame and the data present at the time of this
connection call should not be considered meaningful. Connection
functions may be called more than once for a single instance of a
plugin to allow the host to change the buffers that the unit
generator is reading or writing. They must be called at least
once for each port. When working with blocks of floats, the unit
generator should pay careful attention to the frame size passed
to the run function as the block allocated may only just be large
enough to contain the block of samples. */
void (*m_pfConnectPort)(LADSPA_UGHandle psInstance,
const int iPort,
float * pfDataLocation);
/* This method is a function pointer that runs an instance of a unit
generator for a frame. Two parameters are required: the first is
a handle to the particular instance to be run and the second
indicates the frame size (in samples) for which the unit
generator may run. */
void (*m_pfRun)(LADSPA_UGHandle psInstance,
const int iFrameSize);
/* Once an instance of a unit generator has been finished with it
can be deleted using the following function. The handle passed
ceases to be valid after this call. */
void (*m_pfCleanup)(LADSPA_UGHandle psInstance);
/* Reserved area for extensions. Must be initialised to zero. */
char m_acReserved[1000];
};
/***********************************************************************
******/
/* Loading A Plugin:
----------------- */
/* The exact mechanism by which a plugin is loaded is host-dependent,
however what the host will need to know is:
(1) The library containing the plugin. It should be possible to
load this library using dlopen() and family.
(2) The names of all LADSPA_UGDescriptorAccesor functions
present.
(3) Any additional information required by the host, for instance
an 'opcode' name and spec. */
/***********************************************************************
******/
/* Writing a Plugin:
----------------- */
/* A plugin programmer should write a LADSPA_UGDescriptorAccessor
function for each unit generator present. Functions will be
required to fill in all entries in the LADSPA_UGDescriptor
structure passed so these should be written also. When the plugin
code is complete it should be stored in a dynamic library for use
with dlopen(). With GNU CC this can be achieved using -fPIC.
It is recommended that the following naming convention is used for
accessor functions: "UG_<coder/company>_<UGname>_<version>". For
example, my second oscillator class might be
UG_RichardFurse_Oscillator_2. */
typedef void
LADSPA_UGDescriptorAccessor(struct LADSPA_UGDescriptor * psDescriptor);
/***********************************************************************
******/
#endif
/* EOF */
This archive was generated by hypermail 2b28 : pe maalis 10 2000 - 07:23:27 EST