kobodl/sound/a_plugin.h
Ville Lindholm dbc223eb84
Initial commit
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>
2026-05-28 16:35:31 +03:00

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_*/