package sfx import ( "encoding/binary" "fmt" "io" "math" ) // WriteWAV encodes samples ([-1,1] floats) as a 16-bit mono PCM WAV. func WriteWAV(w io.Writer, samples []float64, sampleRate int) error { dataLen := len(samples) * 2 var hdr [44]byte copy(hdr[0:4], "RIFF") binary.LittleEndian.PutUint32(hdr[4:8], uint32(36+dataLen)) copy(hdr[8:12], "WAVE") copy(hdr[12:16], "fmt ") binary.LittleEndian.PutUint32(hdr[16:20], 16) // fmt chunk size binary.LittleEndian.PutUint16(hdr[20:22], 1) // PCM binary.LittleEndian.PutUint16(hdr[22:24], 1) // mono binary.LittleEndian.PutUint32(hdr[24:28], uint32(sampleRate)) // sample rate binary.LittleEndian.PutUint32(hdr[28:32], uint32(sampleRate*2)) // byte rate binary.LittleEndian.PutUint16(hdr[32:34], 2) // block align binary.LittleEndian.PutUint16(hdr[34:36], 16) // bits per sample copy(hdr[36:40], "data") binary.LittleEndian.PutUint32(hdr[40:44], uint32(dataLen)) if _, err := w.Write(hdr[:]); err != nil { return err } buf := make([]byte, 2*len(samples)) for i, s := range samples { v := int16(math.Round(s * 32767)) binary.LittleEndian.PutUint16(buf[i*2:], uint16(v)) } _, err := w.Write(buf) return err } // ReadWAVHeader sanity-parses a WAV header (used in tests and info). func ReadWAVHeader(r io.Reader) (sampleRate, bits, channels, dataBytes int, err error) { var hdr [44]byte if _, err = io.ReadFull(r, hdr[:]); err != nil { return } if string(hdr[0:4]) != "RIFF" || string(hdr[8:12]) != "WAVE" { err = fmt.Errorf("not a WAV file") return } channels = int(binary.LittleEndian.Uint16(hdr[22:24])) sampleRate = int(binary.LittleEndian.Uint32(hdr[24:28])) bits = int(binary.LittleEndian.Uint16(hdr[34:36])) dataBytes = int(binary.LittleEndian.Uint32(hdr[40:44])) return }