aboutsummaryrefslogtreecommitdiff
path: root/filter.bqn
blob: f6b8f6bb550d882ee7e649d368f0583907fc1bb1 (plain)
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
# 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
SinCosTan  •math

# Generalized filtering
# 𝕩 is the signal to filter
# 𝕨 is ⟨result coefficients , 𝕩 coefficients⟩.
Filter  {
  i0⟩‿⟨o0 𝕊𝕩:
    0 (×o0+i0×)` 𝕩
 ;i0,i1⟩‿⟨o0 𝕊𝕩:
    a1a00
    0 { (o0×𝕨)+(i1×a1𝕩)+i0×a0a1 }` 𝕩
 ;i0,i1,i2⟩‿⟨o0,o1 𝕊𝕩:
    b0a2a1a00
    0 { (o1×b0𝕨)+(o0×b0)+(i2×a2𝕩)+(i1×a1a2)+i0×a0a1 }` 𝕩
 ;coeff 𝕊𝕩:
    ab  0×coeff  # accumulators for input and result
    c  coeff
    {
      a«˜𝕩
      r+´c×ab
      b«˜r
      r
    }¨ 𝕩
}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  { typenPassesisHighpass:
  t  f2types< type
  ! 3 > t
  c  t4√-1 , 1-˜ , 3×0.5-˜·√-0.75 nPasses2 # cutoff frequency correction
  x  t  21 , 21 , 33                         # p and g
  Clp2  {
    e  ¯1  a  𝕨 × (ט) Tom 𝕩
    b  (121 ˜ 2×1-˜÷e) × e ÷ 1++´a
    ¯3 (↑⋈↓) (1-+´) b
  }
  (isHighpass  
    x Clp2 ÷c
    1¯11, 1¯1 × x Clp2 ·{(o.freq÷2)-𝕩}×c
  ) _f
}

Lp2Hp2, Lp2_CDHp2_CD, Lp2_BSHp2_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 ÷˜ 𝕏  -12×) Cos)´ _f
}
Lp2qHp2qBp2q  Resfilt¨  1212˙ , 12¯12˙ , ×10¯1 

# Other 2-pole filters
Peak  { gfq:
  k  Tom f
  ab  140230  +(k×)´¨ ¯202 < {1𝕩1}¨ ⥊≍- q ÷˜ 100⌈-g÷20
  3 (  -) (1 ÷ ) ab
} _f
# Q to bandwidth and vice-versa for peaking filters
NtoQ  {𝕊𝕩: (√÷-1)𝕩 ; 𝕊𝕩: -1(ט)+ 12×ט𝕩} 2
QtoN  NtoQ

Shelf  {
  # t is type: 1 for bass and ¯1 for treble shelf
  tgfq  (2)(3=≠) 𝕩
  k  Tom f
  v1  10  t × 0⌈-g÷40
  ab   102˘ (ט1v1) ÷˜ (k×v1) {+(𝕨×)´𝕩} ¯202 < {1𝕩1}¨ - q
  2 (  -) (1 ÷ ) ab
} _f
# Shortcuts for low- and high-shelf
LShelf   1Shelf
HShelf  ¯1Shelf

Notch  {
  # Filter with no gain outside of the notch due to A.G. Constantinides
  fw  𝕩  # w is width of ≥3dB attenuation in Hz.
  c2×Cos Om f  tTom w
  1,-c,1⟩‿⟨t-1,c ÷ 1+t
} _f

AllPass  ((-1) 1,¯2×⊑,+´×˜{𝕎𝕩}¨<) _f