Documentation
¶
Overview ¶
Package synth renders the family's procedural sound effects and loops as interleaved 16-bit stereo PCM at SampleRate, ready for an Ebiten/oto audio player.
Render is the core loop: it samples a generator function over a duration, clips, and interleaves. Sine, Noise, Env, and Attack are the building blocks; Pan and Panned place a finished sound in the stereo field. On top of those sit the parameterised cue shapes the games share — Blip, TwoTone, Arpeggio, Slide, Rumble, Thud, and Drone — while each game's cue enums and sound design stay its own.
Everything is deterministic: noise takes explicit seeds, so a cue renders sample-identical everywhere.
Index ¶
- Constants
- func Arpeggio(notes []float64, step, dur, decay float64) []byte
- func Attack(t, rate float64) float64
- func Blip(freq, dur, decay float64) []byte
- func Drone(voices []Voice, lfoRate, lfoDepth, dur float64) []byte
- func Env(t, decay float64) float64
- func Noise(seed1, seed2 uint64) func() float64
- func Pan(bearing float64) (left, right float64)
- func Panned(pcm []byte, left, right float64) []byte
- func Render(dur float64, gen func(t float64) float64) []byte
- func Rumble(base, rise, dur float64, seed1, seed2 uint64) []byte
- func Sine(freq, t float64) float64
- func Slide(f0, rate, floor, dur, decay float64) []byte
- func Thud(freq, dur, decay float64, seed1, seed2 uint64) []byte
- func TwoTone(f1, f2, split, dur, decay float64) []byte
- func WAV(pcm []byte) []byte
- type Voice
Examples ¶
Constants ¶
const ( // SampleRate is the render and playback rate in Hz. SampleRate = 44100 // ChannelCount is the number of interleaved channels (stereo). ChannelCount = 2 // BitDepthInBytes is the sample width (16-bit). BitDepthInBytes = 2 // BytesPerFrame is the size of one interleaved sample frame. BytesPerFrame = ChannelCount * BitDepthInBytes )
Audio format constants shared by every player in the family.
Variables ¶
This section is empty.
Functions ¶
func Arpeggio ¶
Arpeggio steps through notes at the given interval, restarting the decay envelope on each note — the conventional secret/fanfare shape.
Example ¶
ExampleArpeggio renders the family's conventional secret-found fanfare: a C5-E5-G5 arpeggio.
package main
import (
"fmt"
"github.com/danielriddell21/crucible/synth"
)
func main() {
pcm := synth.Arpeggio([]float64{523, 659, 784}, 0.15, 0.45, 10)
fmt.Printf("%.2fs\n", float64(len(pcm)/synth.BytesPerFrame)/synth.SampleRate)
}
Output: 0.45s
func Drone ¶
Drone layers the given voices (frequency, amplitude pairs) under a slow sine LFO — the conventional ambient bed. The LFO rate is in Hz.
func Noise ¶
Noise returns a deterministic white-noise sampler seeded with the given values. Each call to the sampler yields a value in [-1, 1].
func Pan ¶
Pan converts a bearing relative to the listener's facing (0 = dead ahead, positive to the right) into constant-power left/right channel gains in [0, 1]. Sounds ahead or behind sit centred; sounds to a side swing toward that ear.
Example ¶
ExamplePan places a sound by its bearing from the listener's facing: ahead is centred, hard right favours the right ear.
package main
import (
"fmt"
"math"
"github.com/danielriddell21/crucible/synth"
)
func main() {
l, r := synth.Pan(0)
fmt.Printf("ahead %.2f %.2f\n", l, r)
l, r = synth.Pan(math.Pi / 2)
fmt.Printf("right %.2f %.2f\n", l, r)
}
Output: ahead 0.71 0.71 right 0.00 1.00
func Panned ¶
Panned returns a copy of interleaved 16-bit stereo PCM with the left and right channels scaled by the given gains.
func Render ¶
Render synthesises dur seconds of audio by sampling gen at each frame time. gen returns an amplitude in [-1, 1]; values outside are clipped. Both stereo channels carry the same signal — use Panned to place a sound.
Example ¶
ExampleRender synthesises half a second of a decaying 440 Hz tone — the bones of every cue in the family.
package main
import (
"fmt"
"github.com/danielriddell21/crucible/synth"
)
func main() {
pcm := synth.Render(0.5, func(t float64) float64 {
return 0.5 * synth.Sine(440, t) * synth.Env(t, 6)
})
fmt.Println(len(pcm) / synth.BytesPerFrame)
}
Output: 22050
func Rumble ¶
Rumble is a rising low tone with a noise grit layer and a brief attack — the conventional door sound.
func Slide ¶
Slide sweeps a tone from f0 by rate Hz per second, clamped at floor — the conventional downward death groan when rate is negative.
func TwoTone ¶
TwoTone plays f1 then jumps to f2 at the split point — the conventional pickup chirp.
func WAV ¶ added in v0.15.2
WAV wraps rendered PCM in a RIFF/WAVE header, describing it with the package's own format constants.
Render and the cue shapes above it produce raw interleaved samples, which suits an Ebiten or oto player because those take a stream of them directly. Most other front-ends do not: raylib, SDL and the browser all load an encoded file, and will not accept bare samples however well described. Forty-four bytes of header is the whole difference, so this saves every non-Ebiten caller from carrying its own copy of a fiddly little spec.
sound := rl.LoadSoundFromWave(rl.LoadWaveFromMemory(".wav", w, int32(len(w))))
The returned slice is a fresh buffer; pcm is not retained or modified.