222 lines
5.2 KiB
Go
222 lines
5.2 KiB
Go
//go:build !disable_format_flac
|
|
// +build !disable_format_flac
|
|
|
|
package flac
|
|
|
|
import (
|
|
"bytes"
|
|
"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/cgo"
|
|
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) Description() string {
|
|
return "libFLAC (S.O.N.G/goflac)"
|
|
}
|
|
|
|
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 audio.Source{}, err
|
|
}
|
|
if decoder, err = libflac.NewDecoderReaderOgg(r); err != nil {
|
|
return audio.Source{}, err
|
|
}
|
|
}
|
|
|
|
newChannel := make(chan []float32)
|
|
|
|
go func() {
|
|
defer close(newChannel)
|
|
defer decoder.Close()
|
|
|
|
frameNumber := 0
|
|
|
|
for {
|
|
currentFrame, err := decoder.ReadFrame()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
bitDepth := decoder.Depth
|
|
|
|
if currentFrame.Depth != 0 {
|
|
bitDepth = currentFrame.Depth
|
|
}
|
|
|
|
newChannel <- cgo.Int32ToFloat32(currentFrame.Buffer, bitDepth)
|
|
|
|
frameNumber++
|
|
}
|
|
}()
|
|
|
|
return audio.Source{
|
|
Channels: decoder.Channels,
|
|
SampleRate: decoder.Rate,
|
|
Blocks: newChannel,
|
|
}, nil
|
|
}
|
|
|
|
func (f Format) OpenAnalyzer(r io.ReadSeekCloser) (audio.Source, format.AnalyzerChannel, 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 audio.Source{}, nil, err
|
|
}
|
|
if decoder, err = libflac.NewDecoderReaderOgg(r); err != nil {
|
|
return audio.Source{}, nil, err
|
|
}
|
|
}
|
|
|
|
newChannel := make(chan []float32)
|
|
analyzerChannel := make(format.AnalyzerChannel)
|
|
|
|
go func() {
|
|
defer close(newChannel)
|
|
defer close(analyzerChannel)
|
|
defer decoder.Close()
|
|
|
|
frameNumber := 0
|
|
|
|
for {
|
|
currentFrame, err := decoder.ReadFrame()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
bitDepth := decoder.Depth
|
|
channels := decoder.Channels
|
|
|
|
if currentFrame.Depth != 0 {
|
|
bitDepth = currentFrame.Depth
|
|
}
|
|
if currentFrame.Channels != 0 {
|
|
channels = currentFrame.Channels
|
|
}
|
|
|
|
newChannel <- cgo.Int32ToFloat32(currentFrame.Buffer, bitDepth)
|
|
|
|
analyzerChannel <- &format.AnalyzerPacket{
|
|
Samples: currentFrame.Buffer,
|
|
Channels: channels,
|
|
BitDepth: bitDepth,
|
|
SampleRate: decoder.Rate,
|
|
}
|
|
|
|
frameNumber++
|
|
}
|
|
}()
|
|
|
|
return audio.Source{
|
|
Channels: decoder.Channels,
|
|
SampleRate: decoder.Rate,
|
|
Blocks: newChannel,
|
|
}, analyzerChannel, nil
|
|
}
|
|
|
|
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.Channels, bitsPerSample, source.SampleRate, compressionLevel, streamable, blockSize)
|
|
} else {
|
|
encoder, err = libflac.NewEncoderWriteSeeker(writeSeeker, source.Channels, bitsPerSample, source.SampleRate, compressionLevel, streamable, blockSize)
|
|
}
|
|
} else {
|
|
if ogg {
|
|
encoder, err = libflac.NewEncoderWriterOgg(writer, source.Channels, bitsPerSample, source.SampleRate, compressionLevel, streamable, blockSize)
|
|
} else {
|
|
encoder, err = libflac.NewEncoderWriter(writer, source.Channels, bitsPerSample, source.SampleRate, compressionLevel, streamable, blockSize)
|
|
}
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer encoder.Close()
|
|
|
|
for block := range source.Blocks {
|
|
|
|
err = encoder.WriteFrame(libflac.Frame{
|
|
Rate: source.SampleRate,
|
|
Channels: source.Channels,
|
|
Depth: bitsPerSample,
|
|
Buffer: cgo.Float32ToInt32(block, bitsPerSample),
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f Format) Identify(peek []byte, extension string) bool {
|
|
return bytes.Compare(peek[:4], []byte{'O', 'g', 'g', 'S'}) == 0 || 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"
|
|
}
|