88 lines
2.5 KiB
Go
88 lines
2.5 KiB
Go
package wav
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"math"
|
|
)
|
|
|
|
type EncodeOptions struct {
|
|
Bits int
|
|
Float bool
|
|
Loop bool
|
|
LoopStart int64
|
|
LoopEnd int64
|
|
}
|
|
|
|
func Encode(pcm []float32, rate, channels int, o EncodeOptions) []byte {
|
|
bits := o.Bits
|
|
switch {
|
|
case o.Float:
|
|
bits = 32
|
|
case bits != 8 && bits != 24 && bits != 32:
|
|
bits = 16
|
|
}
|
|
bps := bits / 8
|
|
data := make([]byte, 0, len(pcm)*bps)
|
|
for _, s := range pcm {
|
|
if !o.Float {
|
|
s = min(max(s, -1), 1)
|
|
}
|
|
switch {
|
|
case o.Float:
|
|
data = binary.LittleEndian.AppendUint32(data, math.Float32bits(s))
|
|
case bps == 1:
|
|
data = append(data, byte(int(math.Round(float64(s)*127))+128))
|
|
case bps == 2:
|
|
data = binary.LittleEndian.AppendUint16(data, uint16(int16(math.Round(float64(s)*32767))))
|
|
case bps == 3:
|
|
v := uint32(int32(math.Round(float64(s) * 8388607)))
|
|
data = append(data, byte(v), byte(v>>8), byte(v>>16))
|
|
default:
|
|
data = binary.LittleEndian.AppendUint32(data, uint32(int32(math.Round(float64(s)*2147483647))))
|
|
}
|
|
}
|
|
format := uint16(fmtPCM)
|
|
if o.Float {
|
|
format = fmtFloat
|
|
}
|
|
pad := len(data) & 1
|
|
smpl := 0
|
|
if o.Loop {
|
|
smpl = 8 + 60
|
|
}
|
|
riff := 4 + 8 + 16 + smpl + 8 + len(data) + pad
|
|
out := make([]byte, 0, riff+8)
|
|
out = append(out, "RIFF"...)
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(riff))
|
|
out = append(out, "WAVEfmt "...)
|
|
out = binary.LittleEndian.AppendUint32(out, 16)
|
|
out = binary.LittleEndian.AppendUint16(out, format)
|
|
out = binary.LittleEndian.AppendUint16(out, uint16(channels))
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(rate))
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(rate*channels*bps))
|
|
out = binary.LittleEndian.AppendUint16(out, uint16(channels*bps))
|
|
out = binary.LittleEndian.AppendUint16(out, uint16(bits))
|
|
if o.Loop {
|
|
out = append(out, "smpl"...)
|
|
out = binary.LittleEndian.AppendUint32(out, 60)
|
|
for i := 0; i < 7; i++ {
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
}
|
|
out = binary.LittleEndian.AppendUint32(out, 1)
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(o.LoopStart))
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(o.LoopEnd-1))
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
out = binary.LittleEndian.AppendUint32(out, 0)
|
|
}
|
|
out = append(out, "data"...)
|
|
out = binary.LittleEndian.AppendUint32(out, uint32(len(data)))
|
|
out = append(out, data...)
|
|
if pad == 1 {
|
|
out = append(out, 0)
|
|
}
|
|
return out
|
|
}
|