1043 lines · 25.6 KB · Text
RawDownload
#+TITLE: Bigverb* IntroductionBigverb is an implementation of a classic waveguidereverberator. It is composed upof 8 feedback delay lines running in parallel, in what isknown as a [[https://ccrma.stanford.edu/~jos/cfdn/Feedback_Delay_Networks.html][Feedback Delay Network]]. It is largely inspired bythe works Julius O. Smith, and the work he did in usingwaveguide networks. In particular, this algorithm is basedon ideas in a 1985 paper called "A New Approach to DigitalReverberation using Waveguide Networks". Bigverb, as the name implies, has a very nice warm soundwhen set to be a very large space, and is ideal for use inambient style music. Bigverb is not designed to soundrealistic, and is not an ideal choice for anything thatneeds room modelling.* Distinct CharactersticsThe "sound" of this algorithm comes from a few majorthings.** Use of Jitter ModulationEvery delay line has a bit of jitter applied tothe delay time, which causes a little of pitch modulation.Several schroeder and FDN-style reverb designs use thiskind of modulation, such as the Mutable instrumentsclouds reverb, and the tank reverb described in the Dattorropaper "Effect design, part 1: reverberator and other filters".Such modulation creates the illusion of the reverb beinga higher-order system than it actual (aka: a more complexreverb algorithm with more delay lines).While these designs tend to use a sinusoidal LFO on only afew delay lines, the Costello topology applies non-correlatedmodulation to every single delay line. As a result, you geta very dense-sounding reverb output.** Parameter TuningsThe parameter tunings of this reverb add to theparticular character of this sound. All units are set insuch a way so that they can be stored as integer values. The tuning consists of 8 parameter sets, corresponding toeach delay line "unit". A set has the following parameters: delay time(=delay=) (in units of samples with a samplerate of44.1kHz), the amount of variation in delay time to apply(=drift=) (in units of tenth milliseconds, in order to useinteger values), the random variation frequency (=randfreq=)(in Hz multiplied by 1000 to avoid decimals),as well as a starting seed for the internal RNG. The credit for these parameter tunings (and this topology)go to Sean Costello, who also originally designed thisreverb algorithm in 1999. This work would later pave the wayfor his famous commercial reverb plugins and algorithmspublished by his company ValhallaDSP. #+NAME: parameter_set#+BEGIN_SRC cstruct bigverb_paramset {    int delay; /* in samples, 44.1 kHz */    int drift; /* 1/10 milliseconds */    int randfreq; /* Hertz * 1000 */    int seed;}; static const struct bigverb_paramset params[8] = {    {0x09a9, 0x0a, 0xc1c, 0x07ae},    {0x0acf, 0x0b, 0xdac, 0x7333},    {0x0c91, 0x11, 0x456, 0x5999},    {0x0de5, 0x06, 0xf85, 0x2666},    {0x0f43, 0x0a, 0x925, 0x50a3},    {0x101f, 0x0b, 0x769, 0x5999},    {0x085f, 0x11, 0x37b, 0x7333},    {0x078d, 0x06, 0xc95, 0x3851}};#+END_SRC** Delay Line ConfigurationNormally, this family of reverbs is a combination of allpassfilters and comb filters in various series/parallelcombinations. This implementation has a far simplertopology: it is essentially (but not *quite*) 8 feedback delaylines in parallel with a 1-pole lowpass IIR filter for tonecontrol. If it weren't for the massive amount delaymodulation, this would be a pretty metallic sounding reverb!* Top Level Files and Structs** Files to Tangle ToThis Bigverb document tanlges to two files:=bigverb.c= and =bigverb.h=. =bigverb.h= contains typedefs, structs, and forwardfunction declarations. To enable structs, define themacro =SK_BIGVERB_PRIV=. #+NAME: bigverb.c#+BEGIN_SRC c :tangle bigverb.c#include <stdlib.h>#include <stdio.h>#include <math.h>#define SK_BIGVERB_PRIV#include "bigverb.h"#ifndef M_PI#define M_PI 3.14159265358979323846#endif<<parameter_set>><<fdelay_constants>><<static_funcdefs>><<funcs>>#+END_SRC #+NAME: bigverb.h#+BEGIN_SRC c :tangle bigverb.h#ifndef SK_BIGVERB_H#define SK_BIGVERB_H #ifndef SKFLT#define SKFLT float#endif<<typedefs>><<funcdefs>> #ifdef SK_BIGVERB_PRIV<<main_struct>>#endif#endif#+END_SRC** The Bigverb StructAn instance of Bigverb is contained inside of a structcalled =sk_bigverb=.#+NAME: typedefs#+BEGIN_SRC ctypedef struct sk_bigverb sk_bigverb;#+END_SRC #+NAME: main_struct#+BEGIN_SRC c<<delay_struct>>struct sk_bigverb {    int sr;    <<sk_bigverb>>};#+END_SRC* Setup and CleanupA new instance of bigverb is created with=sk_bigverb_new=. The only argument required is the samplingrate. If something goes wrong, this will return =NULL=. #+NAME: funcdefs#+BEGIN_SRC csk_bigverb * sk_bigverb_new(int sr);#+END_SRC #+NAME: funcs#+BEGIN_SRC csk_bigverb * sk_bigverb_new(int sr){    sk_bigverb *bv;     bv = calloc(1, sizeof(sk_bigverb));     bv->sr = sr;    <<init_variables>>    <<setup_delay_lines>>     return bv;}#+END_SRC When it is done being used, bigverb must be cleanly freedwith =sk_bigverb_del=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_bigverb_del(sk_bigverb *bv);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_bigverb_del(sk_bigverb *bv){    <<cleanup>>    free(bv);    bv = NULL;}#+END_SRC* High level parametersHigh level parametric control of bigverb includes"size" and "cutoff". Set parameters before computingaudio. These are are just floating point values containedin the struct that can be indirectly set with setters insituations where the struct is opaque.** SizeSet the reverb size with =sk_bigverb_size= #+NAME: funcdefs#+BEGIN_SRC cvoid sk_bigverb_size(sk_bigverb *bv, SKFLT size);#+END_SRC Size is a variable between 0-1, whichcontrols the feedback level for the delay line. The =size= parameter is stored as a variable called=size=, and is set to be a pretty sounding value of=0.93=. #+NAME: sk_bigverb#+BEGIN_SRC cSKFLT size;#+END_SRC #+NAME: init_variables#+BEGIN_SRC csk_bigverb_size(bv, 0.93);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_bigverb_size(sk_bigverb *bv, SKFLT size){    bv->size = size;}#+END_SRC** CutoffThe tone of bigverb can be set with =sk_bigverb_cutoff=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_bigverb_cutoff(sk_bigverb *bv, SKFLT cutoff);#+END_SRC =cutoff= is a parameter in Hz that determines the overalltimbre of the reverb. This controls the cutoff frequency ofthe one pole lowpass filter applied to the reverb. It is set to be a default value of 10kHz, or 10,000 hz. #+NAME: init_variables#+BEGIN_SRC csk_bigverb_cutoff(bv, 10000.0);#+END_SRC Cutoff uses caching in order to monitor if the parameterhas changed. It does this in order to prevent needing tocompute filter coefficients every sample. The main variableto be set is =cutoff=, and the cached variable is =pcutoff=.At the beginning, =pcutoff= is set to be a negative value,which will cause bigverb to calculate coefficients in thefirst call to the tick function after initialization. #+NAME: sk_bigverb#+BEGIN_SRC cSKFLT cutoff;SKFLT pcutoff;#+END_SRC #+NAME: init_variables#+BEGIN_SRC cbv->pcutoff = -1;#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_bigverb_cutoff(sk_bigverb *bv, SKFLT cutoff){    bv->cutoff = cutoff;}#+END_SRC* FilterState in a constant called =filt=. #+NAME: sk_bigverb#+BEGIN_SRC cSKFLT filt;#+END_SRC #+NAME: init_variables#+BEGIN_SRC cbv->filt = 1.0;#+END_SRC* Computing AudioAfter bigverb has been initialized, it is ready toprocess audio. This implementation uses what is knownas a =tick= function, or a function that computes audioone sample at a time instead of one block at a time. Thissimplifies the implementation at the cost of a little bitof performance overhead, depending on the compiler andoptimization settings.** Top-Level Tick FunctionThe function to tick one sample unit of audio is done with=sk_bigverb_tick=. It takes in two stereo inputvalues, and returns two stereo output values. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_bigverb_tick(sk_bigverb *bv,                     SKFLT inL, SKFLT inR,                     SKFLT *outL, SKFLT *outR);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_bigverb_tick(sk_bigverb *bv,                     SKFLT inL, SKFLT inR,                     SKFLT *outL, SKFLT *outR){    /* TODO: implement */    SKFLT lsum, rsum;     lsum = 0;    rsum = 0;     <<update_filter_coefficients>>    <<calculate_junction_pressure>>    <<compute_delay_bank>>     *outL = lsum;    *outR = rsum;}#+END_SRC** Updating filter coefficientsBigverb uses parameter caching for the =cutoff= parameter inorder to save on computation time. Any time =cutoff= changes, the filter coefficients must beupdated. This happens in the tick function, before anycomputation happens. The filter is a simple 1-pole IIR lowpass filter whosedifference equation been reduced to only require a singleparameter. This in turn then gets used in each filter delayline. #+NAME: update_filter_coefficients#+BEGIN_SRC cif (bv->pcutoff != bv->cutoff) {    bv->pcutoff = bv->cutoff;    bv->filt = 2.0 - cos(bv->pcutoff * 2 * M_PI / bv->sr);    bv->filt = bv->filt - sqrt(bv->filt * bv->filt - 1.0);}#+END_SRC** Calculating Resultant Junction Pressure AmountThe resultant junction pressure amount is calculated fromthe delay bank, and then factored into the input signals. Sum of all the delay line signals, and scaled by 0.25, or2/N, where N is the number of delay lines (8). #+NAME: calculate_junction_pressure#+BEGIN_SRC c{    int i;    SKFLT jp;     jp = 0;     for (i = 0; i < 8; i++) {        jp += bv->delay[i].y;    }     jp *= 0.25;     inL = jp + inL;    inR = jp + inR;}#+END_SRC** Computing the delay bankThe delay bank is then computed. Each delay line is computedand summed with either the left or right input signal, andthen sent to a corresponding left or right channel. At the end, a final scaling out of the output happens. Thisis hard coded to be 35 percent. #+NAME: compute_delay_bank#+BEGIN_SRC c{    int i;    for (i = 0; i < 8; i++) {        if (i & 1) {            rsum += delay_compute(&bv->delay[i],                                  inR,                                  bv->size,                                  bv->filt,                                  bv->sr);        } else {            lsum += delay_compute(&bv->delay[i],                                  inL,                                  bv->size,                                  bv->filt,                                  bv->sr);        }    }}rsum *= 0.35f;lsum *= 0.35f;#+END_SRC* The Feedback Delay Line Bank8 delay units come together to make the delay line bank.Each is initialized using one of the parameter sets.** Memory Allocation + Setup#+NAME: sk_bigverb#+BEGIN_SRC cSKFLT *buf;#+END_SRC #+NAME: init_variables#+BEGIN_SRC cbv->buf = NULL;#+END_SRC #+NAME: setup_delay_lines#+BEGIN_SRC c{unsigned long total_size;int i;SKFLT *buf; total_size = 0;buf = NULL;<<calculate_pool_size>><<allocate_memory>><<initialize_delay_banks>>}#+END_SRCThe delay bank is the abstraction in charge of properlyallocating all the memory needed for the buffers. Memory is allocated in one giant chunk, and then divied upto each delay line. The total memory size is obtained by summing all the delaytimes. These times are stored as fixed delay times in unitsof samples. These parameters assume a sampling rate of44.1kHz. If this is not the case, this value must be scaledaccordingly, and then truncated to be an integer. This valueis used again to properly slice up the big memory chunk. #+NAME: static_funcdefs#+BEGIN_SRC cstatic int get_delay_size(const struct bigverb_paramset *p, int sr);#+END_SRC #+NAME: funcs#+BEGIN_SRC cstatic int get_delay_size(const struct bigverb_paramset *p, int sr){    SKFLT sz;    sz = (SKFLT)p->delay/44100 + (p->drift * 0.0001) * 1.125;    return floor(16 + sz*sr);}#+END_SRC #+NAME: calculate_pool_size#+BEGIN_SRC cfor (i = 0; i < 8; i++) {    total_size += get_delay_size(&params[i], sr);}#+END_SRC Allocation is done with =calloc=, which zeros out the memoryas well. This memory will eventually be freed in=sk_bigverb_del=. #+NAME: allocate_memory#+BEGIN_SRC cbuf = calloc(1, sizeof(SKFLT) * total_size);bv->buf = buf;#+END_SRC #+NAME: cleanup#+BEGIN_SRC cfree(bv->buf);#+END_SRC #+NAME: sk_bigverb#+BEGIN_SRC csk_bigverb_delay delay[8];#+END_SRC #+NAME: initialize_delay_banks#+BEGIN_SRC c{    unsigned long bufpos;    bufpos = 0;    for (i = 0; i < 8; i++) {        unsigned int sz;        sz = get_delay_size(&params[i], sr);         delay_init(&bv->delay[i], &params[i],                   &buf[bufpos], sz, sr);        bufpos += sz;    }}#+END_SRC** A Single Delay Line UnitA delay unit in a bank consists of variable delay line withcubic interpolation with a 1 pole low-pass filter for tonecontrol, whose frequency is determined using a masterparameter, as well as a jitter generator. Feedback as well.*** Struct DeclarationA delay unit is known as a struct called =sk_bigverb_delay=. #+NAME: typedefs

Lines 1–500 of 1043.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t