1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# -*- encoding: binary -*-
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
#
# provides support for generating appropriate effects for ReplayGain
# MAYBE: account for non-standard reference loudness (89.0 dB is standard)
require_relative '../dtas'
require_relative 'serialize'
class DTAS::RGState # :nodoc:
include DTAS::Serialize
RG_MODE = {
# attribute name => method to use
"album_gain" => :rg_vol_gain,
"track_gain" => :rg_vol_gain,
"album_peak" => :rg_vol_norm,
"track_peak" => :rg_vol_norm,
}
RG_DEFAULT = {
# skip the effect if the adjustment is too small to be noticeable
"gain_threshold" => 0.00000001, # in dB
"norm_threshold" => 0.00000001,
"preamp" => 0, # no extra adjustment
# "mode" => "album_gain", # nil: off
"mode" => nil, # nil: off
"fallback_gain" => -6.0, # adjustment dB if necessary RG tag is missing
"fallback_track" => true,
"norm_level" => 1.0, # dBFS
}
SIVS = RG_DEFAULT.keys
SIVS.each { |iv| attr_accessor iv }
def initialize
RG_DEFAULT.each do |k,v|
instance_variable_set("@#{k}", v)
end
end
def self.load(hash)
rv = new
hash.each { |k,v| rv.__send__("#{k}=", v) } if hash
rv
end
def to_hash
ivars_to_hash(SIVS)
end
def to_hsh
# no point in dumping default values, it's just a waste of space
to_hash.delete_if { |k,v| RG_DEFAULT[k] == v }
end
# returns a dB argument to the "vol" effect, nil if nothing found
def rg_vol_gain(val)
val = val.to_f + @preamp
return if val.abs < @gain_threshold
sprintf('vol %0.8gdB', val)
end
# returns a linear argument to the "vol" effect
def rg_vol_norm(val)
diff = @norm_level - val.to_f
return if (@norm_level - diff).abs < @norm_threshold
diff += @norm_level
sprintf('vol %0.8g', diff)
end
# The ReplayGain fallback adjustment value (in dB), in case a file is
# missing ReplayGain tags. This is useful to avoid damage to speakers,
# eardrums and amplifiers in case a file without then necessary ReplayGain
# tag slips into the queue
def rg_fallback_effect(reason)
@fallback_gain or return
warn(reason) if $DEBUG
"vol #{@fallback_gain + @preamp}dB"
end
# returns an array (for command-line argument) for the effect needed
# to apply ReplayGain
# this may return nil
def effect(source)
return unless @mode
rg = source.replaygain or
return rg_fallback_effect("ReplayGain tags missing")
val = rg.__send__(@mode)
if ! val && @fallback_track && @mode =~ /\Aalbum_(\w+)/
tag = "track_#$1"
val = rg.__send__(tag) or
return rg_fallback_effect("ReplayGain tag for #@mode missing")
warn("tag for #@mode missing, using #{tag}")
end
# this may be nil if the adjustment is too small:
__send__(RG_MODE[@mode], val)
end
end
|