package sfx import ( "fmt" "math/rand" "sort" ) // Preset returns ready-made parameters for classic game sounds. // seed 0 gives the canonical version; other seeds vary it slightly so // agents can generate alternatives ("give me three coin variants"). func Preset(name string, seed int64) (*Params, error) { p := Defaults() p.Name = name switch name { case "jump": p.Wave = "square" p.Duty = 0.5 p.Freq = 330 p.FreqSlide = 900 p.Attack = 0.01 p.Sustain = 0.08 p.Decay = 0.18 case "coin": p.Wave = "square" p.Duty = 0.5 p.Freq = 988 p.ArpFactor = 1.335 // up a fourth: B5 -> E6 p.ArpTime = 0.06 p.Attack = 0.005 p.Sustain = 0.08 p.Decay = 0.25 case "laser": p.Wave = "saw" p.Freq = 1400 p.FreqSlide = -6000 p.Attack = 0.005 p.Sustain = 0.05 p.Decay = 0.12 p.HighPass = 300 case "explosion": p.Wave = "noise" p.Freq = 900 p.FreqSlide = -600 p.Attack = 0.01 p.Sustain = 0.15 p.Decay = 0.55 p.LowPass = 2200 case "hurt": p.Wave = "saw" p.Freq = 300 p.FreqSlide = -700 p.Attack = 0.005 p.Sustain = 0.04 p.Decay = 0.14 case "powerup": p.Wave = "square" p.Duty = 0.4 p.Freq = 220 p.FreqSlide = 700 p.VibratoDepth = 25 p.VibratoRate = 9 p.Attack = 0.01 p.Sustain = 0.25 p.Decay = 0.25 case "blip": p.Wave = "square" p.Duty = 0.4 p.Freq = 660 p.Attack = 0.002 p.Sustain = 0.03 p.Decay = 0.05 default: return nil, fmt.Errorf("unknown preset %q (available: %s)", name, PresetNames()) } if seed != 0 { vary(&p, seed) } if err := p.Validate(); err != nil { return nil, fmt.Errorf("preset %s (seed %d): %w", name, seed, err) } return &p, nil } // vary nudges the tonal parameters deterministically from the seed. func vary(p *Params, seed int64) { rng := rand.New(rand.NewSource(seed)) jitter := func(v, amount float64) float64 { return v * (1 + amount*(rng.Float64()*2-1)) } p.Freq = jitter(p.Freq, 0.15) p.FreqSlide = jitter(p.FreqSlide, 0.25) p.Sustain = jitter(p.Sustain, 0.2) p.Decay = jitter(p.Decay, 0.2) if p.ArpFactor != 0 { p.ArpFactor = jitter(p.ArpFactor, 0.05) } p.Seed = seed // noise variation too } var presetNames = []string{"blip", "coin", "explosion", "hurt", "jump", "laser", "powerup"} // PresetNames lists the available presets, sorted. func PresetNames() string { sort.Strings(presetNames) out := "" for i, n := range presetNames { if i > 0 { out += ", " } out += n } return out }