Extended ReplayGain normalization
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
8927e7b1a6
commit
010e8d73f7
|
@ -13,7 +13,7 @@ Radio streamer ([kawa](https://github.com/Luminarys/kawa) drop-in compatible).
|
|||
* Use `queue.buffer_size` to specify number of seconds to buffer (by default 0, automatic per client).
|
||||
* Implements `queue.nr` and `/random` (to be deprecated/changed)
|
||||
* Supports extra encoder bitrate control settings (CBR, VBR, auto, etc.)
|
||||
* Can read and apply ReplayGain tags.
|
||||
* Can read and apply ReplayGain tags, or normalize audio loudness.
|
||||
* Can have audio sources over HTTP(s) URLs on `path` property, and supports seeking.
|
||||
|
||||
# Future improvements
|
||||
|
|
|
@ -43,6 +43,14 @@ fallback="/tmp/in.flac"
|
|||
buffer_duration=0
|
||||
#
|
||||
# Apply replaygain track tags if existent on files played.
|
||||
# If ReplayGain tags are not existent, it will normalize audio with a running weighted window of 5 seconds.
|
||||
# ReplayGain tag overrides can be added as properties on track blobs:
|
||||
# {
|
||||
# "replay_gain" : {
|
||||
# "track_peak": 1.0000,
|
||||
# "track_gain": -3.15821
|
||||
# }
|
||||
# }
|
||||
replaygain=false
|
||||
|
||||
[radio]
|
||||
|
|
3
go.mod
3
go.mod
|
@ -3,13 +3,14 @@ module git.gammaspectra.live/S.O.N.G/MeteorLight
|
|||
go 1.18
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220306150518-7aa672a6166d
|
||||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220308124222-54bb437c0b50
|
||||
github.com/BurntSushi/toml v1.0.0
|
||||
github.com/dhowden/tag v0.0.0-20201120070457-d52dcb253c63
|
||||
github.com/enriquebris/goconcurrentqueue v0.6.3
|
||||
)
|
||||
|
||||
require (
|
||||
git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220308113719-afad5c6e5c28 // indirect
|
||||
git.gammaspectra.live/S.O.N.G/go-fdkaac v0.0.0-20220228131722-e9cb84c52f48 // indirect
|
||||
git.gammaspectra.live/S.O.N.G/go-pus v0.0.0-20220227175608-6cc027f24dba // indirect
|
||||
git.gammaspectra.live/S.O.N.G/go-tta v0.2.1-0.20220226150007-096de1072bd6 // indirect
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,5 +1,7 @@
|
|||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220306150518-7aa672a6166d h1:7Vys7abOvnEeGinIsEGu0KWFZUqQxOcwARBYDAUnJcQ=
|
||||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220306150518-7aa672a6166d/go.mod h1:slLvZqRcR9yMu3Ety7AKzyxu87tHfUKR49ae83sCAM8=
|
||||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220308124222-54bb437c0b50 h1:mncq7NhkVifcjIuNZEKWQ3QtuZNopnP4MWQhAaqLeVM=
|
||||
git.gammaspectra.live/S.O.N.G/Kirika v0.0.0-20220308124222-54bb437c0b50/go.mod h1:S3VhlpN5phBm/HfYqxh9Ik7ZsWj2EAO4+ZgAwX8wAk0=
|
||||
git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220308113719-afad5c6e5c28 h1:7YLU2eyGBX8juV445KlBxW71NjFAzbRvfotZBUP16Bs=
|
||||
git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220308113719-afad5c6e5c28/go.mod h1:5H4eVW9uknpn8REFr+C3ejhvXdncgm/pbGqKGC43gFY=
|
||||
git.gammaspectra.live/S.O.N.G/go-fdkaac v0.0.0-20220228131722-e9cb84c52f48 h1:MaKiBfXQl0keyfdCi1PxGOKRTiWhIs8PqCal5GhKDi0=
|
||||
git.gammaspectra.live/S.O.N.G/go-fdkaac v0.0.0-20220228131722-e9cb84c52f48/go.mod h1:pkWt//S9hLVEQaJDPu/cHHPk8vPpo/0+zHy0me4LIP4=
|
||||
git.gammaspectra.live/S.O.N.G/go-pus v0.0.0-20220227175608-6cc027f24dba h1:JEaxCVgdr3XXAuDCPAx7ttLFZaaHzTEzG+oRnVUtUKU=
|
||||
|
|
58
queue.go
58
queue.go
|
@ -6,10 +6,10 @@ import (
|
|||
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format/guess"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio/packetizer"
|
||||
"git.gammaspectra.live/S.O.N.G/Kirika/audio/replaygain"
|
||||
"github.com/dhowden/tag"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
|
@ -25,18 +25,17 @@ const maxBufferSize = 10
|
|||
type QueueTrackEntry struct {
|
||||
QueueIdentifier audio.QueueIdentifier
|
||||
Path string
|
||||
ReplayGain struct {
|
||||
Apply bool
|
||||
TrackPeak float64
|
||||
TrackGain float64
|
||||
AlbumPeak float64
|
||||
AlbumGain float64
|
||||
}
|
||||
Metadata struct {
|
||||
Title string `json:"title"`
|
||||
Album string `json:"album"`
|
||||
Artist string `json:"artist"`
|
||||
Art string `json:"art"`
|
||||
Metadata struct {
|
||||
Title string `json:"title"`
|
||||
Album string `json:"album"`
|
||||
Artist string `json:"artist"`
|
||||
Art string `json:"art"`
|
||||
ReplayGain struct {
|
||||
TrackPeak float64 `json:"track_peak"`
|
||||
TrackGain float64 `json:"track_gain"`
|
||||
AlbumPeak float64 `json:"album_peak"`
|
||||
AlbumGain float64 `json:"album_gain"`
|
||||
} `json:"replay_gain,omitempty"`
|
||||
}
|
||||
reader io.ReadSeekCloser
|
||||
source audio.Source
|
||||
|
@ -117,29 +116,30 @@ func (e *QueueTrackEntry) Load() error {
|
|||
var value interface{}
|
||||
var ok bool
|
||||
|
||||
if !e.ReplayGain.Apply {
|
||||
getDb := func(strValue string) (ret float64) {
|
||||
ret, _ = strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(strings.ToLower(strValue), "db")), 64)
|
||||
return
|
||||
}
|
||||
|
||||
if e.Metadata.ReplayGain.TrackPeak == 0 {
|
||||
if value, ok = tags["replaygain_track_gain"]; ok {
|
||||
if strValue, ok = value.(string); ok {
|
||||
if e.ReplayGain.TrackGain, err = strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(strValue, "dB")), 64); err == nil {
|
||||
e.ReplayGain.Apply = true
|
||||
}
|
||||
e.Metadata.ReplayGain.TrackGain = getDb(strValue)
|
||||
}
|
||||
}
|
||||
if value, ok = tags["replaygain_track_peak"]; ok {
|
||||
if strValue, ok = value.(string); ok {
|
||||
if e.ReplayGain.TrackPeak, err = strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(strValue, "dB")), 64); err == nil {
|
||||
e.ReplayGain.Apply = true
|
||||
}
|
||||
e.Metadata.ReplayGain.TrackPeak = getDb(strValue)
|
||||
}
|
||||
}
|
||||
if value, ok = tags["replaygain_album_gain"]; ok {
|
||||
if strValue, ok = value.(string); ok {
|
||||
e.ReplayGain.AlbumGain, _ = strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(strValue, "dB")), 64)
|
||||
e.Metadata.ReplayGain.AlbumGain = getDb(strValue)
|
||||
}
|
||||
}
|
||||
if value, ok = tags["replaygain_album_peak"]; ok {
|
||||
if strValue, ok = value.(string); ok {
|
||||
e.ReplayGain.AlbumPeak, _ = strconv.ParseFloat(strings.TrimSpace(strings.TrimSuffix(strValue, "dB")), 64)
|
||||
e.Metadata.ReplayGain.AlbumPeak = getDb(strValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,14 +250,12 @@ func (q *Queue) AddTrack(entry *QueueTrackEntry, tail bool) error {
|
|||
defer q.mutex.Unlock()
|
||||
|
||||
source := entry.source
|
||||
if q.config.Queue.ReplayGain && entry.ReplayGain.Apply {
|
||||
const ReplayGainPreamp = 0 //in dB
|
||||
volume := math.Pow(10, (entry.ReplayGain.TrackGain+ReplayGainPreamp)/20)
|
||||
|
||||
//prevent clipping
|
||||
volume = math.Min(volume, 1/entry.ReplayGain.TrackPeak)
|
||||
|
||||
source = audio.NewVolumeFilter(float32(volume)).Process(source)
|
||||
if q.config.Queue.ReplayGain {
|
||||
if entry.Metadata.ReplayGain.TrackPeak != 0 {
|
||||
source = replaygain.NewReplayGainFilter(entry.Metadata.ReplayGain.TrackGain, entry.Metadata.ReplayGain.TrackPeak, 0).Process(source)
|
||||
} else {
|
||||
source = replaygain.NewNormalizationFilter(5).Process(source)
|
||||
}
|
||||
}
|
||||
|
||||
if tail {
|
||||
|
|
Loading…
Reference in a new issue