refactor(encode): use a replaygain enum

This commit is contained in:
sentriz
2021-09-13 16:55:15 +01:00
parent b9f8ea7048
commit 0edee132ed

View File

@@ -20,13 +20,20 @@ const (
ffmpeg = "ffmpeg" ffmpeg = "ffmpeg"
) )
type replayGain int
const (
rgForce replayGain = iota
rgHigh
)
type Profile struct { type Profile struct {
Format string Format string
Bitrate int Bitrate int
ffmpegOptions []string
forceRG bool options []string
hiGainRG bool replayGain replayGain
upsample bool upsample bool
} }
func fileExists(filename string) bool { func fileExists(filename string) bool {
@@ -39,11 +46,35 @@ func fileExists(filename string) bool {
func Profiles() map[string]Profile { func Profiles() map[string]Profile {
return map[string]Profile{ return map[string]Profile{
"mp3": {"mp3", 128, []string{"-c:a", "libmp3lame"}, false, false, false}, "mp3": {
"mp3_rg": {"mp3", 128, []string{"-c:a", "libmp3lame"}, true, false, false}, Format: "mp3",
"opus": {"opus", 96, []string{"-c:a", "libopus", "-vbr", "on"}, false, false, false}, Bitrate: 128,
"opus_rg": {"opus", 96, []string{"-c:a", "libopus", "-vbr", "on"}, true, false, false}, options: []string{"-c:a", "libmp3lame"},
"opus_car": {"opus", 96, []string{"-c:a", "libopus", "-vbr", "on"}, true, true, true}, },
"mp3_rg": {
Format: "mp3",
Bitrate: 128,
options: []string{"-c:a", "libmp3lame"},
replayGain: rgForce,
},
"opus": {
Format: "opus",
Bitrate: 96,
options: []string{"-c:a", "libopus", "-vbr", "on"},
},
"opus_rg": {
Format: "opus",
Bitrate: 96,
options: []string{"-c:a", "libopus", "-vbr", "on"},
replayGain: rgForce,
},
"opus_car": {
Format: "opus",
Bitrate: 96,
options: []string{"-c:a", "libopus", "-vbr", "on"},
replayGain: rgHigh,
upsample: true,
},
} }
} }
@@ -96,45 +127,40 @@ func ffmpegCommand(filePath string, profile Profile) (*exec.Cmd, error) {
"-vn", "-vn",
"-b:a", fmt.Sprintf("%dk", profile.Bitrate), "-b:a", fmt.Sprintf("%dk", profile.Bitrate),
} }
args = append(args, profile.ffmpegOptions...) args = append(args, profile.options...)
if profile.forceRG {
aBaselineGain := 6
if profile.hiGainRG {
// This baseline gain results in final track being +3~5dB louder
// than Foobar2000's default ReplayGain target volume.
// This makes it easier to listen to music in a car, where all other
// sources are usually ten thousand times louder than RG-adjusted music.
// -- @spijet
aBaselineGain = 15
}
aFilters := []string{
fmt.Sprintf("volume=replaygain=track:replaygain_preamp=%ddB:replaygain_noclip=0", aBaselineGain),
"alimiter=level=disabled",
"asidedata=mode=delete:type=REPLAYGAIN",
}
// opus always forces output to 48kHz sampling rate, but we can still use upsampling var aFilters []string
// to increase RG and alimiter's peak limiting precision, which is desirable in some var aMetadata []string
// cases. ffmpeg's `soxr` resampler is quite fast on x86-64: it takes around 5 seconds
// on my Ryzen 3600 to transcode an 8-minute FLAC with 2x upsample and RG applied. // opus always forces output to 48kHz sampling rate, but we can still use upsampling
// -- @spijet // to increase RG and alimiter's peak limiting precision, which is desirable in some
if profile.upsample { // cases. ffmpeg's `soxr` resampler is quite fast on x86-64: it takes around 5 seconds
aFilters = append([]string{"aresample=96000:resampler=soxr"}, aFilters...) // on my Ryzen 3600 to transcode an 8-minute FLAC with 2x upsample and RG applied.
} // -- @spijet
aFilterString := strings.Join(aFilters, ", ") if profile.upsample {
args = append(args, aFilters = append(aFilters, "aresample=96000:resampler=soxr")
// set up replaygain processing
"-af", aFilterString,
// drop redundant replaygain tags
"-metadata", "replaygain_album_gain=",
"-metadata", "replaygain_album_peak=",
"-metadata", "replaygain_track_gain=",
"-metadata", "replaygain_track_peak=",
"-metadata", "r128_album_gain=",
"-metadata", "r128_track_gain=",
)
} }
switch profile.replayGain {
case rgForce:
aFilters = append(aFilters, ffmpegPreamp(6)...)
aMetadata = append(aMetadata, ffmpegStripRG()...)
case rgHigh:
// this baseline gain results in final track being +3~5dB louder
// than Foobar2000's default ReplayGain target volume.
// this makes it easier to listen to music in a car, where all other
// sources are usually ten thousand times louder than RG-adjusted music.
// -- @spijet
aFilters = append(aFilters, ffmpegPreamp(15)...)
}
if len(aFilters) > 0 {
args = append(args, "-af", strings.Join(aFilters, ", "))
}
args = append(args, aMetadata...)
args = append(args, "-f", profile.Format, "-") args = append(args, "-f", profile.Format, "-")
ffmpegPath, err := exec.LookPath(ffmpeg) ffmpegPath, err := exec.LookPath(ffmpeg)
if err != nil { if err != nil {
return nil, fmt.Errorf("find ffmpeg binary path: %w", err) return nil, fmt.Errorf("find ffmpeg binary path: %w", err)
@@ -144,6 +170,25 @@ func ffmpegCommand(filePath string, profile Profile) (*exec.Cmd, error) {
// but please do let me know if you see otherwise // but please do let me know if you see otherwise
} }
func ffmpegPreamp(dB int) []string {
return []string{
fmt.Sprintf("volume=replaygain=track:replaygain_preamp=%ddB:replaygain_noclip=0", dB),
"alimiter=level=disabled",
"asidedata=mode=delete:type=REPLAYGAIN",
}
}
func ffmpegStripRG() []string {
return []string{
"-metadata", "replaygain_album_gain=",
"-metadata", "replaygain_album_peak=",
"-metadata", "replaygain_track_gain=",
"-metadata", "replaygain_track_peak=",
"-metadata", "r128_album_gain=",
"-metadata", "r128_track_gain=",
}
}
func encode(out io.Writer, trackPath, cachePath string, profile Profile) error { func encode(out io.Writer, trackPath, cachePath string, profile Profile) error {
// prepare cache part file path // prepare cache part file path
cachePartPath := fmt.Sprintf("%s.part", cachePath) cachePartPath := fmt.Sprintf("%s.part", cachePath)