test(02-01): add failing tests for synth config, oscillator, and layer
- TestAllClassesHaveConfig, TestFrequenciesInRange, TestFrequenciesUnique - TestHarmonicsNonEmpty, TestPanPositionsInRange - TestOscillatorAdvance, TestOscillatorPhaseWrap, TestOscillatorDistinctFreqs - TestEMAAmplitudeRise, TestEMAAmplitudeDecay, TestWhisperFloor, TestWhisperFloorNotSeenIsZero
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package synth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/netsynth/netsynth/classify"
|
||||
"github.com/netsynth/netsynth/synth"
|
||||
)
|
||||
|
||||
func TestAllClassesHaveConfig(t *testing.T) {
|
||||
for _, class := range classify.AllClasses() {
|
||||
if _, ok := synth.ClassFreqConfigs[class]; !ok {
|
||||
t.Errorf("class %q has no entry in ClassFreqConfigs", class)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrequenciesInRange(t *testing.T) {
|
||||
for class, cfg := range synth.ClassFreqConfigs {
|
||||
if cfg.BaseHz < 60 || cfg.BaseHz > 800 {
|
||||
t.Errorf("class %q BaseHz=%.1f is out of range [60, 800]", class, cfg.BaseHz)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrequenciesUnique(t *testing.T) {
|
||||
seen := make(map[float64]classify.TrafficClass)
|
||||
for class, cfg := range synth.ClassFreqConfigs {
|
||||
if prev, exists := seen[cfg.BaseHz]; exists {
|
||||
t.Errorf("classes %q and %q share the same BaseHz=%.1f", prev, class, cfg.BaseHz)
|
||||
}
|
||||
seen[cfg.BaseHz] = class
|
||||
}
|
||||
}
|
||||
|
||||
func TestHarmonicsNonEmpty(t *testing.T) {
|
||||
for class, cfg := range synth.ClassFreqConfigs {
|
||||
if len(cfg.Harmonics) < 2 {
|
||||
t.Errorf("class %q has fewer than 2 harmonics (got %d)", class, len(cfg.Harmonics))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPanPositionsInRange(t *testing.T) {
|
||||
for class, cfg := range synth.ClassFreqConfigs {
|
||||
if cfg.Pan < -1.0 || cfg.Pan > 1.0 {
|
||||
t.Errorf("class %q Pan=%.2f is out of range [-1.0, 1.0]", class, cfg.Pan)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package synth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/netsynth/netsynth/synth"
|
||||
)
|
||||
|
||||
func TestEMAAmplitudeRise(t *testing.T) {
|
||||
cfg2 := synth.FreqConfig{
|
||||
BaseHz: 440.0,
|
||||
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
||||
Pan: 0.0,
|
||||
}
|
||||
layer := synth.NewLayer(cfg2, synth.SampleRate, 1.0)
|
||||
|
||||
// Force the layer to have been seen and set target to 1.0
|
||||
layer.UpdateTarget(100, 100)
|
||||
|
||||
// After 1 second (SampleRate samples) with tau=1.0, currentAmp should be > 0.5
|
||||
// EMA: after tau seconds, amplitude reaches ~63% of target
|
||||
for i := 0; i < synth.SampleRate; i++ {
|
||||
layer.AdvanceSample()
|
||||
}
|
||||
if layer.CurrentAmp() <= 0.5 {
|
||||
t.Errorf("expected currentAmp > 0.5 after 1 second rise, got %.4f", layer.CurrentAmp())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEMAAmplitudeDecay(t *testing.T) {
|
||||
cfg := synth.FreqConfig{
|
||||
BaseHz: 440.0,
|
||||
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
||||
Pan: 0.0,
|
||||
}
|
||||
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
||||
|
||||
// Set up: layer has been seen (currentAmp starts at 1.0) and target is whisper floor
|
||||
// We'll manually prime by updating target with 100/100 first then re-route to whisper
|
||||
layer.UpdateTarget(100, 100) // mark as seen, target=1.0
|
||||
// Force currentAmp to 1.0 by running a few cycles at target 1.0
|
||||
for i := 0; i < synth.SampleRate*3; i++ {
|
||||
layer.AdvanceSample()
|
||||
}
|
||||
// Now decay: set count=0 (whisper floor kicks in)
|
||||
layer.UpdateTarget(0, 100)
|
||||
// After 1 second, currentAmp should be < 0.5
|
||||
for i := 0; i < synth.SampleRate; i++ {
|
||||
layer.AdvanceSample()
|
||||
}
|
||||
if layer.CurrentAmp() >= 0.5 {
|
||||
t.Errorf("expected currentAmp < 0.5 after 1 second decay, got %.4f", layer.CurrentAmp())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhisperFloor(t *testing.T) {
|
||||
cfg := synth.FreqConfig{
|
||||
BaseHz: 440.0,
|
||||
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
||||
Pan: 0.0,
|
||||
}
|
||||
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
||||
|
||||
// Mark layer as seen by passing count=1, then set count=0
|
||||
layer.UpdateTarget(1, 100)
|
||||
layer.UpdateTarget(0, 100)
|
||||
|
||||
// Target should be whisper floor (not zero) because seen=true
|
||||
if layer.TargetAmp() < synth.WhisperFloor {
|
||||
t.Errorf("expected targetAmp >= WhisperFloor (%.2f) for seen layer at zero count, got %.4f",
|
||||
synth.WhisperFloor, layer.TargetAmp())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhisperFloorNotSeenIsZero(t *testing.T) {
|
||||
cfg := synth.FreqConfig{
|
||||
BaseHz: 440.0,
|
||||
Harmonics: []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}},
|
||||
Pan: 0.0,
|
||||
}
|
||||
layer := synth.NewLayer(cfg, synth.SampleRate, 1.0)
|
||||
|
||||
// Never seen — UpdateTarget with zero count
|
||||
layer.UpdateTarget(0, 100)
|
||||
if layer.TargetAmp() != 0.0 {
|
||||
t.Errorf("expected targetAmp == 0.0 for unseen layer, got %.4f", layer.TargetAmp())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package synth_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/netsynth/netsynth/synth"
|
||||
)
|
||||
|
||||
func TestOscillatorAdvance(t *testing.T) {
|
||||
harmonics := []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}, {Ratio: 2, Amplitude: 0.5}}
|
||||
osc := synth.NewOscillator(440.0, synth.SampleRate)
|
||||
|
||||
hasPos := false
|
||||
hasNeg := false
|
||||
for i := 0; i < 100; i++ {
|
||||
s := osc.Advance(harmonics)
|
||||
if s > 0 {
|
||||
hasPos = true
|
||||
}
|
||||
if s < 0 {
|
||||
hasNeg = true
|
||||
}
|
||||
}
|
||||
if !hasPos || !hasNeg {
|
||||
t.Errorf("expected both positive and negative samples; hasPos=%v hasNeg=%v", hasPos, hasNeg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOscillatorPhaseWrap(t *testing.T) {
|
||||
harmonics := []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}}
|
||||
osc := synth.NewOscillator(440.0, synth.SampleRate)
|
||||
|
||||
for i := 0; i < synth.SampleRate; i++ {
|
||||
osc.Advance(harmonics)
|
||||
}
|
||||
p := osc.Phase()
|
||||
if p < 0 || p >= 1.0 {
|
||||
t.Errorf("expected phase in [0, 1), got %.6f", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOscillatorDistinctFreqs(t *testing.T) {
|
||||
harmonics := []synth.HarmonicDef{{Ratio: 1, Amplitude: 1.0}}
|
||||
osc65 := synth.NewOscillator(65.0, synth.SampleRate)
|
||||
osc440 := synth.NewOscillator(440.0, synth.SampleRate)
|
||||
|
||||
same := true
|
||||
for i := 0; i < 100; i++ {
|
||||
s65 := osc65.Advance(harmonics)
|
||||
s440 := osc440.Advance(harmonics)
|
||||
if s65 != s440 {
|
||||
same = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if same {
|
||||
t.Error("expected oscillators at 65 Hz and 440 Hz to produce different sample sequences")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user