Files

46 lines
1.8 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package sound
import (
"bytes"
"encoding/binary"
"math"
"time"
)
// tone_wav generates mono, 16-bit PCM at 22050 Hz for the existing Player.
// Callers supply a positive frequency/duration, amplitude in [0, 1], and a
// nonnegative edge ramp. A zero ramp preserves an unsmoothed sine tone.
// Playback volume, pan, queuing, and cancellation belong to the sound service.
func tone_wav(frequency float64, duration time.Duration, amplitude float64, ramp time.Duration) []byte {
const sampleRate = 22050
sampleCount := int(float64(sampleRate) * duration.Seconds())
rampSamples := int(float64(sampleRate) * ramp.Seconds())
dataSize := sampleCount * 2
buffer := bytes.NewBuffer(make([]byte, 0, 44+dataSize))
buffer.WriteString("RIFF")
_ = binary.Write(buffer, binary.LittleEndian, uint32(36+dataSize))
buffer.WriteString("WAVEfmt ")
_ = binary.Write(buffer, binary.LittleEndian, uint32(16))
_ = binary.Write(buffer, binary.LittleEndian, uint16(1))
_ = binary.Write(buffer, binary.LittleEndian, uint16(1))
_ = binary.Write(buffer, binary.LittleEndian, uint32(sampleRate))
_ = binary.Write(buffer, binary.LittleEndian, uint32(sampleRate*2))
_ = binary.Write(buffer, binary.LittleEndian, uint16(2))
_ = binary.Write(buffer, binary.LittleEndian, uint16(16))
buffer.WriteString("data")
_ = binary.Write(buffer, binary.LittleEndian, uint32(dataSize))
for sample := 0; sample < sampleCount; sample++ {
envelope := 1.0
if rampSamples > 0 {
envelope = min(1.0, float64(min(sample, sampleCount-1-sample))/float64(rampSamples))
}
value := int16(math.Sin(2*math.Pi*frequency*float64(sample)/sampleRate) * amplitude * envelope * math.MaxInt16)
_ = binary.Write(buffer, binary.LittleEndian, value)
}
return buffer.Bytes()
}