201 lines
4.9 KiB
C++
201 lines
4.9 KiB
C++
/*
|
|
Copyright 2013 Peter Nelson
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <math.h>
|
|
#include <lv2.h>
|
|
#include "ptap.h"
|
|
|
|
#define BUFFER_SECONDS 10
|
|
|
|
struct PTap
|
|
{
|
|
double sample_rate;
|
|
size_t buffer_max;
|
|
|
|
/* Ports */
|
|
struct {
|
|
float *in[NUM_CHANNELS];
|
|
float *out[NUM_CHANNELS];
|
|
float *matrix[NUM_GAINS][NUM_GAINS];
|
|
float *gain[NUM_GAINS];
|
|
float *delay[NUM_TAPS];
|
|
} ports;
|
|
|
|
float *buffers[NUM_TAPS]; ///< Tap audio buffers
|
|
const float *buffers_end[NUM_TAPS]; ///< Audio buffer end points
|
|
float *wp[NUM_TAPS]; ///< Write pointers
|
|
|
|
PTap(double sample_rate);
|
|
~PTap();
|
|
};
|
|
|
|
PTap::PTap(double sample_rate)
|
|
{
|
|
this->sample_rate = sample_rate;
|
|
this->buffer_max = sample_rate * BUFFER_SECONDS + 1;
|
|
|
|
for (int i = 0; i < NUM_TAPS; i++) {
|
|
this->buffers[i] = new float[this->buffer_max]();
|
|
this->buffers_end[i] = this->buffers[i] + this->buffer_max;
|
|
this->wp[i] = this->buffers[i];
|
|
}
|
|
}
|
|
|
|
PTap::~PTap()
|
|
{
|
|
for (int i = 0; i < NUM_TAPS; i++) {
|
|
delete[] this->buffers[i];
|
|
}
|
|
}
|
|
|
|
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(sample_rate);
|
|
|
|
return (LV2_Handle)ptap;
|
|
}
|
|
|
|
static void ptap_connect_port(LV2_Handle lv2instance, uint32_t port, void *data)
|
|
{
|
|
PTap *ptap = (PTap *)lv2instance;
|
|
float *fdata = (float *)data;
|
|
|
|
/* Audio ports */
|
|
if (port >= TAP_INS && port < TAP_OUTS) {
|
|
port -= TAP_INS;
|
|
ptap->ports.in[port] = fdata;
|
|
return;
|
|
}
|
|
|
|
if (port >= TAP_OUTS && port < TAP_MATRIX) {
|
|
port -= TAP_OUTS;
|
|
ptap->ports.out[port] = fdata;
|
|
return;
|
|
}
|
|
|
|
if (port >= TAP_MATRIX && port < TAP_GAINS) {
|
|
port -= TAP_MATRIX;
|
|
ptap->ports.matrix[port / NUM_GAINS][port % NUM_GAINS] = fdata;
|
|
return;
|
|
}
|
|
|
|
if (port >= TAP_GAINS && port < TAP_DELAYS) {
|
|
port -= TAP_GAINS;
|
|
ptap->ports.gain[port] = fdata;
|
|
return;
|
|
}
|
|
|
|
if (port >= TAP_DELAYS && port < TAP_END) {
|
|
port -= TAP_DELAYS;
|
|
ptap->ports.delay[port] = fdata;
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "Unknown port index %u\n", port);
|
|
}
|
|
|
|
static void ptap_run(LV2_Handle lv2instance, uint32_t sample_count)
|
|
{
|
|
PTap *ptap = (PTap *)lv2instance;
|
|
|
|
const float *readp[NUM_GAINS];
|
|
float gain[NUM_GAINS];
|
|
bool check_overflow = false;
|
|
|
|
/* Position read pointers behind write pointers */
|
|
for (int i = 0; i < NUM_TAPS; i++) {
|
|
int delay = *ptap->ports.delay[i] * 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->ports.gain[i];
|
|
}
|
|
|
|
float *out[NUM_CHANNELS];
|
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
|
out[i] = ptap->ports.out[i];
|
|
readp[NUM_TAPS + i] = ptap->ports.in[i];
|
|
gain[NUM_TAPS + i] = *ptap->ports.gain[NUM_TAPS + i];
|
|
}
|
|
|
|
while (sample_count--) {
|
|
float rp[NUM_GAINS];
|
|
float wp[NUM_GAINS];
|
|
|
|
/* Read input to local buffers */
|
|
for (int i = 0; i < NUM_GAINS; i++) rp[i] = *readp[i]++;
|
|
|
|
/* Process delays */
|
|
for (int i = 0; i < NUM_GAINS; i++) {
|
|
float sample = 0;
|
|
for (int j = 0; j < NUM_GAINS; j++) sample += rp[j] * *ptap->ports.matrix[i][j];
|
|
wp[i] = sample * gain[i];
|
|
}
|
|
|
|
/* Write outputs */
|
|
for (int i = 0; i < NUM_TAPS; i++) *ptap->wp[i]++ = wp[i];
|
|
for (int i = 0; i < NUM_CHANNELS; i++) *out[i]++ = wp[NUM_TAPS + i];
|
|
|
|
/* Check read/write pointers */
|
|
if (check_overflow) {
|
|
for (int i = 0; i < NUM_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;
|
|
delete ptap;
|
|
}
|
|
|
|
static const LV2_Descriptor s_lv2descriptor =
|
|
{
|
|
PTAP_URI,
|
|
&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;
|
|
}
|