package wave

This commit is contained in:
Markus Tzoe 2017-05-09 15:57:50 +08:00
parent c2cf4a033c
commit aeb57b323f
6 changed files with 194 additions and 177 deletions

View file

@ -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

View file

@ -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
}

View file

@ -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
View file

@ -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
View 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
}

View file

@ -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)