History

swell: initial code

Paul Batchelor committed 5 years ago

Commit db4cc421 parent 8d0e21d1 file · +289 −6Browse files

Unified · Split

swell.org+289−6
@@ -23,29 +23,267 @@ Tangles to files =swell.c= and =swell.h=. · 22 unmodified lines
23 23
24Defining =SK_SWELL_PRIV= exposes the contents =sk_swell=24Defining =SK_SWELL_PRIV= exposes the contents =sk_swell=
25struct. Otherwise, it is opaque.25struct. Otherwise, it is opaque.
26
27#+NAME: swell.h
28#+BEGIN_SRC c :tangle swell.h
29#ifndef SK_SWELL_H
30#define SK_SWELL_H
31
32#ifndef SKFLT
33#define SKFLT float
34#endif
35
36<<typedefs>>
37
38#ifdef SK_SWELL_PRIV
39<<structs>>
40#endif
41
42<<funcdefs>>
43#endif
44#+END_SRC
45
46#+NAME: swell.c
47#+BEGIN_SRC c :tangle swell.c
48#include <math.h>
49#define SK_SWELL_PRIV
50#include "swell.h"
51<<funcs>>
52#+END_SRC
26* Initialization53* Initialization
27The struct for swell is called =sk_swell=. It is54The struct for swell is called =sk_swell=. It is
28initialized with =sk_swell_init=. The sampling rate is55initialized with =sk_swell_init=. The sampling rate is
29required.56required.
57
58#+NAME: typedefs
59#+BEGIN_SRC c
60typedef struct sk_swell sk_swell;
61#+END_SRC
62
63#+NAME: structs
64#+BEGIN_SRC c
65struct sk_swell {
66 <<sk_swell>>
67};
68#+END_SRC
69
70#+NAME: funcdefs
71#+BEGIN_SRC c
72void sk_swell_init(sk_swell *sw, int sr);
73#+END_SRC
74
75#+NAME: funcs
76#+BEGIN_SRC c
77void sk_swell_init(sk_swell *sw, int sr)
78{
79 <<init>>
80}
81#+END_SRC
30* Filter Components82* Filter Components
31There are two recursive one-pole IIR filters known as the83There are two recursive one-pole IIR filters known as the
32smoothing filter and the switching filter. Both require 184smoothing filter and the switching filter. Both require 1
33sample of filter memory, as well as a coefficient used to85sample of filter memory (which will be called =y=), as well
34compute the filter.86as the coefficients used to
87compute the filter, which in a DSP context are known
88as =a1= and =b0=.
89
90The strength of the filters are each controlled by a
91variable in units of seconds, known as the half-time. This
92is time it takes for the function to slur itself over to
93the new value. This will be referred to as =amt=. This
94variable will be =cached= so that filter coefficients
95don't need to be updated every sample, called =pamt=.
96
97Here are the smoothing filter variables, prefixed with
98=smooth_=.
99
100#+NAME: sk_swell
101#+BEGIN_SRC c
102SKFLT smooth_y;
103SKFLT smooth_a1;
104SKFLT smooth_b0;
105SKFLT smooth_pamt;
106#+END_SRC
107
108#+NAME: init
109#+BEGIN_SRC c
110sw->smooth_y = 0;
111sw->smooth_b0 = 0;
112sw->smooth_a1 = 0;
113sw->smooth_pamt = -100;
114#+END_SRC
115
116Here are the inertia filter parameters, prefixed with =
117
118#+NAME: sk_swell
119#+BEGIN_SRC c
120SKFLT inertia_y;
121SKFLT inertia_a1;
122SKFLT inertia_b0;
123SKFLT inertia_pamt;
124#+END_SRC
125
126#+NAME: init
127#+BEGIN_SRC c
128sw->inertia_y = 0;
129sw->inertia_b0 = 0;
130sw->inertia_a1 = 0;
131sw->inertia_pamt = -100;
132#+END_SRC
133
134one divided by samplerate constant. Used to update filter
135coefficients.
136
137#+NAME: sk_swell
138#+BEGIN_SRC c
139SKFLT onedsr;
140#+END_SRC
141
142#+NAME: init
143#+BEGIN_SRC c
144sw->onedsr = 1.0 / (SKFLT) sr;
145#+END_SRC
146* Unit Delay For Direction Detection
147In order to the direction of a sample, the previous
148sample is stored in what could be considered a
1491-sample delay, or =unit delay=.
150
151A more robust way to determine direction would be to
152examine more than just the previous sample. But this
153approach is faster, simpler, and good enough for the
154kinds of slow moving control signals that will be
155expected. Also, since there's also inertia filtering on
156top of this, having a flickering direction isn't too
157big of a deal.
158
159The variable, called =prev=, will be set to be an unlikely
160starting value of =-123.456=.
161
162#+NAME: sk_swell
163#+BEGIN_SRC c
164SKFLT prev;
165#+END_SRC
166
167#+BEGIN_SRC c
168sw->prev = -123.456;
169#+END_SRC
35* Parameters170* Parameters
36Swell takes in three parameters: smooth factor A, smooth171Swell takes in three parameters: smooth factor A, smooth
37factor B, and inertia.172factor B, and inertia.
38 173
39Set smoothing factor A with =sk_swell_smootha=.174Set smoothing factor A with =sk_swell_smootha=.
40 175
176#+NAME: funcdefs
177#+BEGIN_SRC c
178void sk_swell_smootha(sk_swell *sw, SKFLT smooth);
179#+END_SRC
180
181#+NAME: funcs
182#+BEGIN_SRC c
183void sk_swell_smootha(sk_swell *sw, SKFLT smooth)
184{
185 sw->sm_a = smooth;
186}
187#+END_SRC
188
189#+NAME: sk_swell
190#+BEGIN_SRC c
191SKFLT sm_a;
192#+END_SRC
193
194#+NAME: init
195#+BEGIN_SRC c
196sk_swell_smootha(sw, 10);
197#+END_SRC
198
41Set smoothing factor B with =sk_swell_smoothb=.199Set smoothing factor B with =sk_swell_smoothb=.
42 200
43Set inertia with =sk_swell_inertia=. This parameter uses201#+NAME: funcdefs
44@!(ref "parameter_caching" "parameter caching")!@.202#+BEGIN_SRC c
203void sk_swell_smoothb(sk_swell *sw, SKFLT smooth);
204#+END_SRC
205
206#+NAME: funcs
207#+BEGIN_SRC c
208void sk_swell_smoothb(sk_swell *sw, SKFLT smooth)
209{
210 sw->sm_b = smooth;
211}
212#+END_SRC
213
214#+NAME: sk_swell
215#+BEGIN_SRC c
216SKFLT sm_b;
217#+END_SRC
218
219#+NAME: init
220#+BEGIN_SRC c
221sk_swell_smoothb(sw, 0.1);
222#+END_SRC
223
224Set inertia with =sk_swell_inertia=.
225
226#+NAME: funcdefs
227#+BEGIN_SRC c
228void sk_swell_inertia(sk_swell *sw, SKFLT inertia);
229#+END_SRC
230
231#+NAME: funcs
232#+BEGIN_SRC c
233void sk_swell_inertia(sk_swell *sw, SKFLT inertia)
234{
235 sw->inertia_amt = inertia;
236}
237#+END_SRC
238
239#+NAME: sk_swell
240#+BEGIN_SRC c
241SKFLT inertia_amt;
242#+END_SRC
243
244#+NAME: init
245#+BEGIN_SRC c
246sk_swell_inertia(sw, 0.001);
247#+END_SRC
248* Direction Storage Pointer
249At any point in time, the smoothing amount can either be
250parameter A or B. A pointer =dir= is used to store the
251address of one of these values.
252
253#+NAME: sk_swell
254#+BEGIN_SRC c
255SKFLT *dir;
256#+END_SRC
257
258#+NAME: init
259#+BEGIN_SRC c
260sw->dir = &sw->sm_a;
261#+END_SRC
45* Computation262* Computation
46The function =sk_swell_tick= computes one sample of audio263The function =sk_swell_tick= computes one sample of audio
47from an input signal =in=.264from an input signal =in=.
48 265
266#+NAME: funcdefs
267#+BEGIN_SRC c
268SKFLT sk_swell_tick(sk_swell *sw, SKFLT in);
269#+END_SRC
270
271#+NAME: funcs
272#+BEGIN_SRC c
273SKFLT sk_swell_tick(sk_swell *sw, SKFLT in)
274{
275 SKFLT out;
276 SKFLT switching;
277 SKFLT dir;
278 const SKFLT eps = 0.0001;
279 out = 0;
280 <<determine_direction>>
281 <<filter_switch_signal>>
282 <<snap_signal>>
283 return out;
284}
285#+END_SRC
286
49The direction is determined by comparing the current input287The direction is determined by comparing the current input
50signal with the previous input signal. A positive change288signal with the previous input signal. A positive change
51will select the A smoothing factor. A negative change will289will select the A smoothing factor. A negative change will
@@ -53,11 +291,37 @@ select the B smoothing factor. No change will hold onto the · 1 unmodified line
53last selected factor. This will be referred to as the291last selected factor. This will be referred to as the
54=switching= signal.292=switching= signal.
55 293
294#+NAME: determine_direction
295#+BEGIN_SRC c
296if (in > sw->prev) {
297 sw->dir = &sw->sm_a;
298} else if (in < sw->prev) {
299 sw->dir = &sw->sm_b;
300}
301sw->prev = in;
302dir = *sw->dir;
303switching = dir;
304#+END_SRC
305
56The switching signal is put through a 1-pole filter known306The switching signal is put through a 1-pole filter known
57as the inertia filter. Before computing this filter, the307as the inertia filter. Before computing this filter, the
58inertia parameter is checked to see if it needs to be308inertia parameter is checked to see if it needs to be
59updated via @!(ref "parameter_caching" "parameter309updated via @!(ref "parameter_caching"
60caching")!@.310"parameter caching")!@.
311
312#+NAME: filter_switch_signal
313#+BEGIN_SRC c
314if (sw->inertia_amt != sw->inertia_pamt) {
315 sw->inertia_pamt = sw->inertia_amt;
316 sw->inertia_a1 = pow(0.5, sw->onedsr/sw->inertia_amt);
317 sw->inertia_b0 = 1.0 - sw->inertia_a1;
318}
319
320switching =
321 sw->inertia_b0*switching +
322 sw->inertia_a1*sw->inertia_y;
323sw->inertia_y = switching;
324#+END_SRC
61 325
62The filtered switching signal is then snapped to the A/B326The filtered switching signal is then snapped to the A/B
63smoothing parameter if it is within an epsilon boundary.327smoothing parameter if it is within an epsilon boundary.
@@ -66,7 +330,26 @@ constantly re-calculating filter coefficients. Calls to · 2 unmodified lines
66math functions like =exp= can be expensive, and should be330math functions like =exp= can be expensive, and should be
67avoided if possible.331avoided if possible.
68 332
333#+NAME: snap_signal
334#+BEGIN_SRC c
335if (fabs(switching - dir) < eps) switching = dir;
336#+END_SRC
337
69Using the filtered-and-snapped switching signal, the main338Using the filtered-and-snapped switching signal, the main
70smoothing filter can now be computed on the input signal.339smoothing filter can now be computed on the input signal.
71Similar to the inertia filter, parameter caching is also340Similar to the inertia filter, parameter caching is also
72used.341used.
342
343#+NAME: compute_smoothing_filter
344#+BEGIN_SRC c
345if (switching != sw->smooth_pamt) {
346 sw->smooth_pamt = switching;
347 sw->smooth_a1 = pow(0.5, sw->onedsr/switching);
348 sw->smooth_b0 = 1.0 - sw->smooth_a1;
349}
350
351out =
352 sw->smooth_b0*switching +
353 sw->smooth_a1*sw->smooth_y;
354sw->smooth_y = out;
355#+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