145 lines
3.2 KiB
Go
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)(¤tFrame.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)(¤tFrame.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"
|
|
}
|