2022-05-20 15:23:50 +00:00
|
|
|
//go:build !disable_format_alac && enable_codec_libalac && cgo
|
2022-04-22 12:06:01 +00:00
|
|
|
|
2022-04-21 11:49:22 +00:00
|
|
|
package alac
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
|
|
|
|
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
|
2022-07-19 12:02:41 +00:00
|
|
|
"git.gammaspectra.live/S.O.N.G/Kirika/vector"
|
2022-10-03 09:34:56 +00:00
|
|
|
goAlac "git.gammaspectra.live/S.O.N.G/go-alac"
|
2022-04-21 11:49:22 +00:00
|
|
|
"io"
|
2022-07-22 22:12:37 +00:00
|
|
|
"runtime"
|
2022-04-21 11:49:22 +00:00
|
|
|
"time"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Format struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFormat() Format {
|
|
|
|
return Format{}
|
|
|
|
}
|
|
|
|
|
2022-04-22 12:06:01 +00:00
|
|
|
func (f Format) Name() string {
|
|
|
|
return "alac"
|
|
|
|
}
|
|
|
|
|
2022-07-20 16:08:28 +00:00
|
|
|
func (f Format) DecoderDescription() string {
|
2022-04-23 18:41:14 +00:00
|
|
|
return "libalac (S.O.N.G/go-alac)"
|
2022-04-22 12:06:01 +00:00
|
|
|
}
|
|
|
|
|
2022-07-20 16:08:28 +00:00
|
|
|
func (f Format) EncoderDescription() string {
|
|
|
|
return f.DecoderDescription()
|
|
|
|
}
|
|
|
|
|
2022-04-21 11:49:22 +00:00
|
|
|
func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
|
2022-04-23 18:41:14 +00:00
|
|
|
mp4Demuxer, err := tryDecodeMP4(r)
|
|
|
|
if err != nil {
|
2022-07-19 08:36:22 +00:00
|
|
|
return nil, err
|
2022-04-23 18:41:14 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 09:34:56 +00:00
|
|
|
decoder := goAlac.NewFrameDecoder(mp4Demuxer.cookie)
|
2022-04-21 11:49:22 +00:00
|
|
|
if decoder == nil {
|
2022-07-19 08:36:22 +00:00
|
|
|
return nil, errors.New("could not decode")
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-22 09:39:58 +00:00
|
|
|
source := audio.NewSource[int32](decoder.GetBitDepth(), decoder.GetSampleRate(), decoder.GetChannels())
|
2022-04-21 11:49:22 +00:00
|
|
|
|
|
|
|
go func() {
|
2022-07-19 08:36:22 +00:00
|
|
|
defer source.Close()
|
2022-04-21 11:49:22 +00:00
|
|
|
|
|
|
|
for {
|
2022-04-23 18:41:14 +00:00
|
|
|
samples := mp4Demuxer.Read()
|
|
|
|
if samples == nil {
|
2022-04-21 11:49:22 +00:00
|
|
|
return
|
|
|
|
}
|
2022-04-23 18:41:14 +00:00
|
|
|
for _, sample := range samples {
|
|
|
|
_, pcm := decoder.ReadPacket(sample)
|
|
|
|
if pcm == nil {
|
|
|
|
return
|
|
|
|
}
|
2022-07-19 08:36:22 +00:00
|
|
|
|
2022-07-19 12:02:41 +00:00
|
|
|
source.IngestInt32(vector.BytesToInt32(pcm, decoder.GetBitDepth()), decoder.GetBitDepth())
|
2022-04-23 18:41:14 +00:00
|
|
|
}
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2022-07-19 08:36:22 +00:00
|
|
|
return source, nil
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 13:47:19 +00:00
|
|
|
func (f Format) OpenAnalyzer(r io.ReadSeekCloser) (audio.Source, format.AnalyzerChannel, error) {
|
|
|
|
return format.NewAnalyzerChannel(f.Open(r))
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-03 09:34:56 +00:00
|
|
|
encoder := goAlac.NewFormatEncoder(writer, source.GetSampleRate(), source.GetChannels(), bitsPerSample, fastMode, segmentDuration)
|
2022-04-21 11:49:22 +00:00
|
|
|
|
|
|
|
if encoder == nil {
|
|
|
|
return errors.New("could not create encoder")
|
|
|
|
}
|
|
|
|
|
|
|
|
defer encoder.Flush()
|
|
|
|
|
|
|
|
switch bitsPerSample {
|
|
|
|
case 32:
|
2022-07-19 08:36:22 +00:00
|
|
|
for block := range source.ToInt32(32).GetBlocks() {
|
2023-04-09 11:10:30 +00:00
|
|
|
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*4))
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(block)
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
case 24:
|
2022-07-20 17:02:31 +00:00
|
|
|
for block := range source.ToInt32(24).GetBlocks() {
|
|
|
|
samples := vector.Int32ToBytes(block, 24)
|
2023-04-09 11:10:30 +00:00
|
|
|
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)))
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(samples)
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
case 16:
|
2022-07-19 08:36:22 +00:00
|
|
|
for block := range source.ToInt16().GetBlocks() {
|
2023-04-09 11:10:30 +00:00
|
|
|
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(block))), len(block)*2))
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(block)
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
case 8:
|
2022-07-20 17:02:31 +00:00
|
|
|
for block := range source.ToInt32(8).GetBlocks() {
|
|
|
|
samples := vector.Int32ToBytes(block, 8)
|
2023-04-09 11:10:30 +00:00
|
|
|
encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(unsafe.SliceData(samples))), len(samples)))
|
2022-07-22 22:12:37 +00:00
|
|
|
runtime.KeepAlive(samples)
|
2022-04-21 11:49:22 +00:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
return errors.New("not supported bits per sample")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-29 11:48:40 +00:00
|
|
|
func (f Format) Identify(_ [format.IdentifyPeekBytes]byte, extension string) bool {
|
2022-04-21 11:49:22 +00:00
|
|
|
return extension == "alac" || extension == "mp4" || extension == "m4a"
|
|
|
|
}
|