229 lines
4.5 KiB
C
229 lines
4.5 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <lv2.h>
|
|
|
|
#define CHANNELS 2
|
|
#define TAPS 6
|
|
#define BUFFER_SECONDS 10
|
|
|
|
struct tap_t {
|
|
float *t_gain[TAPS];
|
|
float *l_gain;
|
|
float *r_gain;
|
|
float *gain;
|
|
float *delay;
|
|
};
|
|
|
|
static const int CONTROLS_PER_TAP = sizeof (struct tap_t) / sizeof (float *);
|
|
|
|
struct ptap_t
|
|
{
|
|
double sample_rate;
|
|
size_t buffer_max;
|
|
|
|
struct tap_t tap[TAPS];
|
|
struct tap_t l_out;
|
|
struct tap_t r_out;
|
|
|
|
float *buffers[TAPS]; ///< Tap audio buffers
|
|
float *rp[TAPS]; ///< Read pointers
|
|
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 */
|
|
struct ptap_t *ptap = malloc(sizeof *ptap);
|
|
if (ptap == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(ptap, 0, sizeof *ptap);
|
|
|
|
ptap->sample_rate = sample_rate;
|
|
ptap->buffer_max = ptap->sample_rate * BUFFER_SECONDS + 1;
|
|
|
|
int i, j;
|
|
for (i = 0; i < TAPS; i++) {
|
|
ptap->buffers[i] = malloc(ptap->buffer_max * sizeof (float));
|
|
|
|
if (ptap->buffers[i] == NULL) {
|
|
for (j = 0; j < i; j++) {
|
|
free(ptap->buffers[i]);
|
|
}
|
|
free(ptap);
|
|
|
|
printf("Failed to allocate buffers\n");
|
|
return NULL;
|
|
}
|
|
|
|
memset(ptap->buffers[i], 0, ptap->buffer_max * sizeof (float));
|
|
|
|
ptap->rp[i] = ptap->buffers[i];
|
|
ptap->wp[i] = ptap->buffers[i];
|
|
}
|
|
|
|
return (LV2_Handle)ptap;
|
|
}
|
|
|
|
static void ptap_connect_port(LV2_Handle lv2instance, uint32_t port, void *data)
|
|
{
|
|
struct ptap_t *ptap = (struct ptap_t *)lv2instance;
|
|
struct tap_t *tap;
|
|
|
|
/* Audio ports */
|
|
if (port < 4) {
|
|
switch (port) {
|
|
case 0: ptap->in_l = data; break;
|
|
case 1: ptap->in_r = data; break;
|
|
case 2: ptap->out_l = data; break;
|
|
case 3: ptap->out_r = data; break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
port -= 4;
|
|
|
|
int tap_index = port / CONTROLS_PER_TAP;
|
|
|
|
if (tap_index < TAPS) {
|
|
tap = &ptap->tap[tap_index];
|
|
} else if (tap_index - TAPS == 0) {
|
|
tap = &ptap->l_out;
|
|
} else if (tap_index - TAPS == 1) {
|
|
tap = &ptap->r_out;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
int tap_port = port % CONTROLS_PER_TAP;
|
|
|
|
if (tap_port < TAPS) {
|
|
tap->t_gain[tap_port] = data;
|
|
} else if (tap_port - TAPS == 0) {
|
|
tap->l_gain = data;
|
|
} else if (tap_port - TAPS == 1) {
|
|
tap->r_gain = data;
|
|
} else if (tap_port - TAPS == 2) {
|
|
tap->gain = data;
|
|
} else if (tap_port - TAPS == 3) {
|
|
tap->delay = data;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void ptap_run(LV2_Handle lv2instance, uint32_t sample_count)
|
|
{
|
|
struct ptap_t *ptap = (struct ptap_t *)lv2instance;
|
|
const struct tap_t *tap;
|
|
float *wp;
|
|
int i, j;
|
|
|
|
/* Position read pointers behind write pointers */
|
|
for (i = 0; i < TAPS; i++) {
|
|
int delay = *ptap->tap[i].delay * ptap->sample_rate;
|
|
ptap->rp[i] = ptap->wp[i] - delay;
|
|
if (ptap->rp[i] < ptap->buffers[i]) {
|
|
ptap->rp[i] += ptap->buffer_max;
|
|
}
|
|
}
|
|
|
|
float *il = ptap->in_l;
|
|
float *ir = ptap->in_r;
|
|
float *ol = ptap->out_l;
|
|
float *or = ptap->out_r;
|
|
|
|
while (sample_count--) {
|
|
for (i = 0; i < TAPS; i++) {
|
|
wp = ptap->wp[i];
|
|
tap = &ptap->tap[i];
|
|
|
|
*wp = *il * *tap->l_gain;
|
|
*wp += *ir * *tap->r_gain;
|
|
for (j = 0; j < TAPS; j++) {
|
|
*wp += *ptap->rp[j] * *tap->t_gain[j];
|
|
}
|
|
*wp *= *tap->gain;
|
|
}
|
|
|
|
/* Write to left output */
|
|
tap = &ptap->l_out;
|
|
*ol = *il * *tap->l_gain;
|
|
*ol += *ir * *tap->r_gain;
|
|
for (j = 0; j < TAPS; j++) {
|
|
*ol += *ptap->rp[j] * *tap->t_gain[j];
|
|
}
|
|
*ol *= *tap->gain;
|
|
|
|
/* Write to right output */
|
|
tap = &ptap->r_out;
|
|
*or = *il * *tap->l_gain;
|
|
*or += *ir * *tap->r_gain;
|
|
for (j = 0; j < TAPS; j++) {
|
|
*or += *ptap->rp[j] * *tap->t_gain[j];
|
|
}
|
|
*or *= *tap->gain;
|
|
|
|
/* Progress read pointers */
|
|
for (i = 0; i < TAPS; i++) {
|
|
ptap->wp[i]++;
|
|
if (ptap->wp[i] >= ptap->buffers[i] + ptap->buffer_max) {
|
|
ptap->wp[i] = ptap->buffers[i];
|
|
}
|
|
|
|
ptap->rp[i]++;
|
|
if (ptap->rp[i] >= ptap->buffers[i] + ptap->buffer_max) {
|
|
ptap->rp[i] = ptap->buffers[i];
|
|
}
|
|
}
|
|
|
|
il++;
|
|
ir++;
|
|
ol++;
|
|
or++;
|
|
}
|
|
}
|
|
|
|
static void ptap_cleanup(LV2_Handle lv2instance)
|
|
{
|
|
struct ptap_t *ptap = (struct ptap_t *)lv2instance;
|
|
|
|
int i;
|
|
for (i = 0; i < TAPS; i++) {
|
|
free(ptap->buffers[i]);
|
|
}
|
|
|
|
free(ptap);
|
|
}
|
|
|
|
static const LV2_Descriptor s_lv2descriptor =
|
|
{
|
|
.URI = "http://fuzzle.org/~petern/ptap/1",
|
|
.instantiate = &ptap_instantiate,
|
|
.connect_port = &ptap_connect_port,
|
|
.run = &ptap_run,
|
|
.cleanup = &ptap_cleanup,
|
|
};
|
|
|
|
const LV2_Descriptor *lv2_descriptor(uint32_t index)
|
|
{
|
|
if (index == 0) {
|
|
return &s_lv2descriptor;
|
|
}
|
|
|
|
return NULL;
|
|
}
|