Skip to content

Instantly share code, notes, and snippets.

@wareya
Last active April 14, 2024 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wareya/20569f967f4031a2a52fef0025eab1a5 to your computer and use it in GitHub Desktop.
Save wareya/20569f967f4031a2a52fef0025eab1a5 to your computer and use it in GitHub Desktop.
declicking lua plugin for protoplug (https://github.com/pac-dev/protoplug)
--[[
name: Declicker
description: >
Declicker for saliva sounds etc. Based on a built-in filter for protoplug (but none of the original code remains).
--]]
require "include/protoplug"
local filters = {}
stereoFx.init()
local LowpassFilter = {}
LowpassFilter.__index = LowpassFilter
function LowpassFilter:init(cutoff_freq)
local self = setmetatable({}, LowpassFilter)
self:update_freq(cutoff_freq)
return self
end
filter_size = 64
function LowpassFilter:update_freq(cutoff_freq)
self.kernel = {}
self.memory = {}
local f = cutoff_freq / (44100.0 / 2.0)
local n = 0.0
for i = 0.0,filter_size do
-- sinc
local x = (i - filter_size/2.0) * math.pi * f
if (not(x == 0)) then
self.kernel[i] = math.sin(x)/x
else
self.kernel[i] = 1.0
end
-- window function
local y = ((i / filter_size)-0.5)*2.0
y = y * y
y = 1.0 - y
y = y * y
-- full krnel
self.kernel[i] = self.kernel[i] * y
-- normalizer
n = n + self.kernel[i]
-- init memory to zero
self.memory[i] = 0.0
end
-- normalize kernel
for i = 0,filter_size do
if (i == 20) then
print(self.kernel[i])
end
self.kernel[i] = self.kernel[i] / n
end
end
function LowpassFilter:filter(input_sample)
for i = filter_size,1,-1 do
self.memory[i] = self.memory[i-1]
end
self.memory[0] = input_sample
output_sample = 0.0
for i = 0,filter_size do
output_sample = output_sample + self.memory[i]*self.kernel[i]
end
--return (self.memory[0] + self.memory[1])/2
return {l = output_sample; h = self.memory[filter_size/2]-output_sample}
end
function stereoFx.Channel:init()
res = 0.5;
self.filter = LowpassFilter:init(3000.0)
table.insert(filters, self.filter)
self.follower = 0.0
self.fwsmooth = 0.0
self.amp = 1.0
self.ampsmooth = 1.0
self.hplog = {}
self.hplog_i = 0
self.lplog = {}
self.lplog_i = 0
self.pop = 0
end
myparams = {
sustain = 100;
decay = 0.01;
fwdecay = 0.005;
fwsmoothing = 0.05;
ampsmoothing = 0.5;
lookahead = 200;
}
function stereoFx.Channel:processBlock(s, smax)
local hp = {}
local lp = {}
local sustain = myparams.sustain
local decay = myparams.decay
local fwdecay = myparams.fwdecay
local fwsmoothing = myparams.fwsmoothing
local ampsmoothing = myparams.ampsmoothing
local lookahead = myparams.lookahead
for i = 0,smax do
local out = self.filter:filter(s[i])
lp[i] = out.l
hp[i] = out.h
if self.pop < 0 then
self.follower = self.follower*(1.0-fwdecay)
else
self.pop = self.pop - 1
end
self.follower = math.max(self.follower, math.abs(hp[i]))
self.fwsmooth = self.fwsmooth*(1.0-fwsmoothing) + self.follower*fwsmoothing
local real_amp = self.fwsmooth
self.hplog[self.hplog_i] = hp[i]
self.lplog[self.lplog_i] = lp[i]
self.hplog_i = self.hplog_i + 1
if (self.hplog_i > lookahead) then
self.hplog_i = 0
end
self.lplog_i = self.lplog_i + 1
if (self.lplog_i > lookahead) then
self.lplog_i = 0
end
hpout = self.hplog[self.hplog_i]
if (hpout == nil) then
hpout = 0.0
end
lpout = self.lplog[self.lplog_i]
if (lpout == nil) then
lpout = 0.0
end
self.amp = self.amp*(1.0-decay) + 1.0*decay
if (math.abs(hpout)*self.amp > real_amp) then
self.amp = real_amp/math.abs(hpout)
self.pop = sustain
end
self.ampsmooth = self.ampsmooth*(1.0-ampsmoothing) + self.amp*ampsmoothing
s[i] = lpout + hpout * self.ampsmooth
end
end
local function updateFilters(args)
for _, f in pairs(filters) do
f:update_freq(args)
end
end
params = plugin.manageParams {
{
name = "Cutoff Frequency";
min = 10;
max = 9990;
default = 5000;
changed = function(val) updateFilters(val) end;
};
{
name = "Lookahead samples";
min = 1;
max = 1999;
default = 1000;
changed = function(val) myparams.lookahead=val end;
};
{
name = "Sustain samples";
min = 0;
max = 1000;
default = 500;
changed = function(val) myparams.sustain=val end;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment