Kirika/audio/replaygain/filter_normalization.go
2022-05-20 17:23:50 +02:00

117 lines
2.4 KiB
Go

//go:build cgo
package replaygain
import (
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
libebur128 "git.gammaspectra.live/S.O.N.G/go-ebur128"
"math"
"time"
)
// NormalizationFilter Normalizes running audio source
type NormalizationFilter struct {
delay int
}
func NewNormalizationFilter(delayInSeconds int) NormalizationFilter {
return NormalizationFilter{
delay: delayInSeconds,
}
}
func (f NormalizationFilter) Process(source audio.Source) audio.Source {
outBlocks := make(chan []float32)
go func() {
defer close(outBlocks)
state := libebur128.NewState(source.Channels, source.SampleRate, libebur128.LoudnessShortTerm|libebur128.SamplePeak)
if state == nil {
return
}
defer state.Close()
if state.SetMaxWindow(time.Second*time.Duration(f.delay)) != nil {
return
}
var sampleBuffer []float32
var adjustment float32 = 1.0
for block := range source.Blocks {
if state.AddFloat(block) != nil {
return
}
sampleBuffer = append(sampleBuffer, block...)
loudness, _ := state.GetLoudnessWindow(time.Second * time.Duration(f.delay))
peakSlice, _ := state.GetPreviousSamplePeak()
var peak float64
for _, p := range peakSlice {
if p > peak {
peak = p
}
}
gain := referenceLevel - loudness
if gain > 52 {
gain = 52
} else if gain < -52 {
gain = -52
}
volume := math.Pow(10, (gain)/20)
nsamples := source.SampleRate * source.Channels * f.delay
ratio := float32(math.Min(1, float64(len(block))/float64(nsamples)))
adjustment = adjustment*(1-ratio) + float32(volume)*ratio
if adjustment > float32(1/peak) {
adjustment = float32(1 / peak)
}
if len(sampleBuffer) > nsamples {
adjustment = adjustment*(1-ratio) + float32(volume)*ratio
if adjustment > float32(1/peak) {
adjustment = float32(1 / peak)
}
size := len(sampleBuffer) - nsamples
out := make([]float32, size)
for i, e := range sampleBuffer[:size] {
out[i] = e * adjustment
}
outBlocks <- out
sampleBuffer = sampleBuffer[size:]
} else {
adjustment = float32(volume)
if adjustment > float32(1/peak) {
adjustment = float32(1 / peak)
}
}
}
//flush
if len(sampleBuffer) > 0 {
out := make([]float32, len(sampleBuffer))
for i := range sampleBuffer {
out[i] = sampleBuffer[i] * adjustment
}
outBlocks <- out
}
}()
return audio.Source{
Channels: source.Channels,
SampleRate: source.SampleRate,
Blocks: outBlocks,
}
}