[linux-audio-dev] Plugin APIs (again)

Tim Hockin thockin at hockin.org
Mon Dec 2 19:09:00 UTC 2002


Somehow I got unsubbed and didn't realize it.  I've missed a few month of
discussion, and it seems like I've missed a lot, and yet not much has
changed :)

I regret that I can not completely read 4 months of archive, so if I need to
read something more than the skimming I've done, just point me at it, please
:)

It seems that people are still bandying about the idea of extending or
building from LADSPA, in several respects.  A while back, I said I was, too.
I thought it only fair to solicit input from people here.

My goal has always been to implement a music app that tastes somewhat like
FruityLoops.  Something that is easy to get started with, but has much depth
and complexity if you know how to ask for it.  I envisioned it as an
all-in-one music studio application with synths, FX, etc.

LADSPA has a strong base, but I wanted a plugin API that was explicit about
note-control, multiple inputs/outputs and multi-channel audio.  I wanted to
keep it pure C and truly as simple as possible, but no more.  I wanted to
take away some complexity from the host without burdening the plugin too
much.

That said, here is the header file I've been dinking with.  I haven't spent
a whole lot of time on it, just a few weekends thinking and
proof-of-concepting.  I'd really like to get feedback on it.  I've tried to
make it resemble LADSPA, as much as possible.  There are a couple FIXME
notes where I've been undecided or haven't experimented completely yet.

I know there are probably other people who are writing similar apps.  If we
have similar goals, I'd be happy to collaborate instead.

Thanks for the time.  I'm tired of waiting for new versions of Windows music
apps, only to find the features I want STILL not implemented.  Linux needs a
TRULY GOOD music studio project.

Tim



/*
 * Tim Hockin <thockin at hockin.org>
 * Copyright (c) 2001-2002 Tim Hockin
 *
 * This header file defines the API for the Open Audio Plugin Interface
 * (OAPI).
 *
 * Goals:
 * The main goal of this API is to provide a system that is full-featured
 * enough to be the primary plugin system for an audio-development
 * application, while remaining as simple, lightweight, and self-contained
 * as possible.
 *
 * Credits:
 * This API is based largely on LADSPA by Richard W.E. Furse et al.
 *
 * Overview:
 * Plugins are loaded from shared object files.  A shared object file holds
 * one or more plugin descriptors, accessed by index.  Each descriptor holds
 * all the information about a single plugin - it's identification,
 * capabilities, controls, and access methods.
 */
#ifndef OAPI_H__
#define OAPI_H__

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

/*
 * A single audio sample: this datatype is used for passing audio data
 * between plugins and hosts.  Data is normalized between -1.0 and 1.0
 * as the 0dB value, with 0.0 being silence.
 */
typedef float OAPI_sample;

/* forward declarations of the fundamental OAPI types */
typedef struct OAPI_descriptor		OAPI_descriptor;
typedef struct OAPI_control		OAPI_control;
typedef struct OAPI_ctrl_int		OAPI_ctrl_int;
typedef struct OAPI_ctrl_float		OAPI_ctrl_float;
typedef struct OAPI_ctrl_enum		OAPI_ctrl_enum;
typedef struct OAPI_ctrl_string		OAPI_ctrl_string;
typedef struct OAPI_state		OAPI_state;

/* Plugin types: all OAPI plugins are one or more of these. */
#define OAPI_TYPE_SOURCE		0x01
#define OAPI_TYPE_EFFECT		0x02
#define OAPI_TYPE_SINK			0x04

/*
 * A plugin descriptor: this structure describes what an plugin can do.
 * Every plugin has exactly one descriptor, which the host must treat as 
 * READ-ONLY.
 */
struct OAPI_descriptor {
	/*
	 * The label is a file-unique, non-whitespace, string identifier for
	 * the plugin.  Hosts can use the filename and label to identify a
	 * plugin uniquely.
	 */
	const char *oapi_label;

	/*
	 * The type is a bitmask of OAPI_TYPE_* values.  A plugin may
	 * specify more than one type, for example a synth that can be a
	 * vocoder.
	 */
	//FIXME: needed? if note_on: SRC, if ins+outs: FX, if !outs: SINK
	uint_32_t oapi_type;

	/*
	 * The serial is simply a number by which the host can compare two
	 * versions of a plugin and pick the later version.  The actual
	 * value has no meaning to the host.  The only requirement for this
	 * field is that the value never gets smaller in new releases.
	 */
	unsigned oapi_serial;

	/*
	 * The name is an arbitrary string, which hosts can display to
	 * users.
	 */
	const char *oapi_name;

	/*
	 * These are display-friendly fields, which hosts can display to
	 * users.
	 */
	const char *oapi_version;
	const char *oapi_author;
	const char *oapi_copyright;
	const char *oapi_license;
	const char *oapi_url;
	const char *oapi_notes;

	/*
	 * Plugins can have multiple inputs and outputs (in/outs).  These
	 * fields identify the capabilities of the plugin. Negative values
	 * mean no limit, all other values are literal.
	 */
	int oapi_inputs_min;
	int oapi_inputs_max;
	int oapi_outputs_min;
	int oapi_outputs_max;

