2022-09-28 06:14:26 +00:00
|
|
|
package audio
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Skip riff header and `fmt ` just 16 bytes
|
|
|
|
const (
|
2022-10-02 11:29:57 +00:00
|
|
|
FmtHeaderOffset = 0x0c // 12
|
2022-09-28 06:14:26 +00:00
|
|
|
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
|
2022-10-02 11:29:57 +00:00
|
|
|
var _headers [FmtHeaderOffset]byte
|
|
|
|
if _, err := f.Read(_headers[:]); err != nil {
|
2022-09-28 06:14:26 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var id [4]byte
|
|
|
|
if _, err := f.Read(id[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-10-02 11:29:57 +00:00
|
|
|
if bytes.Equal(id[:], []byte("fmt ")) {
|
2022-09-28 06:14:26 +00:00
|
|
|
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)
|
|
|
|
}
|