Files
go-sip/internal/media/wav.go
T

45 lines
1.6 KiB
Go

package media
import (
"encoding/binary"
"errors"
)
var ErrWAVEmpty = errors.New("no captured PCM16 recording")
var ErrWAVInvalidPCM = errors.New("captured PCM16 has an odd byte length")
var ErrWAVTooLarge = errors.New("captured WAV exceeds its configured byte limit")
// EncodeMonoWAV wraps already decoded mono PCM16/16-kHz audio in memory.
// It is only a WAV container writer: RTP decoding and audio codecs remain in
// the selected media libraries. No business file is created on this path.
func EncodeMonoWAV(pcm []byte, maxBytes int64) ([]byte, int64, error) {
const headerBytes = 44
if len(pcm) == 0 {
return nil, 0, ErrWAVEmpty
}
if len(pcm)%2 != 0 {
return nil, 0, ErrWAVInvalidPCM
}
if maxBytes <= headerBytes || int64(len(pcm)) > maxBytes-headerBytes || uint64(len(pcm)) > uint64(^uint32(0))-36 {
return nil, 0, ErrWAVTooLarge
}
const sampleRate = 16000
dataSize := uint32(len(pcm))
wav := make([]byte, headerBytes+len(pcm))
copy(wav[:4], "RIFF")
binary.LittleEndian.PutUint32(wav[4:8], 36+dataSize)
copy(wav[8:12], "WAVE")
copy(wav[12:16], "fmt ")
binary.LittleEndian.PutUint32(wav[16:20], 16)
binary.LittleEndian.PutUint16(wav[20:22], 1)
binary.LittleEndian.PutUint16(wav[22:24], 1)
binary.LittleEndian.PutUint32(wav[24:28], sampleRate)
binary.LittleEndian.PutUint32(wav[28:32], sampleRate*2)
binary.LittleEndian.PutUint16(wav[32:34], 2)
binary.LittleEndian.PutUint16(wav[34:36], 16)
copy(wav[36:40], "data")
binary.LittleEndian.PutUint32(wav[40:44], dataSize)
copy(wav[44:], pcm)
return wav, int64(len(pcm)) * 1000 / (sampleRate * 2), nil
}