about summary refs log tree commit homepage
path: root/lib/dtas/source/file.rb
blob: 00b8f8ef7e6dc45c970f23cf49b57ac8126e8ab1 (plain)
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
# License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
# frozen_string_literal: true
require_relative '../../dtas'
require_relative '../source'
require_relative '../command'
require_relative '../format'
require_relative '../process'
require_relative '../cue_index'

module DTAS::Source::File # :nodoc:
  attr_reader :infile
  attr_reader :offset
  attr_accessor :tryorder
  require_relative 'common' # dtas/source/common
  require_relative 'mp3gain'
  include DTAS::Command
  include DTAS::Process
  include DTAS::Source::Common
  include DTAS::Source::Mp3gain

  FILE_SIVS = %w(infile comments command env) # for the "current" command
  SRC_SIVS = %w(command env tryorder)

  def source_file_dup(infile, offset, trim)
    rv = dup
    rv.__file_init(infile, offset, trim)
    rv
  end

  def __file_init(infile, offset, trim)
    @env = @env.dup
    @format = nil
    @infile = infile
    @offset = offset
    @trim = trim
    @comments = nil
    @samples = nil
    @cuebp = 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
    off = __offset_samples
    return off unless @trim
    tbeg = @trim[0] * format.rate
    tbeg < off ? off : tbeg
  end

  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

  # creates the effect to fill the TRIMFX env
  def trimfx
    return unless @offset || @trim
    fx = "trim #{offset_samples}s".dup
    if @trim && @trim[1]
      fx << sprintf(' =%0.9gs', (@trim[0] + @trim[1]) * format.rate)
    end
    fx
  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

  def replaygain(mode)
    @rg ||= DTAS::ReplayGain.new(comments, mode) ||
            DTAS::ReplayGain.new(mp3gain_comments, mode)
  end

  def to_source_cat
    ivars_to_hash(SRC_SIVS)
  end

  def load!(src_hsh)
    SRC_SIVS.each do |field|
      val = src_hsh[field] and instance_variable_set("@#{field}", val)
    end
  end

  def to_state_hash
    defaults = source_defaults # see dtas/source/{av,sox}.rb
    to_source_cat.delete_if { |k,v| v == defaults[k] }
  end

  def cuebreakpoints
    rv = @cuebp and return rv
    rv = []
    begin
      str = qx(@env, %W(metaflac --export-cuesheet-to=- #@infile))
    rescue
      return rv
    end
    str.scan(/^    INDEX (\d+) (\S+)/) do |m|
      index = m[0]
      time = m[1].dup
      case time
      when /\A\d+\z/
        time << "s" # sample count (flac 1.3.0)
      else # HH:MM:SS:FF
        # FF/75 CDDA frames per second, convert to fractional seconds
        time.sub!(/:(\d+)\z/, "")
        frames = $1.to_f
        if frames > 0
          time = sprintf("#{time}.%0.6g", frames / 75.0)
        end
      end
      rv << DTAS::CueIndex.new(index, time)
    end
    @cuebp = rv
  end
end