Kirika/audio/format/alac/libalac.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

142 lines
3.4 KiB
Go

//go:build !disable_format_alac && enable_codec_libalac && cgo
package alac
import (
"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"
goAlac "git.gammaspectra.live/S.O.N.G/go-alac"
"io"
"runtime"
"time"
"unsafe"
)
type Format struct {
}
func NewFormat() Format {
return Format{}
}
func (f Format) Name() string {
return "alac"
}
func (f Format) DecoderDescription() string {
return "libalac (S.O.N.G/go-alac)"
}
func (f Format) EncoderDescription() string {
return f.DecoderDescription()
}
func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
mp4Demuxer, err := tryDecodeMP4(r)
if err != nil {
return nil, err
}
decoder := goAlac.NewFrameDecoder(mp4Demuxer.cookie)
if decoder == nil {
return nil, errors.New("could not decode")
}
source := audio.NewSource[int32](decoder.GetBitDepth(), decoder.GetSampleRate(), decoder.GetChannels())
go func() {
defer source.Close()
for {
samples := mp4Demuxer.Read()
if samples == nil {
return
}
for _, sample := range samples {
_, pcm := decoder.ReadPacket(sample)
if pcm == nil {
return
}
source.IngestInt32(vector.BytesToInt32(pcm, decoder.GetBitDepth()), decoder.GetBitDepth())
}
}
}()
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
var fastMode = false
var segmentDuration = time.Millisecond * 100
if options != nil {
var val interface{}
var ok bool
var intVal int
var int64Val int64
var boolVal bool
if val, ok = options["bitdepth"]; ok {
if intVal, ok = val.(int); ok {
bitsPerSample = intVal
} else if int64Val, ok = val.(int64); ok {
bitsPerSample = int(int64Val)
}
}
if val, ok = options["fast"]; ok {
if boolVal, ok = val.(bool); ok {
fastMode = boolVal
}
}
}
encoder := goAlac.NewFormatEncoder(writer, source.GetSampleRate(), source.GetChannels(), bitsPerSample, fastMode, segmentDuration)
if encoder == nil {
return errors.New("could not create encoder")
}
defer encoder.Flush()
switch bitsPerSample {
case 32:
for block := range source.ToInt32(32).GetBlocks() {
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*4))
runtime.KeepAlive(block)
}
case 24:
for block := range source.ToInt32(24).GetBlocks() {
samples := vector.Int32ToBytes(block, 24)
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)))
runtime.KeepAlive(samples)
}
case 16:
for block := range source.ToInt16().GetBlocks() {
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*2))
runtime.KeepAlive(block)
}
case 8:
for block := range source.ToInt32(8).GetBlocks() {
samples := vector.Int32ToBytes(block, 8)
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)))
runtime.KeepAlive(samples)
}
default:
return errors.New("not supported bits per sample")
}
return nil
}
func (f Format) Identify(_ [format.IdentifyPeekBytes]byte, extension string) bool {
return extension == "alac" || extension == "mp4" || extension == "m4a"
}