main.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // Command gentone writes the WAV test pack's tone (0.8 s at 440 Hz, then
  2. // 0.2 s at 880 Hz as an end marker) as integer PCM, optionally with the
  3. // WAVE_FORMAT_EXTENSIBLE header many audio tools write.
  4. //
  5. // go run ./tools/checkpoint-a/gentone -rate 48000 -bits 24 -channels 2 -extensible out.wav
  6. package main
  7. import (
  8. "bytes"
  9. "encoding/binary"
  10. "flag"
  11. "fmt"
  12. "math"
  13. "os"
  14. )
  15. func main() {
  16. rate := flag.Int("rate", 44100, "sample rate")
  17. bits := flag.Int("bits", 16, "bits per sample: 16 or 24")
  18. channels := flag.Int("channels", 1, "1 or 2")
  19. ext := flag.Bool("extensible", false, "write a WAVE_FORMAT_EXTENSIBLE header (PCM sub-format)")
  20. flag.Parse()
  21. if flag.NArg() != 1 || (*bits != 16 && *bits != 24) || *channels < 1 || *channels > 2 {
  22. fmt.Fprintln(os.Stderr, "usage: gentone [-rate N] [-bits 16|24] [-channels 1|2] [-extensible] out.wav")
  23. os.Exit(2)
  24. }
  25. if err := os.WriteFile(flag.Arg(0), tone(*rate, *bits, *channels, *ext), 0o644); err != nil {
  26. fmt.Fprintln(os.Stderr, err)
  27. os.Exit(1)
  28. }
  29. }
  30. func tone(rate, bits, channels int, ext bool) []byte {
  31. le := binary.LittleEndian
  32. width := bits / 8
  33. var data bytes.Buffer
  34. fade := 0.005 * float64(rate)
  35. for i := 0; i < rate; i++ {
  36. t := float64(i) / float64(rate)
  37. f := 440.0
  38. if t >= 0.8 {
  39. f = 880
  40. }
  41. env := math.Min(1, math.Min(float64(i)/fade, float64(rate-1-i)/fade))
  42. v := 0.5 * env * math.Sin(2*math.Pi*f*t)
  43. s := int32(math.Round(v * float64(int32(1)<<(bits-1)-1)))
  44. var b [4]byte
  45. le.PutUint32(b[:], uint32(s))
  46. for c := 0; c < channels; c++ {
  47. data.Write(b[:width])
  48. }
  49. }
  50. align := channels * width
  51. var fmtc bytes.Buffer
  52. tag := uint16(1)
  53. if ext {
  54. tag = 0xFFFE
  55. }
  56. for _, v := range []any{tag, uint16(channels), uint32(rate), uint32(rate * align), uint16(align), uint16(bits)} {
  57. binary.Write(&fmtc, le, v)
  58. }
  59. if ext {
  60. mask := uint32(4) // front centre
  61. if channels == 2 {
  62. mask = 3 // front left, front right
  63. }
  64. for _, v := range []any{uint16(22), uint16(bits), mask} {
  65. binary.Write(&fmtc, le, v)
  66. }
  67. // KSDATAFORMAT_SUBTYPE_PCM 00000001-0000-0010-8000-00aa00389b71
  68. fmtc.Write([]byte{1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xaa, 0, 0x38, 0x9b, 0x71})
  69. }
  70. var body bytes.Buffer
  71. body.WriteString("WAVEfmt ")
  72. binary.Write(&body, le, uint32(fmtc.Len()))
  73. body.Write(fmtc.Bytes())
  74. body.WriteString("data")
  75. binary.Write(&body, le, uint32(data.Len()))
  76. body.Write(data.Bytes())
  77. if data.Len()%2 == 1 {
  78. body.WriteByte(0)
  79. }
  80. var out bytes.Buffer
  81. out.WriteString("RIFF")
  82. binary.Write(&out, le, uint32(body.Len()))
  83. out.Write(body.Bytes())
  84. return out.Bytes()
  85. }