376 lines · 8.1 KB · Text
RawDownload
#+TITLE: Vardelay* OverviewVardelay is an implementation of a variable-length delayline, using third order [[https://ccrma.stanford.edu/~jos/pasp/Lagrange_Interpolation.html][Langrange Interpolation]].This delay time of this delay line can be adjusted overtime. Because it uses interpolation, any adjustments cancause pitch shifting to occur. This itself is often adesirable artifact, and is the basis for audio effectslike flanging and chorusing. In addition to delay time, a feedback parameter will also besupplied, as it is so often used with delay lines.* Tangled Files=vardelay.c= and =vardelay.h= are the tangled files. #+NAME: vardelay.c#+BEGIN_SRC c :tangle vardelay.c#include <math.h>#include <stdlib.h>#define SK_VARDELAY_PRIV#include "vardelay.h"<<funcs>>#+END_SRC If =SK_VARDELAY_PRIV= is defined, private structs are madepublic. #+NAME: vardelay.h#+BEGIN_SRC c :tangle vardelay.h#ifndef SK_VARDELAY_H#define SK_VARDELAY_H #ifndef SKFLT#define SKFLT float#endif <<typedefs>><<funcdefs>>#ifdef SK_VARDELAY_PRIV<<structs>>#endif#endif#+END_SRC* InitializationInitialized with =sk_vardelay_init=. must supply your ownbuffer, with buffer size. This way, no internal memoryallocation is required. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_vardelay_init(sk_vardelay *vd, int sr,                      SKFLT *buf, unsigned long sz);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_vardelay_init(sk_vardelay *vd, int sr,                      SKFLT *buf, unsigned long sz){    <<init>>}#+END_SRC Anything buffer size less than 4 samples is not enoughto do third-order interpolation, so it returns an error.* StructCalled =sk_vardelay=. #+NAME: typedefs#+BEGIN_SRC ctypedef struct sk_vardelay sk_vardelay;#+END_SRC #+NAME: structs#+BEGIN_SRC cstruct sk_vardelay {    <<sk_vardelay>>};#+END_SRC* Constants and Variables** Sample ratea copy of the sampling rate is stored in the struct.It is used to convert delay time seconds to samples. #+NAME: sk_vardelay#+BEGIN_SRC cint sr;#+END_SRC #+NAME: init#+BEGIN_SRC cvd->sr = sr;#+END_SRC** Buffer, Buffer SizeThe delay buffer is the chunk of memory where samples willbe read/written. It is assumed to be neatly pre-allocatedand zeroed out externally. The size is also assumed to beknown ahead of time and correct. If the buffer size is less than 4, the delay line isdisabled, as there aren't enough samples to do theinterpolation. #+NAME: sk_vardelay#+BEGIN_SRC cSKFLT *buf;unsigned long sz;#+END_SRC #+NAME: init#+BEGIN_SRC cif (sz < 4) {    vd->buf = NULL;    vd->buf = 0;} else {    vd->buf = buf;    vd->sz = sz;}#+END_SRC** Previous outputA variable is used to store the output of the previous delayin what is known as a single-sample delay or a unit delay.This delay is used to introduce feedback into the system.In discrete digital systems like this one, unit delays areinevitable for feedback, which is why they are referred toas *implicit* delays. #+NAME: sk_vardelay#+BEGIN_SRC cSKFLT prev;#+END_SRC #+NAME: init#+BEGIN_SRC cvd->prev = 0;#+END_SRC** Write PositionThe write position is used to store the current indexposition in the buffer being written to. The write positionstarts at zero, moves forward until it reaches the end ofthe buffer, and then goes back to the beginning again. Thedelayed samples start the write position, and move backwardsin time. If that position is a negative, it wraps around. Notably, the write position is a signed =long= to compensatefor negative values. This is needed for the delay to readstuff back in time. #+NAME: sk_vardelay#+BEGIN_SRC clong writepos;#+END_SRC #+NAME: init#+BEGIN_SRC cvd->writepos = 0;#+END_SRC* Parameters** Delay TimeSet with =sk_vardelay_delay=, In units of seconds. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_vardelay_delay(sk_vardelay *vd, SKFLT delay);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_vardelay_delay(sk_vardelay *vd, SKFLT delay){    vd->delay = delay;}#+END_SRC #+NAME: sk_vardelay#+BEGIN_SRC cSKFLT delay;#+END_SRC The initial delay time is set to be half the delay time, inunits of seconds. #+NAME: init#+BEGIN_SRC csk_vardelay_delay(vd, ((SKFLT)sz / sr) * 0.5);#+END_SRC** FeedbackSet with =sk_vardelay_feedback=. Should be a value between0 and 1. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_vardelay_feedback(sk_vardelay *vd, SKFLT feedback);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_vardelay_feedback(sk_vardelay *vd, SKFLT feedback){    vd->feedback = feedback;}#+END_SRC #+NAME: sk_vardelay#+BEGIN_SRC cSKFLT feedback;#+END_SRC No feedback by default. #+NAME: init#+BEGIN_SRC csk_vardelay_feedback(vd, 0);#+END_SRC* ComputationDone with =sk_vardelay_tick=. #+NAME: funcdefs#+BEGIN_SRC cSKFLT sk_vardelay_tick(sk_vardelay *vd, SKFLT in);#+END_SRC #+NAME: funcs#+BEGIN_SRC cSKFLT sk_vardelay_tick(sk_vardelay *vd, SKFLT in){    SKFLT out;    SKFLT dels;    SKFLT f;    long i;    SKFLT s[4];    unsigned long n[4];    SKFLT a, b, c, d;     out = 0;    <<return_if_empty>>    <<write_to_buffer>>    <<calculate_read_position>>    <<wrap_and_flip>>    <<read_samples>>    <<calculate_interpolation_coefficients>>    <<interpolate>>    <<update_position>>    <<update_feedback>>     return out;}#+END_SRC If buffer is NULL or size is 0, return 0. #+NAME: return_if_empty#+BEGIN_SRC cif (vd->buf == NULL || vd->sz == 0) return 0;#+END_SRC Write to buffer with feedback. #+NAME: write_to_buffer#+BEGIN_SRC cvd->buf[vd->writepos] = in + vd->prev * vd->feedback;#+END_SRC Calculate the read position. This is two parts: a fractionalcomponent, and an integer component. Get floating point + integer components of delay time.The delay time position is set to be the write position=dels= samples in the past, where =dels= is the delay timein samples. #+NAME: calculate_read_position#+BEGIN_SRC cdels = vd->delay * vd->sr;i = floor(dels);f = i - dels;i = vd->writepos - i;#+END_SRC Because we're looking backwards, the fractional component isbackwards too. A fractional value here is set to benegative (reaching back in time). If there is a fractional component that is negative, or theinteger component is negative, these must be corrected. The fractional value is flipped to be positive by adding 1to itself. The integer position is set back in time onesample. This sets the interpolation up so that instead oftaking a sample and interpolating backwards, you start withthe previous sample and move forwards. The integer positionis then set to be in bounds. By default, the position is wrapped around using additionuntil the sample is in bounds. #+NAME: wrap_and_flip#+BEGIN_SRC cif ((f < 0.0) || (i < 0)) {    /* flip fractional component */    f = f + 1.0;    /* go backwards one sample */    i = i - 1;    while (i < 0) i += vd->sz;} else while(i >= vd->sz) i -= vd->sz;#+END_SRC Read samples. This includes the current sample, the previoussample, and two samples in the future. #+NAME: read_samples#+BEGIN_SRC c/* x(n) */n[1] = i; /* x(n + 1) */if (i == (vd->sz - 1)) n[2] = 0;else n[2] = n[1] + 1; /* x(n - 1) */if (i == 0) n[0] = vd->sz - 1;else n[0] = i - 1; if (n[2] == vd->sz - 1) n[3] = 0;else n[3] = n[2] + 1; {    int j;    for (j = 0; j < 4; j++) s[j] = vd->buf[n[j]];}#+END_SRC Calculate interpolation coefficients. These fourcoefficients correspond with the four samples read. #+NAME: calculate_interpolation_coefficients#+BEGIN_SRC c{    SKFLT tmp[2];     d = ((f * f) - 1) * 0.1666666667;    tmp[0] = (f + 1.0) * 0.5;    tmp[1] = 3.0 * d;    a = tmp[0] - 1.0 - d;    c = tmp[0] - tmp[1];    b = tmp[1] - f;}#+END_SRC Interpolate. This follows the following equation: $$y(n) = (a x(n - 1) + b x(n) + c x(n + 1) + d x(n + 2)) \cdot f + x(n)$$ #+NAME: interpolate#+BEGIN_SRC cout = (a*s[0] + b*s[1] + c*s[2] + d*s[3]) * f + s[1];#+END_SRC Update position. Increment the write position, and wrapback to zero if it reaches the end of the delay buffer. #+NAME: update_position#+BEGIN_SRC cvd->writepos++;if (vd->writepos == vd->sz) vd->writepos = 0;#+END_SRC Update feedback. The current output is set to be the =prev=value in the delay line. #+NAME: update_feedback#+BEGIN_SRC cvd->prev = out;#+END_SRC

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