You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
8.5 KiB
284 lines
8.5 KiB
4 years ago
|
#include <math.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <string.h>
|
||
|
#include "reverbsc.h"
|
||
|
|
||
|
#define REVSC_OK 0
|
||
|
#define REVSC_NOT_OK 1
|
||
|
|
||
|
#define DEFAULT_SRATE 48000.0
|
||
|
#define MIN_SRATE 5000.0
|
||
|
#define MAX_SRATE 1000000.0
|
||
|
#define MAX_PITCHMOD 20.0
|
||
|
#define DELAYPOS_SHIFT 28
|
||
|
#define DELAYPOS_SCALE 0x10000000
|
||
|
#define DELAYPOS_MASK 0x0FFFFFFF
|
||
|
|
||
|
#ifndef M_PI
|
||
|
#define M_PI 3.14159265358979323846 /* pi */
|
||
|
#endif
|
||
|
|
||
|
using namespace daisysp;
|
||
|
|
||
|
/* kReverbParams[n][0] = delay time (in seconds) */
|
||
|
/* kReverbParams[n][1] = random variation in delay time (in seconds) */
|
||
|
/* kReverbParams[n][2] = random variation frequency (in 1/sec) */
|
||
|
/* kReverbParams[n][3] = random seed (0 - 32767) */
|
||
|
|
||
|
static const float kReverbParams[8][4]
|
||
|
= {{(2473.0 / DEFAULT_SRATE), 0.0010, 3.100, 1966.0},
|
||
|
{(2767.0 / DEFAULT_SRATE), 0.0011, 3.500, 29491.0},
|
||
|
{(3217.0 / DEFAULT_SRATE), 0.0017, 1.110, 22937.0},
|
||
|
{(3557.0 / DEFAULT_SRATE), 0.0006, 3.973, 9830.0},
|
||
|
{(3907.0 / DEFAULT_SRATE), 0.0010, 2.341, 20643.0},
|
||
|
{(4127.0 / DEFAULT_SRATE), 0.0011, 1.897, 22937.0},
|
||
|
{(2143.0 / DEFAULT_SRATE), 0.0017, 0.891, 29491.0},
|
||
|
{(1933.0 / DEFAULT_SRATE), 0.0006, 3.221, 14417.0}};
|
||
|
|
||
|
static int DelayLineMaxSamples(float sr, float i_pitch_mod, int n);
|
||
|
//static int InitDelayLine(dsy_reverbsc_dl *lp, int n);
|
||
|
static int DelayLineBytesAlloc(float sr, float i_pitch_mod, int n);
|
||
|
static const float kOutputGain = 0.35;
|
||
|
static const float kJpScale = 0.25;
|
||
|
|
||
|
int ReverbSc::Init(float sr)
|
||
|
{
|
||
|
i_sample_rate_ = sr;
|
||
|
sample_rate_ = sr;
|
||
|
feedback_ = 0.97;
|
||
|
lpfreq_ = 10000;
|
||
|
i_pitch_mod_ = 1;
|
||
|
i_skip_init_ = 0;
|
||
|
damp_fact_ = 1.0;
|
||
|
prv_lpfreq_ = 0.0;
|
||
|
init_done_ = 1;
|
||
|
int i, n_bytes = 0;
|
||
|
n_bytes = 0;
|
||
|
for(i = 0; i < 8; i++)
|
||
|
{
|
||
|
if(n_bytes > DSY_REVERBSC_MAX_SIZE)
|
||
|
return 1;
|
||
|
delay_lines_[i].buf = (aux_) + n_bytes;
|
||
|
InitDelayLine(&delay_lines_[i], i);
|
||
|
n_bytes += DelayLineBytesAlloc(sr, 1, i);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int DelayLineMaxSamples(float sr, float i_pitch_mod, int n)
|
||
|
{
|
||
|
float max_del;
|
||
|
|
||
|
max_del = kReverbParams[n][0];
|
||
|
max_del += (kReverbParams[n][1] * (float)i_pitch_mod * 1.125);
|
||
|
return (int)(max_del * sr + 16.5);
|
||
|
}
|
||
|
|
||
|
static int DelayLineBytesAlloc(float sr, float i_pitch_mod, int n)
|
||
|
{
|
||
|
int n_bytes = 0;
|
||
|
|
||
|
n_bytes += (DelayLineMaxSamples(sr, i_pitch_mod, n) * (int)sizeof(float));
|
||
|
return n_bytes;
|
||
|
}
|
||
|
|
||
|
void ReverbSc::NextRandomLineseg(ReverbScDl *lp, int n)
|
||
|
{
|
||
|
float prv_del, nxt_del, phs_inc_val;
|
||
|
|
||
|
/* update random seed */
|
||
|
if(lp->seed_val < 0)
|
||
|
lp->seed_val += 0x10000;
|
||
|
lp->seed_val = (lp->seed_val * 15625 + 1) & 0xFFFF;
|
||
|
if(lp->seed_val >= 0x8000)
|
||
|
lp->seed_val -= 0x10000;
|
||
|
/* length of next segment in samples */
|
||
|
lp->rand_line_cnt = (int)((sample_rate_ / kReverbParams[n][2]) + 0.5);
|
||
|
prv_del = (float)lp->write_pos;
|
||
|
prv_del -= ((float)lp->read_pos
|
||
|
+ ((float)lp->read_pos_frac / (float)DELAYPOS_SCALE));
|
||
|
while(prv_del < 0.0)
|
||
|
prv_del += lp->buffer_size;
|
||
|
prv_del = prv_del / sample_rate_; /* previous delay time in seconds */
|
||
|
nxt_del = (float)lp->seed_val * kReverbParams[n][1] / 32768.0;
|
||
|
/* next delay time in seconds */
|
||
|
nxt_del = kReverbParams[n][0] + (nxt_del * (float)i_pitch_mod_);
|
||
|
/* calculate phase increment per sample */
|
||
|
phs_inc_val = (prv_del - nxt_del) / (float)lp->rand_line_cnt;
|
||
|
phs_inc_val = phs_inc_val * sample_rate_ + 1.0;
|
||
|
lp->read_pos_frac_inc = (int)(phs_inc_val * DELAYPOS_SCALE + 0.5);
|
||
|
}
|
||
|
|
||
|
int ReverbSc::InitDelayLine(ReverbScDl *lp, int n)
|
||
|
{
|
||
|
float read_pos;
|
||
|
/* int i; */
|
||
|
|
||
|
/* calculate length of delay line */
|
||
|
lp->buffer_size = DelayLineMaxSamples(sample_rate_, 1, n);
|
||
|
lp->dummy = 0;
|
||
|
lp->write_pos = 0;
|
||
|
/* set random seed */
|
||
|
lp->seed_val = (int)(kReverbParams[n][3] + 0.5);
|
||
|
/* set initial delay time */
|
||
|
read_pos = (float)lp->seed_val * kReverbParams[n][1] / 32768;
|
||
|
read_pos = kReverbParams[n][0] + (read_pos * (float)i_pitch_mod_);
|
||
|
read_pos = (float)lp->buffer_size - (read_pos * sample_rate_);
|
||
|
lp->read_pos = (int)read_pos;
|
||
|
read_pos = (read_pos - (float)lp->read_pos) * (float)DELAYPOS_SCALE;
|
||
|
lp->read_pos_frac = (int)(read_pos + 0.5);
|
||
|
/* initialise first random line segment */
|
||
|
NextRandomLineseg(lp, n);
|
||
|
/* clear delay line to zero */
|
||
|
lp->filter_state = 0.0;
|
||
|
for(int i = 0; i < lp->buffer_size; i++)
|
||
|
{
|
||
|
lp->buf[i] = 0;
|
||
|
}
|
||
|
return REVSC_OK;
|
||
|
}
|
||
|
|
||
|
int ReverbSc::Process(const float &in1,
|
||
|
const float &in2,
|
||
|
float * out1,
|
||
|
float * out2)
|
||
|
{
|
||
|
float a_in_l, a_in_r, a_out_l, a_out_r;
|
||
|
float vm1, v0, v1, v2, am1, a0, a1, a2, frac;
|
||
|
ReverbScDl *lp;
|
||
|
int read_pos;
|
||
|
uint32_t n;
|
||
|
int buffer_size; /* Local copy */
|
||
|
float damp_fact = damp_fact_;
|
||
|
|
||
|
//if (init_done_ <= 0) return REVSC_NOT_OK;
|
||
|
if(init_done_ <= 0)
|
||
|
return REVSC_NOT_OK;
|
||
|
|
||
|
/* calculate tone filter coefficient if frequency changed */
|
||
|
if(lpfreq_ != prv_lpfreq_)
|
||
|
{
|
||
|
prv_lpfreq_ = lpfreq_;
|
||
|
damp_fact
|
||
|
= 2.0f - cosf(prv_lpfreq_ * (2.0f * (float)M_PI) / sample_rate_);
|
||
|
damp_fact = damp_fact_
|
||
|
= damp_fact - sqrtf(damp_fact * damp_fact - 1.0f);
|
||
|
}
|
||
|
|
||
|
/* calculate "resultant junction pressure" and mix to input signals */
|
||
|
|
||
|
a_in_l = a_out_l = a_out_r = 0.0;
|
||
|
for(n = 0; n < 8; n++)
|
||
|
{
|
||
|
a_in_l += delay_lines_[n].filter_state;
|
||
|
}
|
||
|
a_in_l *= kJpScale;
|
||
|
a_in_r = a_in_l + in2;
|
||
|
a_in_l = a_in_l + in1;
|
||
|
|
||
|
/* loop through all delay lines */
|
||
|
|
||
|
for(n = 0; n < 8; n++)
|
||
|
{
|
||
|
lp = &delay_lines_[n];
|
||
|
buffer_size = lp->buffer_size;
|
||
|
|
||
|
/* send input signal and feedback to delay line */
|
||
|
|
||
|
lp->buf[lp->write_pos]
|
||
|
= (float)((n & 1 ? a_in_r : a_in_l) - lp->filter_state);
|
||
|
if(++lp->write_pos >= buffer_size)
|
||
|
{
|
||
|
lp->write_pos -= buffer_size;
|
||
|
}
|
||
|
|
||
|
/* read from delay line with cubic interpolation */
|
||
|
|
||
|
if(lp->read_pos_frac >= DELAYPOS_SCALE)
|
||
|
{
|
||
|
lp->read_pos += (lp->read_pos_frac >> DELAYPOS_SHIFT);
|
||
|
lp->read_pos_frac &= DELAYPOS_MASK;
|
||
|
}
|
||
|
if(lp->read_pos >= buffer_size)
|
||
|
lp->read_pos -= buffer_size;
|
||
|
read_pos = lp->read_pos;
|
||
|
frac = (float)lp->read_pos_frac * (1.0 / (float)DELAYPOS_SCALE);
|
||
|
|
||
|
/* calculate interpolation coefficients */
|
||
|
|
||
|
a2 = frac * frac;
|
||
|
a2 -= 1.0;
|
||
|
a2 *= (1.0 / 6.0);
|
||
|
a1 = frac;
|
||
|
a1 += 1.0;
|
||
|
a1 *= 0.5;
|
||
|
am1 = a1 - 1.0;
|
||
|
a0 = 3.0 * a2;
|
||
|
a1 -= a0;
|
||
|
am1 -= a2;
|
||
|
a0 -= frac;
|
||
|
|
||
|
/* read four samples for interpolation */
|
||
|
|
||
|
if(read_pos > 0 && read_pos < (buffer_size - 2))
|
||
|
{
|
||
|
vm1 = (float)(lp->buf[read_pos - 1]);
|
||
|
v0 = (float)(lp->buf[read_pos]);
|
||
|
v1 = (float)(lp->buf[read_pos + 1]);
|
||
|
v2 = (float)(lp->buf[read_pos + 2]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* at buffer wrap-around, need to check index */
|
||
|
|
||
|
if(--read_pos < 0)
|
||
|
read_pos += buffer_size;
|
||
|
vm1 = (float)lp->buf[read_pos];
|
||
|
if(++read_pos >= buffer_size)
|
||
|
read_pos -= buffer_size;
|
||
|
v0 = (float)lp->buf[read_pos];
|
||
|
if(++read_pos >= buffer_size)
|
||
|
read_pos -= buffer_size;
|
||
|
v1 = (float)lp->buf[read_pos];
|
||
|
if(++read_pos >= buffer_size)
|
||
|
read_pos -= buffer_size;
|
||
|
v2 = (float)lp->buf[read_pos];
|
||
|
}
|
||
|
v0 = (am1 * vm1 + a0 * v0 + a1 * v1 + a2 * v2) * frac + v0;
|
||
|
|
||
|
/* update buffer read position */
|
||
|
|
||
|
lp->read_pos_frac += lp->read_pos_frac_inc;
|
||
|
|
||
|
/* apply feedback gain and lowpass filter */
|
||
|
|
||
|
v0 *= (float)feedback_;
|
||
|
v0 = (lp->filter_state - v0) * damp_fact + v0;
|
||
|
lp->filter_state = v0;
|
||
|
|
||
|
/* mix to output */
|
||
|
|
||
|
if(n & 1)
|
||
|
{
|
||
|
a_out_r += v0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
a_out_l += v0;
|
||
|
}
|
||
|
|
||
|
/* start next random line segment if current one has reached endpoint */
|
||
|
|
||
|
if(--(lp->rand_line_cnt) <= 0)
|
||
|
{
|
||
|
NextRandomLineseg(lp, n);
|
||
|
}
|
||
|
}
|
||
|
/* someday, use a_out_r for multimono out */
|
||
|
|
||
|
*out1 = a_out_l * kOutputGain;
|
||
|
*out2 = a_out_r * kOutputGain;
|
||
|
return REVSC_OK;
|
||
|
}
|