//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" }