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 }