dtas 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/Documentation/.gitignore +1 -1
- data/Documentation/GNUmakefile +3 -1
- data/Documentation/dtas-archive.txt +61 -0
- data/Documentation/dtas-console.txt +4 -3
- data/Documentation/dtas-ctl.txt +4 -3
- data/Documentation/dtas-cueedit.txt +4 -3
- data/Documentation/dtas-enq.txt +4 -3
- data/Documentation/dtas-env.txt +60 -0
- data/Documentation/dtas-msinkctl.txt +4 -3
- data/Documentation/dtas-player.txt +6 -3
- data/Documentation/dtas-player_effects.txt +4 -3
- data/Documentation/dtas-player_protocol.txt +27 -4
- data/Documentation/dtas-player_sink_examples.txt +7 -3
- data/Documentation/dtas-sinkedit.txt +20 -3
- data/Documentation/dtas-sourceedit.txt +21 -3
- data/Documentation/dtas-splitfx.txt +14 -5
- data/Documentation/dtas-tl.txt +4 -3
- data/Documentation/dtas-xdelay.txt +4 -3
- data/Documentation/update-footer.rb +52 -0
- data/GIT-VERSION-GEN +2 -2
- data/GNUmakefile +1 -1
- data/HACKING +3 -2
- data/INSTALL +6 -6
- data/README +1 -1
- data/Rakefile +2 -3
- data/TODO +1 -1
- data/bin/dtas-archive +187 -0
- data/bin/dtas-console +7 -1
- data/bin/dtas-ctl +1 -1
- data/bin/dtas-cueedit +3 -3
- data/bin/dtas-enq +1 -1
- data/bin/dtas-msinkctl +1 -1
- data/bin/dtas-partstats +10 -4
- data/bin/dtas-player +1 -1
- data/bin/dtas-sinkedit +82 -20
- data/bin/dtas-sourceedit +64 -22
- data/bin/dtas-splitfx +1 -1
- data/bin/dtas-tl +1 -1
- data/bin/dtas-xdelay +1 -1
- data/dtas-linux.gemspec +1 -1
- data/dtas-mpris.gemspec +1 -1
- data/dtas.gemspec +1 -1
- data/examples/splitfx.sample.yml +11 -3
- data/examples/{trimfx.sample.yml → tfx.sample.yml} +1 -1
- data/lib/dtas.rb +2 -1
- data/lib/dtas/buffer.rb +5 -5
- data/lib/dtas/buffer/read_write.rb +8 -5
- data/lib/dtas/buffer/splice.rb +29 -8
- data/lib/dtas/command.rb +2 -2
- data/lib/dtas/compat_onenine.rb +1 -1
- data/lib/dtas/cue_index.rb +3 -1
- data/lib/dtas/disclaimer.rb +1 -1
- data/lib/dtas/edit_client.rb +1 -1
- data/lib/dtas/fadefx.rb +100 -0
- data/lib/dtas/format.rb +4 -2
- data/lib/dtas/parse_time.rb +3 -1
- data/lib/dtas/partstats.rb +8 -10
- data/lib/dtas/pipe.rb +2 -1
- data/lib/dtas/player.rb +33 -11
- data/lib/dtas/player/client_handler.rb +43 -8
- data/lib/dtas/process.rb +6 -14
- data/lib/dtas/replaygain.rb +3 -3
- data/lib/dtas/rg_state.rb +1 -1
- data/lib/dtas/serialize.rb +3 -1
- data/lib/dtas/sigevent.rb +1 -1
- data/lib/dtas/sigevent/efd.rb +5 -4
- data/lib/dtas/sigevent/pipe.rb +4 -1
- data/lib/dtas/sink.rb +4 -4
- data/lib/dtas/source.rb +1 -1
- data/lib/dtas/source/av.rb +2 -2
- data/lib/dtas/source/av_ff_common.rb +22 -11
- data/lib/dtas/source/cmd.rb +3 -3
- data/lib/dtas/source/common.rb +3 -2
- data/lib/dtas/source/ff.rb +2 -2
- data/lib/dtas/source/file.rb +22 -4
- data/lib/dtas/source/mp3gain.rb +1 -1
- data/lib/dtas/source/sox.rb +7 -7
- data/lib/dtas/source/splitfx.rb +99 -0
- data/lib/dtas/spawn_fix.rb +10 -0
- data/lib/dtas/splitfx.rb +63 -24
- data/lib/dtas/state_file.rb +3 -1
- data/lib/dtas/{trimfx.rb → tfx.rb} +50 -24
- data/lib/dtas/tracklist.rb +2 -1
- data/lib/dtas/unix_accepted.rb +2 -2
- data/lib/dtas/unix_client.rb +2 -2
- data/lib/dtas/unix_server.rb +1 -1
- data/lib/dtas/util.rb +1 -1
- data/lib/dtas/watchable.rb +54 -0
- data/lib/dtas/writable_iter.rb +2 -1
- data/lib/dtas/xs.rb +2 -2
- data/perl/dtas-graph +1 -1
- data/test/covshow.rb +1 -1
- data/test/helper.rb +1 -1
- data/test/player_integration.rb +1 -1
- data/test/test_buffer.rb +1 -1
- data/test/test_env.rb +1 -1
- data/test/test_fadefx.rb +45 -0
- data/test/test_format.rb +1 -1
- data/test/test_format_change.rb +1 -1
- data/test/test_player.rb +1 -1
- data/test/test_player_client_handler.rb +1 -1
- data/test/test_player_integration.rb +8 -8
- data/test/test_process.rb +1 -1
- data/test/test_rg_integration.rb +1 -1
- data/test/test_rg_state.rb +1 -1
- data/test/test_sink.rb +1 -1
- data/test/test_sink_pipe_size.rb +2 -2
- data/test/test_sink_tee_integration.rb +1 -1
- data/test/test_source_av.rb +1 -1
- data/test/test_source_sox.rb +1 -1
- data/test/test_splitfx.rb +43 -13
- data/test/test_tfx.rb +85 -0
- data/test/test_tracklist.rb +1 -1
- data/test/test_unixserver.rb +1 -1
- data/test/test_util.rb +1 -1
- metadata +17 -6
- data/lib/dtas/compat_rbx.rb +0 -12
- data/test/test_trimfx.rb +0 -81
data/lib/dtas/source/mp3gain.rb
CHANGED
data/lib/dtas/source/sox.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2013-
|
1
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
require_relative '../../dtas'
|
4
4
|
require_relative '../source'
|
@@ -38,13 +38,13 @@ class DTAS::Source::Sox # :nodoc:
|
|
38
38
|
command_init(SOX_DEFAULTS)
|
39
39
|
end
|
40
40
|
|
41
|
-
def try(infile, offset = nil)
|
41
|
+
def try(infile, offset = nil, trim = nil)
|
42
42
|
err = ""
|
43
43
|
cmd = %W(soxi -s #{infile})
|
44
44
|
s = qx(@env.dup, cmd, err_str: err, no_raise: true)
|
45
45
|
return if err =~ /soxi FAIL formats:/
|
46
46
|
self.class.try_to_fail_harder(infile, s, cmd) or return
|
47
|
-
source_file_dup(infile, offset)
|
47
|
+
source_file_dup(infile, offset, trim)
|
48
48
|
end
|
49
49
|
|
50
50
|
def format
|
@@ -75,13 +75,13 @@ class DTAS::Source::Sox # :nodoc:
|
|
75
75
|
tmp
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
79
|
-
raise "BUG: #{self.inspect}#
|
78
|
+
def src_spawn(player_format, rg_state, opts)
|
79
|
+
raise "BUG: #{self.inspect}#src_spawn called twice" if @to_io
|
80
80
|
e = @env.merge!(player_format.to_env)
|
81
|
-
e["INFILE"] = @infile
|
81
|
+
e["INFILE"] = xs(@infile)
|
82
82
|
|
83
83
|
# make sure these are visible to the "current" command...
|
84
|
-
e["TRIMFX"] =
|
84
|
+
e["TRIMFX"] = trimfx
|
85
85
|
e["RGFX"] = rg_state.effect(self) || nil
|
86
86
|
e.merge!(@rg.to_env) if @rg
|
87
87
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Copyright (C) 2014-2015 all contributors <dtas-all@nongnu.org>
|
2
|
+
# License: GPLv3 or later <https://www.gnu.org/licenses/gpl-3.0.txt>
|
3
|
+
require 'yaml'
|
4
|
+
require_relative 'sox'
|
5
|
+
require_relative '../splitfx'
|
6
|
+
require_relative '../watchable'
|
7
|
+
|
8
|
+
class DTAS::Source::SplitFX < DTAS::Source::Sox # :nodoc:
|
9
|
+
MAX_YAML_SIZE = 512 * 1024
|
10
|
+
attr_writer :sox, :sfx
|
11
|
+
include DTAS::Watchable if defined?(DTAS::Watchable)
|
12
|
+
|
13
|
+
SPLITFX_DEFAULTS = SOX_DEFAULTS.merge(
|
14
|
+
"command" => "#{SOX_DEFAULTS["command"]} $FX",
|
15
|
+
"tryorder" => 3,
|
16
|
+
)
|
17
|
+
|
18
|
+
def initialize(sox = DTAS::Source::Sox.new)
|
19
|
+
command_init(SPLITFX_DEFAULTS)
|
20
|
+
@sox = sox
|
21
|
+
end
|
22
|
+
|
23
|
+
def try(ymlfile, offset = nil, trim = nil)
|
24
|
+
@splitfx = @ymlhash = nil
|
25
|
+
st = File.stat(ymlfile)
|
26
|
+
return false if !st.file? || st.size > MAX_YAML_SIZE
|
27
|
+
|
28
|
+
# read 4 bytes first to ensure we have a YAML file with a hash:
|
29
|
+
buf = ""
|
30
|
+
File.open(ymlfile, "rb") do |fp|
|
31
|
+
return false if fp.read(4, buf) != "---\n"
|
32
|
+
buf << fp.read
|
33
|
+
end
|
34
|
+
|
35
|
+
sfx = DTAS::SplitFX.new
|
36
|
+
Dir.chdir(File.dirname(ymlfile)) do # ugh
|
37
|
+
@ymlhash = YAML.load(buf)
|
38
|
+
@ymlhash['tracks'] ||= [ "t 0 default" ]
|
39
|
+
sfx.import(@ymlhash)
|
40
|
+
sfx.infile.replace(File.expand_path(sfx.infile))
|
41
|
+
end
|
42
|
+
@splitfx = sfx
|
43
|
+
@infile = ymlfile
|
44
|
+
sox = @sox.try(sfx.infile, offset, trim) or return false
|
45
|
+
rv = source_file_dup(ymlfile, offset, trim)
|
46
|
+
rv.sox = sox
|
47
|
+
rv.env = sfx.env
|
48
|
+
rv.sfx = sfx
|
49
|
+
rv
|
50
|
+
rescue => e
|
51
|
+
warn "#{e.message} (#{e.class})"
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def __load_comments
|
56
|
+
@ymlhash["comments"] || @sox.__load_comments
|
57
|
+
end
|
58
|
+
|
59
|
+
def command_string
|
60
|
+
@ymlhash["command"] || super
|
61
|
+
end
|
62
|
+
|
63
|
+
def src_spawn(player_format, rg_state, opts)
|
64
|
+
raise "BUG: #{self.inspect}#src_spawn called twice" if @to_io
|
65
|
+
e = @env.merge!(player_format.to_env)
|
66
|
+
@sfx.infile_env(e, @sox.infile)
|
67
|
+
|
68
|
+
# make sure these are visible to the "current" command...
|
69
|
+
e["TRIMFX"] = trimfx
|
70
|
+
e["RGFX"] = rg_state.effect(self) || nil
|
71
|
+
e.merge!(@rg.to_env) if @rg
|
72
|
+
|
73
|
+
@pid = dtas_spawn(e, command_string, opts)
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_hsh
|
77
|
+
to_hash.delete_if { |k,v| v == SPLITFX_DEFAULTS[k] }
|
78
|
+
end
|
79
|
+
|
80
|
+
def format
|
81
|
+
@sox.format
|
82
|
+
end
|
83
|
+
|
84
|
+
def samples!
|
85
|
+
@sox.samples!
|
86
|
+
end
|
87
|
+
|
88
|
+
def samples
|
89
|
+
@sox.samples
|
90
|
+
end
|
91
|
+
|
92
|
+
def source_defaults
|
93
|
+
SPLITFX_DEFAULTS
|
94
|
+
end
|
95
|
+
|
96
|
+
def cuebreakpoints
|
97
|
+
@splitfx.cuebreakpoints
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
# workaround for older Rubies: https://bugs.ruby-lang.org/issues/8770
|
4
|
+
module DTAS::SpawnFix # :nodoc:
|
5
|
+
def spawn(*args)
|
6
|
+
super(*args)
|
7
|
+
rescue Errno::EINTR
|
8
|
+
retry
|
9
|
+
end if RUBY_VERSION.to_f <= 2.1
|
10
|
+
end
|
data/lib/dtas/splitfx.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
|
-
# Copyright (C) 2013-
|
1
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
-
# Unlike the stuff for dtas-player, dtas-splitfx is fairly tied to sox
|
4
|
-
# (but we may still pipe to ecasound or anything else)
|
5
3
|
require_relative '../dtas'
|
6
4
|
require_relative 'format'
|
7
5
|
require_relative 'process'
|
8
6
|
require_relative 'xs'
|
9
7
|
require 'tempfile'
|
8
|
+
|
9
|
+
# The backend for dtas-splitfx(1) command, but also supported by dtas-player
|
10
|
+
# Unlike the stuff for dtas-player, dtas-splitfx is fairly tied to sox
|
11
|
+
# (but we may still pipe to ecasound or anything else)
|
10
12
|
class DTAS::SplitFX # :nodoc:
|
11
13
|
CMD = 'sox "$INFILE" $COMMENTS $OUTFMT "$TRACKNUMBER.$SUFFIX" '\
|
12
|
-
'$TRIMFX $RATEFX $DITHERFX'
|
14
|
+
'$TRIMFX $FX $RATEFX $DITHERFX'
|
13
15
|
include DTAS::Process
|
14
16
|
include DTAS::XS
|
17
|
+
attr_reader :infile, :env
|
15
18
|
|
16
|
-
class Skip < Struct.new(:
|
19
|
+
class Skip < Struct.new(:tbeg) # :nodoc:
|
17
20
|
def commit(_)
|
18
21
|
# noop
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
|
-
class T < Struct.new(:env, :comments, :
|
25
|
+
class T < Struct.new(:env, :comments, :tbeg, :fade_in, :fade_out) # :nodoc:
|
23
26
|
def commit(advance_track_samples)
|
24
|
-
tlen = advance_track_samples -
|
25
|
-
trimfx = "trim #{
|
27
|
+
tlen = advance_track_samples - tbeg
|
28
|
+
trimfx = "trim #{tbeg}s #{tlen}s"
|
26
29
|
if fade_in
|
27
30
|
trimfx << " #{fade_in}"
|
28
31
|
end
|
@@ -34,6 +37,10 @@ class DTAS::SplitFX # :nodoc:
|
|
34
37
|
fade = " fade #{fade_type} 0 #{tlen}s #{fade_out_len}"
|
35
38
|
trimfx << fade
|
36
39
|
end
|
40
|
+
|
41
|
+
# raw sample counts (without 's' suffix)
|
42
|
+
env["TBEG"] = tbeg.to_s
|
43
|
+
env["TLEN"] = tlen.to_s
|
37
44
|
env["TRIMFX"] = trimfx
|
38
45
|
end
|
39
46
|
end
|
@@ -60,7 +67,7 @@ class DTAS::SplitFX # :nodoc:
|
|
60
67
|
},
|
61
68
|
"opusenc" => {
|
62
69
|
"command" => 'sox "$INFILE" $COMMENTS $OUTFMT - ' \
|
63
|
-
'$TRIMFX $RATEFX $DITHERFX | opusenc --music ' \
|
70
|
+
'$TRIMFX $FX $RATEFX $DITHERFX | opusenc --music ' \
|
64
71
|
'--raw-bits $BITS_PER_SAMPLE ' \
|
65
72
|
'$OPUSENC_BITRATE --raw-rate $RATE --raw-chan $CHANNELS ' \
|
66
73
|
'--raw-endianness $ENDIAN_OPUSENC ' \
|
@@ -76,6 +83,8 @@ class DTAS::SplitFX # :nodoc:
|
|
76
83
|
}
|
77
84
|
@tracks = []
|
78
85
|
@infmt = nil # wait until input is assigned
|
86
|
+
@cuebp = nil # for playback
|
87
|
+
@command = nil # top-level, for playback
|
79
88
|
end
|
80
89
|
|
81
90
|
def _bool(hash, key)
|
@@ -98,7 +107,6 @@ class DTAS::SplitFX # :nodoc:
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
101
|
-
hash = hash.merge(overrides)
|
102
110
|
case v = hash["track_zpad"]
|
103
111
|
when Integer then @track_zpad = val
|
104
112
|
else
|
@@ -132,6 +140,7 @@ class DTAS::SplitFX # :nodoc:
|
|
132
140
|
|
133
141
|
load_input!(hash)
|
134
142
|
load_tracks!(hash)
|
143
|
+
@command = hash["command"] # nil by default
|
135
144
|
end
|
136
145
|
|
137
146
|
def load_input!(hash)
|
@@ -149,7 +158,7 @@ class DTAS::SplitFX # :nodoc:
|
|
149
158
|
{ "command" => CMD, "format" => outfmt }
|
150
159
|
end
|
151
160
|
|
152
|
-
def
|
161
|
+
def splitfx_spawn(target, t, opts)
|
153
162
|
target = @targets[target] || generic_target(target)
|
154
163
|
outfmt = target["format"]
|
155
164
|
|
@@ -185,16 +194,29 @@ class DTAS::SplitFX # :nodoc:
|
|
185
194
|
comments.puts("#{k}=#{v}")
|
186
195
|
end
|
187
196
|
env["COMMENTS"] = "--comment-file=#{comments.path}"
|
188
|
-
env
|
197
|
+
infile_env(env, @infile)
|
189
198
|
env["OUTFMT"] = xs(outfmt.to_sox_arg)
|
190
199
|
env["SUFFIX"] = outfmt.type
|
191
200
|
env.merge!(t.env)
|
192
201
|
|
193
202
|
command = target["command"]
|
194
|
-
|
195
|
-
|
203
|
+
|
204
|
+
# if a default dtas-player command is set, use that.
|
205
|
+
# we'll clobber our default environment since we assume play_cmd
|
206
|
+
# already takes those into account. In other words, use our
|
207
|
+
# target-specific commands like a dtas-player sink:
|
208
|
+
# @command | (INFILE= FX= TRIMFX=; target['command'])
|
209
|
+
if player_cmd = @command
|
210
|
+
sub_env = { 'INFILE' => '-', 'FX' => '', 'TRIMFX' => '' }
|
211
|
+
sub_env_s = sub_env.inject("") { |s,(k,v)| s << "#{k}=#{v} " }
|
212
|
+
command = "#{player_cmd} | (#{sub_env_s}; #{command})"
|
213
|
+
show_cmd = [ _expand_cmd(env, player_cmd), '|', '(', "#{sub_env_s};",
|
214
|
+
_expand_cmd(env.merge(sub_env), command), ')' ].flatten
|
215
|
+
else
|
216
|
+
show_cmd = _expand_cmd(env, command)
|
196
217
|
end
|
197
|
-
|
218
|
+
|
219
|
+
echo = "echo #{xs(show_cmd)}"
|
198
220
|
if opts[:dryrun]
|
199
221
|
command = echo
|
200
222
|
else
|
@@ -216,8 +238,6 @@ class DTAS::SplitFX # :nodoc:
|
|
216
238
|
fmt = "%0#{max.to_s.size}d"
|
217
239
|
when Integer
|
218
240
|
fmt = "%0#{@track_zpad}d"
|
219
|
-
else
|
220
|
-
fmt = "%d"
|
221
241
|
end
|
222
242
|
nr = @track_start
|
223
243
|
@tracks.delete_if do |t|
|
@@ -241,7 +261,7 @@ class DTAS::SplitFX # :nodoc:
|
|
241
261
|
start_time = argv.shift
|
242
262
|
title = argv.shift
|
243
263
|
t = T.new
|
244
|
-
t.
|
264
|
+
t.tbeg = @t2s.call(start_time)
|
245
265
|
t.comments = @comments.dup
|
246
266
|
t.comments["TITLE"] = title
|
247
267
|
t.env = @env.dup
|
@@ -260,17 +280,17 @@ class DTAS::SplitFX # :nodoc:
|
|
260
280
|
end
|
261
281
|
end
|
262
282
|
|
263
|
-
prev = @tracks.last and prev.commit(t.
|
283
|
+
prev = @tracks.last and prev.commit(t.tbeg)
|
264
284
|
@tracks << t
|
265
285
|
when "skip"
|
266
286
|
stop_time = argv.shift
|
267
287
|
argv.empty? or raise ArgumentError, "skip does not take extra args"
|
268
288
|
s = Skip.new
|
269
|
-
s.
|
289
|
+
s.tbeg = @t2s.call(stop_time)
|
270
290
|
# s.comments = {}
|
271
291
|
# s.env = {}
|
272
292
|
prev = @tracks.last or raise ArgumentError, "no tracks to skip"
|
273
|
-
prev.commit(s.
|
293
|
+
prev.commit(s.tbeg)
|
274
294
|
@tracks << s
|
275
295
|
when "stop"
|
276
296
|
stop_time = argv.shift
|
@@ -278,7 +298,7 @@ class DTAS::SplitFX # :nodoc:
|
|
278
298
|
samples = @t2s.call(stop_time)
|
279
299
|
prev = @tracks.last and prev.commit(samples)
|
280
300
|
else
|
281
|
-
raise ArgumentError, "unknown command: #{xs(
|
301
|
+
raise ArgumentError, "unknown command: #{xs(cmd)}"
|
282
302
|
end
|
283
303
|
end
|
284
304
|
|
@@ -309,7 +329,7 @@ class DTAS::SplitFX # :nodoc:
|
|
309
329
|
jobs = opts[:jobs] || tracks.size # jobs == nil => everything at once
|
310
330
|
jobs.times.each do
|
311
331
|
t = tracks.shift or break
|
312
|
-
pid, tmp =
|
332
|
+
pid, tmp = splitfx_spawn(target, t, opts)
|
313
333
|
pids[pid] = [ t, tmp ]
|
314
334
|
end
|
315
335
|
|
@@ -318,7 +338,7 @@ class DTAS::SplitFX # :nodoc:
|
|
318
338
|
done = pids.delete(pid)
|
319
339
|
if status.success?
|
320
340
|
if t = tracks.shift
|
321
|
-
pid, tmp =
|
341
|
+
pid, tmp = splitfx_spawn(target, t, opts)
|
322
342
|
pids[pid] = [ t, tmp ]
|
323
343
|
end
|
324
344
|
puts "DONE #{done[0].inspect}" if $DEBUG
|
@@ -334,4 +354,23 @@ class DTAS::SplitFX # :nodoc:
|
|
334
354
|
end
|
335
355
|
false
|
336
356
|
end
|
357
|
+
|
358
|
+
def cuebreakpoints
|
359
|
+
rv = @cuebp and return rv
|
360
|
+
require_relative 'cue_index'
|
361
|
+
@cuebp = @tracks.map { |t| DTAS::CueIndex.new(1, "#{t.tbeg}s") }
|
362
|
+
end
|
363
|
+
|
364
|
+
def infile_env(env, infile)
|
365
|
+
env["INFILE"] = xs(infile)
|
366
|
+
dir, base = File.split(File.expand_path(infile))
|
367
|
+
env["INDIR"] = xs(dir)
|
368
|
+
env["INBASE"] = xs(base)
|
369
|
+
end
|
370
|
+
|
371
|
+
def _expand_cmd(env, command)
|
372
|
+
Shellwords.split(command).map do |arg|
|
373
|
+
qx(env, "printf %s \"#{arg}\"")
|
374
|
+
end
|
375
|
+
end
|
337
376
|
end
|
data/lib/dtas/state_file.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
# Copyright (C) 2013-
|
1
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
require 'yaml'
|
4
4
|
require 'tempfile'
|
5
|
+
|
6
|
+
# state file preserves state across restarts of dtas-player
|
5
7
|
class DTAS::StateFile # :nodoc:
|
6
8
|
attr_reader :path
|
7
9
|
|
@@ -1,19 +1,26 @@
|
|
1
|
-
# Copyright (C) 2013-
|
1
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
require_relative '../dtas'
|
4
4
|
require_relative 'parse_time'
|
5
|
+
require_relative 'format'
|
5
6
|
require 'shellwords'
|
6
7
|
|
7
|
-
|
8
|
+
# this will represent a trim section inside -splitfx for applying
|
9
|
+
# effects to only a part of the output
|
10
|
+
class DTAS::TFX
|
8
11
|
include DTAS::ParseTime
|
9
12
|
|
10
13
|
attr_reader :tbeg
|
11
14
|
attr_reader :tlen
|
12
15
|
attr_reader :cmd
|
13
16
|
|
14
|
-
def initialize(args)
|
17
|
+
def initialize(args, format = DTAS::Format.new)
|
18
|
+
@format = format
|
15
19
|
args = args.dup
|
16
20
|
case args.shift
|
21
|
+
when :pad # [ :pad, start_time, end_time ]
|
22
|
+
@tbeg = args.shift
|
23
|
+
@tlen = args.shift - @tbeg
|
17
24
|
when "trim"
|
18
25
|
parse_trim!(args)
|
19
26
|
when "all"
|
@@ -22,7 +29,7 @@ class DTAS::TrimFX
|
|
22
29
|
else
|
23
30
|
raise ArgumentError, "#{args.inspect} not understood"
|
24
31
|
end
|
25
|
-
case tmp =
|
32
|
+
case tmp = args.shift
|
26
33
|
when "sh" then @cmd = args
|
27
34
|
when "sox" then tfx_sox(args)
|
28
35
|
when "eca" then tfx_eca(args)
|
@@ -45,34 +52,32 @@ class DTAS::TrimFX
|
|
45
52
|
@cmd.concat(%w(| sox $ECA2SOX - $SOXOUT))
|
46
53
|
end
|
47
54
|
|
48
|
-
def to_sox_arg
|
55
|
+
def to_sox_arg
|
49
56
|
if @tbeg && @tlen
|
50
|
-
|
51
|
-
len = @tlen * format.rate
|
52
|
-
%W(trim #{beg.round}s #{len.round}s)
|
57
|
+
%W(trim #{@tbeg}s #{@tlen}s)
|
53
58
|
elsif @tbeg
|
54
59
|
return [] if @tbeg == 0
|
55
|
-
|
56
|
-
%W(trim #{beg.round}s)
|
60
|
+
%W(trim #{@tbeg}s)
|
57
61
|
else
|
58
62
|
[]
|
59
63
|
end
|
60
64
|
end
|
61
65
|
|
66
|
+
# tries to interpret "trim" time args the same way the sox trim effect does
|
67
|
+
# This takes _time_ arguments only, not sample counts;
|
68
|
+
# otherwise, deviations from sox are considered bugs in dtas
|
62
69
|
def parse_trim!(args)
|
63
70
|
tbeg = parse_time(args.shift)
|
64
71
|
if args[0] =~ /\A=?[\d\.]+\z/
|
65
72
|
tlen = args.shift
|
66
73
|
is_stop_time = tlen.sub!(/\A=/, "") ? true : false
|
67
74
|
tlen = parse_time(tlen)
|
68
|
-
if is_stop_time
|
69
|
-
|
70
|
-
end
|
75
|
+
tlen = tlen - tbeg if is_stop_time
|
76
|
+
@tlen = (tlen * @format.rate).round
|
71
77
|
else
|
72
|
-
tlen = nil
|
78
|
+
@tlen = nil
|
73
79
|
end
|
74
|
-
@tbeg = tbeg
|
75
|
-
@tlen = tlen
|
80
|
+
@tbeg = (tbeg * @format.rate).round
|
76
81
|
end
|
77
82
|
|
78
83
|
def <=>(other)
|
@@ -87,7 +92,20 @@ class DTAS::TrimFX
|
|
87
92
|
end
|
88
93
|
end
|
89
94
|
|
90
|
-
#
|
95
|
+
# sorts and converts an array of TFX objects into non-overlapping arrays
|
96
|
+
# of epochs
|
97
|
+
#
|
98
|
+
# input:
|
99
|
+
# [ tfx1, tfx2, tfx3, ... ]
|
100
|
+
#
|
101
|
+
# output:
|
102
|
+
# [
|
103
|
+
# [ tfx1 ], # first epoch
|
104
|
+
# [ tfx2, tfx3 ], # second epoch
|
105
|
+
# ...
|
106
|
+
# ]
|
107
|
+
# There are multiple epochs only if ranges overlap,
|
108
|
+
# There is only one epoch if there are no overlaps
|
91
109
|
def self.schedule(ary)
|
92
110
|
sorted = []
|
93
111
|
ary.each_with_index { |tfx, i| sorted << TFXSort[tfx, i] }
|
@@ -96,41 +114,49 @@ class DTAS::TrimFX
|
|
96
114
|
epoch = 0
|
97
115
|
prev_end = 0
|
98
116
|
defer = []
|
117
|
+
|
99
118
|
begin
|
100
119
|
while tfxsort = sorted.shift
|
101
120
|
tfx = tfxsort.tfx
|
102
121
|
if tfx.tbeg >= prev_end
|
122
|
+
# great, no overlap, append to the current epoch
|
103
123
|
prev_end = tfx.tbeg + tfx.tlen
|
104
124
|
(rv[epoch] ||= []) << tfx
|
105
125
|
else
|
126
|
+
# overlapping region, we'll need a new epoch
|
106
127
|
defer << tfxsort
|
107
128
|
end
|
108
129
|
end
|
109
|
-
|
130
|
+
|
131
|
+
if defer[0] # do we need another epoch?
|
110
132
|
epoch += 1
|
111
133
|
sorted = defer
|
112
134
|
defer = []
|
113
135
|
prev_end = 0
|
114
136
|
end
|
115
137
|
end while sorted[0]
|
138
|
+
|
116
139
|
rv
|
117
140
|
end
|
118
141
|
|
119
|
-
|
142
|
+
# like schedule, but fills in the gaps with pass-through (no-op) TFX objs
|
143
|
+
# This does not change the number of epochs.
|
144
|
+
def self.expand(ary, total_samples)
|
120
145
|
rv = []
|
121
|
-
schedule(ary).each_with_index do |sary,
|
146
|
+
schedule(ary).each_with_index do |sary, epoch|
|
122
147
|
tip = 0
|
123
|
-
dst = rv[
|
148
|
+
dst = rv[epoch] = []
|
124
149
|
while tfx = sary.shift
|
125
150
|
if tfx.tbeg > tip
|
126
|
-
|
151
|
+
# fill in the previous gap
|
152
|
+
nfx = new([:pad, tip, tfx.tbeg])
|
127
153
|
dst << nfx
|
128
154
|
dst << tfx
|
129
155
|
tip = tfx.tbeg + tfx.tlen
|
130
156
|
end
|
131
157
|
end
|
132
|
-
if tip <
|
133
|
-
nfx = new(
|
158
|
+
if tip < total_samples # fill until the last chunk
|
159
|
+
nfx = new([:pad, tip, total_samples])
|
134
160
|
dst << nfx
|
135
161
|
end
|
136
162
|
end
|