356 lines · 7.8 KB · Text
RawDownload
#+TITLE: Swell* Overviewswell is a special kind of smoothing filter on a controlsignal that takes in two smoothing parameters A and B. whenthe signal is increasing it uses smoothing parameter A, andwhen it is decreasing it uses parameter B. No change willcontinue using whatever paremeter was chosen last. Thiscreates a certain kind of resistance that is very usefulfor swellling effects, which is where it gets its name. An inertia filter is included in this design to allow forchanges between the two smoothing states to be moregradual. This is another one pole smoothing filter, appliedto the signal switching between the two smoothing times. To summarize the topology of this filter design: Swell is aone-pole smoothing filter whose smoothing factor isdetermined by the direction of the input signal, placedthrough another one-pole smoothing filter for inertialcontrol.* Tangled FilesTangles to files =swell.c= and =swell.h=. Defining =SK_SWELL_PRIV= exposes the contents =sk_swell=struct. Otherwise, it is opaque. #+NAME: swell.h#+BEGIN_SRC c :tangle swell.h#ifndef SK_SWELL_H#define SK_SWELL_H #ifndef SKFLT#define SKFLT float#endif <<typedefs>> #ifdef SK_SWELL_PRIV<<structs>>#endif <<funcdefs>>#endif#+END_SRC #+NAME: swell.c#+BEGIN_SRC c :tangle swell.c#include <math.h>#define SK_SWELL_PRIV#include "swell.h"<<funcs>>#+END_SRC* InitializationThe struct for swell is called =sk_swell=. It isinitialized with =sk_swell_init=. The sampling rate isrequired. #+NAME: typedefs#+BEGIN_SRC ctypedef struct sk_swell sk_swell;#+END_SRC #+NAME: structs#+BEGIN_SRC cstruct sk_swell {    <<sk_swell>>};#+END_SRC #+NAME: funcdefs#+BEGIN_SRC cvoid sk_swell_init(sk_swell *sw, int sr);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_swell_init(sk_swell *sw, int sr){    <<init>>}#+END_SRC* Filter ComponentsThere are two recursive one-pole IIR filters known as thesmoothing filter and the switching filter. Both require 1sample of filter memory (which will be called =y=), as wellas the coefficients used tocompute the filter, which in a DSP context are knownas =a1= and =b0=. The strength of the filters are each controlled by avariable in units of seconds, known as the half-time. Thisis time it takes for the function to slur itself over tothe new value. This will be referred to as =amt=. Thisvariable will be =cached= so that filter coefficientsdon't need to be updated every sample, called =pamt=. Here are the smoothing filter variables, prefixed with=smooth_=. #+NAME: sk_swell#+BEGIN_SRC cSKFLT smooth_y;SKFLT smooth_a1;SKFLT smooth_b0;SKFLT smooth_pamt;#+END_SRC #+NAME: init#+BEGIN_SRC csw->smooth_y = 0;sw->smooth_b0 = 0;sw->smooth_a1 = 0;sw->smooth_pamt = -100;#+END_SRC Here are the inertia filter parameters, prefixed with = #+NAME: sk_swell#+BEGIN_SRC cSKFLT inertia_y;SKFLT inertia_a1;SKFLT inertia_b0;SKFLT inertia_pamt;#+END_SRC #+NAME: init#+BEGIN_SRC csw->inertia_y = 0;sw->inertia_b0 = 0;sw->inertia_a1 = 0;sw->inertia_pamt = -100;#+END_SRC one divided by samplerate constant. Used to update filtercoefficients. #+NAME: sk_swell#+BEGIN_SRC cSKFLT onedsr;#+END_SRC #+NAME: init#+BEGIN_SRC csw->onedsr = 1.0 / (SKFLT) sr;#+END_SRC* Unit Delay For Direction DetectionIn order to the direction of a sample, the previoussample is stored in what could be considered a1-sample delay, or =unit delay=. A more robust way to determine direction would be toexamine more than just the previous sample. But thisapproach is faster, simpler, and good enough for thekinds of slow moving control signals that will beexpected. Also, since there's also inertia filtering ontop of this, having a flickering direction isn't toobig of a deal. The variable, called =prev=, will be set to be an unlikelystarting value of =-123.456=. #+NAME: sk_swell#+BEGIN_SRC cSKFLT prev;#+END_SRC #+BEGIN_SRC csw->prev = -123.456;#+END_SRC* ParametersSwell takes in three parameters: smooth factor A, smoothfactor B, and inertia. Set smoothing factor A with =sk_swell_smootha=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_swell_smootha(sk_swell *sw, SKFLT smooth);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_swell_smootha(sk_swell *sw, SKFLT smooth){    sw->sm_a = smooth;}#+END_SRC #+NAME: sk_swell#+BEGIN_SRC cSKFLT sm_a;#+END_SRC #+NAME: init#+BEGIN_SRC csk_swell_smootha(sw, 10);#+END_SRC Set smoothing factor B with =sk_swell_smoothb=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_swell_smoothb(sk_swell *sw, SKFLT smooth);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_swell_smoothb(sk_swell *sw, SKFLT smooth){    sw->sm_b = smooth;}#+END_SRC #+NAME: sk_swell#+BEGIN_SRC cSKFLT sm_b;#+END_SRC #+NAME: init#+BEGIN_SRC csk_swell_smoothb(sw, 0.1);#+END_SRC Set inertia with =sk_swell_inertia=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_swell_inertia(sk_swell *sw, SKFLT inertia);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_swell_inertia(sk_swell *sw, SKFLT inertia){    sw->inertia_amt = inertia;}#+END_SRC #+NAME: sk_swell#+BEGIN_SRC cSKFLT inertia_amt;#+END_SRC #+NAME: init#+BEGIN_SRC csk_swell_inertia(sw, 0.001);#+END_SRC* Direction Storage PointerAt any point in time, the smoothing amount can either beparameter A or B. A pointer =dir= is used to store theaddress of one of these values. #+NAME: sk_swell#+BEGIN_SRC cSKFLT *dir;#+END_SRC #+NAME: init#+BEGIN_SRC csw->dir = &sw->sm_a;#+END_SRC* ComputationThe function =sk_swell_tick= computes one sample of audiofrom an input signal =in=. #+NAME: funcdefs#+BEGIN_SRC cSKFLT sk_swell_tick(sk_swell *sw, SKFLT in);#+END_SRC #+NAME: funcs#+BEGIN_SRC cSKFLT sk_swell_tick(sk_swell *sw, SKFLT in){    SKFLT out;    SKFLT switching;    SKFLT dir;    const SKFLT eps = 0.0001;    out = 0;    <<determine_direction>>    <<filter_switch_signal>>    <<snap_signal>>    <<compute_smoothing_filter>>    return out;}#+END_SRC The direction is determined by comparing the current inputsignal with the previous input signal. A positive changewill select the A smoothing factor. A negative change willselect the B smoothing factor. No change will hold onto thelast selected factor. This will be referred to as the=switching= signal. #+NAME: determine_direction#+BEGIN_SRC cif (in > sw->prev) {    sw->dir = &sw->sm_a;} else if (in < sw->prev) {    sw->dir = &sw->sm_b;}sw->prev = in;dir = *sw->dir;switching = dir;#+END_SRC The switching signal is put through a 1-pole filter knownas the inertia filter. Before computing this filter, theinertia parameter is checked to see if it needs to beupdated via @!(ref "parameter_caching""parameter caching")!@. #+NAME: filter_switch_signal#+BEGIN_SRC cif (sw->inertia_amt != sw->inertia_pamt) {    sw->inertia_pamt = sw->inertia_amt;    sw->inertia_a1 = pow(0.5, sw->onedsr/sw->inertia_amt);    sw->inertia_b0 = 1.0 - sw->inertia_a1;} switching =    sw->inertia_b0*switching +    sw->inertia_a1*sw->inertia_y;sw->inertia_y = switching;#+END_SRC The filtered switching signal is then snapped to the A/Bsmoothing parameter if it is within an epsilon boundary.This is done to prevent the next smoothing filter fromconstantly re-calculating filter coefficients. Calls tomath functions like =exp= can be expensive, and should beavoided if possible. #+NAME: snap_signal#+BEGIN_SRC cif (fabs(switching - dir) < eps) switching = dir;#+END_SRC Using the filtered-and-snapped switching signal, the mainsmoothing filter can now be computed on the input signal.Similar to the inertia filter, parameter caching is alsoused. #+NAME: compute_smoothing_filter#+BEGIN_SRC cif (switching != sw->smooth_pamt) {    sw->smooth_pamt = switching;    sw->smooth_a1 = pow(0.5, sw->onedsr/switching);    sw->smooth_b0 = 1.0 - sw->smooth_a1;} out =    sw->smooth_b0*in +    sw->smooth_a1*sw->smooth_y;sw->smooth_y = 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