	/*
	 * Each input or output consists of 1 or more audio channels.  These
	 * values are the limits per in/out.
	 */
	int oapi_channels_min;
	int oapi_channels_max;

	/*
	 * Plugins can have controllable variables, things that would be
	 * knobs, buttons, and switches in hardware.  Controls are presented
	 * as an array of the base control type, with enough information to
	 * be cast to the proper specific type.
	 */
	int oapi_ncontrols;
	OAPI_control **oapi_controls;

	//FIXME: instead of void *, make it return a descriptor, which is a
	//clone of this with a 'priv' field, and status fields ?  eliminates
	//'query' method, which is broken anyway
	
	//FIXME: explicit negative return values == better error reporting

	/*
	 * create: instantiate the plugin
	 *
	 * This method is used to create an instance of the plugin described
	 * by the descriptor.  Memory will be allocated and state will be
	 * initialized in this method.  After this method is called, but
	 * before the plugin's activate() method is called, the host must
	 * call the plugin's set_rate() method.
	 *
	 * Arguments:
	 *  descriptor: a pointer to the plugin descriptor.
	 *
	 * Returns:
	 *  This method returns a pointer to a plugin-private handle.  If
	 *  initialization fails for any reason, this method must return
	 *  NULL.
	 */
	void *(*oapi_create)(OAPI_descriptor *descriptor);

	/*
	 * destroy: destroy an instance
	 *
	 * This method is used to destroy and clean up after an instance of
	 * the plugin.  All allocated resources must be released.  After
	 * this method is invoked, the plugin handle is no longer valid.
	 * This function can not fail.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *
	 * Returns:
	 *  This method does not return a value.
	 */
	void (*oapi_destroy)(void *plugin);

	/*
	 * set_rate: set the sample rate
	 * 
	 * This method must be called between the create() and activate()
	 * methods, or the plugin will not know the host's sample rate.
	 * It may be called again at any time, though changing the sample
	 * rate on an activated plugin may be delayed by the plugin until it
	 * is no longer active.  There is no required set of supported
	 * sample rates, but plugins should support the common sample
	 * rates (44100, 48000, 96000) to be generally useful.  Hosts should
	 * always check that all plugins support the desired sample rate.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *  rate: the desired sample rate.
	 *
	 * Returns:
	 *  This method returns 0 on success and -1 if the sample rate is
	 *  not supported or some error occurred.
	 */
	int (*oapi_set_rate)(void *plugin, int rate);

	/*
	 * activate: prepare a plugin for running
	 *
	 * This method is used to prepare a plugin before being run.  This
	 * is an optional method, and if provided, will be called once
	 * before the first call to the run() method.  This method will not
	 * be called again by the host until the deactivate() method is
	 * called.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *
	 * Returns:
	 *  This method returns 0 on success and -1 on error.
	 */
	int (*oapi_activate)(void *plugin);

	/*
	 * deactivate: stop a plugin after running
	 *
	 * This method is used to shut down a plugin when the host is done
	 * running it.  This method, if provided, will be called at least
	 * once some time after a call to the activate() method and before
	 * the destroy() method.  Hosts may use the deactivate()/activate()
	 * pair as a way to reset the plugin's state.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *
	 * Returns:
	 *  This method returns 0 on success and -1 on error.
	 */
	int (*oapi_deactivate)(void *plugin);

	/*
	 * connect_input,connect_output: connect buffers for data
	 *
	 * Each in/out consists of one or more audio channels.  Hosts must
	 * connect in/outs to buffers in order for a plugin to operate.  The
	 * plugin should use these methods to flag in/outs as active or
	 * inactive and to recognize the desired number of channels (up to
	 * the plugin maximums specified in the descriptor).  Plugins are
	 * expected to handle the case of the input buffer being the same as
	 * the output buffer (in-place operation) transparently to the host.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *  index: the in/out number (zero-based value)
	 *  channel: the audio channel within the in/out (zero-based value)
	 *  buffer: the buffer in which to read/write audio data
	 *
	 * Returns:
	 *  These methods return 0 on success and -1 on error.
	 */
	int (*oapi_connect_input)(void *plugin, int index, int channel,
	    OAPI_sample *buffer);
	int (*oapi_connect_output)(void *plugin, int index, int channel,
	    OAPI_sample *buffer);

