Kirika/audio/format/flac/libflac.go

167 lines
4.1 KiB
Go

//go:build !disable_format_flac && !disable_codec_libflac && cgo
package flac
import (
"bytes"
"fmt"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
libflac "git.gammaspectra.live/S.O.N.G/goflac"
"io"
)
type Format struct {
}
func NewFormat() Format {
return Format{}
}
func (f Format) Name() string {
return "flac"
}
func (f Format) DecoderDescription() string {
return fmt.Sprintf("%s (S.O.N.G/goflac)", libflac.Vendor())
}
func (f Format) EncoderDescription() string {
return f.DecoderDescription()
}
func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
currentPosition, _ := r.Seek(0, io.SeekCurrent)
decoder, err := libflac.NewDecoderReader(r)
if err != nil {
if _, err2 := r.Seek(currentPosition, io.SeekStart); err2 != nil {
return nil, err
}
if decoder, err = libflac.NewDecoderReaderOgg(r); err != nil {
return nil, err
}
}
source := audio.NewSource[int32](decoder.Depth, decoder.Rate, decoder.Channels)
go func() {
defer source.Close()
defer decoder.Close()
frameNumber := 0
for {
currentFrame, err := decoder.ReadFrame()
if err != nil {
return
}
bitDepth := decoder.Depth
if currentFrame.Depth != 0 {
bitDepth = currentFrame.Depth
}
source.IngestInt32(currentFrame.Buffer, bitDepth)
frameNumber++
}
}()
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 compressionLevel = 8
var blockSize = 0
var streamable = true
var ogg = false
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["compression_level"]; ok {
if intVal, ok = val.(int); ok {
compressionLevel = intVal
} else if int64Val, ok = val.(int64); ok {
compressionLevel = int(int64Val)
}
}
if val, ok = options["block_size"]; ok {
if intVal, ok = val.(int); ok {
blockSize = intVal
} else if int64Val, ok = val.(int64); ok {
blockSize = int(int64Val)
}
}
if val, ok = options["streamable"]; ok {
if boolVal, ok = val.(bool); ok {
streamable = boolVal
}
}
if val, ok = options["ogg"]; ok {
if boolVal, ok = val.(bool); ok {
ogg = boolVal
}
}
}
var encoder *libflac.Encoder
var err error
if writeSeeker, ok := writer.(libflac.FlacWriter); ok {
if ogg {
encoder, err = libflac.NewEncoderWriteSeekerOgg(writeSeeker, source.GetChannels(), bitsPerSample, source.GetSampleRate(), compressionLevel, streamable, blockSize)
} else {
encoder, err = libflac.NewEncoderWriteSeeker(writeSeeker, source.GetChannels(), bitsPerSample, source.GetSampleRate(), compressionLevel, streamable, blockSize)
}
} else {
if ogg {
encoder, err = libflac.NewEncoderWriterOgg(writer, source.GetChannels(), bitsPerSample, source.GetSampleRate(), compressionLevel, streamable, blockSize)
} else {
encoder, err = libflac.NewEncoderWriter(writer, source.GetChannels(), bitsPerSample, source.GetSampleRate(), compressionLevel, streamable, blockSize)
}
}
if err != nil {
return err
}
defer encoder.Close()
for block := range source.ToInt32(bitsPerSample).GetBlocks() {
err = encoder.WriteFrame(libflac.Frame{
Rate: source.GetSampleRate(),
Channels: source.GetChannels(),
Depth: bitsPerSample,
Buffer: block,
})
if err != nil {
return err
}
}
return nil
}
func (f Format) Identify(peek [format.IdentifyPeekBytes]byte, extension string) bool {
return (bytes.Compare(peek[:4], []byte{'O', 'g', 'g', 'S'}) == 0 && extension != "opus" && extension != "vorbis") || bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || (bytes.Compare(peek[:3], []byte{'I', 'D', '3'}) == 0 && extension != "mp3") || extension == "flac" || extension == "ogg"
}