Created
October 5, 2019 16:06
-
-
Save rxi/e5488c6660154329ddfc4a7a7d2997f8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "freeverb.h" | |
#define undenormalize(n) { if (xabs(n) < 1e-37) { (n) = 0; } } | |
static inline float xabs(float n) { | |
return n < 0 ? -n : n; | |
} | |
static inline void zeroset(void *buf, int n) { | |
while (n--) { ((char*) buf)[n] = 0; } | |
} | |
static inline float allpass_process(fv_Allpass *ap, float input) { | |
float bufout = ap->buf[ap->bufidx]; | |
undenormalize(bufout); | |
float output = -input + bufout; | |
ap->buf[ap->bufidx] = input + bufout * ap->feedback; | |
if (++ap->bufidx >= ap->bufsize) { | |
ap->bufidx = 0; | |
} | |
return output; | |
} | |
static inline float comb_process(fv_Comb *cmb, float input) { | |
float output = cmb->buf[cmb->bufidx]; | |
undenormalize(output); | |
cmb->filterstore = output * cmb->damp2 + cmb->filterstore * cmb->damp1; | |
undenormalize(cmb->filterstore); | |
cmb->buf[cmb->bufidx] = input + cmb->filterstore * cmb->feedback; | |
if (++cmb->bufidx >= cmb->bufsize) { | |
cmb->bufidx = 0; | |
} | |
return output; | |
} | |
static inline void comb_set_damp(fv_Comb *cmb, float n) { | |
cmb->damp1 = n; | |
cmb->damp2 = 1.0 - n; | |
} | |
void fv_init(fv_Context *ctx) { | |
zeroset(ctx, sizeof(*ctx)); | |
for (int i = 0; i < FV_NUMALLPASSES; i++) { | |
ctx->allpassl[i].feedback = 0.5; | |
ctx->allpassr[i].feedback = 0.5; | |
} | |
fv_set_samplerate(ctx, FV_INITIALSR); | |
fv_set_wet(ctx, FV_INITIALWET); | |
fv_set_roomsize(ctx, FV_INITIALROOM); | |
fv_set_dry(ctx, FV_INITIALDRY); | |
fv_set_damp(ctx, FV_INITIALDAMP); | |
fv_set_width(ctx, FV_INITIALWIDTH); | |
} | |
void fv_mute(fv_Context *ctx) { | |
for (int i = 0; i < FV_NUMCOMBS; i++) { | |
zeroset(ctx->combl[i].buf, sizeof(ctx->combl[i].buf)); | |
zeroset(ctx->combr[i].buf, sizeof(ctx->combr[i].buf)); | |
} | |
for (int i = 0; i < FV_NUMALLPASSES; i++) { | |
zeroset(ctx->allpassl[i].buf, sizeof(ctx->allpassl[i].buf)); | |
zeroset(ctx->allpassr[i].buf, sizeof(ctx->allpassr[i].buf)); | |
} | |
} | |
static void update(fv_Context *ctx) { | |
ctx->wet1 = ctx->wet * (ctx->width * 0.5 + 0.5); | |
ctx->wet2 = ctx->wet * ((1 - ctx->width) * 0.5); | |
if (ctx->mode >= FV_FREEZEMODE) { | |
ctx->roomsize1 = 1; | |
ctx->damp1 = 0; | |
ctx->gain = FV_MUTED; | |
} else { | |
ctx->roomsize1 = ctx->roomsize; | |
ctx->damp1 = ctx->damp; | |
ctx->gain = FV_FIXEDGAIN; | |
} | |
for (int i = 0; i < FV_NUMCOMBS; i++) { | |
ctx->combl[i].feedback = ctx->roomsize1; | |
ctx->combr[i].feedback = ctx->roomsize1; | |
comb_set_damp(&ctx->combl[i], ctx->damp1); | |
comb_set_damp(&ctx->combr[i], ctx->damp1); | |
} | |
} | |
void fv_set_samplerate(fv_Context *ctx, float value) { | |
const int combs[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; | |
const int allpasses[] = { 556, 441, 341, 225 }; | |
double multiplier = value / FV_INITIALSR; | |
/* init comb buffers */ | |
for (int i = 0; i < FV_NUMCOMBS; i++) { | |
ctx->combl[i].bufsize = combs[i] * multiplier; | |
ctx->combr[i].bufsize = (combs[i] + FV_STEREOSPREAD) * multiplier; | |
} | |
/* init allpass buffers */ | |
for (int i = 0; i < FV_NUMALLPASSES; i++) { | |
ctx->allpassl[i].bufsize = allpasses[i] * multiplier; | |
ctx->allpassr[i].bufsize = (allpasses[i] + FV_STEREOSPREAD) * multiplier; | |
} | |
} | |
void fv_set_mode(fv_Context *ctx, float value) { | |
ctx->mode = value; | |
update(ctx); | |
} | |
void fv_set_roomsize(fv_Context *ctx, float value) { | |
ctx->roomsize = value * FV_SCALEROOM + FV_OFFSETROOM; | |
update(ctx); | |
} | |
void fv_set_damp(fv_Context *ctx, float value) { | |
ctx->damp = value * FV_SCALEDAMP; | |
update(ctx); | |
} | |
void fv_set_wet(fv_Context *ctx, float value) { | |
ctx->wet = value * FV_SCALEWET; | |
update(ctx); | |
} | |
void fv_set_dry(fv_Context *ctx, float value) { | |
ctx->dry = value * FV_SCALEDRY; | |
} | |
void fv_set_width(fv_Context *ctx, float value) { | |
ctx->width = value; | |
update(ctx); | |
} | |
void fv_process(fv_Context *ctx, float *buf, int n) { | |
for (int i = 0; i < n; i += 2) { | |
float outl = 0; | |
float outr = 0; | |
float input = (buf[i] + buf[i + 1]) * ctx->gain; | |
/* accumulate comb filters in parallel */ | |
for (int i = 0; i < FV_NUMCOMBS; i++) { | |
outl += comb_process(&ctx->combl[i], input); | |
outr += comb_process(&ctx->combr[i], input); | |
} | |
/* feed through allpasses in series */ | |
for (int i = 0; i < FV_NUMALLPASSES; i++) { | |
outl = allpass_process(&ctx->allpassl[i], outl); | |
outr = allpass_process(&ctx->allpassr[i], outr); | |
} | |
/* replace buffer with output */ | |
buf[i ] = outl * ctx->wet1 + outr * ctx->wet2 + buf[i ] * ctx->dry; | |
buf[i+1] = outr * ctx->wet1 + outl * ctx->wet2 + buf[i+1] * ctx->dry; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef FREEVERB_H | |
#define FREEVERB_H | |
/* | |
** freeverb v0.1 | |
** | |
** Public domain C implementation of the original freeverb, with the addition of | |
** support for samplerates other than 44.1khz. | |
** | |
** Original C++ version written by Jezard at Dreampoint, June 2000 | |
*/ | |
#define FV_NUMCOMBS 8 | |
#define FV_NUMALLPASSES 4 | |
#define FV_MUTED 0.0 | |
#define FV_FIXEDGAIN 0.015 | |
#define FV_SCALEWET 3.0 | |
#define FV_SCALEDRY 2.0 | |
#define FV_SCALEDAMP 0.4 | |
#define FV_SCALEROOM 0.28 | |
#define FV_STEREOSPREAD 23 | |
#define FV_OFFSETROOM 0.7 | |
#define FV_INITIALROOM 0.5 | |
#define FV_INITIALDAMP 0.5 | |
#define FV_INITIALWET (1.0 / FV_SCALEWET) | |
#define FV_INITIALDRY 0.0 | |
#define FV_INITIALWIDTH 1.0 | |
#define FV_INITIALMODE 0.0 | |
#define FV_INITIALSR 44100.0 | |
#define FV_FREEZEMODE 0.5 | |
typedef struct { | |
float feedback; | |
float filterstore; | |
float damp1, damp2; | |
float buf[4096]; | |
int bufsize; | |
int bufidx; | |
} fv_Comb; | |
typedef struct { | |
float feedback; | |
float buf[2048]; | |
int bufsize; | |
int bufidx; | |
} fv_Allpass; | |
typedef struct { | |
float mode; | |
float gain; | |
float roomsize, roomsize1; | |
float damp, damp1; | |
float wet, wet1, wet2; | |
float dry; | |
float width; | |
fv_Comb combl[FV_NUMCOMBS]; | |
fv_Comb combr[FV_NUMCOMBS]; | |
fv_Allpass allpassl[FV_NUMALLPASSES]; | |
fv_Allpass allpassr[FV_NUMALLPASSES]; | |
} fv_Context; | |
void fv_init(fv_Context *ctx); | |
void fv_mute(fv_Context *ctx); | |
void fv_process(fv_Context *ctx, float *buf, int n); | |
void fv_set_samplerate(fv_Context *ctx, float value); | |
void fv_set_mode(fv_Context *ctx, float value); | |
void fv_set_roomsize(fv_Context *ctx, float value); | |
void fv_set_damp(fv_Context *ctx, float value); | |
void fv_set_wet(fv_Context *ctx, float value); | |
void fv_set_dry(fv_Context *ctx, float value); | |
void fv_set_width(fv_Context *ctx, float value); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment