Improved UI

This commit is contained in:
Martino Ferrari
2026-05-06 15:55:45 +02:00
parent 0a5a85e4c4
commit 912ecdd9ed
19 changed files with 1141 additions and 279 deletions
@@ -99,6 +99,16 @@ func buildNode(d NodeDef) (dsp.Node, error) {
case "expr":
return &dsp.ExprNode{Expr: stringParam(p, "expr")}, nil
case "lowpass":
order := int(floatParam(p, "order"))
if order < 1 {
order = 1
}
return &dsp.LowPassNode{
Freq: floatParam(p, "freq"),
Order: order,
}, nil
case "lua":
return &dsp.LuaNode{Script: stringParam(p, "script")}, nil
+62
View File
@@ -535,6 +535,68 @@ func (p *exprParser) parseFactor() (float64, error) {
return 0, fmt.Errorf("unexpected character %q at position %d", ch, p.pos)
}
// ── LowPassNode ───────────────────────────────────────────────────────────────
// LowPassNode implements a cascaded first-order IIR (RC) low-pass filter.
// The filter coefficient α is recomputed each sample from the elapsed time so
// it is correct for event-driven (non-fixed-rate) signals.
// For Order > 1 the single-pole stage is cascaded Order times, approximating
// a Butterworth response with -20·Order dB/decade roll-off.
type LowPassNode struct {
Freq float64 // cutoff frequency in Hz (3 dB point)
Order int // number of cascaded stages (18)
}
func (n *LowPassNode) Type() string { return "lowpass" }
func (n *LowPassNode) Process(inputs []float64, state map[string]any) (float64, error) {
if len(inputs) == 0 {
return 0, errors.New("lowpass: no inputs")
}
x := inputs[0]
now := time.Now()
order := n.Order
if order < 1 {
order = 1
}
if order > 8 {
order = 8
}
fc := n.Freq
if fc <= 0 {
fc = 1.0
}
prevT, hasT := state["t"].(time.Time)
if !hasT {
// First sample: prime all stage states with the input value.
for i := range order {
state[fmt.Sprintf("y%d", i)] = x
}
state["t"] = now
return x, nil
}
dt := now.Sub(prevT).Seconds()
state["t"] = now
if dt <= 0 {
dt = 1e-9
}
rc := 1.0 / (2.0 * math.Pi * fc)
alpha := dt / (rc + dt)
y := x
for i := range order {
key := fmt.Sprintf("y%d", i)
prev, _ := state[key].(float64)
y = alpha*y + (1-alpha)*prev
state[key] = y
}
return y, nil
}
// ── LuaNode ───────────────────────────────────────────────────────────────────
// LuaNode runs a Lua script in a sandboxed gopher-lua VM.