Kirika/audio/format/tta/tta.go
DataHoarder 514a88aec1
Some checks failed
continuous-integration/drone/push Build is failing
Update to Go 1.20
2023-04-09 13:10:30 +02:00

186 lines
4.3 KiB
Go

//go:build !disable_format_tta && !disable_codec_tta
package tta
import (
"bytes"
"errors"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
libtta "git.gammaspectra.live/S.O.N.G/go-tta"
"io"
"math"
"runtime"
"unsafe"
)
const BlockSize = 1024 * 128
type Format struct {
}
func (f Format) Name() string {
return "tta"
}
func (f Format) DecoderDescription() string {
return "S.O.N.G/go-tta"
}
func (f Format) EncoderDescription() string {
return f.DecoderDescription()
}
func NewFormat() Format {
return Format{}
}
type fakeReadWriteSeeker struct {
r io.ReadSeekCloser
}
func (i fakeReadWriteSeeker) Read(p []byte) (n int, err error) {
return i.r.Read(p)
}
func (i fakeReadWriteSeeker) Seek(offset int64, whence int) (int64, error) {
return i.r.Seek(offset, whence)
}
func (i fakeReadWriteSeeker) Close() error {
return i.r.Close()
}
func (i fakeReadWriteSeeker) Write(_ []byte) (n int, err error) {
return 0, io.ErrShortWrite
}
type fakeReadWriteSeeker2 struct {
w io.WriteCloser
}
func (i fakeReadWriteSeeker2) Read(_ []byte) (n int, err error) {
return 0, io.ErrNoProgress
}
func (i fakeReadWriteSeeker2) Seek(_ int64, _ int) (int64, error) {
return 0, io.ErrNoProgress
}
func (i fakeReadWriteSeeker2) Close() error {
return i.w.Close()
}
func (i fakeReadWriteSeeker2) Write(p []byte) (n int, err error) {
return i.w.Write(p)
}
func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
decoder := libtta.MakeDecoder(fakeReadWriteSeeker{r})
if decoder == nil {
return nil, errors.New("invalid decoder")
}
info := &libtta.Info{}
if err := decoder.GetInfo(info, 0); err != nil {
return nil, err
}
source := audio.NewSource[int32](int(info.Bps), int(info.Sps), int(info.Nch))
go func() {
defer source.Close()
smpSize := int(info.Nch) * ((int(info.Bps) + 7) / 8)
buffer := make([]byte, BlockSize*smpSize)
var writeLen int
for {
if writeLen = int(decoder.ProcessStream(buffer, nil)) * smpSize; writeLen == 0 {
break
}
source.IngestInt32(vector.BytesToInt32(buffer[:writeLen], int(info.Bps)), int(info.Bps))
}
}()
return source, nil
}
func (f Format) OpenAnalyzer(r io.ReadSeekCloser) (audio.Source, format.AnalyzerChannel, error) {
return format.NewAnalyzerChannel(f.Open(r))
}
func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[string]interface{}) error {
var bitsPerSample = 16
if options != nil {
var val interface{}
var ok bool
var intVal int
if val, ok = options["bitdepth"]; ok {
if intVal, ok = val.(int); ok {
bitsPerSample = intVal
}
}
}
var encoder *libtta.Encoder
if writeSeeker, ok := writer.(io.ReadWriteSeeker); ok {
encoder = libtta.MakeEncoder(writeSeeker)
} else {
encoder = libtta.MakeEncoder(fakeReadWriteSeeker2{w: writer})
}
if encoder == nil {
return errors.New("could not create encoder")
}
defer encoder.Close()
err := encoder.SetInfo(&libtta.Info{
Format: 1,
Nch: uint32(source.GetChannels()),
Bps: uint32(bitsPerSample),
Sps: uint32(source.GetSampleRate()),
Samples: math.MaxUint32,
})
if err != nil {
return err
}
switch bitsPerSample {
case 32:
for block := range source.ToInt32(32).GetBlocks() {
encoder.ProcessStream(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*4), nil)
runtime.KeepAlive(block)
}
case 24:
for block := range source.ToInt32(24).GetBlocks() {
samples := vector.Int32ToBytes(block, 24)
encoder.ProcessStream(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)), nil)
runtime.KeepAlive(samples)
}
case 16:
for block := range source.ToInt16().GetBlocks() {
encoder.ProcessStream(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*2), nil)
runtime.KeepAlive(block)
}
case 8:
for block := range source.ToInt32(8).GetBlocks() {
samples := vector.Int32ToBytes(block, 8)
encoder.ProcessStream(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)), nil)
runtime.KeepAlive(samples)
}
default:
return errors.New("not supported bits per sample")
}
encoder.Finalize()
return nil
}
func (f Format) Identify(peek [format.IdentifyPeekBytes]byte, extension string) bool {
return bytes.Compare(peek[:4], []byte{'T', 'T', 'A', '1'}) == 0 || extension == "tta"
}