45 lines
1.6 KiB
Go
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
|
|
}
|