// Command gentone writes the WAV test pack's tone (0.8 s at 440 Hz, then // 0.2 s at 880 Hz as an end marker) as integer PCM, optionally with the // WAVE_FORMAT_EXTENSIBLE header many audio tools write. // // go run ./tools/checkpoint-a/gentone -rate 48000 -bits 24 -channels 2 -extensible out.wav package main import ( "bytes" "encoding/binary" "flag" "fmt" "math" "os" ) func main() { rate := flag.Int("rate", 44100, "sample rate") bits := flag.Int("bits", 16, "bits per sample: 16 or 24") channels := flag.Int("channels", 1, "1 or 2") ext := flag.Bool("extensible", false, "write a WAVE_FORMAT_EXTENSIBLE header (PCM sub-format)") flag.Parse() if flag.NArg() != 1 || (*bits != 16 && *bits != 24) || *channels < 1 || *channels > 2 { fmt.Fprintln(os.Stderr, "usage: gentone [-rate N] [-bits 16|24] [-channels 1|2] [-extensible] out.wav") os.Exit(2) } if err := os.WriteFile(flag.Arg(0), tone(*rate, *bits, *channels, *ext), 0o644); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func tone(rate, bits, channels int, ext bool) []byte { le := binary.LittleEndian width := bits / 8 var data bytes.Buffer fade := 0.005 * float64(rate) for i := 0; i < rate; i++ { t := float64(i) / float64(rate) f := 440.0 if t >= 0.8 { f = 880 } env := math.Min(1, math.Min(float64(i)/fade, float64(rate-1-i)/fade)) v := 0.5 * env * math.Sin(2*math.Pi*f*t) s := int32(math.Round(v * float64(int32(1)<<(bits-1)-1))) var b [4]byte le.PutUint32(b[:], uint32(s)) for c := 0; c < channels; c++ { data.Write(b[:width]) } } align := channels * width var fmtc bytes.Buffer tag := uint16(1) if ext { tag = 0xFFFE } for _, v := range []any{tag, uint16(channels), uint32(rate), uint32(rate * align), uint16(align), uint16(bits)} { binary.Write(&fmtc, le, v) } if ext { mask := uint32(4) // front centre if channels == 2 { mask = 3 // front left, front right } for _, v := range []any{uint16(22), uint16(bits), mask} { binary.Write(&fmtc, le, v) } // KSDATAFORMAT_SUBTYPE_PCM 00000001-0000-0010-8000-00aa00389b71 fmtc.Write([]byte{1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71}) } var body bytes.Buffer body.WriteString("WAVEfmt ") binary.Write(&body, le, uint32(fmtc.Len())) body.Write(fmtc.Bytes()) body.WriteString("data") binary.Write(&body, le, uint32(data.Len())) body.Write(data.Bytes()) if data.Len()%2 == 1 { body.WriteByte(0) } var out bytes.Buffer out.WriteString("RIFF") binary.Write(&out, le, uint32(body.Len())) out.Write(body.Bytes()) return out.Bytes() }