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

116 lines
2.4 KiB
Go

//go:build cgo
package replaygain
import (
"errors"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
libebur128 "git.gammaspectra.live/S.O.N.G/go-ebur128"
"sync"
)
const referenceLevel = -18.0
//GetTrackReplayGain calculates track ReplayGain 2.0
func GetTrackReplayGain(source audio.Source) (gain, peak float64, err error) {
state := libebur128.NewState(source.Channels, source.SampleRate, libebur128.LoudnessGlobalMomentary|libebur128.SamplePeak)
if state == nil {
err = errors.New("could not initialize state")
return
}
defer state.Close()
for block := range source.Blocks {
if err = state.AddFloat(block); err != nil {
return
}
}
var loudness float64
var peakSlice []float64
if loudness, err = state.GetLoudnessGlobal(); err != nil {
return
}
if peakSlice, err = state.GetSamplePeak(); err != nil {
return
}
for _, p := range peakSlice {
if p > peak {
peak = p
}
}
gain = referenceLevel - loudness
return
}
//GetAlbumReplayGain calculates album and tracks ReplayGain 2.0
func GetAlbumReplayGain(sources []audio.Source) (albumGain, albumPeak float64, trackGains []float64, trackPeaks []float64, err error) {
var states []*libebur128.State
var wg sync.WaitGroup
defer func() {
wg.Wait()
for _, state := range states {
state.Close()
}
}()
for _, source := range sources {
state := libebur128.NewState(source.Channels, source.SampleRate, libebur128.LoudnessGlobalMomentary|libebur128.SamplePeak)
if state == nil {
err = errors.New("could not initialize state")
return
}
states = append(states, state)
wg.Add(1)
go func(source audio.Source) {
defer wg.Done()
for block := range source.Blocks {
if err = state.AddFloat(block); err != nil {
return
}
}
}(source)
}
wg.Wait()
var albumLoudness float64
var peakSlice []float64
var trackLoudness float64
if albumLoudness, err = libebur128.GetLoudnessGlobalMultiple(states); err != nil {
return
}
for _, state := range states {
if trackLoudness, err = state.GetLoudnessGlobal(); err != nil {
return
}
if peakSlice, err = state.GetSamplePeak(); err != nil {
return
}
var tPeak float64
for _, p := range peakSlice {
if p > albumPeak {
albumPeak = p
}
if p > tPeak {
tPeak = p
}
}
trackGains = append(trackGains, referenceLevel-trackLoudness)
trackPeaks = append(trackPeaks, tPeak)
}
albumGain = referenceLevel - albumLoudness
return
}