summaryrefslogtreecommitdiff
path: root/internal/generator/generator_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-26 09:33:29 +0300
committerPaul Buetow <paul@buetow.org>2026-04-26 09:33:29 +0300
commit44f82e79f0b1a65d9df62c3fed7271affcb0a673 (patch)
tree4a41b3065e0d3f39cc8135ed925c216107312de4 /internal/generator/generator_test.go
parenteebb1df14b26b65493702c090d13e1a9c7563db6 (diff)
Add unique ambient melodies for all 19 themes
Each theme now has a distinctive looping melodic phrase (normal + wild variants) played by the Web Audio ambient engine. This replaces the generic random-pulse behavior that made all themes sound the same. Go changes: - Add melodyNote struct with freq/dur/gain fields to ambientPreset - Add n() helper for concise melody construction - Rewrite all 19 theme presets with unique melodic phrases: neon=glassy arpeggio, terminal=sparse CRT beeps, synthwave=warm minor, plasma=tritone shimmer, brutalist=concrete rumble, volcano=phrygian, aurora=high shimmer, matrix=digital ostinato, ocean=wave flow, dos=8-bit arpeggio, retro=VHS warmth, cosmos=vast fifths, retrofuture=lounge minor-7, spaceage=pure ascending, tropicale=pentatonic, noir=smoky minor-7, cathedral=organ thirds, surveillance=fourth alert, biomech=diminished unease JS changes: - Add melodyTimer/melodyIndex state to ambient engine - Add playMelodyNote() with per-note gain and filter support - Add scheduleMelodyNote() with phrase-rest gaps - schedulePulse() now routes to melody player when melody is present - Add sawtooth support to snonuxWaveType() - Reset melody index on startEngine() and stopAll() Test changes: - TestThemeSoundPresetsAmbientPopulated now accepts melody as valid content - TestThemeSoundPresetsAmbientValuesBounded validates melody note freq/dur
Diffstat (limited to 'internal/generator/generator_test.go')
-rw-r--r--internal/generator/generator_test.go16
1 files changed, 12 insertions, 4 deletions
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 3ceb72c..49d1a2f 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -158,11 +158,11 @@ func TestThemeSoundPresetsAmbientPopulated(t *testing.T) {
normal := preset.Ambient.Normal
wild := preset.Ambient.Wild
- if len(normal.DroneFreqs) == 0 && len(normal.PulseFreqs) == 0 {
- t.Errorf("theme %q ambient.Normal has no drone or pulse frequencies", name)
+ if len(normal.DroneFreqs) == 0 && len(normal.PulseFreqs) == 0 && len(normal.Melody) == 0 {
+ t.Errorf("theme %q ambient.Normal has no drone, pulse, or melody frequencies", name)
}
- if len(wild.DroneFreqs) == 0 && len(wild.PulseFreqs) == 0 {
- t.Errorf("theme %q ambient.Wild has no drone or pulse frequencies", name)
+ if len(wild.DroneFreqs) == 0 && len(wild.PulseFreqs) == 0 && len(wild.Melody) == 0 {
+ t.Errorf("theme %q ambient.Wild has no drone, pulse, or melody frequencies", name)
}
}
}
@@ -215,6 +215,14 @@ func TestThemeSoundPresetsAmbientValuesBounded(t *testing.T) {
t.Errorf("theme %q ambient.%s pulseFreqs[%d]=%f; want positive", name, mode, i, f)
}
}
+ for i, m := range a.Melody {
+ if m.Freq <= 0 {
+ t.Errorf("theme %q ambient.%s melody[%d].freq=%f; want positive", name, mode, i, m.Freq)
+ }
+ if m.Dur <= 0 {
+ t.Errorf("theme %q ambient.%s melody[%d].dur=%f; want positive", name, mode, i, m.Dur)
+ }
+ }
if a.CutoffMin < 0 || a.CutoffMin > 10000 {
t.Errorf("theme %q ambient.%s cutoffMin=%f; want [0, 10000]", name, mode, a.CutoffMin)
}