/* * drive.c * * example implementation of a LADSPA 2 plugin, with special consideration * to documenting how the LADSPA architecture works. * * the plugin implemented is a simple second-order saturating waveshaper * also offering a linear amplification control. * * for the sake of simplicity, antialiasing or parameter smoothing * refinements have been omitted. * * compile with: * gcc -Wall -nostartfiles -shared -lm -o drive.so drive.c * * (c) 2004 Tim Goetze, all rights reversed: this code is in the public * domain. */ #include #include #include "ladspa.h" /* this structure holds all the data the plugin needs to be operational. * out plugin exhibits 4 ports: * - audio: 1 in and 1 out, * - control: 1 parameter 'drive' in and 1 'gain' in. */ typedef struct { LADSPA_Data * ports[4]; LADSPA_Data adding_gain; } Drive; /* the 'instantiate' method creates an instance of the plugin and initializes * it to a sane state. in this simple case of memory-less distortion, no * initialization other than allocating a Drive structure is needed. * * plugins that need to do more initialization work before being able to * process should do so in the 'activate' method, but it's not a strict * requirement. */ static LADSPA_Handle drive_instantiate ( const struct _LADSPA_Descriptor * Descriptor, unsigned long SampleRate) { Drive * d = (Drive *) calloc (1, sizeof (Drive)); return (LADSPA_Handle *) d; } /* the 'connect_port' method is called by the host to tell the plugin where * to fetch port data from, or where to put results in the outbound port case. * * our simple plugin only stores the pointer the host passes to the method * for later use in the processing methods. */ static void drive_connect_port ( LADSPA_Handle Instance, unsigned long Port, LADSPA_Data * DataLocation) { Drive * d = (Drive *) Instance; d->ports[Port] = DataLocation; } /* the 'run' method is called to execute the plugin for a block of sample * data. this call is preceded by 'connect_port' for all ports defined by * the plugin, and a call to the 'activate' method of the plugin if it * provides one. * * a plugin should keep the number of system calls made from the 'run' * methods to an absolute minimum. in particular memory allocation functions * like 'malloc' and 'free' imply non-realtime use only because of their * potentially blocking nature. * * of course, you can decide to write a non-realtime plugin, and use whatever * resources you want in run(). in that case, the plugin is required not to set the * the flag 'LADSPA_PROPERTY_HARD_RT' (you'll find it further down in the * descriptor structure for the plugin). but non-realtime is only half the fun * at most. :) */ static void drive_run ( LADSPA_Handle Instance, unsigned long SampleCount) { Drive * d = (Drive *) Instance; LADSPA_Data * src = d->ports[0]; LADSPA_Data drive = 0.5f * *(d->ports[1]); LADSPA_Data gain = pow (10, 0.05 * *(d->ports[2])); LADSPA_Data * dest = d->ports[3]; LADSPA_Data x; unsigned long i; /* correction for attenuation through the drive algorithm */ gain *= 1.0f / (1.0f - drive); for (i = 0; i < SampleCount; ++i) { x = src[i]; dest[i] = gain * (x - drive * fabsf (x) * x); } } /* the 'run_adding' method is expected to provide the exact same functionality * as the 'run' method, with one exception: the destination sample buffers * (outbound audio port data locations) contain sample data that the plugin * should mix its output with. 'adding_gain', set by the host via a separate * method, tells the plugin by what value to scale its output before adding * it to the destination sample buffers. */ static void drive_run_adding ( LADSPA_Handle Instance, unsigned long SampleCount) { Drive * d = (Drive *) Instance; LADSPA_Data * src = d->ports[0]; LADSPA_Data drive = 0.5f * *(d->ports[1]); LADSPA_Data gain = pow (10, 0.05 * *(d->ports[2])); LADSPA_Data * dest = d->ports[3]; LADSPA_Data x; unsigned long i; /* correction for attenuation through the drive algorithm, note * the use of 'adding_gain' instead of 1 as in drive_run() */ gain *= d->adding_gain / (1.0f - drive); for (i = 0; i < SampleCount; ++i) { x = src[i]; /* note the '+=' instead of '=' as in drive_run() */ dest[i] += gain * (x - drive * fabsf (x) * x); } } /* this method is used to communicate the scaling ratio to be used by the * 'run_adding' method. */ static void drive_set_run_adding_gain ( LADSPA_Handle Instance, LADSPA_Data adding_gain) { Drive * d = (Drive *) Instance; d->adding_gain = adding_gain; } /* at the end of the plugin instance lifecycle, the 'cleanup' method is * called to free all memory and other resources used by the plugin. */ static void drive_cleanup ( LADSPA_Handle Instance) { free ((void *) Instance); } /* LADSPA 1.1 port description structures ************************************/ /* for each port: the port name. * * we'll refer to these in the following documentation of port properties. */ static const char * const port_names [] = { "input", "drive", "gain", "output" }; /* this array describes the most basic port features: whether a port is an * input or output, and whether it points to a sample buffer (audio) or to * a single value (control). */ static int port_descriptors [] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, /* "input" */ LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, /* "drive" */ LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, /* "gain" */ LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO /* "output" */ }; /* the member structures of this array tell the host more about port * properties. the nature of the parameter controlled through the port is * expressed via 'hint' bits: a toggling switch would be indicated by * LADSPA_HINT_TOGGLED, or an n-value option by LADSPA_HINT_INTEGER etc. * in addition, upper and lower bounds for a parameter value will be * documented here. * * LADSPA 1.1 default port values are expressed here, too. */ static const LADSPA_PortRangeHint port_range_hints [] = { /* "input" */ {0, -1, 1}, /* "drive" */ { /* hints */ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_LOW, /* lower, upper bound */ 0, 1 }, /* "gain" */ { /* hints */ LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_0, /* lower, upper bound */ -36, 36 }, /* "output" */ {0, -1, 1} }; /* BEGIN VERSION 2 extensions ************************************************/ /* our example plugin chooses to document the meaning of the minimum and * maximum values (0, 1) the "drive" port supports. */ static const LADSPA_PortValueEnum drive_port_enum [] = { {"clean", 0}, {"full", 1}, { } /* end of the list */ }; /* additional useful information about each port on the plugin is communicated * to the host via this array. the members are: * * - the default port value. * - a string indicating the unit of the port parameter, or NULL if the * parameter is dimension-less. * - an optional pointer to an array like the one a few lines above. */ static const LADSPA_PortInfo port_info [] = { {0.0, NULL, NULL}, /* default 0, no unit, no value enum */ {0.1, NULL, drive_port_enum}, /* default 0.1, no unit, drive markers */ {0.0, "dB", NULL}, /* default 0, deciBels, no value enum */ {0.0, NULL, NULL}, /* default 0, no unit, no value enum */ }; /* END VERSION 2 extensions **************************************************/ /* the LADSPA_Descriptor structure puts together everything we have defined * so far. */ static LADSPA_Descriptor drive_descriptor = { /* this is *not* a valid UniqueID for a distribution-ready plugin. * it's ok for testing purposes however. */ .UniqueID = 1, .Label = "Drive", /* VERSION 2: adds LADSPA_PROPERTY_HAVE_VERSION */ .Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE | LADSPA_PROPERTY_HAVE_VERSION, .Name = "2nd order saturating waveshaper", .Maker = "Your Name Here ", .Copyright = "Public Domain, 2004", .PortCount = 4, .PortDescriptors = port_descriptors, .PortNames = port_names, .PortRangeHints = port_range_hints, .ImplementationData = 0, .instantiate = drive_instantiate, .connect_port = drive_connect_port, .activate = 0, .run = drive_run, .run_adding = drive_run_adding, .set_run_adding_gain = drive_set_run_adding_gain, .deactivate = 0, .cleanup = drive_cleanup, /* BEGIN VERSION 2 extensions ****************************************/ .Version = {2, 0}, .Latency = 0.0f, .PortInfo = port_info, /* END VERSION 2 extensions ******************************************/ }; /*****************************************************************************/ /* this function is how the host gets to the plugin. you'll notice it is * the only symbol not declared 'static', because it's the only thing a * host needs to 'see' when accessing the plugin. */ const LADSPA_Descriptor * ladspa_descriptor (unsigned long index) { /* this collection has only one plugin, so any index but 0 returns * NULL. */ if (index != 0) return NULL; return &drive_descriptor; }