package audio import ( "bytes" "encoding/binary" "encoding/json" "errors" "io" "time" ) // Skip riff header and `fmt ` just 16 bytes const ( FmtHeaderOffset = 0x0c // 12 FmtHeaderIDSize = 4 FmtHeaderChunkSizeSize = 4 FmtHeaderSizeDefault = 16 ) type WavHeader struct { ID [4]byte Size uint32 AudioFormat uint16 NumChannels uint16 SampleRate uint32 ByteRate uint32 BlockAlign uint16 BitsPerSample uint16 } func NewHeader(f io.Reader) (*WavHeader, error) { w := &WavHeader{} if err := w.Parse(f); err != nil { return nil, err } return w, nil } func DefaultHeader() *WavHeader { return &WavHeader{ Size: uint32(FmtHeaderSizeDefault), AudioFormat: 1, NumChannels: 2, SampleRate: 48000, // opus only support 48kHz BlockAlign: 4, BitsPerSample: 16, } } func (w *WavHeader) Parse(f io.Reader) error { // skip headers var _headers [FmtHeaderOffset]byte if _, err := f.Read(_headers[:]); err != nil { return err } var id [4]byte if _, err := f.Read(id[:]); err != nil { return err } if bytes.Equal(id[:], []byte("fmt ")) { return errors.New("bad header") } w.ID = id var size [4]byte if _, err := f.Read(size[:]); err != nil { return err } w.Size = binary.LittleEndian.Uint32(size[:]) var af [2]byte if _, err := f.Read(af[:]); err != nil { return err } w.AudioFormat = binary.LittleEndian.Uint16(af[:]) var nc [2]byte if _, err := f.Read(nc[:]); err != nil { return err } w.NumChannels = binary.LittleEndian.Uint16(nc[:]) var sr [4]byte if _, err := f.Read(sr[:]); err != nil { return err } w.SampleRate = binary.LittleEndian.Uint32(sr[:]) var br [4]byte if _, err := f.Read(br[:]); err != nil { return err } w.ByteRate = binary.LittleEndian.Uint32(br[:]) var ba [2]byte if _, err := f.Read(ba[:]); err != nil { return err } w.BlockAlign = binary.LittleEndian.Uint16(ba[:]) var bps [2]byte if _, err := f.Read(bps[:]); err != nil { return err } w.BitsPerSample = binary.LittleEndian.Uint16(bps[:]) return nil } func (w *WavHeader) String() string { b, _ := json.Marshal(w) return string(b) } func (w *WavHeader) GetLatnecy(bufferSizeByBytes uint16) time.Duration { bytesPerSample := w.BitsPerSample / BitsPerByte bufferLength := bufferSizeByBytes / bytesPerSample return time.Second * time.Duration(bufferLength) / time.Duration(w.NumChannels) / time.Duration(w.SampleRate) }