[linux-audio-dev] Plugin API: Multiplugins

New Message Reply About this list Date view Thread view Subject view Author view Other groups

Subject: [linux-audio-dev] Plugin API: Multiplugins
From: Stefan Westerfeld (stefan_AT_space.twc.de)
Date: to maalis 02 2000 - 14:34:24 EST


   Hi!

Nice to see some discussion about a light and simple plugin API going on.
However, I'd like to see the ability to put multiple plugins into one
single shared library. The reason is simple: suppose you can implement
plugins without "real programming". For instance by using a scripting
language, using flow graphs (like in aRts) or an opcode driven system
like in CSound.

Then you'd probably like to provide these plugins to hosts without
providing one shared lib per plugin, but rather one generic plugin
that in turn loads the scripts, flow graphs or opcodes for all plugins
that have been implemented that way. Here is the proposal I discussed
with Richard W.E. Furse.

Richard writes:
> >I'd like to see the ability to have multiple plugins per .so - this would
> >be useful for mapping custom aRts flow graphs (and maybe all aRts objects)
> >to plugins with one artsgeneric.so.
> >
> >What needs to be done to do that is
> >
> >- make plugin descriptor function return an arbitary number of descriptors
>
> Fine by me - but as you probably saw, the first API had limited support and
> terminology in this direction. Could you live without this if it would
> cause problems with other systems? It sounds as if we need to debate this
> again on the list. At the moment I'm coming down on the side of
> single-plugin-per-library for simplicity, but I can work with either
> approach.
>
> Another thing that worries me is the absence of any hard labels in this new
> scheme - perhaps we should add a 'UniqueLabel' field to the descriptor?
> This way existing flow graphs (or whatever we all call them) stored in the
> host can be persuaded not to break when a new version of a library changes
> the plugin numbering.

I see no probleme here. What is supposed to happen is: if you open
artsgeneric.so, then you get plugins with different names from the
descriptors. For instance, if there exists a flanger which is implemented
in a file called "flanger.arts" and a reverb which is implemented in a file
called "reverb.arts", LADSPA_Descriptor_Function(0) would return a
LADSPA_Descriptor for ArtsGeneric_SomeFlanger and LADSPA_Descriptor_Function(1)
would return a LADSPA_Descriptor for ArtsGeneric_SomeReverb.

> >- pass the plugin descriptor explicitely to any function that needs it
> > (e.g. get_portname and such)
> >- maybe add a custom void pointer to the plugin descriptor so that a
> > generic instantiation or get_portname function can hook custom plugin
> > data
>
> Oooo. I've come around on this one - it's really rather useful if you want
> to write a large library of plugins and don't want to have to code up
> instantiate functions separately for every plugin type. But only if we're
> using multiple-plugins-per-library. I've actually scrapped get_port_name in
> favour of a simple array of strings - let me know if this breaks things for
> you (as I'm sure others will!).
>
> I'm reluctant because of the extra complexity in the interface
> (particularly the void pointer) - it's an extra place for a host or plugin
> writer to go wrong. I've added some comments to the header file that will
> hopefully make things clearer - I'd be grateful if you'd have a quick look
> through to see if what I've done makes sense.
>
> I hope you're alright with the idea that these changes are still very
> dependent on everyone being happy with the multiple-plugin-per-library
> idea. If this was really a problem I'm sure aRts could use some kind of
> patch config file to bundle up the plugins - MN will be certainly be using
> one whichever plugin route is used.

And here is the latest proposal which he made out of my changes - I'd like
to hear comments on that ;)

   Cu... Stefan

/* ladspa.h

   Copyright 2000 Richard W.E. Furse, Paul Barton-Davis, Stefan Westerfeld
*/

#ifndef LADSPA_INCLUDED
#define LADSPA_INCLUDED

/**********************************************************************/

/* Overview:

   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 `plugin' audio processors in C and link
   them dynamically (`plug') into a range of these packages (`hosts').
   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 `greatest common denominator' in their logical
   behaviour. Having said this, certain limiting decisions are
   implicit, notably the use of a fixed type for all data transfer and
   absence of a parameterised `initialisation' phase. For this version
   of the API the fixed type will be `float.'
   [Paragraph contains correction.]

   Plugins are expected to use the `control rate' thinking implicit in
   the Music N languages (including Csound). Plugins have `ports' that
   are inputs or outputs for audio or control data and the plugin is
   `run' for a `block' corresponding to a short time interval measured
   in samples. Audio data is communicated using arrays of floats,
   allowing a block of audio to be processed by the plugin in a single
   pass. Control data is communicated using single LADSPA_Data
   values. Control data may only change once per call to the `run'
   function. It is not suitable for the transfer of many types of
   data, however it can a significant performance benefit. The plugin
   may assume that all its inputs and outputs are bound before it is
   asked to run.

   This API contains very limited error-handling at this time.
*/

/* Fundamental data type passed around between plugins. Corresponds
   to one sample and one control value.
 */

typedef float LADSPA_Data;

/*********************************************************************/

typedef int LADSPA_Properties;

/* Indicates that the plugin has a real-time dependency and so
   its output should not be cached or subject to serious latency.
 */
#define LADSPA_PROPERTY_REALTIME 0x1

/* Indicates that the plugin may cease to work correctly if the host
   elects to use the same buffer for both output and input. This
   should be avoided as enabling this flag makes it impossible for
   hosts to use the plugin to process a buffer 'in-place.'
   [New, not generally agreed.]
 */
#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2

#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
#define LADSPA_IS_INPLACE_BROKEN(x) ((x) &
LADSPA_PROPERTY_IS_INPLACE_BROKEN)
/* [New, not generally agreed.] */

/***********************************************************************/

