about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--Documentation/dtas-console.txt5
-rwxr-xr-xbin/dtas-console19
-rw-r--r--lib/dtas/player/client_handler.rb2
-rw-r--r--lib/dtas/rg_state.rb38
4 files changed, 50 insertions, 14 deletions
diff --git a/Documentation/dtas-console.txt b/Documentation/dtas-console.txt
index 2547036..e76f741 100644
--- a/Documentation/dtas-console.txt
+++ b/Documentation/dtas-console.txt
@@ -29,7 +29,10 @@ Key bindings are inspired partially by mplayer(1)
 - SPACE - toggle play/pause state of the playback
 - 'p'/'P' - decrease/increase timer resolution
 - BACKSPACE - seek to the beginning of the track
-- 9/0 - decrease/increase ReplayGain preamp
+- 7/8 - decrease/increase ReplayGain preamp
+- 9/0 - decrease/increase software volume
+- '=' - set software volume to 1.0 (no adjustment)
+- 'm' - mute set software volume to 0.0 (mute)
 - 'f'/'F' - decrease/increase ReplayGain fallback_gain value
 - 'r'/'R' - cycle forward/backwards through ReplayGain modes
 - 'q'/Ctrl-C - exit dtas-console
diff --git a/bin/dtas-console b/bin/dtas-console
index a98244b..a8e1838 100755
--- a/bin/dtas-console
+++ b/bin/dtas-console
@@ -114,6 +114,7 @@ def may_fail(res, events)
   events << res if res != "OK"
 end
 
+pre_mute_vol = 1.0
 enc_locale = Encoding.find("locale")
 $stdout.set_encoding(enc_locale)
 enc_opts = { undef: :replace, invalid: :replace, replace: '?' }
@@ -163,12 +164,18 @@ begin
       Curses.setpos(lineno += 1, 0)
       Curses.clrtoeol
     end
-
-    rgs = rg_string(cur["rg"] || {}, current)
+    rg = cur['rg'] || {}
+    rgs = rg_string(rg, current)
     Curses.setpos(lineno += 1, 0)
     Curses.clrtoeol
     Curses.addstr(rgs)
 
+    Curses.setpos(lineno += 1, 0)
+    Curses.clrtoeol
+    cur_vol = rg['volume'] || 1.0
+    Curses.addstr("volume=#{cur_vol}")
+    pre_mute_vol = cur_vol if cur_vol != 0
+
     show_events(lineno, screen, events)
 
     Curses.refresh # draw and wait
@@ -206,8 +213,12 @@ begin
         # yes, some of us have long audio files
         when Curses::KEY_PPAGE then c.req_ok("seek +600")
         when Curses::KEY_NPAGE then c.req_ok("seek -600")
-        when "9" then c.req_ok("rg preamp-=1")
-        when "0" then c.req_ok("rg preamp+=1")
+        when '9' then c.req_ok('rg volume-=0.01')
+        when '0' then c.req_ok('rg volume+=0.01')
+        when '=' then c.req_ok('rg volume=1')
+        when '7' then c.req_ok('rg preamp-=1')
+        when '8' then c.req_ok('rg preamp+=1')
+        when 'm' then c.req_ok("rg volume=#{cur_vol == 0 ? pre_mute_vol : 0}")
         when "F" then c.req_ok("rg fallback_gain+=1")
         when "f" then c.req_ok("rg fallback_gain-=1")
         when ">" then c.req_ok("tl next")
diff --git a/lib/dtas/player/client_handler.rb b/lib/dtas/player/client_handler.rb
index a27e4f5..498388e 100644
--- a/lib/dtas/player/client_handler.rb
+++ b/lib/dtas/player/client_handler.rb
@@ -259,7 +259,7 @@ module DTAS::Player::ClientHandler # :nodoc:
         rv = set_bool(io, kv, v) { |b| @rg.fallback_track = b }
         rv == true or return rv
       when %r{(?:gain_threshold|norm_threshold|
-              preamp|norm_level|fallback_gain)[+-]?\z}x
+              preamp|norm_level|fallback_gain|volume)[+-]?\z}x
         rv = adjust_numeric(io, @rg, k, v)
         rv == true or return rv
       end
diff --git a/lib/dtas/rg_state.rb b/lib/dtas/rg_state.rb
index b124098..67706aa 100644
--- a/lib/dtas/rg_state.rb
+++ b/lib/dtas/rg_state.rb
@@ -19,6 +19,7 @@ class DTAS::RGState # :nodoc:
   }
 
   RG_DEFAULT = {
+    "volume" => 1.0,
     # skip the effect if the adjustment is too small to be noticeable
     "gain_threshold" => 0.00000001, # in dB
     "norm_threshold" => 0.00000001,
@@ -34,6 +35,13 @@ class DTAS::RGState # :nodoc:
   SIVS = RG_DEFAULT.keys
   SIVS.each { |iv| attr_accessor iv }
 
+  undef_method :volume=
+
+  def volume=(val)
+    val = 0 if val.to_i < 0
+    @volume = val.round(4)
+  end
+
   def initialize
     RG_DEFAULT.each do |k,v|
       instance_variable_set("@#{k}", v)
@@ -50,24 +58,37 @@ class DTAS::RGState # :nodoc:
     ivars_to_hash(SIVS)
   end
 
+  def vol_db
+    linear_to_db(@volume)
+  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
 
+  def to_sox_gain(val)
+    case val.infinite?
+    when -1 then return 'gain -192'
+    when 1 then return 'gain 192'
+    else
+      sprintf('gain %0.8g', val)
+    end
+  end
+
   # returns a dB argument to the "gain" effect, nil if nothing found
   def rg_vol_gain(val)
-    val = val.to_f + @preamp
+    val = val.to_f + @preamp + vol_db
     return if val.abs < @gain_threshold
-    sprintf('gain %0.8g', val)
+    to_sox_gain(val)
   end
 
   # returns a DB argument to the "gain" effect
   def rg_vol_norm(val)
-    diff = @norm_level - val.to_f
+    diff = @norm_level - val.to_f + @volume
     return if (@norm_level - diff).abs < @norm_threshold
     diff += @norm_level
-    sprintf('gain %0.8g', linear_to_db(diff))
+    to_sox_gain(linear_to_db(diff))
   end
 
   # The ReplayGain fallback adjustment value (in dB), in case a file is
@@ -75,18 +96,19 @@ class DTAS::RGState # :nodoc:
   # 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
-    val = @fallback_gain + @preamp
+    val = (@fallback_gain || 0) + @preamp + vol_db
     return if val.abs < @gain_threshold
     warn(reason) if $DEBUG
-    "gain #{val}"
+    to_sox_gain(val)
   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
+    unless @mode
+      return @volume == 1.0 ? nil : to_sox_gain(vol_db)
+    end
     rg = source.replaygain or
       return rg_fallback_effect("ReplayGain tags missing")
     val = rg.__send__(@mode)