2026-03-26 11:57:43 +01:00
|
|
|
package synth_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/netsynth/netsynth/synth"
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
func TestEnvelopeAttackRise(t *testing.T) {
|
|
|
|
|
// Sustained protocol (Bursty=false): slow 2s attack
|
|
|
|
|
cfg := synth.FreqConfig{
|
2026-03-26 11:57:43 +01:00
|
|
|
BaseHz: 440.0,
|
|
|
|
|
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
|
|
|
|
Pan: 0.0,
|
2026-03-27 19:38:58 +01:00
|
|
|
Group: "Web",
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
2026-03-27 19:38:58 +01:00
|
|
|
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
2026-03-26 11:57:43 +01:00
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
// Trigger envelope by sending traffic
|
2026-03-26 11:57:43 +01:00
|
|
|
layer.UpdateTarget(100, 100)
|
|
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
// After 2 seconds (full attack), envelope level should be significant
|
|
|
|
|
for i := 0; i < synth.SampleRate*2; i++ {
|
2026-03-26 11:57:43 +01:00
|
|
|
layer.AdvanceSample()
|
|
|
|
|
}
|
|
|
|
|
if layer.CurrentAmp() <= 0.5 {
|
2026-03-27 19:38:58 +01:00
|
|
|
t.Errorf("expected envelope level > 0.5 after 2 second attack, got %.4f", layer.CurrentAmp())
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
func TestBurstyEnvelopeFastAttack(t *testing.T) {
|
|
|
|
|
// Bursty protocol: fast 30ms attack
|
2026-03-26 11:57:43 +01:00
|
|
|
cfg := synth.FreqConfig{
|
|
|
|
|
BaseHz: 440.0,
|
|
|
|
|
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
|
|
|
|
Pan: 0.0,
|
2026-03-27 19:38:58 +01:00
|
|
|
Group: "Infrastructure",
|
|
|
|
|
Bursty: true,
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
|
|
|
|
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
|
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
layer.UpdateTarget(100, 100)
|
|
|
|
|
|
|
|
|
|
// After 100ms, bursty envelope should have peaked and be decaying
|
|
|
|
|
for i := 0; i < synth.SampleRate/10; i++ {
|
|
|
|
|
layer.AdvanceSample()
|
|
|
|
|
}
|
|
|
|
|
// Bursty: sustain=0, so it should already be fading
|
|
|
|
|
// But it should have been triggered (not zero at peak)
|
|
|
|
|
if !layer.Seen() {
|
|
|
|
|
t.Error("expected layer.Seen() to be true after receiving traffic")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestEnvelopeRelease(t *testing.T) {
|
|
|
|
|
cfg := synth.FreqConfig{
|
|
|
|
|
BaseHz: 440.0,
|
|
|
|
|
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
|
|
|
|
Pan: 0.0,
|
|
|
|
|
Group: "Web",
|
|
|
|
|
}
|
|
|
|
|
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
|
|
|
|
|
|
|
|
|
// Build up
|
|
|
|
|
layer.UpdateTarget(100, 100)
|
2026-03-26 11:57:43 +01:00
|
|
|
for i := 0; i < synth.SampleRate*3; i++ {
|
|
|
|
|
layer.AdvanceSample()
|
|
|
|
|
}
|
2026-03-27 19:38:58 +01:00
|
|
|
peakAmp := layer.CurrentAmp()
|
|
|
|
|
|
|
|
|
|
// Release: set count to 0
|
2026-03-26 11:57:43 +01:00
|
|
|
layer.UpdateTarget(0, 100)
|
2026-03-27 19:38:58 +01:00
|
|
|
for i := 0; i < synth.SampleRate*5; i++ {
|
2026-03-26 11:57:43 +01:00
|
|
|
layer.AdvanceSample()
|
|
|
|
|
}
|
2026-03-27 19:38:58 +01:00
|
|
|
|
|
|
|
|
// After 5 seconds of release (release time is 4s), should be much lower
|
|
|
|
|
if layer.CurrentAmp() >= peakAmp*0.5 {
|
|
|
|
|
t.Errorf("expected envelope to decay significantly, peak=%.4f, current=%.4f", peakAmp, layer.CurrentAmp())
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-27 19:38:58 +01:00
|
|
|
func TestLayerNotSeenIssilent(t *testing.T) {
|
2026-03-26 11:57:43 +01:00
|
|
|
cfg := synth.FreqConfig{
|
|
|
|
|
BaseHz: 440.0,
|
|
|
|
|
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
|
|
|
|
Pan: 0.0,
|
2026-03-27 19:38:58 +01:00
|
|
|
Group: "Web",
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
|
|
|
|
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
|
|
|
|
|
|
|
|
|
// Never seen — UpdateTarget with zero count
|
|
|
|
|
layer.UpdateTarget(0, 100)
|
2026-03-27 19:38:58 +01:00
|
|
|
if layer.Seen() {
|
|
|
|
|
t.Error("expected Seen()=false for layer that never had traffic")
|
|
|
|
|
}
|
|
|
|
|
// Advance some samples — output should be zero
|
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
|
sample := layer.AdvanceSample()
|
|
|
|
|
if sample != 0 {
|
|
|
|
|
t.Errorf("expected zero output for unseen layer, got %v at sample %d", sample, i)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestLayerSeenFlag(t *testing.T) {
|
|
|
|
|
cfg := synth.FreqConfig{
|
|
|
|
|
BaseHz: 440.0,
|
|
|
|
|
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
|
|
|
|
Pan: 0.0,
|
|
|
|
|
Group: "Web",
|
|
|
|
|
}
|
|
|
|
|
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
|
|
|
|
|
|
|
|
|
if layer.Seen() {
|
|
|
|
|
t.Error("expected Seen()=false initially")
|
|
|
|
|
}
|
|
|
|
|
layer.UpdateTarget(1, 100)
|
|
|
|
|
if !layer.Seen() {
|
|
|
|
|
t.Error("expected Seen()=true after receiving traffic")
|
2026-03-26 11:57:43 +01:00
|
|
|
}
|
|
|
|
|
}
|