/* Unit Generator Ports:

   Plugins have `ports' that are inputs or outputs of audio or
   data. Ports can communicate arrays of LADSPA_Data (for audio-rate
   inputs/outputs) or single LADSPA_Data values (for control-rate
   input/outputs). This information is encapsulated in the
   LADSPA_PortDescriptor type which is assembled by ORing properties
   together.

   Note that a port must be an input or an output but not both and
   that a port must be a control rate or audio rate port but not both.
   [Further clarification. Not generally agreed.]
*/

typedef int LADSPA_PortDescriptor;

#define LADSPA_PORT_INPUT 0x1 /* port is input */
#define LADSPA_PORT_OUTPUT 0x2 /* port is output */
#define LADSPA_PORT_CONTROL 0x4 /* port is control-rate (single
LADSPA_Data) */
#define LADSPA_PORT_AUDIO 0x8 /* port is audio-rate (LADSPA_Data array)
*/

#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)

/**********************************************************************/

/* Plugin Handles:

   This plugin handle indicates access to a particular
   instance of the plugin concerned. It is valid to compare
   this to NULL (0 for C++).
 */

typedef void * LADSPA_Handle;

/**********************************************************************/

/* Descriptor for a Type of Plugin:

   This structure is used to describe a type of plugin. It
   provides a number of functions to examine the type, instantiate it,
   link it to buffers and workspaces and to run it.
 */

struct LADSPA_Descriptor {

  /* This indicates a number of properties of the plugin. */

  LADSPA_Properties Properties;

  /* This member points to the null-terminated name of the plugin
     (e.g. "Sine Oscillator").
   */

  const char * Name;

  /* This indicates the number of ports (input AND output) present on
     the plugin.
   */

  unsigned long PortCount;

  /* This member indicates an array of port descriptor. Valid index
     numbers vary from 0 to PortCount-1.
   */

  LADSPA_PortDescriptor * PortDescriptors;

  /* This member indicates an array of null-terminated strings
     describing ports (e.g. "frequency (Hz)"). Valid index numbers
     vary from 0 to PortCount-1.
     [Changed from function to array of strings. Shout if you mind.]
   */

  const char ** PortName;

  /* This can be used by the plugin developer internally to put custom
     implementation data here for use within an instantiate call. The
     plugin does not need to make use of this facility. This facility
     should be considered private for the plugin to do with as it will
     and the host must not attempt to interpret it in any way. Note that
     this implementation data will be shared between all instances of
     the plugin and generally is not the place to store
     instance-specific data (e.g. the phase of an oscillator). This is
     what LADSPA_Handle entities are for.
     [Added clarification, not generally agreed.]
   */

  void * ImplementationData;

  /* This member is a function pointer that instantiates a plugin.
     A handle is returned, indicating the new plugin. The
     instantiation function accepts a sample rate as a
     parameter. The plugin descriptor from which this instantiate
     function was found must also be passed. Returns NULL if
     instantiation fails.
     [Small fix, description of new parameter, not generally agreed.]
   */

  LADSPA_Handle (*instantiate)(struct LADSPA_Descriptor *descriptor,
                                 unsigned long SampleRate);

  /* This member is a function pointer that connects a port on an
     instantiated plugin to a memory location at which a frame
     of data for the port will be read/written. The data location is
     expected to be an array of LADSPA_Data for audio rate ports or a
     single LADSPA_Data value for control rate ports. Memory issues
     will be managed by the host. The plugin 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 LADSPA_Data
     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.

     Plugins should be aware that the host may elect to use the same
     buffer for more than one port and even use the same buffer for
     both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
     However, overlapped buffers may result in unexpected behaviour.
     [New, not generally agreed (and slightly edited).]
  */

   void (*connect_port)(LADSPA_Handle Instance,
                          unsigned long Port,
                        LADSPA_Data * DataLocation);

  /* 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 (*run)(LADSPA_Handle Instance,
              unsigned long sampleCount)

  /* Once an instance of a plugin has been finished with it
     can be deleted using the following function. The handle passed
     ceases to be valid after this call.
   */

  void (*cleanup)(LADSPA_Handle instance);

  /* Reserved area for extensions. Must be initialised to zero. */

  char reserved[1000];

};

/**********************************************************************/

/* Loading A Plugin:

   The exact mechanism by which a plugin is loaded is host-dependent,
   however all most hosts will need to know is the name of the
   shared object file containing the plugin. Each shared object file
   will contain at least one plugin.
   [The change from one plugin/one library is not generally agreed.]

*/

/**********************************************************************/

/* Writing a Plugin:

   A plugin programmer should include a function called
   "descriptor" with the following function prototype. This function
   should reside in the shared object corresponding to the plugin.

   A host will find the plugin shared object file by one means or
   another, and then lookup the "descriptor" function, call it,
   and proceed from there.

   A plugin developer may put any amount of plugins in one file - this
   is not recommended for conventional standalone plugins, but may be
   helpful to provide for instance all plugins implemented in a specific
   extension language or other non-C means of implementation.

   Plugin descriptors start at zero and plugin descriptors beyond the
   number of implemented plugins are supposed to return zero.

   [The change from one plugin/one library is not generally agreed.]
*/

typedef struct LADSPA_Descriptor * (*LADSPA_Descriptor_Function)(int
number);

/**********************************************************************/

#endif /* LADSPA_INCLUDED */

/* EOF */

-- 
  -* Stefan Westerfeld, stefan_AT_space.twc.de (PGP!), Hamburg/Germany
     KDE Developer, project infos at http://space.twc.de/~stefan/kde *-


New Message Reply About this list Date view Thread view Subject view Author view Other groups

This archive was generated by hypermail 2b28 : pe maalis 10 2000 - 07:23:28 EST