Files

96 lines
3.7 KiB
C

#include <ladspa.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
enum { BANDS = 5, INPUT = 0, OUTPUT = 1, PREAMP = 2, FIRST = 3, PORTS = FIRST + BANDS * 3 };
typedef struct { float b0, b1, b2, a1, a2, z1, z2; } Biquad;
typedef struct {
LADSPA_Data *port[PORTS];
float preamp;
Biquad band[BANDS];
unsigned count;
unsigned long rate;
} Filter;
static float bounded(float v, float low, float high, float fallback) {
return isfinite(v) ? fminf(high, fmaxf(low, v)) : fallback;
}
static LADSPA_Handle instantiate(const LADSPA_Descriptor *desc, unsigned long rate) {
(void)desc;
Filter *filter = calloc(1, sizeof(*filter));
if (filter) { filter->rate = rate; filter->preamp = 1; }
return filter;
}
static void connect_port(LADSPA_Handle handle, unsigned long port, LADSPA_Data *value) {
((Filter *)handle)->port[port] = value;
}
static void activate(LADSPA_Handle handle) {
Filter *filter = handle;
filter->preamp = powf(10, bounded(*filter->port[PREAMP], -12, 0, 0) / 20);
filter->count = 0;
for (unsigned i = 0; i < BANDS; ++i) {
float *f = filter->port[FIRST + i * 3];
float *q = filter->port[FIRST + i * 3 + 1];
float *g = filter->port[FIRST + i * 3 + 2];
float gain = bounded(*g, -6, 0, 0);
if (gain > -0.01f) continue;
float omega = 6.28318530718f * bounded(*f, 100, 1600, 300) / filter->rate;
float a = powf(10, gain / 40);
float alpha = sinf(omega) / (2 * bounded(*q, 0.5f, 3, 1));
float a0 = 1 + alpha / a;
float b1 = -2 * cosf(omega) / a0;
filter->band[filter->count++] = (Biquad){
(1 + alpha * a) / a0, b1, (1 - alpha * a) / a0,
b1, (1 - alpha / a) / a0, 0, 0
};
}
}
static void run(LADSPA_Handle handle, unsigned long count) {
Filter *filter = handle;
const float *input = filter->port[INPUT];
float *output = filter->port[OUTPUT];
for (unsigned long i = 0; i < count; ++i) {
float value = input[i] * filter->preamp;
for (unsigned j = 0; j < filter->count; ++j) {
Biquad *b = &filter->band[j];
float next = b->b0 * value + b->z1;
b->z1 = b->b1 * value - b->a1 * next + b->z2;
b->z2 = b->b2 * value - b->a2 * next;
value = next;
}
output[i] = bounded(value, -1, 1, 0);
}
}
#define CONTROL (LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL)
#define RANGE (LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE)
#define BAND_HINTS {RANGE,100,1600}, {RANGE,0.5f,3}, {RANGE,-6,0}
#define BAND_NAMES(n) "Frequency " #n, "Q " #n, "Cut dB " #n
static const LADSPA_PortDescriptor types[PORTS] = {
LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL,
CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL, CONTROL
};
static const LADSPA_PortRangeHint hints[PORTS] = {
{0,0,0}, {0,0,0}, {RANGE,-12,0}, BAND_HINTS, BAND_HINTS, BAND_HINTS, BAND_HINTS, BAND_HINTS
};
static const char *const names[PORTS] = {
"Input", "Output", "Preamp dB", BAND_NAMES(1), BAND_NAMES(2), BAND_NAMES(3), BAND_NAMES(4), BAND_NAMES(5)
};
static const LADSPA_Descriptor descriptor = {
.UniqueID = 61987, .Label = "alice_room_eq", .Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE,
.Name = "Alice Room EQ", .Maker = "Alice Backend", .Copyright = "None",
.PortCount = PORTS, .PortDescriptors = types, .PortNames = names, .PortRangeHints = hints,
.instantiate = instantiate, .connect_port = connect_port, .activate = activate,
.run = run, .cleanup = free
};
const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) {
return index == 0 ? &descriptor : NULL;
}