pplugins/ptap/ptap.cpp

200 lines
4.3 KiB
C++

//#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <math.h>
#include <lv2.h>
#define CHANNELS 2
#define TAPS 6
#define BUFFER_SECONDS 10
struct Tap {
float *t_gain[TAPS + CHANNELS];
float *gain;
float *delay;
};
static const int CONTROLS_PER_TAP = sizeof (Tap) / sizeof (float *);
struct PTap
{
double sample_rate;
size_t buffer_max;
Tap tap[TAPS + CHANNELS];
float *buffers[TAPS]; ///< Tap audio buffers
const float *buffers_end[TAPS]; ///< Audio buffer end points
float *wp[TAPS]; ///< Write pointers
float *in_l;
float *in_r;
float *out_l;
float *out_r;
};
static LV2_Handle ptap_instantiate(
const LV2_Descriptor *descriptor,
double sample_rate,
const char *bundle_path,
const LV2_Feature *const *host_features)
{
/* Allocate local data */
PTap *ptap = new PTap();
ptap->sample_rate = sample_rate;
ptap->buffer_max = ptap->sample_rate * BUFFER_SECONDS + 1;
for (int i = 0; i < TAPS; i++) {
ptap->buffers[i] = new float[ptap->buffer_max]();
if (ptap->buffers[i] == NULL) {
for (int j = 0; j < i; j++) {
delete ptap->buffers[i];
}
delete ptap;
printf("Failed to allocate buffers\n");
return NULL;
}
ptap->buffers_end[i] = ptap->buffers[i] + ptap->buffer_max;
ptap->wp[i] = ptap->buffers[i];
}
return (LV2_Handle)ptap;
}
static void ptap_connect_port(LV2_Handle lv2instance, uint32_t port, void *data)
{
PTap *ptap = (PTap *)lv2instance;
Tap *tap;
float *fdata = (float *)data;
/* Audio ports */
if (port < 4) {
switch (port) {
case 0: ptap->in_l = fdata; break;
case 1: ptap->in_r = fdata; break;
case 2: ptap->out_l = fdata; break;
case 3: ptap->out_r = fdata; break;
}
return;
}
port -= 4;
int tap_index = port / CONTROLS_PER_TAP;
if (tap_index < TAPS + CHANNELS) {
tap = &ptap->tap[tap_index];
} else {
return;
}
int tap_port = port % CONTROLS_PER_TAP;
if (tap_port < TAPS + CHANNELS) {
tap->t_gain[tap_port] = fdata;
} else if (tap_port - TAPS == 2) {
tap->gain = fdata;
} else if (tap_port - TAPS == 3) {
tap->delay = fdata;
} else {
return;
}
}
static void ptap_run(LV2_Handle lv2instance, uint32_t sample_count)
{
PTap *ptap = (PTap *)lv2instance;
const float *readp[TAPS + CHANNELS];
float gain[TAPS + CHANNELS];
bool check_overflow = false;
/* Position read pointers behind write pointers */
for (int i = 0; i < TAPS; i++) {
int delay = *ptap->tap[i].delay * ptap->sample_rate;
/* Zero delay results in processing order dependencies. Tapiir doesn't permit it either... */
if (delay < 1) delay = 1;
readp[i] = ptap->wp[i] - delay;
if (readp[i] < ptap->buffers[i]) {
readp[i] += ptap->buffer_max;
}
if (ptap->wp[i] + sample_count >= ptap->buffers_end[i]) check_overflow = true;
if (readp[i] + sample_count >= ptap->buffers_end[i]) check_overflow = true;
gain[i] = *ptap->tap[i].gain;
}
float *out_l = ptap->out_l;
float *out_r = ptap->out_r;
readp[TAPS ] = ptap->in_l;
readp[TAPS + 1] = ptap->in_r;
gain[TAPS ] = *ptap->tap[TAPS ].gain;
gain[TAPS + 1] = *ptap->tap[TAPS + 1].gain;
while (sample_count--) {
float rp[TAPS + CHANNELS];
float wp[TAPS + CHANNELS];
/* Read input to local buffers */
for (int i = 0; i < TAPS + CHANNELS; i++) rp[i] = *readp[i]++;
/* Process delays */
for (int i = 0; i < TAPS + CHANNELS; i++) {
const Tap *tap = &ptap->tap[i];
float sample = 0;
for (int j = 0; j < TAPS + CHANNELS; j++) sample += rp[j] * *tap->t_gain[j];
wp[i] = sample * gain[i];
}
/* Write outputs */
for (int i = 0; i < TAPS; i++) *ptap->wp[i]++ = wp[i];
*out_l++ = wp[TAPS];
*out_r++ = wp[TAPS + 1];
/* Check read/write pointers */
if (check_overflow) {
for (int i = 0; i < TAPS; i++) {
if (ptap->wp[i] >= ptap->buffers_end[i]) ptap->wp[i] = ptap->buffers[i];
if (readp[i] >= ptap->buffers_end[i]) readp[i] = ptap->buffers[i];
}
}
}
}
static void ptap_cleanup(LV2_Handle lv2instance)
{
PTap *ptap = (PTap *)lv2instance;
for (int i = 0; i < TAPS; i++) {
delete ptap->buffers[i];
}
delete ptap;
}
static const LV2_Descriptor s_lv2descriptor =
{
"urn:fuzzle:ptap",
&ptap_instantiate,
&ptap_connect_port,
NULL,
&ptap_run,
NULL,
&ptap_cleanup,
NULL,
};
const LV2_Descriptor *lv2_descriptor(uint32_t index)
{
if (index == 0) {
return &s_lv2descriptor;
}
return NULL;
}