go-tta/encoder.go
2015-08-18 21:47:26 +08:00

300 lines
7 KiB
Go

package tta
import (
"fmt"
"io"
"os"
)
type Encoder struct {
codec [MAX_NCH]tta_codec // 1 per channel
channels int // number of channels/codecs
data [8]byte // codec initialization data
fifo tta_fifo
seek_table []uint64 // the playing position table
format uint32 // tta data format
rate uint32 // bitrate (kbps)
offset uint64 // data start position (header size, bytes)
frames uint32 // total count of frames
depth uint32 // bytes per sample
flen_std uint32 // default frame length in samples
flen_last uint32 // last frame length in samples
flen uint32 // current frame length in samples
fnum uint32 // currently playing frame index
fpos uint32 // the current position in frame
shift_bits uint32 // packing int to pcm
}
func Compress(infile, outfile *os.File, passwd string, cb Callback) (err error) {
wave_hdr := WaveHeader{}
var data_size uint32
if data_size, err = wave_hdr.Read(infile); err != nil {
err = TTA_READ_ERROR
return
} else if data_size >= 0x7FFFFFFF {
err = fmt.Errorf("incorrect data size info in wav file: %x", data_size)
return
}
if (wave_hdr.chunk_id != _RIFF_SIGN) ||
(wave_hdr.format != _WAVE_SIGN) ||
(wave_hdr.num_channels == 0) ||
(wave_hdr.num_channels > MAX_NCH) ||
(wave_hdr.bits_per_sample == 0) ||
(wave_hdr.bits_per_sample > MAX_BPS) {
err = TTA_FORMAT_ERROR
return
}
encoder := NewEncoder(outfile)
smp_size := uint32(wave_hdr.num_channels * ((wave_hdr.bits_per_sample + 7) / 8))
info := tta_info{
nch: uint32(wave_hdr.num_channels),
bps: uint32(wave_hdr.bits_per_sample),
sps: wave_hdr.sample_rate,
format: TTA_FORMAT_SIMPLE,
samples: data_size / smp_size,
}
if len(passwd) > 0 {
encoder.SetPassword(passwd)
info.format = TTA_FORMAT_ENCRYPTED
}
buf_size := PCM_BUFFER_LENGTH * smp_size
buffer := make([]byte, buf_size)
if err = encoder.SetInfo(&info, 0); err != nil {
return
}
var read_len int = 0
for data_size > 0 {
if buf_size >= data_size {
buf_size = data_size
}
if read_len, err = infile.Read(buffer[:buf_size]); err != nil || read_len != int(buf_size) {
err = TTA_READ_ERROR
return
}
encoder.ProcessStream(buffer[:buf_size], cb)
data_size -= buf_size
}
encoder.Close()
return
}
func NewEncoder(iocb io.ReadWriteSeeker) *Encoder {
enc := Encoder{}
enc.fifo.io = iocb
return &enc
}
func (this *Encoder) ProcessStream(in []byte, cb Callback) {
if len(in) == 0 {
return
}
var res, curr, next, tmp int32
next = read_buffer(in, this.depth)
in = in[this.depth:]
tmp = next << this.shift_bits
i := 0
index := 0
for {
curr = next
if index < len(in) {
next = read_buffer(in[index:], this.depth)
tmp = next << this.shift_bits
} else {
next = 0
tmp = 0
}
index += int(this.depth)
// transform data
if this.channels > 1 {
if i < this.channels-1 {
res = next - curr
curr = res
} else {
curr -= res / 2
}
}
// compress stage 1: fixed order 1 prediction
tmp = curr
curr -= ((this.codec[i].prev * ((1 << 5) - 1)) >> 5)
this.codec[i].prev = tmp
// compress stage 2: adaptive hybrid filter
this.codec[i].filter.Encode(&curr)
this.fifo.put_value(&this.codec[i].rice, curr)
if i < this.channels-1 {
i++
} else {
i = 0
this.fpos++
}
if this.fpos == this.flen {
this.fifo.flush_bit_cache()
this.seek_table[this.fnum] = uint64(this.fifo.count)
this.fnum++
// update dynamic info
this.rate = (this.fifo.count << 3) / 1070
if cb != nil {
cb(this.rate, this.fnum, this.frames)
}
this.frame_init(this.fnum)
}
if index >= int(this.depth)+len(in) {
break
}
}
}
func (this *Encoder) ProcessFrame(in []byte) {
if len(in) == 0 {
return
}
var res, curr, next, tmp int32
next = read_buffer(in, this.depth)
in = in[this.depth:]
tmp = next << this.shift_bits
i := 0
index := 0
for {
curr = next
if index < len(in) {
next = read_buffer(in[index:], this.depth)
tmp = next << this.shift_bits
} else {
next = 0
tmp = 0
}
index += int(this.depth)
// transform data
if this.channels > 1 {
if i < this.channels-1 {
res = next - curr
curr = res
} else {
curr -= res / 2
}
}
// compress stage 1: fixed order 1 prediction
tmp = curr
curr -= ((this.codec[i].prev * ((1 << 5) - 1)) >> 5)
this.codec[i].prev = tmp
// compress stage 2: adaptive hybrid filter
this.codec[i].filter.Encode(&curr)
this.fifo.put_value(&this.codec[i].rice, curr)
if i < this.channels-1 {
i++
} else {
i = 0
this.fpos++
}
if this.fpos == this.flen {
this.fifo.flush_bit_cache()
// update dynamic info
this.rate = (this.fifo.count << 3) / 1070
break
}
if index >= int(this.depth)+len(in) {
break
}
}
}
func (this *Encoder) write_seek_table() (err error) {
if this.seek_table == nil {
return
}
if _, err = this.fifo.io.Seek(int64(this.offset), os.SEEK_SET); err != nil {
return
}
this.fifo.write_start()
this.fifo.reset()
for i := uint32(0); i < this.frames; i++ {
this.fifo.write_uint32(uint32(this.seek_table[i] & 0xFFFFFFFF))
}
this.fifo.write_crc32()
this.fifo.write_done()
return
}
func (this *Encoder) SetPassword(pass string) {
this.data = compute_key_digits(convert_password(pass))
}
func (this *Encoder) frame_init(frame uint32) (err error) {
if frame >= this.frames {
return
}
shift := flt_set[this.depth-1]
this.fnum = frame
if this.fnum == this.frames-1 {
this.flen = this.flen_last
} else {
this.flen = this.flen_std
}
// init entropy encoder
for i := 0; i < this.channels; i++ {
if SSE_Enabled {
this.codec[i].filter = NewSSEFilter(this.data, shift)
} else {
this.codec[i].filter = NewCompatibleFilter(this.data, shift)
}
this.codec[i].rice.init(10, 10)
this.codec[i].prev = 0
}
this.fpos = 0
this.fifo.reset()
return
}
func (this *Encoder) frame_reset(frame uint32, iocb io.ReadWriteSeeker) {
this.fifo.io = iocb
this.fifo.read_start()
this.frame_init(frame)
}
func (this *Encoder) SetInfo(info *tta_info, pos int64) (err error) {
if info.format > 2 ||
info.bps < MIN_BPS ||
info.bps > MAX_BPS ||
info.nch > MAX_NCH {
return TTA_FORMAT_ERROR
}
// set start position if required
if pos != 0 {
if _, err = this.fifo.io.Seek(int64(pos), os.SEEK_SET); err != nil {
err = TTA_SEEK_ERROR
return
}
}
this.fifo.write_start()
var p uint32
if p, err = this.fifo.write_tta_header(info); err != nil {
return
}
this.offset = uint64(pos) + uint64(p)
this.format = info.format
this.depth = (info.bps + 7) / 8
this.flen_std = (256 * (info.sps) / 245)
this.flen_last = info.samples % this.flen_std
this.frames = info.samples / this.flen_std
if this.flen_last != 0 {
this.frames += 1
} else {
this.flen_last = this.flen_std
}
this.rate = 0
this.fifo.write_skip_bytes((this.frames + 1) * 4)
this.seek_table = make([]uint64, this.frames)
this.channels = int(info.nch)
this.shift_bits = (4 - this.depth) << 3
this.frame_init(0)
return
}
func (this *Encoder) Close() {
this.Finalize()
}
func (this *Encoder) Finalize() {
this.fifo.write_done()
this.write_seek_table()
}