DataHoarder
09f3cf3b56
All checks were successful
continuous-integration/drone/push Build is passing
163 lines
3.6 KiB
Go
163 lines
3.6 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.GetChannels(), source.GetSampleRate(), libebur128.LoudnessGlobalMomentary|libebur128.SamplePeak)
|
|
if state == nil {
|
|
err = errors.New("could not initialize state")
|
|
return
|
|
}
|
|
defer state.Close()
|
|
|
|
if _, ok := source.(audio.TypedSource[int16]); ok {
|
|
for block := range source.ToInt16().GetBlocks() {
|
|
if err = state.AddShort(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else if int32Source, ok := source.(audio.TypedSource[int32]); ok {
|
|
if source.GetBitDepth() == 16 {
|
|
for block := range source.ToInt16().GetBlocks() {
|
|
if err = state.AddShort(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
for block := range int32Source.ToInt32(32).GetBlocks() {
|
|
if err = state.AddInt(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for block := range source.ToFloat32().GetBlocks() {
|
|
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.GetChannels(), source.GetSampleRate(), 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()
|
|
var err error
|
|
|
|
if _, ok := source.(audio.TypedSource[int16]); ok {
|
|
for block := range source.ToInt16().GetBlocks() {
|
|
if err = state.AddShort(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else if _, ok := source.(audio.TypedSource[int32]); ok {
|
|
if source.GetBitDepth() == 16 {
|
|
for block := range source.ToInt16().GetBlocks() {
|
|
if err = state.AddShort(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
for block := range source.ToInt32(32).GetBlocks() {
|
|
if err = state.AddInt(block); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for block := range source.ToFloat32().GetBlocks() {
|
|
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
|
|
}
|