Kirika/audio/format/flac/flac.go
2022-02-22 10:35:08 +01:00

145 lines
3.2 KiB
Go

package flac
/*
#cgo CFLAGS: -I"${SRCDIR}/../../../cgo" -march=native -Ofast -std=c99
#include "audio.h"
*/
import "C"
import (
"bytes"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
libflac "github.com/cocoonlife/goflac"
"io"
)
type Format struct {
}
func NewFormat() Format {
return Format{}
}
func (f Format) Open(r io.ReadSeekCloser, blockSize int) (*audio.Stream, error) {
decoder, err := libflac.NewDecoderReader(r)
if err != nil {
return nil, 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
}
buf := make([]float32, len(currentFrame.Buffer))
C.audio_int32_to_float32((*C.int32_t)(&currentFrame.Buffer[0]), C.size_t(len(currentFrame.Buffer)), (*C.float)(&buf[0]), C.int(bitDepth))
newChannel <- buf
frameNumber++
}
}()
return audio.NewStream(newChannel, decoder.Channels, float64(decoder.Rate), blockSize), nil
}
func (f Format) OpenAnalyzer(r io.ReadSeekCloser, blockSize int) (*audio.Stream, chan *format.AnalyzerPacket, error) {
decoder, err := libflac.NewDecoderReader(r)
if err != nil {
return nil, nil, err
}
newChannel := make(chan []float32)
analyzerChannel := make(chan *format.AnalyzerPacket)
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
}
buf := make([]float32, len(currentFrame.Buffer))
C.audio_int32_to_float32((*C.int32_t)(&currentFrame.Buffer[0]), C.size_t(len(currentFrame.Buffer)), (*C.float)(&buf[0]), C.int(bitDepth))
newChannel <- buf
analyzerChannel <- &format.AnalyzerPacket{
Samples: currentFrame.Buffer,
Channels: channels,
BitDepth: bitDepth,
SampleRate: decoder.Rate,
}
frameNumber++
}
}()
return audio.NewStream(newChannel, decoder.Channels, float64(decoder.Rate), blockSize), analyzerChannel, nil
}
func (f Format) Encode(stream *audio.Stream, writer format.WriteSeekCloser) error {
const bitsPerSample = 16
encoder, err := libflac.NewEncoderWriter(writer, stream.GetChannels(), bitsPerSample, int(stream.GetSampleRate()))
if err != nil {
return err
}
defer encoder.Close()
for block := range stream.GetAsBlockChannel() {
samples := make([]int32, len(block))
C.audio_float32_to_int32((*C.float)(&block[0]), C.size_t(len(block)), (*C.int32_t)(&samples[0]), C.int(bitsPerSample))
err = encoder.WriteFrame(libflac.Frame{
Rate: int(stream.GetSampleRate()),
Channels: stream.GetChannels(),
Depth: bitsPerSample,
Buffer: samples,
})
if err != nil {
return err
}
}
return nil
}
func (f Format) Identify(peek []byte, extension string) bool {
return bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || extension == "flac"
}