62 lines
1.7 KiB
Go
62 lines
1.7 KiB
Go
package mic
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
)
|
|
|
|
const (
|
|
SampleRate = 16000
|
|
Channels = 1
|
|
BitsPerSample = 16
|
|
BytesPerSample = BitsPerSample / 8 * Channels
|
|
)
|
|
|
|
var wavHeaderSize = 44
|
|
|
|
// EncodeWAV wraps 16-bit mono PCM in a complete WAV file.
|
|
func EncodeWAV(pcm []byte) []byte {
|
|
if len(pcm) == 0 {
|
|
return nil
|
|
}
|
|
if len(pcm)%BytesPerSample != 0 {
|
|
pcm = pcm[:len(pcm)-len(pcm)%BytesPerSample]
|
|
}
|
|
out := make([]byte, wavHeaderSize+len(pcm))
|
|
writeWAVHeader(out, len(pcm))
|
|
copy(out[wavHeaderSize:], pcm)
|
|
return out
|
|
}
|
|
|
|
func writeWAVHeader(buf []byte, dataLen int) {
|
|
if len(buf) < wavHeaderSize {
|
|
return
|
|
}
|
|
copy(buf[0:4], "RIFF")
|
|
binary.LittleEndian.PutUint32(buf[4:8], uint32(36+dataLen))
|
|
copy(buf[8:12], "WAVE")
|
|
copy(buf[12:16], "fmt ")
|
|
binary.LittleEndian.PutUint32(buf[16:20], 16)
|
|
binary.LittleEndian.PutUint16(buf[20:22], 1) // PCM
|
|
binary.LittleEndian.PutUint16(buf[22:24], Channels)
|
|
binary.LittleEndian.PutUint32(buf[24:28], SampleRate)
|
|
byteRate := SampleRate * Channels * BytesPerSample
|
|
binary.LittleEndian.PutUint32(buf[28:32], uint32(byteRate))
|
|
binary.LittleEndian.PutUint16(buf[32:34], uint16(Channels*BytesPerSample))
|
|
binary.LittleEndian.PutUint16(buf[34:36], BitsPerSample)
|
|
copy(buf[36:40], "data")
|
|
binary.LittleEndian.PutUint32(buf[40:44], uint32(dataLen))
|
|
}
|
|
|
|
// PCMBytesFromWAV returns PCM payload length implied by a WAV header.
|
|
func PCMBytesFromWAV(hdr []byte) (int, error) {
|
|
if len(hdr) < wavHeaderSize {
|
|
return 0, errors.New("wav header too short")
|
|
}
|
|
if string(hdr[0:4]) != "RIFF" || string(hdr[8:12]) != "WAVE" {
|
|
return 0, errors.New("not a wav file")
|
|
}
|
|
dataLen := int(binary.LittleEndian.Uint32(hdr[40:44]))
|
|
return dataLen, nil
|
|
}
|