diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-09-13 17:26:53 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-09-13 17:26:53 +0300 |
commit | 7fc0f17fb37326c86a83627b856a5b75f522c090 (patch) | |
tree | bc5de8b30c705afafc9b30e6fd3f5bc2b12f19d6 /filters.lua | |
parent | 1cfaa431c1f3c4f417811dcff342c9f28600f13f (diff) | |
download | applause2-7fc0f17fb37326c86a83627b856a5b75f522c090.tar.gz |
added LDoc documentation
* gives a useful overview of everything supported right now
* especially the type documentation is useful, as these things are not self-evident in Lua (because of dynamic typing).
* The LDoc page can later be published as the Github pages of the project.
This can even be done automatically by a Github action.
However, we should first make sure that it's okay to publish the project before defending the thesis since
Github pages will always be public even for private repositories.
* Documentation of command-line parameters is lacking (TODO).
* It may be possible to use types like "Stream(number)" to describe streams of numbers.
The LDoc documentation mentions boxed types.
Perhaps there can even be Streamable(number)?
* We are also lacking good example programs and/or introductory material.
Diffstat (limited to 'filters.lua')
-rw-r--r-- | filters.lua | 186 |
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 +]==] |