Import existing source tree; original VCS history is no longer available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
387 lines
12 KiB
C
387 lines
12 KiB
C
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
a_plugin.h - Audio Engine Plugin API
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2002, David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#ifndef _A_PLUGIN_H_
|
|
#define _A_PLUGIN_H_
|
|
|
|
#include "a_types.h"
|
|
|
|
/*
|
|
* Plugin States
|
|
* Hosts are allowed to inc/dec the FXC_STATE control
|
|
* by only 1 unit at a time. If you want to "leap", you
|
|
* must work through the the states in between, to allow
|
|
* the plugin to do the required actions for each
|
|
* transition. The toolkit function audio_plugin_state()
|
|
* handles this automatically.
|
|
*
|
|
* This arrangement makes it easier to implement plugins;
|
|
* just check whether the incoming FXC_STATE argument is
|
|
* higher or lower than the current value, to select one
|
|
* of two switch() statements; one with code for entering
|
|
* states, and another for leaving states.
|
|
*
|
|
* What To Do in the case of a transition from...
|
|
* CLOSED to OPEN:
|
|
* Create private instance user data, and set
|
|
* p->user to point at it.
|
|
*
|
|
* OPEN to READY:
|
|
* Allocate larger buffers, and buffers that
|
|
* depend on system parameter settings.
|
|
*
|
|
* READY to PAUSED:
|
|
* Here you're *not* supposed to do anything
|
|
* that couldn't be done in "interrupt context"
|
|
* or similar, as this change may be made from
|
|
* within the actual real time engine core.
|
|
* Preferably, any time consuming buffer
|
|
* clearing and stuff should be done in the
|
|
* OPEN -> READY and PAUSED -> READY
|
|
* transitions instead of here.
|
|
*
|
|
* PAUSED to RUNNING:
|
|
* RUNNING to PAUSED:
|
|
* In most cases, you should do absolutely
|
|
* nothing here. If your plugin cares about the
|
|
* world outside, these transitions can be used
|
|
* to manage the real world/engine time slip
|
|
* when pausing.
|
|
*
|
|
* PAUSED to READY:
|
|
* Now you may kill reverb tails and that sort
|
|
* of things.
|
|
*
|
|
* READY to OPEN:
|
|
* Free any system parameter dependent buffers.
|
|
* Basically, free everything except what you
|
|
* need to store any incoming system parameter
|
|
* values.
|
|
*
|
|
* OPEN to CLOSED:
|
|
* Delete any private instance data, and set
|
|
* p->user to NULL.
|
|
*
|
|
* The SILENT and RESTING states:
|
|
* This is just a special case of the RUNNING
|
|
* state (see below). It's *not* possible to
|
|
* force a plugin to this state - plugins may,
|
|
* but do not have to, switch between the
|
|
* RUNNING and SILENT states by themselves.
|
|
*
|
|
* What To Expect, and How To Act when in the...
|
|
* CLOSED state:
|
|
* Well, basically, you don't exist yet! Expect
|
|
* someone to switch you into the OPEN state,
|
|
* passing a fresh audio_plugin_t with a NULL
|
|
* 'user' field for you to fill in.
|
|
*
|
|
* OPEN state:
|
|
* You must accept calls to control() for setting
|
|
* system parameters, but you should only store
|
|
* the values (or whatever you calculate from
|
|
* them) for later use. Other parameter changes
|
|
* are not allowed.
|
|
*
|
|
* READY state:
|
|
* If system parameters are changed while in the
|
|
* READY state, they are expected to take effect
|
|
* immediately. Preferably, work as quickly as
|
|
* possible, but assume that no one is stupid
|
|
* enough to tell you to reallocate you buffers
|
|
* from within a real time thread.
|
|
*
|
|
* PAUSED state:
|
|
* In this state, there will be no calls to
|
|
* process() or control(). (After a thread has
|
|
* put a plugin in this state, it's safe to
|
|
* operate the plugin from another thread, and
|
|
* then have that thread change the state back
|
|
* to RUNNING when done.)
|
|
*
|
|
* RUNNING state:
|
|
* Same as for the PAUSED state, but of course,
|
|
* frequent calls to process() are to be expected.
|
|
*
|
|
* RUNNING, SILENT and RESTING are the *only*
|
|
* states in which process() may be called.
|
|
*
|
|
* IMPORTANT:
|
|
* Note that process*() may get NULL
|
|
* input buffer pointers, which is a way
|
|
* for the host to say that you have no
|
|
* input.
|
|
*
|
|
* The easiest way to handle that is just grabbing
|
|
* the ever present audio_silent_buffer (provided
|
|
* by the host) and go on.
|
|
*
|
|
* The in-place process() call will never get
|
|
* NULL input buffer pointers, as it has no
|
|
* separate output buffer pointer. This requires
|
|
* a special case: the process() callback must
|
|
* *always* do it's work, thus effectively
|
|
* ignoring the SILENT and RESTING states.
|
|
*
|
|
* However, the idea is that you should consider
|
|
* this as a useful hint! Most DSP algorithms,
|
|
* when given silent input, will eventually
|
|
* produce silent output. When that point is
|
|
* reached, a plugin should spontaneously switch
|
|
* to the SILENT state, to tell everyone that
|
|
* there will be no output for a while, and thus,
|
|
* that there's no point in calling process().
|
|
*
|
|
* SILENT state:
|
|
* Just handle control() calls. process() will
|
|
* be called as usual, but you're not required to
|
|
* do anything with the output buffers. (Or
|
|
* rather, you *shouldn't*, as it would just be
|
|
* a waste of CPU cycles.)
|
|
*
|
|
* As soon as you feel like producing output
|
|
* again, switch back to the RUNNING state and
|
|
* do so.
|
|
*
|
|
* If you know that you will not produce output
|
|
* again, until you get fresh input, switch to
|
|
* the RESTING state instead!
|
|
*
|
|
* Host designers should note that a plugin in
|
|
* the SILENT state *is* actually still running,
|
|
* and also that it's most probably *not*
|
|
* producing valid output! It's also important
|
|
* that output buffer pointers passed to
|
|
* process() are valid at all times, as a SILENT
|
|
* plugin may switch to the RUNNING state at any
|
|
* time, for no obvious reason whatsoever. Input
|
|
* is *not* required.)
|
|
*
|
|
* That is, it will need process() to be called
|
|
* as usual, *even* if only to pass NULL input
|
|
* buffers. This is to allow plugins to keep
|
|
* track of time, and of course, to detect any
|
|
* input that might - instantaneously or after
|
|
* some time - make the plugin switch back to
|
|
* RUNNING mode.
|
|
*
|
|
* RESTING state:
|
|
* Do nothing (except the usual handling of
|
|
* control() calls) until you start getting
|
|
* input buffers again. You'r *not* allowed to
|
|
* switch from this state unless you get
|
|
* non-NULL input buffer pointers.
|
|
*
|
|
* This state exists to tell the host that a
|
|
* plugin is *really* done with any tails and
|
|
* stuff, and that there will be no more output
|
|
* without fresh input buffers. Hosts may use
|
|
* this to figure out when to stop recording
|
|
* and things like that.
|
|
*
|
|
* IMPORTANT: You should *NEVER* return from
|
|
* process() in the SILENT or RESTING
|
|
* states unless the buffer you just
|
|
* generated (or should have generated)
|
|
* actually *is* garbage, or silent!
|
|
*
|
|
* ANY OUTPUT WILL BE IGNORED.
|
|
*
|
|
* General Rules for Hosts:
|
|
* * System Parameter changes must *never* be done from
|
|
* real time context, period.
|
|
*
|
|
* * Plugins should *not* be expected to be thread safe.
|
|
* That is, never use a plugin from more than one thread
|
|
* at a time, without making sure only one callback is
|
|
* running at any time.
|
|
*/
|
|
typedef enum
|
|
{
|
|
FX_STATE_CLOSED = 0,
|
|
FX_STATE_OPEN,
|
|
FX_STATE_READY,
|
|
FX_STATE_PAUSED,
|
|
FX_STATE_RUNNING,
|
|
FX_STATE_SILENT,
|
|
FX_STATE_RESTING
|
|
} audio_fxstates_t;
|
|
|
|
typedef enum
|
|
{
|
|
/*
|
|
* System Parameters.
|
|
* Will be set before the state is changed.
|
|
*/
|
|
FXC_MAX_FRAMES = 0, /* ...per process() call. */
|
|
FXC_SAMPLERATE, /* Buffer sample rate (Hz) */
|
|
FXC_QUALITY, /* Audio quality setting */
|
|
|
|
/* Uniform Parameters */
|
|
FXC_PARAM_1, /* (fixp) Time/f/Amount 1 */
|
|
FXC_PARAM_2, /* (fixp) Time/f/Amount 2 */
|
|
FXC_PARAM_3, /* (fixp) Depth/Level 1 */
|
|
FXC_PARAM_4, /* (fixp) Depth/Level 2 */
|
|
FXC_PARAM_5, /* (fixp) Rate/Level */
|
|
FXC_PARAM_6, /* (int) Mode/Special */
|
|
|
|
FXC_USER, /* First user defined control */
|
|
FXC_COUNT = FXC_USER
|
|
|
|
/* Higher indices are free to use for custom controls. */
|
|
} audio_fxcontrols_t;
|
|
|
|
#ifdef TODO
|
|
typedef enum
|
|
{
|
|
/* Timing and dependencies (flags) */
|
|
FXCAP_TIMING_ = 0x0000000f,
|
|
FXCAP_TIMING_SYSCALLS = 0x00000001, /* Uses malloc() etc... */
|
|
FXCAP_TIMING_SLOW = 0x00000002, /* in relation to process() */
|
|
|
|
/* Data type (enumeration) */
|
|
FXCAP_TYPE_ = 0x000000f0,
|
|
FXCAP_TYPE_STROBE = 0x00000000, /* Event; value ignored */
|
|
FXCAP_TYPE_BOOLEAN = 0x00000010, /* 0 = false, !=0 = true */
|
|
FXCAP_TYPE_INTEGER = 0x00000020,
|
|
FXCAP_TYPE_FIXED_8 = 0x00000030, /* 8 fraction bits */
|
|
FXCAP_TYPE_FIXED_16 = 0x00000040, /* 16 fraction bits */
|
|
FXCAP_TYPE_FIXED_24 = 0x00000050, /* 24 fraction bits */
|
|
FXCAP_TYPE_FLOAT = 0x00000060, /* IEEE 32 bit float */
|
|
|
|
/* Access rules (flags) */
|
|
FXCAP_AXS_ = 0x00000f00,
|
|
FXCAP_AXS_READ = 0x00000100, /* May be read */
|
|
FXCAP_AXS_WRITE = 0x00000200, /* May be written */
|
|
FXCAP_AXS_READ_CALL = 0x00000400, /* Must use read_control()! */
|
|
FXCAP_AXS_WRITE_CALL = 0x00000800, /* Must use control()! */
|
|
|
|
/* Lifetime info - "When can I access this?" (flags)
|
|
* Note that there's no flag for PAUSED, as it's *never* legal
|
|
* to use any callback but state() in the PAUSED state.
|
|
*/
|
|
FXCAP_LIFE_ = 0x0000f000,
|
|
FXCAP_LIFE_OPEN = 0x00001000,
|
|
FXCAP_LIFE_READY = 0x00002000,
|
|
FXCAP_LIFE_RUNNING = 0x00004000
|
|
} audio_fxcaps_t;
|
|
#endif
|
|
|
|
|
|
typedef struct audio_plugin_t
|
|
{
|
|
void *user; /* Custom instance data */
|
|
int current_state; /* DO NOT write in callbacks! */
|
|
|
|
unsigned controls; /* Number of controls */
|
|
int *ctl; /* Array of parameter values */
|
|
#ifdef TODO
|
|
audio_fxcaps_t *control_caps;
|
|
#endif
|
|
/* Plugin state management */
|
|
int (*state)(struct audio_plugin_t *p, audio_fxstates_t new_state);
|
|
|
|
/* Parameter Control */
|
|
void (*control)(struct audio_plugin_t *p, unsigned ctl, int arg);
|
|
|
|
/* Running the plugin */
|
|
void (*process)(struct audio_plugin_t *p,
|
|
int *buf, unsigned frames);
|
|
void (*process_r)(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames);
|
|
void (*process_m)(struct audio_plugin_t *p,
|
|
int *in, int *out, unsigned frames);
|
|
} audio_plugin_t;
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Handy wrappers & tools
|
|
----------------------------------------------------------*/
|
|
|
|
/*
|
|
* Bring a plugin to the specified state.
|
|
*
|
|
* If anything goes wrong, this call stops and returns
|
|
* the error code from the plugin's state() callback.
|
|
*/
|
|
int audio_plugin_state(audio_plugin_t *p, audio_fxstates_t new_state);
|
|
|
|
/*
|
|
* Fill in missing callbacks with standard wrappers where
|
|
* possible.
|
|
*
|
|
* IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!
|
|
* Note that these wrappers are *not* thread safe
|
|
* as of this version! Some of them use a single
|
|
* global mixing buffer when emulating missing
|
|
* process callback variants. (The alternative
|
|
* would be to use one buffer per plugin instance,
|
|
* which would result in more cache thrashing.)
|
|
*
|
|
* Returns a negative error code if it isn't possible to
|
|
* turn the passed struct into a valid plugin.
|
|
*/
|
|
int audio_plugin_init(audio_plugin_t *p);
|
|
|
|
/*
|
|
* Initialize a plugin (audio_plugin_init()), bring it
|
|
* plugin into the OPEN state and set up the system
|
|
* parameters.
|
|
*
|
|
* Returns any error code from the plugin.
|
|
*/
|
|
int audio_plugin_open(audio_plugin_t *p, unsigned max_frames,
|
|
int fs, int quality);
|
|
|
|
/*
|
|
* Bring a plugin to the CLOSED state.
|
|
*/
|
|
void audio_plugin_close(audio_plugin_t *p);
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Tools for plugins (NOT for hosts!)
|
|
----------------------------------------------------------*/
|
|
|
|
/*
|
|
* Set number of controls and allocate a ctl array with
|
|
* sufficient space for them.
|
|
*
|
|
* Note that freeing the array is done by the host. (Using
|
|
* audio_plugin_close() or audio_plugin_state() to close a
|
|
* plugin does that automatically, so only hosts that
|
|
* implement state changing "to the metal" have to worry
|
|
* about this.)
|
|
*
|
|
* Returns the address of the allocated ctl array.
|
|
*/
|
|
int *audio_plugin_alloc_ctls(audio_plugin_t *p, unsigned count);
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
Host Provided Services
|
|
----------------------------------------------------------*/
|
|
|
|
/* The Silent Buffer */
|
|
extern int *audio_silent_buffer;
|
|
|
|
#endif /*_A_PLUGIN_H_*/
|