Class: DTAS::PartStats
- Inherits:
-
Object
- Object
- DTAS::PartStats
- Includes:
- Process
- Defined in:
- lib/dtas/partstats.rb
Defined Under Namespace
Classes: TrimPart
Constant Summary collapse
- CMD =
'sox "$INFILE" -n $TRIMFX $SOXFX stats $STATSOPTS'
Constants included from Process
Instance Attribute Summary collapse
-
#key_idx ⇒ Object
readonly
Returns the value of attribute key_idx.
-
#key_width ⇒ Object
readonly
Returns the value of attribute key_width.
Instance Method Summary collapse
-
#initialize(infile) ⇒ PartStats
constructor
A new instance of PartStats.
-
#parse_stats(stats, trim_part, buf) ⇒ Object
Overall Left Right DC offset 0.001074 0.000938 0.001074 Min level -0.997711 -0.997711 -0.997711 Max level 0.997681 0.997681 0.997681 Pk lev dB -0.02 -0.02 -0.02 RMS lev dB -10.38 -9.90 -10.92 RMS Pk dB -4.62 -4.62 -5.10 RMS Tr dB -87.25 -86.58 -87.25 Crest factor - 3.12 3.51 Flat factor 19.41 19.66 18.89 Pk count 117k 156k 77.4k Bit-depth 16/16 16/16 16/16 Num samples 17.2M Length s 389.373 Scale max 1.000000 Window s 0.050.
- #partitions(chunk_sec) ⇒ Object
- #run(opts = {}) ⇒ Object
- #spawn(trim_part, opts) ⇒ Object
Methods included from Process
#dtas_spawn, #env_expand, #qx, reaper
Methods included from XS
Constructor Details
#initialize(infile) ⇒ PartStats
Returns a new instance of PartStats.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/dtas/partstats.rb', line 26 def initialize(infile) @infile = infile %w(samples rate channels).each do |iv| sw = iv[0] # -s, -r, -c i = qx(%W(soxi -#{sw} #@infile)).to_i raise ArgumentError, "invalid #{iv}: #{i}" if i <= 0 instance_variable_set("@#{iv}", i) end # "Pk lev dB" => 1, "RMS lev dB" => 2, ... @key_nr = 0 @key_idx = Hash.new { |h,k| h[k] = (@key_nr += 1) } @key_width = {} end |
Instance Attribute Details
#key_idx ⇒ Object (readonly)
Returns the value of attribute key_idx.
13 14 15 |
# File 'lib/dtas/partstats.rb', line 13 def key_idx @key_idx end |
#key_width ⇒ Object (readonly)
Returns the value of attribute key_width.
14 15 16 |
# File 'lib/dtas/partstats.rb', line 14 def key_width @key_width end |
Instance Method Details
#parse_stats(stats, trim_part, buf) ⇒ Object
Overall Left Right DC offset 0.001074 0.000938 0.001074 Min level -0.997711 -0.997711 -0.997711 Max level 0.997681 0.997681 0.997681 Pk lev dB -0.02 -0.02 -0.02 RMS lev dB -10.38 -9.90 -10.92 RMS Pk dB -4.62 -4.62 -5.10 RMS Tr dB -87.25 -86.58 -87.25 Crest factor - 3.12 3.51 Flat factor 19.41 19.66 18.89 Pk count 117k 156k 77.4k Bit-depth 16/16 16/16 16/16 Num samples 17.2M Length s 389.373 Scale max 1.000000 Window s 0.050
becomes:
[
TrimPart,
[ -0.02, -0.02, -0.02 ], # Pk lev dB
[ -10.38, -9.90, -10.92 ], # RMS lev dB
...
]
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/dtas/partstats.rb', line 162 def parse_stats(stats, trim_part, buf) trim_row = [ trim_part ] buf.split(/\n/).each do |line| do_map = true case line when /\A(\S+ \S+ dB)\s/, /\A(Crest factor)\s+-\s/ nshift = 3 when /\A(Flat factor)\s/ nshift = 2 when /\A(Pk count)\s/ nshift = 2 do_map = false else next end key = $1 key.freeze key_idx = @key_idx[key] parts = line.split(/\s+/) nshift.times { parts.shift } # remove stuff we don't need @key_width[key] = parts.size trim_row[key_idx] = do_map ? parts.map!(&:to_f) : parts end stats[trim_part.tbeg / trim_part.tlen] = trim_row end |
#partitions(chunk_sec) ⇒ Object
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/dtas/partstats.rb', line 41 def partitions(chunk_sec) n = 0 part_samples = chunk_sec * @rate rv = [] begin rv << TrimPart.new(n, part_samples, @rate) n += part_samples end while n < @samples rv end |
#run(opts = {}) ⇒ Object
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 |
# File 'lib/dtas/partstats.rb', line 68 def run(opts = {}) sev = DTAS::Sigevent.new trap(:CHLD) { sev.signal } jobs = opts[:jobs] || 2 pids = {} rset = {} stats = [] fails = [] do_spawn = lambda do |trim_part| pid, rpipe = spawn(trim_part, opts) rset[rpipe] = [ trim_part, "" ] pids[pid] = [ trim_part, rpipe ] end parts = partitions(opts[:chunk_length] || 10) jobs.times do trim_part = parts.shift or break do_spawn.call(trim_part) end rset[sev] = true while pids.size > 0 r = IO.select(rset.keys) or next r[0].each do |rd| if DTAS::Sigevent === rd rd.readable_iter do |_,_| begin pid, status = Process.waitpid2(-1, Process::WNOHANG) pid or break done = pids.delete(pid) done_part = done[0] if status.success? trim_part = parts.shift and do_spawn.call(trim_part) puts "DONE #{done_part}" if $DEBUG else fails << [ done_part, status ] end rescue Errno::ECHILD break end while true end else # spurious wakeup should not happen on local pipes, # so readpartial should be safe trim_part, buf = rset[rd] begin buf << rd.readpartial(666) rescue EOFError rset.delete(rd) rd.close parse_stats(stats, trim_part, buf) end end end end return stats if fails.empty? && parts.empty? fails.each do |(trim_part,status)| warn "FAIL #{status.inspect} #{trim_part}" end false ensure sev.close end |
#spawn(trim_part, opts) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/dtas/partstats.rb', line 52 def spawn(trim_part, opts) rd, wr = IO.pipe env = opts[:env] env = env ? env.dup : {} env["INFILE"] = @infile env["TRIMFX"] = "trim #{trim_part.tbeg}s #{trim_part.tlen}s" opts = { pgroup: true, close_others: true, err: wr } pid = begin Process.spawn(env, CMD, opts) rescue Errno::EINTR # Ruby bug? retry end wr.close [ pid, rd ] end |