From: Richard W.E. Furse [richard@muse.demon.co.uk] Sent: 28 February 2000 15:04 To: 'Paul Barton-Davis'; 'linux-audio-dev@ginette.musique.umontreal.ca'; 'David O'Toole' Subject: A Plugin API 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___". For example, my second oscillator class might be UG_RichardFurse_Oscillator_2. */ typedef void LADSPA_UGDescriptorAccessor(struct LADSPA_UGDescriptor * psDescriptor); /*****************************************************************************/ #endif /* EOF */