aboutsummaryrefslogtreecommitdiffhomepage
path: root/filters.lua
diff options
context:
space:
mode:
Diffstat (limited to 'filters.lua')
-rw-r--r--filters.lua186
1 files changed, 106 insertions, 80 deletions
diff --git a/filters.lua b/filters.lua
index e9b2900..3deb790 100644
--- a/filters.lua
+++ b/filters.lua
@@ -1,86 +1,15 @@
---[==[
---
--- Non-working FIR filters (FIXME)
---
-
--- Normalized Sinc function
-local function Sinc(x)
- return x == 0 and 1 or
- math.sin(2*math.pi*x)/(2*math.pi*x)
-end
-
-local function Hamming(n, window)
- local alpha = 0.54
- return alpha - (1-alpha)*math.cos((2*math.pi*n)/(window-1))
-end
-local function Blackman(n, window)
- local alpha = 0.16
- return (1-alpha)/2 -
- 0.5*math.cos((2*math.pi*n)/(window-1)) +
- alpha*0.5*math.cos((4*math.pi*n)/(window-1))
-end
-
-FIRStream = DeriveClass(Stream)
-
-function FIRStream:ctor(stream, freq_stream)
- self.stream = tostream(stream)
- self.freq_stream = tostream(freq_stream)
-end
-
-function FIRStream:gtick()
- local window = {}
-
- -- window size (max. 1024 samples)
- -- this is the max. latency introduced by the filter
- -- since the window must be filled before we can generate
- -- (filtered) samples
- local window_size = math.min(1024, self.stream:len())
- local window_p = window_size-1
- local accu = 0
-
- local blackman = {}
- for i = 1, window_size do blackman[i] = Blackman(i-1, window_size) end
-
- local tick = self.stream:gtick()
- local freq_tick = self.freq_stream:gtick()
-
- return function()
- -- fill buffer (initial)
- while #window < window_size-1 do
- table.insert(window, tick())
- end
-
- window[window_p+1] = tick()
- window_p = (window_p + 1) % window_size
-
- local period = freq_tick()/samplerate
-
- local sample = 0
- local i = window_p
- repeat
- -- FIXME
- sample = sample + window[(i % window_size)+1] *
- Sinc((i-window_p - window_size/2)/period) *
- blackman[i-window_p+1]
- i = i + 1
- until (i % window_size) == window_p
-
- return sample
- end
-end
-
-function FIRStream:len()
- return self.stream:len()
-end
-]==]
+---
+--- @module applause
+---
--
-- General-purpose IIR filters:
-- These are direct translations of ChucK's LPF, HPF, BPF and BRF
-- ugens which are in turn adapted from SuperCollider 3.
+-- See also https://chuck.stanford.edu/doc/program/ugen_full.html#LPF
--
--- De-denormalize function adapted from ChucK.
+--- De-denormalize function adapted from ChucK.
-- Not quite sure why this is needed - properly to make the
-- IIR filters numerically more stable.
local function ddn(f)
@@ -88,6 +17,9 @@ local function ddn(f)
(f < -1e-15 and f > -1e15 and f or 0)
end
+--- Low Pass Filter streams.
+-- @type LPFStream
+-- @local
LPFStream = DeriveClass(MuxableStream)
function LPFStream:muxableCtor(stream, freq)
@@ -147,6 +79,11 @@ function LPFStream:len()
return self.stream:len()
end
+--- Apply Low Pass Filter to stream.
+-- This is a resonant filter (2nd order Butterworth).
+-- @within Class Stream
+-- @StreamableNumber freq Cutoff frequency.
+-- @treturn Stream
function Stream:LPF(freq)
return LPFStream:new(self, freq)
end
@@ -213,12 +150,15 @@ function HPFStream:len()
return self.stream:len()
end
+--- Apply High Pass Filter to stream.
+-- This is a resonant filter (2nd order Butterworth).
+-- @within Class Stream
+-- @StreamableNumber freq Cutoff frequency.
+-- @treturn Stream
function Stream:HPF(freq)
return HPFStream:new(self, freq)
end
--- NOTE: The quality factor, indirectly proportional
--- to the passband width
BPFStream = DeriveClass(MuxableStream)
function BPFStream:muxableCtor(stream, freq, quality)
@@ -284,12 +224,16 @@ function BPFStream:len()
return self.stream:len()
end
+--- Apply Band Pass Filter to stream (2nd order Butterworth).
+-- @within Class Stream
+-- @StreamableNumber freq Center frequency.
+-- @StreamableNumber quality
+-- The quality factor, indirectly proportional to the passband width.
+-- @treturn Stream
function Stream:BPF(freq, quality)
return BPFStream:new(self, freq, quality)
end
--- NOTE: The quality factor, indirectly proportional
--- to the passband width
BRFStream = DeriveClass(MuxableStream)
function BRFStream:muxableCtor(stream, freq, quality)
@@ -356,6 +300,88 @@ function BRFStream:len()
return self.stream:len()
end
+--- Apply Band Reject Filter to stream (2nd order Butterworth).
+-- @within Class Stream
+-- @StreamableNumber freq Center frequency.
+-- @StreamableNumber quality
+-- The quality factor, indirectly proportional to the rejectband width.
+-- @treturn Stream
function Stream:BRF(freq, quality)
return BRFStream:new(self, freq, quality)
end
+
+--[==[
+--
+-- Non-working FIR filters (FIXME)
+--
+
+-- Normalized Sinc function
+local function Sinc(x)
+ return x == 0 and 1 or
+ math.sin(2*math.pi*x)/(2*math.pi*x)
+end
+
+local function Hamming(n, window)
+ local alpha = 0.54
+ return alpha - (1-alpha)*math.cos((2*math.pi*n)/(window-1))
+end
+local function Blackman(n, window)
+ local alpha = 0.16
+ return (1-alpha)/2 -
+ 0.5*math.cos((2*math.pi*n)/(window-1)) +
+ alpha*0.5*math.cos((4*math.pi*n)/(window-1))
+end
+
+FIRStream = DeriveClass(Stream)
+
+function FIRStream:ctor(stream, freq_stream)
+ self.stream = tostream(stream)
+ self.freq_stream = tostream(freq_stream)
+end
+
+function FIRStream:gtick()
+ local window = {}
+
+ -- window size (max. 1024 samples)
+ -- this is the max. latency introduced by the filter
+ -- since the window must be filled before we can generate
+ -- (filtered) samples
+ local window_size = math.min(1024, self.stream:len())
+ local window_p = window_size-1
+ local accu = 0
+
+ local blackman = {}
+ for i = 1, window_size do blackman[i] = Blackman(i-1, window_size) end
+
+ local tick = self.stream:gtick()
+ local freq_tick = self.freq_stream:gtick()
+
+ return function()
+ -- fill buffer (initial)
+ while #window < window_size-1 do
+ table.insert(window, tick())
+ end
+
+ window[window_p+1] = tick()
+ window_p = (window_p + 1) % window_size
+
+ local period = freq_tick()/samplerate
+
+ local sample = 0
+ local i = window_p
+ repeat
+ -- FIXME
+ sample = sample + window[(i % window_size)+1] *
+ Sinc((i-window_p - window_size/2)/period) *
+ blackman[i-window_p+1]
+ i = i + 1
+ until (i % window_size) == window_p
+
+ return sample
+ end
+end
+
+function FIRStream:len()
+ return self.stream:len()
+end
+]==]