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
2323
2424 Defining =SK_SWELL_PRIV= exposes the contents =sk_swell=
2525 struct. 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
2653 * Initialization
2754 The struct for swell is called =sk_swell=. It is
2855 initialized with =sk_swell_init=. The sampling rate is
2956 required.
57+
58+#+NAME: typedefs
59+#+BEGIN_SRC c
60+typedef struct sk_swell sk_swell;
61+#+END_SRC
62+
63+#+NAME: structs
64+#+BEGIN_SRC c
65+struct sk_swell {
66+ <<sk_swell>>
67+};
68+#+END_SRC
69+
70+#+NAME: funcdefs
71+#+BEGIN_SRC c
72+void sk_swell_init(sk_swell *sw, int sr);
73+#+END_SRC
74+
75+#+NAME: funcs
76+#+BEGIN_SRC c
77+void sk_swell_init(sk_swell *sw, int sr)
78+{
79+ <<init>>
80+}
81+#+END_SRC
3082 * Filter Components
3183 There are two recursive one-pole IIR filters known as the
3284 smoothing filter and the switching filter. Both require 1
33−sample of filter memory, as well as a coefficient used to
34−compute the filter.
85+sample of filter memory (which will be called =y=), as well
86+as the coefficients used to
87+compute the filter, which in a DSP context are known
88+as =a1= and =b0=.
89+
90+The strength of the filters are each controlled by a
91+variable in units of seconds, known as the half-time. This
92+is time it takes for the function to slur itself over to
93+the new value. This will be referred to as =amt=. This
94+variable will be =cached= so that filter coefficients
95+don't need to be updated every sample, called =pamt=.
96+
97+Here are the smoothing filter variables, prefixed with
98+=smooth_=.
99+
100+#+NAME: sk_swell
101+#+BEGIN_SRC c
102+SKFLT smooth_y;
103+SKFLT smooth_a1;
104+SKFLT smooth_b0;
105+SKFLT smooth_pamt;
106+#+END_SRC
107+
108+#+NAME: init
109+#+BEGIN_SRC c
110+sw->smooth_y = 0;
111+sw->smooth_b0 = 0;
112+sw->smooth_a1 = 0;
113+sw->smooth_pamt = -100;
114+#+END_SRC
115+
116+Here are the inertia filter parameters, prefixed with =
117+
118+#+NAME: sk_swell
119+#+BEGIN_SRC c
120+SKFLT inertia_y;
121+SKFLT inertia_a1;
122+SKFLT inertia_b0;
123+SKFLT inertia_pamt;
124+#+END_SRC
125+
126+#+NAME: init
127+#+BEGIN_SRC c
128+sw->inertia_y = 0;
129+sw->inertia_b0 = 0;
130+sw->inertia_a1 = 0;
131+sw->inertia_pamt = -100;
132+#+END_SRC
133+
134+one divided by samplerate constant. Used to update filter
135+coefficients.
136+
137+#+NAME: sk_swell
138+#+BEGIN_SRC c
139+SKFLT onedsr;
140+#+END_SRC
141+
142+#+NAME: init
143+#+BEGIN_SRC c
144+sw->onedsr = 1.0 / (SKFLT) sr;
145+#+END_SRC
146+* Unit Delay For Direction Detection
147+In order to the direction of a sample, the previous
148+sample is stored in what could be considered a
149+1-sample delay, or =unit delay=.
150+
151+A more robust way to determine direction would be to
152+examine more than just the previous sample. But this
153+approach is faster, simpler, and good enough for the
154+kinds of slow moving control signals that will be
155+expected. Also, since there's also inertia filtering on
156+top of this, having a flickering direction isn't too
157+big of a deal.
158+
159+The variable, called =prev=, will be set to be an unlikely
160+starting value of =-123.456=.
161+
162+#+NAME: sk_swell
163+#+BEGIN_SRC c
164+SKFLT prev;
165+#+END_SRC
166+
167+#+BEGIN_SRC c
168+sw->prev = -123.456;
169+#+END_SRC
35170 * Parameters
36171 Swell takes in three parameters: smooth factor A, smooth
37172 factor B, and inertia.
38173
39174 Set smoothing factor A with =sk_swell_smootha=.
40175
176+#+NAME: funcdefs
177+#+BEGIN_SRC c
178+void sk_swell_smootha(sk_swell *sw, SKFLT smooth);
179+#+END_SRC
180+
181+#+NAME: funcs
182+#+BEGIN_SRC c
183+void 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
191+SKFLT sm_a;
192+#+END_SRC
193+
194+#+NAME: init
195+#+BEGIN_SRC c
196+sk_swell_smootha(sw, 10);
197+#+END_SRC
198+
41199 Set smoothing factor B with =sk_swell_smoothb=.
42200
43−Set inertia with =sk_swell_inertia=. This parameter uses
44−@!(ref "parameter_caching" "parameter caching")!@.
201+#+NAME: funcdefs
202+#+BEGIN_SRC c
203+void sk_swell_smoothb(sk_swell *sw, SKFLT smooth);
204+#+END_SRC
205+
206+#+NAME: funcs
207+#+BEGIN_SRC c
208+void 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
216+SKFLT sm_b;
217+#+END_SRC
218+
219+#+NAME: init
220+#+BEGIN_SRC c
221+sk_swell_smoothb(sw, 0.1);
222+#+END_SRC
223+
224+Set inertia with =sk_swell_inertia=.
225+
226+#+NAME: funcdefs
227+#+BEGIN_SRC c
228+void sk_swell_inertia(sk_swell *sw, SKFLT inertia);
229+#+END_SRC
230+
231+#+NAME: funcs
232+#+BEGIN_SRC c
233+void 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
241+SKFLT inertia_amt;
242+#+END_SRC
243+
244+#+NAME: init
245+#+BEGIN_SRC c
246+sk_swell_inertia(sw, 0.001);
247+#+END_SRC
248+* Direction Storage Pointer
249+At any point in time, the smoothing amount can either be
250+parameter A or B. A pointer =dir= is used to store the
251+address of one of these values.
252+
253+#+NAME: sk_swell
254+#+BEGIN_SRC c
255+SKFLT *dir;
256+#+END_SRC
257+
258+#+NAME: init
259+#+BEGIN_SRC c
260+sw->dir = &sw->sm_a;
261+#+END_SRC
45262 * Computation
46263 The function =sk_swell_tick= computes one sample of audio
47264 from an input signal =in=.
48265
266+#+NAME: funcdefs
267+#+BEGIN_SRC c
268+SKFLT sk_swell_tick(sk_swell *sw, SKFLT in);
269+#+END_SRC
270+
271+#+NAME: funcs
272+#+BEGIN_SRC c
273+SKFLT 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+
49287 The direction is determined by comparing the current input
50288 signal with the previous input signal. A positive change
51289 will 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
53291 last selected factor. This will be referred to as the
54292 =switching= signal.
55293
294+#+NAME: determine_direction
295+#+BEGIN_SRC c
296+if (in > sw->prev) {
297+ sw->dir = &sw->sm_a;
298+} else if (in < sw->prev) {
299+ sw->dir = &sw->sm_b;
300+}
301+sw->prev = in;
302+dir = *sw->dir;
303+switching = dir;
304+#+END_SRC
305+
56306 The switching signal is put through a 1-pole filter known
57307 as the inertia filter. Before computing this filter, the
58308 inertia parameter is checked to see if it needs to be
59−updated via @!(ref "parameter_caching" "parameter
60−caching")!@.
309+updated via @!(ref "parameter_caching"
310+"parameter caching")!@.
311+
312+#+NAME: filter_switch_signal
313+#+BEGIN_SRC c
314+if (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+
320+switching =
321+ sw->inertia_b0*switching +
322+ sw->inertia_a1*sw->inertia_y;
323+sw->inertia_y = switching;
324+#+END_SRC
61325
62326 The filtered switching signal is then snapped to the A/B
63327 smoothing parameter if it is within an epsilon boundary.
@@ -66,7 +330,26 @@ constantly re-calculating filter coefficients. Calls to · 2 unmodified lines
66330 math functions like =exp= can be expensive, and should be
67331 avoided if possible.
68332
333+#+NAME: snap_signal
334+#+BEGIN_SRC c
335+if (fabs(switching - dir) < eps) switching = dir;
336+#+END_SRC
337+
69338 Using the filtered-and-snapped switching signal, the main
70339 smoothing filter can now be computed on the input signal.
71340 Similar to the inertia filter, parameter caching is also
72341 used.
342+
343+#+NAME: compute_smoothing_filter
344+#+BEGIN_SRC c
345+if (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+
351+out =
352+ sw->smooth_b0*switching +
353+ sw->smooth_a1*sw->smooth_y;
354+sw->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