-
Notifications
You must be signed in to change notification settings - Fork 1
/
filter.bqn
131 lines (114 loc) · 4.46 KB
/
filter.bqn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# IIR filters
# Filters are used to make some frequencies louder or quieter in order
# to change a sound's tone.
# Each one takes some parameters for its left argument and a signal on
# the right.
⟨
# 1-pole filters (f:frequency)
Lp1, Hp1 # f low/high-pass
# 2-pole filters (g:gain, w:width, Q decreases with width)
Lp2, Hp2 # f Butterworth low/high-pass
Lp2q,Hp2q,Bp2q # f Q Butterworth low/high/band-pass with resonance
Peak # g f Q
LShelf, HShelf # g f Q? low/high-shelf
Notch # f w
AllPass # a‿b a+bi is complex with magnitude <1
# Utilities
NtoQ, QtoN # Bandwidth ←→ Q for peak filters
# Use Lp2⍟2 and Hp2⍟2 (4-pole Linkwitz-Riley) for crossovers
# Other 2-pole low/high-pass filters (I never use these)
# Filter2 generates Lp2 and Hp2 filters except the resonant ones
# If the second argument is n then the filter should be called n
# times and then gives a (2×n)-pass filter
Filter2
# Critically damped and Bessel filters
Lp2_CD,Hp2_CD, Lp2_BS,Hp2_BS
⟩⇐
"filter.bqn takes a single option namespace, or no arguments" ! 1≥≠•args
o ← ≠◶⟨•Import∘"options.bqn", ⊑⟩ •args
Sin‿Cos‿Tan ← •math
# Generalized filtering
# 𝕩 is the signal to filter
# 𝕨 is ⟨result coefficients , 𝕩 coefficients⟩.
Filter ← {
⟨i0⟩‿⟨o0⟩ 𝕊𝕩:
0 (×⟜o0+i0⊸×)` 𝕩
;⟨i0,i1⟩‿⟨o0⟩ 𝕊𝕩:
a1←a0←0
0 { (o0×𝕨)+(i1×a1↩𝕩)+i0×a0↩a1 }` 𝕩
;⟨i0,i1,i2⟩‿⟨o0,o1⟩ 𝕊𝕩:
b0←a2←a1←a0←0
0 { (o1×b0↩𝕨)+(o0×b0)+(i2×a2↩𝕩)+(i1×a1↩a2)+i0×a0↩a1 }` 𝕩
;coeff 𝕊𝕩:
a‿b ← 0×coeff # accumulators for input and result
c ← ∾coeff
{
a«˜↩𝕩
r←+´c×a∾b
b«˜↩r
r
}¨ 𝕩
}⎉1
{𝕤
fi ← "lib.so" (•state•BQN"•FFI"){𝔽} "&"‿"filter"∾∾("u64"⋈∾⟜"f64")¨"&**"
Filter ↩ Fi∘{ ∾≠⊸⋈¨ ⟨𝕩⟩∾𝕨 }⎉1
}⎊⊢@
_f ← { !∘0⊘(𝔽⊸Filter) }
# Compute the frequency response from coefficients
#Response ← ⋈⟜(1∾-)○⌽´ ⊸ ((|·÷´{+⟜(𝕩⊸×)´𝕨}¨)⎉∞‿0) ⟜ (⋆0i1×Om)
Om ← (2×π)×{𝕩÷o.freq}
Tom ← Tan Om÷2˙
# 1-pole low-pass and high-pass filters
Lp1 ← (⋈¨ ·⋈⟜¬ 1+⌾÷Om) _f
Hp1 ← ((-⊸⋈ ⋈ ⋈) 1÷∘+Om) _f
# 2-pole *-pass
f2types ← "bw"‿"cd"‿"bessel"
Filter2 ← { type‿nPasses‿isHighpass:
t ← f2types⊸⊐⌾< type
! 3 > t
c ← t◶⟨4√-⟜1 , √1-˜√ , √3×0.5-˜·√-⟜0.75⟩ nPasses√2 # cutoff frequency correction
x ← t ⊑ ⟨√2‿1 , 2‿1 , 3‿3⟩ # p and g
Clp2 ← {
e ← ¯1 ⊑ a ← 𝕨 × ≍⟜(ט) Tom 𝕩
b ← (1‿2‿1 ∾˜ 2×1-˜÷e) × e ÷ 1++´a
¯3 (↑⋈↓) (1-+´)⊸∾ b
}
(isHighpass ⊑ ⟨
x Clp2 ÷⟜c
⟨1‿¯1‿1, 1‿¯1⟩ × x Clp2 ·{(o.freq÷2)-𝕩}×⟜c
⟩) _f
}
⟨Lp2‿Hp2, Lp2_CD‿Hp2_CD, Lp2_BS‿Hp2_BS⟩ ← <˘f2types Filter2∘{𝕨‿1‿𝕩}⌜ ↕2
# Once more, with resonance
# Lp2q and Hp2q are identical to Lp2 and Hp2 when Q=÷√2
Resfilt ← {
Om⊸(Sin⊸÷⟜(2⊸×) (+⟜1 ÷˜ 𝕏 ⋈ -⟜1≍2⊸×) Cos∘⊣)´ _f
}
Lp2q‿Hp2q‿Bp2q ← Resfilt¨ ⟨ 1⊸-÷2‿1‿2˙ , 1⊸+÷2‿¯1‿2˙ , ×⟜1‿0‿¯1 ⟩
# Other 2-pole filters
Peak ← { g‿f‿q:
k ← Tom f
ab ← 1‿4‿0‿2‿3‿0 ⊏ +⟜(k⊸×)´¨ ¯2‿0‿2 <⊸∾ {1‿𝕩‿1}¨ ⥊≍⟜- q ÷˜ 10⋆0⌈-⊸≍g÷20
3 (↑ ⋈ -∘↓) (1⊸↓ ÷ ⊑) ab
} _f
# Q to bandwidth and vice-versa for peaking filters
NtoQ ← {𝕊𝕩: (√÷-⟜1)𝕩 ; 𝕊⁼𝕩: -⟜1⌾(ט)⊸+ 1+÷2×ט𝕩} 2⊸⋆
QtoN ← NtoQ⁼
Shelf ← {
# t is type: 1 for bass and ¯1 for treble shelf
t‿g‿f‿q ← ∾⟜(√2)⍟(3=≠) 𝕩
k ← Tom f
v1 ← 10 ⋆ t × 0⌈-⊸≍g÷40
ab ← ⥊ 1‿0‿2⊸⊏˘ (ט1⌊v1) ÷˜ (k×v1) {+⟜(𝕨⊸×)´𝕩}⌜ ¯2‿0‿2 <⊸∾ {1‿𝕩‿1}¨ ≍⟜- q
2 (↓ ⋈○⌽ -∘↑) (1⊸↓ ÷ ⊑) ab
} _f
# Shortcuts for low- and high-shelf
LShelf ← 1⊸∾⊸Shelf
HShelf ← ¯1⊸∾⊸Shelf
Notch ← {
# Filter with no gain outside of the notch due to A.G. Constantinides
f‿w ← 𝕩 # w is width of ≥3dB attenuation in Hz.
c←2×Cos Om f ⋄ t←Tom w
⟨1,-c,1⟩‿⟨t-1,c⟩ ÷ 1+t
} _f
AllPass ← (⋈⟜(-∘⌽1⊸↓) ⟨1,¯2×⊑,+´×˜⟩{𝕎𝕩}¨<) _f