about summary refs log tree commit homepage
path: root/lib/dtas/source
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-08-25 09:25:07 +0000
committerEric Wong <normalperson@yhbt.net>2013-08-25 09:29:46 +0000
commit68ffa097e187da663fa3f537b430428ea5e8de2e (patch)
treebe7d441b68ab8c77a7ebc062c129b5701bf2a5b1 /lib/dtas/source
parent9cd8e2776edc246950d2c7ebdea833489efb1d1f (diff)
downloaddtas-68ffa097e187da663fa3f537b430428ea5e8de2e.tar.gz
We should've done this at the start, but we didn't.
Diffstat (limited to 'lib/dtas/source')
-rw-r--r--lib/dtas/source/cmd.rb (renamed from lib/dtas/source/command.rb)2
-rw-r--r--lib/dtas/source/file.rb63
-rw-r--r--lib/dtas/source/mp3gain.rb (renamed from lib/dtas/source/mp3.rb)2
-rw-r--r--lib/dtas/source/sox.rb100
4 files changed, 165 insertions, 2 deletions
diff --git a/lib/dtas/source/command.rb b/lib/dtas/source/cmd.rb
index 930c5cf..2507101 100644
--- a/lib/dtas/source/command.rb
+++ b/lib/dtas/source/cmd.rb
@@ -6,7 +6,7 @@ require_relative '../source'
 require_relative '../command'
 require_relative '../serialize'
 
-class DTAS::Source::Command # :nodoc:
+class DTAS::Source::Cmd # :nodoc:
   require_relative '../source/common'
 
   include DTAS::Command
diff --git a/lib/dtas/source/file.rb b/lib/dtas/source/file.rb
new file mode 100644
index 0000000..472cb3d
--- /dev/null
+++ b/lib/dtas/source/file.rb
@@ -0,0 +1,63 @@
+# -*- encoding: binary -*-
+# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative '../../dtas'
+require_relative '../source'
+require_relative '../command'
+require_relative '../format'
+require_relative '../process'
+
+module DTAS::Source::File # :nodoc:
+  attr_reader :infile
+  attr_reader :offset
+  require_relative 'common' # dtas/source/common
+  include DTAS::Command
+  include DTAS::Process
+  include DTAS::Source::Common
+
+  FILE_SIVS = %w(infile comments command env)
+
+  def source_file_init(infile, offset)
+    @format = nil
+    @infile = infile
+    @offset = offset
+    @comments = nil
+    @samples = nil
+    @rg = nil
+  end
+
+  # this exists mainly to make the mpris interface easier, but it's not
+  # necessary, the mpris interface also knows the sample rate
+  def offset_us
+    (offset_samples / format.rate.to_f) * 1000000
+  end
+
+  # returns any offset in samples (relative to the original source file),
+  # likely zero unless seek was used
+  def offset_samples
+    return 0 unless @offset
+    case @offset
+    when /\A\d+s\z/
+      @offset.to_i
+    else
+      format.hhmmss_to_samples(@offset)
+    end
+  end
+
+  # A user may be downloading the file and start playing
+  # it before the download completes, this refreshes
+  def samples!
+    @samples = nil
+    samples
+  end
+
+  def comments
+    @comments ||= __load_comments
+  end
+
+  def to_hash
+    rv = ivars_to_hash(FILE_SIVS)
+    rv["samples"] = samples
+    rv
+  end
+end
diff --git a/lib/dtas/source/mp3.rb b/lib/dtas/source/mp3gain.rb
index 7ceaf8a..03bc37a 100644
--- a/lib/dtas/source/mp3.rb
+++ b/lib/dtas/source/mp3gain.rb
@@ -3,7 +3,7 @@
 # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
 require_relative '../process'
 
-module DTAS::Source::Mp3 # :nodoc:
+module DTAS::Source::Mp3gain # :nodoc:
   include DTAS::Process
   # we use dBFS = 1.0 as scale (not 32768)
   def __mp3gain_peak(str)
diff --git a/lib/dtas/source/sox.rb b/lib/dtas/source/sox.rb
new file mode 100644
index 0000000..44b5f17
--- /dev/null
+++ b/lib/dtas/source/sox.rb
@@ -0,0 +1,100 @@
+# -*- encoding: binary -*-
+# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
+# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
+require_relative '../../dtas'
+require_relative '../source'
+require_relative '../replaygain'
+
+# this is usually one input file
+class DTAS::Source::Sox # :nodoc:
+  require_relative 'file'
+  require_relative 'mp3gain'
+
+  include DTAS::Source::File
+  include DTAS::Source::Mp3gain
+
+  SOX_DEFAULTS = COMMAND_DEFAULTS.merge(
+    "command" => 'exec sox "$INFILE" $SOXFMT - $TRIMFX $RGFX',
+    "comments" => nil,
+  )
+
+  def initialize(infile, offset = nil)
+    command_init(SOX_DEFAULTS)
+    source_file_init(infile, offset)
+  end
+
+  def precision
+    qx(%W(soxi -p #@infile), err: "/dev/null").to_i # sox.git f4562efd0aa3
+  rescue # fallback to parsing the whole output
+    s = qx(%W(soxi #@infile), err: "/dev/null")
+    s =~ /Precision\s+:\s*(\d+)-bit/
+    v = $1.to_i
+    return v if v > 0
+    raise TypeError, "could not determine precision for #@infile"
+  end
+
+  def format
+    @format ||= begin
+      fmt = DTAS::Format.new
+      fmt.from_file(@infile)
+      fmt.bits ||= precision
+      fmt
+    end
+  end
+
+  # This is the number of samples according to the samples in the source
+  # file itself, not the decoded output
+  def samples
+    @samples ||= qx(%W(soxi -s #@infile)).to_i
+  rescue => e
+    warn e.message
+    0
+  end
+
+  # just run soxi -a
+  def __load_comments
+    tmp = {}
+    case @infile
+    when String
+      err = ""
+      cmd = %W(soxi -a #@infile)
+      begin
+        qx(cmd, err: err).split(/\n/).each do |line|
+          key, value = line.split(/=/, 2)
+          key && value or next
+          # TODO: multi-line/multi-value/repeated tags
+          tmp[key.upcase] = value
+        end
+      rescue => e
+        if /FAIL formats: no handler for file extension/ =~ err
+          warn("#{xs(cmd)}: #{err}")
+        else
+          warn("#{e.message} (#{e.class})")
+        end
+      end
+    end
+    tmp
+  end
+
+  def replaygain
+    @rg = DTAS::ReplayGain.new(comments) ||
+          DTAS::ReplayGain.new(mp3gain_comments)
+  end
+
+  def spawn(format, rg_state, opts)
+    raise "BUG: #{self.inspect}#spawn called twice" if @to_io
+    e = format.to_env
+    e["INFILE"] = @infile
+
+    # make sure these are visible to the "current" command...
+    @env["TRIMFX"] = @offset ? "trim #@offset" : nil
+    @env["RGFX"] = rg_state.effect(self) || nil
+    e.merge!(@rg.to_env) if @rg
+
+    @pid = dtas_spawn(e.merge!(@env), command_string, opts)
+  end
+
+  def to_hsh
+    to_hash.delete_if { |k,v| v == SOX_DEFAULTS[k] }
+  end
+end