/* NAME wav.go DESCRIPTION wav.go contains functions for processing wav. AUTHOR David Sutton LICENSE wav.go is Copyright (C) 2023 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses). */ // Package wav provides functions for converting wav audio. package wav import ( "encoding/binary" "fmt" ) // ConvertFormat converts the common name for a format in a string type to the specific // integer required by the wav encoder. var ConvertFormat = map[string]int{"pcm": PCMFormat} const PCMFormat = 1 // PCMFormat defines the value for pcm audio as defined by the wav std. var ( errInvalidFormat = fmt.Errorf("invalid or no format defined") errInvalidRate = fmt.Errorf("invalid or no sample rate defined") errInvalidChannels = fmt.Errorf("invalid or no number of channels defined") errInvalidBitDepth = fmt.Errorf("invalid or no bit depth defined") ) // Metadata defines the format of the audio file for reading. type Metadata struct { AudioFormat int Channels int SampleRate int BitDepth int } type WAV struct { Metadata Metadata Audio []byte } // Write writes the given audio byte slice to the WAV, encoding the appropriate headings. func (w *WAV) Write(p []byte) (n int, err error) { // Create header slice. header := make([]byte, 44) // Write RIFF type. copy(header[0:4], []byte("RIFF")) // Write the size of overall file. buf := make([]byte, 4) binary.LittleEndian.PutUint32(buf, uint32(len(p)+44)) copy(header[4:8], buf) // Write WAVE type. copy(header[8:12], []byte("WAVE")) // Write fmt chunk marker. copy(header[12:16], []byte("fmt ")) // Write the subchunk1 Size. binary.LittleEndian.PutUint32(buf, 16) copy(header[16:20], buf) // Write the encoded audio format. if w.Metadata.AudioFormat != PCMFormat { // TODO: allow for more encoding formats. return 0, errInvalidFormat } binary.LittleEndian.PutUint16(buf[0:2], 1) copy(header[20:22], buf[0:2]) // Write the number of channels. if w.Metadata.Channels == 0 { return 0, errInvalidChannels } binary.LittleEndian.PutUint16(buf[0:2], uint16(w.Metadata.Channels)) copy(header[22:24], buf[0:2]) // Write the sample rate. if w.Metadata.SampleRate == 0 { return 0, errInvalidRate } binary.LittleEndian.PutUint32(buf[0:4], uint32(w.Metadata.SampleRate)) copy(header[24:28], buf[0:4]) // Write bit depth values. if w.Metadata.BitDepth == 0 { return 0, errInvalidBitDepth } var val uint32 = uint32((w.Metadata.SampleRate * w.Metadata.BitDepth * w.Metadata.Channels) / 8) binary.LittleEndian.PutUint32(buf[0:4], val) copy(header[28:32], buf[0:4]) val = uint32((w.Metadata.BitDepth * w.Metadata.Channels) / 8) binary.LittleEndian.PutUint32(buf[0:4], val) copy(header[32:34], buf[0:4]) binary.LittleEndian.PutUint32(buf[0:4], uint32(w.Metadata.BitDepth)) copy(header[34:36], buf[0:4]) // Mark start of data. copy(header[36:40], []byte("data")) // Write size of data chunk. binary.LittleEndian.PutUint32(buf[0:4], uint32(len(p))) copy(header[40:44], buf[0:4]) // Append audio data. w.Audio = header w.Audio = append(w.Audio, p...) // Return successful write. return len(p) + 44, nil }