	/*
	 * run: use the plugin
	 *
	 * This method invokes the plugin for a number of audio samples,
	 * which are provided on connected in/outs.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance.
	 *  nsamples: the number of samples available on in/out buffers.
	 *
	 * Returns:
	 *  This method returns 0 on success and -1 on error.
	 */
	int (*oapi_run)(void *plugin, int nsamples);

#if 1 //two options for note-control
	/*
	 * voice_on: trigger a note
	 *
	 * This method is provided by source plugins, such as synthesizers
	 * and samplers.
	 *
	 * Arguments:
	 *  plugin: a pointer to the plugin instance
	 *  note: a MIDI compatible note number.  MIDI specifies that note
	 *    number 60 is middle C, and all other notes are relative, with
	 *    half-steps assigned at whole number increments.  Hosts should
	 *    support at least 128 note values (0-127) as designated by
	 *    MIDI, but are free to support more.  Plugins may respond to
	 *    less than the full 128 note range.
	 *
	 * Returns:
	 *  This method returns a non-negative voice-id on success and -1 on
	 *  error.  The voice-id is per-instance and has no meaning to the
	 *  host.  Voice-ids are unique, and if a plugin returns a duplicate
	 *  voice-id, the prior instance of that voice has been stopped.
	 *  Plugins and hosts can both use this to control polyphony.
	 */
	int (*oapi_voice_on)(void *plugin, int note);

	/*
	 * manipulate a playing voice
	 */
	int (*oapi_note_off)(void *plugin, int voice);
	//FIXME: how to indicate that a voice has finished?
#else
	typedef enum OAPI_voice_op		OAPI_voice_op;
	enum OAPI_voice_op {
		OAPI_VOICE_NONE		= 0
		OAPI_VOICE_ON		= 1,
		OAPI_VOICE_OFF		= 2,
		OAPI_VOICE_ISON		= 3,
	};
	int (oapi_voice_ctrl)(void *plugin, OAPI_voice_op, ...);
	//voice_ctrl(plug, OAPI_VOICE_ON, note); /* voice-id or -1 */
	//voice_ctrl(plug, OAPI_VOICE_OFF, voice); /* 0, 1 if already off, -1 */
	//voice_ctrl(plug, OAPI_VOICE_ISON, voice); /* 0 or 1 */
#endif

	/* query the current state */
	//FIXME: need to identify channels per in/out - this is b-r-o-k-e-n
	int (*oapi_query)(void *plugin, OAPI_state *state);
};

/* the various types that a control may be */
typedef enum {
	OAPI_CTRL_INT = 1,
	OAPI_CTRL_FLOAT,
	OAPI_CTRL_ENUM,
	OAPI_CTRL_STRING,
} OAPI_ctrl_type;

/* for declaring ctrl sub-types */
#define OAPI_CONTROL_COMMON_FIELDS \
	/* unique (within this plugin), non-whitespace string identifier */ \
	const char *ctrl_label; \
	/* display-friendly name */ \
	const char *ctrl_name; \
	/* the type of control */ \
	OAPI_ctrl_type ctrl_type; \
	/*
	 * all ctrl types have the following methods
	 * int (*ctrl_set)(void *instance, <type> value);
	 * <type> (*ctrl_get)(void *instance);
	 */

/* a knob, button, etc. of a plugin */
struct OAPI_control {
	OAPI_CONTROL_COMMON_FIELDS
};
/* to help in declaring arrays of pointers to structs statically */
#define OAPI_DECL_CONTROL(type)	(OAPI_control *)&(OAPI_ctrl_ ## type)

/* the expanded structs for each control type */
struct OAPI_ctrl_int {
	OAPI_CONTROL_COMMON_FIELDS
	int ctrl_min;
	int ctrl_max;
	int ctrl_default;
	uint_32_t ctrl_flags;
	int (*ctrl_set)(void *plugin, int value);
	int (*ctrl_get)(void *plugin);
};

struct OAPI_ctrl_float {
	OAPI_CONTROL_COMMON_FIELDS
	float ctrl_min;
	float ctrl_max;
	float ctrl_default;
	uint_32_t ctrl_flags;
	int (*ctrl_set)(void *plugin, float value);
	float (*ctrl_get)(void *plugin);
};

struct OAPI_ctrl_enum {
	OAPI_CONTROL_COMMON_FIELDS
	int ctrl_min;
	int ctrl_max;
	int ctrl_default;
	const char **ctrl_options;
	int (*ctrl_set)(void *plugin, int value);
	int (*ctrl_get)(void *plugin);
};

struct OAPI_ctrl_string {
	OAPI_CONTROL_COMMON_FIELDS
	const char *ctrl_default;
	uint_32_t ctrl_flags;
	int (*ctrl_set)(void *plugin, const char *value);
	char *(*ctrl_get)(void *plugin);
};

/* flags for various control types */
#define OAPI_CTRL_FL_SCALAR	0x01	/* is scalar to the sample rate */
#define OAPI_CTRL_FL_LOG	0x02	/* is logarithmic */
#define OAPI_CTRL_FL_FILE	0x04	/* is a file name */
#define OAPI_CTRL_FL_DIR	0x08	/* is a directory */
#define OAPI_CTRL_FL_BOOL	0x10	/* is a boolean */
#define OAPI_CTRL_FL_FRAMES	0x20	/* is a measurement of frames */
#define OAPI_CTRL_FL_RDONLY	0x40	/* is read-only */

/* how a host queries the state of a plugin */
struct OAPI_state {
	int st_inputs;
	int st_outputs;
	int st_channels;
};

/* how the host learns about the plugin */
OAPI_descriptor *oapi_descriptor(int index);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* OAPI_H__ */



More information about the Linux-audio-dev mailing list