96 lines
3.7 KiB
C
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;
|
|
}
|