aboutsummaryrefslogtreecommitdiff
path: root/filter.bqn
blob: effdde4640103422dd6dc9b151b103ee1cd9a659 (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
128
129
130
131
132
# 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

# Generalized filtering
# 𝕩 is the signal to filter
# 𝕨 is ⟨result coefficients , 𝕩 coefficients⟩.
Filter  {
  i0⟩‿⟨o0 𝕊𝕩:
    b0a00
    { b0(o0×b0)+i0×a0𝕩 }¨ 𝕩
 ;i0,i1⟩‿⟨o0 𝕊𝕩:
    b0a1a00
    { b0(o0×b0)+(i1×a1𝕩)+i0×a0a1 }¨ 𝕩
 ;i0,i1,i2⟩‿⟨o0,o1 𝕊𝕩:
    b1b0a2a1a00
    { b1(o1×b0b1)+(o0×b0)+(i2×a2𝕩)+(i1×a1a2)+i0×a0a1 }¨ 𝕩
 ;coeff 𝕊𝕩:
    ab  0×coeff  # accumulators for input and result
    {
      a«˜𝕩
      r+´coeff+´×¨ab
      b«˜r
      r
    }¨ 𝕩
}1
_f  { !0(𝔽Filter) }

# Compute the frequency response from coefficients
#Response ← ≍○<⟜(1∾-)○⌽´ ⊸ ((|·÷´{+⟜(𝕩⊸×)´𝕨}¨)⎉∞‿0) ⟜ (⋆0i1×Om)

# Trig (approximations for now)
SP  (4×÷5-) 4׬×
Sin  (⊢-(>1)·SP 1|) 2π
Cos  Sin (π÷2)-
Tan  Sin ÷ Cos

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