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
| | # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
require_relative '../dtas'
require_relative 'parse_time'
require 'shellwords'
class DTAS::TrimFX
include DTAS::ParseTime
attr_reader :tbeg
attr_reader :tlen
attr_reader :cmd
def initialize(args)
args = args.dup
case args.shift
when "trim"
parse_trim!(args)
when "all"
@tbeg = 0
@tlen = nil
else
raise ArgumentError, "#{args.inspect} not understood"
end
case tmp = args.shift
when "sh" then @cmd = args
when "sox" then tfx_sox(args)
when "eca" then tfx_eca(args)
when nil
@cmd = []
else
raise ArgumentError, "unknown effect type: #{tmp}"
end
end
def tfx_sox(args)
@cmd = %w(sox $SOXIN $SOXOUT $TRIMFX)
@cmd.concat(args)
end
def tfx_eca(args)
@cmd = %w(sox $SOXIN $SOX2ECA $TRIMFX)
@cmd.concat(%w(| ecasound $ECAFMT -i stdin -o stdout))
@cmd.concat(args)
@cmd.concat(%w(| sox $ECA2SOX - $SOXOUT))
end
def to_sox_arg(format)
if @tbeg && @tlen
beg = @tbeg * format.rate
len = @tlen * format.rate
%W(trim #{beg.round}s #{len.round}s)
elsif @tbeg
return [] if @tbeg == 0
beg = @tbeg * format.rate
%W(trim #{beg.round}s)
else
[]
end
end
def parse_trim!(args)
tbeg = parse_time(args.shift)
if args[0] =~ /\A=?[\d\.]+\z/
tlen = args.shift
is_stop_time = tlen.sub!(/\A=/, "") ? true : false
tlen = parse_time(tlen)
if is_stop_time
tlen = tlen - tbeg
end
else
tlen = nil
end
@tbeg = tbeg
@tlen = tlen
end
def <=>(other)
tbeg <=> other.tbeg
end
# for stable sorting
class TFXSort < Struct.new(:tfx, :idx)
def <=>(other)
cmp = tfx <=> other.tfx
0 == cmp ? idx <=> other.idx : cmp
end
end
# there'll be multiple epochs if ranges overlap
def self.schedule(ary)
sorted = []
ary.each_with_index { |tfx, i| sorted << TFXSort[tfx, i] }
sorted.sort!
rv = []
epoch = 0
prev_end = 0
defer = []
begin
while tfxsort = sorted.shift
tfx = tfxsort.tfx
if tfx.tbeg >= prev_end
prev_end = tfx.tbeg + tfx.tlen
(rv[epoch] ||= []) << tfx
else
defer << tfxsort
end
end
if defer[0]
epoch += 1
sorted = defer
defer = []
prev_end = 0
end
end while sorted[0]
rv
end
def self.expand(ary, total_len)
rv = []
schedule(ary).each_with_index do |sary, i|
tip = 0
dst = rv[i] = []
while tfx = sary.shift
if tfx.tbeg > tip
nfx = new(%W(trim #{tip} =#{tfx.tbeg}))
dst << nfx
dst << tfx
tip = tfx.tbeg + tfx.tlen
end
end
if tip < total_len
nfx = new(%W(trim #{tip} =#{total_len}))
dst << nfx
end
end
rv
end
end
|