From 3e09ac0c10c95bb24a08af62393b4f761e2743d0 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 24 Aug 2013 09:54:45 +0000 Subject: initial commit --- lib/dtas/rg_state.rb | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 lib/dtas/rg_state.rb (limited to 'lib/dtas/rg_state.rb') diff --git a/lib/dtas/rg_state.rb b/lib/dtas/rg_state.rb new file mode 100644 index 0000000..6463be7 --- /dev/null +++ b/lib/dtas/rg_state.rb @@ -0,0 +1,100 @@ +# -*- encoding: binary -*- +# :stopdoc: +# Copyright (C) 2013, Eric Wong +# 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 + 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 -- cgit v1.2.3-24-ge0c7