package wave
This commit is contained in:
parent
c2cf4a033c
commit
aeb57b323f
1
const.go
1
const.go
|
@ -32,7 +32,6 @@ var (
|
|||
errNotSupported = errors.New("unsupported architecture")
|
||||
// Partial io
|
||||
errPartialWritten = errors.New("partial written")
|
||||
errPartialRead = errors.New("partial read")
|
||||
)
|
||||
|
||||
const ( // CPU_ARCH_TYPE
|
||||
|
|
16
decoder.go
16
decoder.go
|
@ -3,6 +3,8 @@ package tta
|
|||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/zyxar/tta/wave"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
|
@ -36,19 +38,7 @@ func Decompress(infile io.ReadWriteSeeker, outfile io.WriteSeeker, passwd string
|
|||
}
|
||||
smpSize := info.nch * ((info.bps + 7) / 8)
|
||||
dataSize := info.samples * smpSize
|
||||
waveHdr := WaveHeader{
|
||||
chunkId: riffSign,
|
||||
chunkSize: dataSize + 36,
|
||||
format: waveSign,
|
||||
subchunkId: fmtSign,
|
||||
subchunkSize: 16,
|
||||
audioFormat: 1,
|
||||
numChannels: uint16(info.nch),
|
||||
sampleRate: info.sps,
|
||||
bitsPerSample: uint16(info.bps),
|
||||
byteRate: info.sps * smpSize,
|
||||
blockAlign: uint16(smpSize),
|
||||
}
|
||||
waveHdr := wave.NewHeader(dataSize, info.nch, info.sps, info.bps, smpSize)
|
||||
if err = waveHdr.Write(outfile, dataSize); err != nil {
|
||||
return
|
||||
}
|
||||
|
|
19
encoder.go
19
encoder.go
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/zyxar/tta/wave"
|
||||
)
|
||||
|
||||
type Encoder struct {
|
||||
|
@ -26,7 +28,7 @@ type Encoder struct {
|
|||
}
|
||||
|
||||
func Compress(infile io.ReadSeeker, outfile io.ReadWriteSeeker, passwd string, cb Callback) (err error) {
|
||||
waveHdr := WaveHeader{}
|
||||
waveHdr := wave.Header{}
|
||||
var dataSize uint32
|
||||
if dataSize, err = waveHdr.Read(infile); err != nil {
|
||||
err = errRead
|
||||
|
@ -35,21 +37,16 @@ func Compress(infile io.ReadSeeker, outfile io.ReadWriteSeeker, passwd string, c
|
|||
err = fmt.Errorf("incorrect data size info in wav file: %x", dataSize)
|
||||
return
|
||||
}
|
||||
if (waveHdr.chunkId != riffSign) ||
|
||||
(waveHdr.format != waveSign) ||
|
||||
(waveHdr.numChannels == 0) ||
|
||||
(waveHdr.numChannels > maxNCH) ||
|
||||
(waveHdr.bitsPerSample == 0) ||
|
||||
(waveHdr.bitsPerSample > maxBPS) {
|
||||
if !waveHdr.Validate(maxNCH, maxBPS) {
|
||||
err = errFormat
|
||||
return
|
||||
}
|
||||
encoder := NewEncoder(outfile)
|
||||
smpSize := uint32(waveHdr.numChannels * ((waveHdr.bitsPerSample + 7) / 8))
|
||||
smpSize := uint32(waveHdr.NumChannels * ((waveHdr.BitsPerSample + 7) / 8))
|
||||
info := Info{
|
||||
nch: uint32(waveHdr.numChannels),
|
||||
bps: uint32(waveHdr.bitsPerSample),
|
||||
sps: waveHdr.sampleRate,
|
||||
nch: uint32(waveHdr.NumChannels),
|
||||
bps: uint32(waveHdr.BitsPerSample),
|
||||
sps: waveHdr.SampleRate,
|
||||
format: formatSimple,
|
||||
samples: dataSize / smpSize,
|
||||
}
|
||||
|
|
147
wav.go
147
wav.go
|
@ -1,147 +0,0 @@
|
|||
package tta
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
riffSign = 0x46464952
|
||||
waveSign = 0x45564157
|
||||
fmtSign = 0x20746D66
|
||||
dataSign = 0x61746164
|
||||
|
||||
waveFormatPcm = 1
|
||||
waveFormatExtensible = 0xFFFE
|
||||
)
|
||||
|
||||
type WaveHeader struct {
|
||||
chunkId uint32
|
||||
chunkSize uint32
|
||||
format uint32
|
||||
subchunkId uint32
|
||||
subchunkSize uint32
|
||||
audioFormat uint16
|
||||
numChannels uint16
|
||||
sampleRate uint32
|
||||
byteRate uint32
|
||||
blockAlign uint16
|
||||
bitsPerSample uint16
|
||||
}
|
||||
|
||||
type WaveSubchunkHeader struct {
|
||||
subchunkId uint32
|
||||
subchunkSize uint32
|
||||
}
|
||||
|
||||
type WaveExtHeader struct {
|
||||
cbSize uint16
|
||||
validBits uint16
|
||||
chMask uint32
|
||||
est struct {
|
||||
f1 uint32
|
||||
f2 uint16
|
||||
f3 uint16
|
||||
f4 [8]byte
|
||||
} // WaveSubformat
|
||||
}
|
||||
|
||||
func (w *WaveHeader) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *WaveSubchunkHeader) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *WaveExtHeader) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *WaveHeader) Read(fd io.ReadSeeker) (subchunkSize uint32, err error) {
|
||||
var defaultSubchunkSize uint32 = 16
|
||||
b := w.Bytes()
|
||||
var readLen int
|
||||
// Read WAVE header
|
||||
if readLen, err = fd.Read(b); err != nil {
|
||||
return
|
||||
} else if readLen != len(b) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
if w.audioFormat == waveFormatExtensible {
|
||||
waveHdrEx := WaveExtHeader{}
|
||||
if readLen, err = fd.Read(waveHdrEx.Bytes()); err != nil {
|
||||
return
|
||||
} else if readLen != int(unsafe.Sizeof(waveHdrEx)) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
defaultSubchunkSize += uint32(unsafe.Sizeof(waveHdrEx))
|
||||
w.audioFormat = uint16(waveHdrEx.est.f1)
|
||||
}
|
||||
|
||||
// Skip extra format bytes
|
||||
if w.subchunkSize > defaultSubchunkSize {
|
||||
extraLen := w.subchunkSize - defaultSubchunkSize
|
||||
if _, err = fd.Seek(int64(extraLen), os.SEEK_SET); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Skip unsupported chunks
|
||||
subchunkHdr := WaveSubchunkHeader{}
|
||||
for {
|
||||
if readLen, err = fd.Read(subchunkHdr.Bytes()); err != nil {
|
||||
return
|
||||
} else if readLen != int(unsafe.Sizeof(subchunkHdr)) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
if subchunkHdr.subchunkId == dataSign {
|
||||
break
|
||||
}
|
||||
if _, err = fd.Seek(int64(subchunkHdr.subchunkSize), os.SEEK_SET); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
subchunkSize = subchunkHdr.subchunkSize
|
||||
return
|
||||
}
|
||||
|
||||
func (w *WaveHeader) Write(fd io.Writer, size uint32) (err error) {
|
||||
var writeLen int
|
||||
// Write WAVE header
|
||||
if writeLen, err = fd.Write(w.Bytes()); err != nil {
|
||||
return
|
||||
} else if writeLen != int(unsafe.Sizeof(*w)) {
|
||||
err = errPartialWritten
|
||||
return
|
||||
}
|
||||
// Write Subchunk header
|
||||
subchunkHdr := WaveSubchunkHeader{dataSign, size}
|
||||
if writeLen, err = fd.Write(subchunkHdr.Bytes()); err != nil {
|
||||
return
|
||||
} else if writeLen != int(unsafe.Sizeof(subchunkHdr)) {
|
||||
err = errPartialWritten
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
178
wave/wav.go
Normal file
178
wave/wav.go
Normal file
|
@ -0,0 +1,178 @@
|
|||
package wave
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
magicRiff = 0x46464952
|
||||
magicFormat = 0x20746D66
|
||||
magicChunk = 0x61746164
|
||||
MAGIC = 0x45564157
|
||||
|
||||
formatPCM = 1
|
||||
formatExt = 0xFFFE
|
||||
)
|
||||
|
||||
var (
|
||||
errPartialWritten = errors.New("partial written")
|
||||
errPartialRead = errors.New("partial read")
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
ChunkId uint32
|
||||
ChunkSize uint32
|
||||
Format uint32
|
||||
SubchunkId uint32
|
||||
SubchunkSize uint32
|
||||
AudioFormat uint16
|
||||
NumChannels uint16
|
||||
SampleRate uint32
|
||||
ByteRate uint32
|
||||
BlockAlign uint16
|
||||
BitsPerSample uint16
|
||||
}
|
||||
|
||||
type SubchunkHeader struct {
|
||||
SubchunkId uint32
|
||||
SubchunkSize uint32
|
||||
}
|
||||
|
||||
type ExtHeader struct {
|
||||
cbSize uint16
|
||||
validBits uint16
|
||||
chMask uint32
|
||||
est struct {
|
||||
f1 uint32
|
||||
f2 uint16
|
||||
f3 uint16
|
||||
f4 [8]byte
|
||||
} // WaveSubformat
|
||||
}
|
||||
|
||||
func NewHeader(dataSize, nch, sps, bps, smpSize uint32) *Header {
|
||||
return &Header{
|
||||
ChunkId: magicRiff,
|
||||
ChunkSize: dataSize + 36,
|
||||
Format: MAGIC,
|
||||
SubchunkId: magicFormat,
|
||||
SubchunkSize: 16,
|
||||
AudioFormat: 1,
|
||||
NumChannels: uint16(nch),
|
||||
SampleRate: sps,
|
||||
ByteRate: sps * smpSize,
|
||||
BlockAlign: uint16(smpSize),
|
||||
BitsPerSample: uint16(bps),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Header) Validate(maxNCH, maxBPS uint16) bool {
|
||||
return (h.ChunkId == magicRiff) &&
|
||||
(h.Format == MAGIC) &&
|
||||
(h.NumChannels != 0) &&
|
||||
(h.NumChannels <= maxNCH) &&
|
||||
(h.BitsPerSample != 0) &&
|
||||
(h.BitsPerSample <= maxBPS)
|
||||
}
|
||||
|
||||
func (w *Header) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *SubchunkHeader) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *ExtHeader) Bytes() []byte {
|
||||
size := int(unsafe.Sizeof(*w))
|
||||
return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
|
||||
Data: uintptr(unsafe.Pointer(w)),
|
||||
Len: size,
|
||||
Cap: size,
|
||||
}))
|
||||
}
|
||||
|
||||
func (w *Header) Read(fd io.ReadSeeker) (subchunkSize uint32, err error) {
|
||||
var defaultSubchunkSize uint32 = 16
|
||||
b := w.Bytes()
|
||||
var readLen int
|
||||
// Read WAVE header
|
||||
if readLen, err = fd.Read(b); err != nil {
|
||||
return
|
||||
} else if readLen != len(b) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
if w.AudioFormat == formatExt {
|
||||
waveHdrEx := ExtHeader{}
|
||||
if readLen, err = fd.Read(waveHdrEx.Bytes()); err != nil {
|
||||
return
|
||||
} else if readLen != int(unsafe.Sizeof(waveHdrEx)) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
defaultSubchunkSize += uint32(unsafe.Sizeof(waveHdrEx))
|
||||
w.AudioFormat = uint16(waveHdrEx.est.f1)
|
||||
}
|
||||
|
||||
// Skip extra format bytes
|
||||
if w.SubchunkSize > defaultSubchunkSize {
|
||||
extraLen := w.SubchunkSize - defaultSubchunkSize
|
||||
if _, err = fd.Seek(int64(extraLen), os.SEEK_SET); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Skip unsupported chunks
|
||||
subchunkHdr := SubchunkHeader{}
|
||||
for {
|
||||
if readLen, err = fd.Read(subchunkHdr.Bytes()); err != nil {
|
||||
return
|
||||
} else if readLen != int(unsafe.Sizeof(subchunkHdr)) {
|
||||
err = errPartialRead
|
||||
return
|
||||
}
|
||||
if subchunkHdr.SubchunkId == magicChunk {
|
||||
break
|
||||
}
|
||||
if _, err = fd.Seek(int64(subchunkHdr.SubchunkSize), os.SEEK_SET); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
subchunkSize = subchunkHdr.SubchunkSize
|
||||
return
|
||||
}
|
||||
|
||||
func (w *Header) Write(fd io.Writer, size uint32) (err error) {
|
||||
var writeLen int
|
||||
// Write WAVE header
|
||||
if writeLen, err = fd.Write(w.Bytes()); err != nil {
|
||||
return
|
||||
} else if writeLen != int(unsafe.Sizeof(*w)) {
|
||||
err = errPartialWritten
|
||||
return
|
||||
}
|
||||
// Write Subchunk header
|
||||
subchunkHdr := SubchunkHeader{magicChunk, size}
|
||||
if writeLen, err = fd.Write(subchunkHdr.Bytes()); err != nil {
|
||||
return
|
||||
} else if writeLen != int(unsafe.Sizeof(subchunkHdr)) {
|
||||
err = errPartialWritten
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package tta
|
||||
package wave
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -13,17 +13,17 @@ var wavSize = uint32(0x0374)
|
|||
|
||||
func TestReadHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
file, err := os.Open("./data/sample.wav")
|
||||
file, err := os.Open("../data/sample.wav")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
wav := WaveHeader{}
|
||||
wav := Header{}
|
||||
if size, err := wav.Read(file); err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
if bytes.Compare(wavSlice, wav.Bytes()) != 0 || size != wavSize {
|
||||
t.Error("WaveHeader::Read fail")
|
||||
t.Error("Header::Read fail")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func TestWriteHeader(t *testing.T) {
|
|||
}
|
||||
defer file.Close()
|
||||
defer os.Remove(filename)
|
||||
wav := WaveHeader{}
|
||||
wav := Header{}
|
||||
copy(wav.Bytes(), wavSlice)
|
||||
if err = wav.Write(file, wavSize); err != nil {
|
||||
t.Error(err)
|
Loading…
Reference in a new issue