dtas 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Documentation/.gitignore +1 -1
  4. data/Documentation/GNUmakefile +3 -1
  5. data/Documentation/dtas-archive.txt +61 -0
  6. data/Documentation/dtas-console.txt +4 -3
  7. data/Documentation/dtas-ctl.txt +4 -3
  8. data/Documentation/dtas-cueedit.txt +4 -3
  9. data/Documentation/dtas-enq.txt +4 -3
  10. data/Documentation/dtas-env.txt +60 -0
  11. data/Documentation/dtas-msinkctl.txt +4 -3
  12. data/Documentation/dtas-player.txt +6 -3
  13. data/Documentation/dtas-player_effects.txt +4 -3
  14. data/Documentation/dtas-player_protocol.txt +27 -4
  15. data/Documentation/dtas-player_sink_examples.txt +7 -3
  16. data/Documentation/dtas-sinkedit.txt +20 -3
  17. data/Documentation/dtas-sourceedit.txt +21 -3
  18. data/Documentation/dtas-splitfx.txt +14 -5
  19. data/Documentation/dtas-tl.txt +4 -3
  20. data/Documentation/dtas-xdelay.txt +4 -3
  21. data/Documentation/update-footer.rb +52 -0
  22. data/GIT-VERSION-GEN +2 -2
  23. data/GNUmakefile +1 -1
  24. data/HACKING +3 -2
  25. data/INSTALL +6 -6
  26. data/README +1 -1
  27. data/Rakefile +2 -3
  28. data/TODO +1 -1
  29. data/bin/dtas-archive +187 -0
  30. data/bin/dtas-console +7 -1
  31. data/bin/dtas-ctl +1 -1
  32. data/bin/dtas-cueedit +3 -3
  33. data/bin/dtas-enq +1 -1
  34. data/bin/dtas-msinkctl +1 -1
  35. data/bin/dtas-partstats +10 -4
  36. data/bin/dtas-player +1 -1
  37. data/bin/dtas-sinkedit +82 -20
  38. data/bin/dtas-sourceedit +64 -22
  39. data/bin/dtas-splitfx +1 -1
  40. data/bin/dtas-tl +1 -1
  41. data/bin/dtas-xdelay +1 -1
  42. data/dtas-linux.gemspec +1 -1
  43. data/dtas-mpris.gemspec +1 -1
  44. data/dtas.gemspec +1 -1
  45. data/examples/splitfx.sample.yml +11 -3
  46. data/examples/{trimfx.sample.yml → tfx.sample.yml} +1 -1
  47. data/lib/dtas.rb +2 -1
  48. data/lib/dtas/buffer.rb +5 -5
  49. data/lib/dtas/buffer/read_write.rb +8 -5
  50. data/lib/dtas/buffer/splice.rb +29 -8
  51. data/lib/dtas/command.rb +2 -2
  52. data/lib/dtas/compat_onenine.rb +1 -1
  53. data/lib/dtas/cue_index.rb +3 -1
  54. data/lib/dtas/disclaimer.rb +1 -1
  55. data/lib/dtas/edit_client.rb +1 -1
  56. data/lib/dtas/fadefx.rb +100 -0
  57. data/lib/dtas/format.rb +4 -2
  58. data/lib/dtas/parse_time.rb +3 -1
  59. data/lib/dtas/partstats.rb +8 -10
  60. data/lib/dtas/pipe.rb +2 -1
  61. data/lib/dtas/player.rb +33 -11
  62. data/lib/dtas/player/client_handler.rb +43 -8
  63. data/lib/dtas/process.rb +6 -14
  64. data/lib/dtas/replaygain.rb +3 -3
  65. data/lib/dtas/rg_state.rb +1 -1
  66. data/lib/dtas/serialize.rb +3 -1
  67. data/lib/dtas/sigevent.rb +1 -1
  68. data/lib/dtas/sigevent/efd.rb +5 -4
  69. data/lib/dtas/sigevent/pipe.rb +4 -1
  70. data/lib/dtas/sink.rb +4 -4
  71. data/lib/dtas/source.rb +1 -1
  72. data/lib/dtas/source/av.rb +2 -2
  73. data/lib/dtas/source/av_ff_common.rb +22 -11
  74. data/lib/dtas/source/cmd.rb +3 -3
  75. data/lib/dtas/source/common.rb +3 -2
  76. data/lib/dtas/source/ff.rb +2 -2
  77. data/lib/dtas/source/file.rb +22 -4
  78. data/lib/dtas/source/mp3gain.rb +1 -1
  79. data/lib/dtas/source/sox.rb +7 -7
  80. data/lib/dtas/source/splitfx.rb +99 -0
  81. data/lib/dtas/spawn_fix.rb +10 -0
  82. data/lib/dtas/splitfx.rb +63 -24
  83. data/lib/dtas/state_file.rb +3 -1
  84. data/lib/dtas/{trimfx.rb → tfx.rb} +50 -24
  85. data/lib/dtas/tracklist.rb +2 -1
  86. data/lib/dtas/unix_accepted.rb +2 -2
  87. data/lib/dtas/unix_client.rb +2 -2
  88. data/lib/dtas/unix_server.rb +1 -1
  89. data/lib/dtas/util.rb +1 -1
  90. data/lib/dtas/watchable.rb +54 -0
  91. data/lib/dtas/writable_iter.rb +2 -1
  92. data/lib/dtas/xs.rb +2 -2
  93. data/perl/dtas-graph +1 -1
  94. data/test/covshow.rb +1 -1
  95. data/test/helper.rb +1 -1
  96. data/test/player_integration.rb +1 -1
  97. data/test/test_buffer.rb +1 -1
  98. data/test/test_env.rb +1 -1
  99. data/test/test_fadefx.rb +45 -0
  100. data/test/test_format.rb +1 -1
  101. data/test/test_format_change.rb +1 -1
  102. data/test/test_player.rb +1 -1
  103. data/test/test_player_client_handler.rb +1 -1
  104. data/test/test_player_integration.rb +8 -8
  105. data/test/test_process.rb +1 -1
  106. data/test/test_rg_integration.rb +1 -1
  107. data/test/test_rg_state.rb +1 -1
  108. data/test/test_sink.rb +1 -1
  109. data/test/test_sink_pipe_size.rb +2 -2
  110. data/test/test_sink_tee_integration.rb +1 -1
  111. data/test/test_source_av.rb +1 -1
  112. data/test/test_source_sox.rb +1 -1
  113. data/test/test_splitfx.rb +43 -13
  114. data/test/test_tfx.rb +85 -0
  115. data/test/test_tracklist.rb +1 -1
  116. data/test/test_unixserver.rb +1 -1
  117. data/test/test_util.rb +1 -1
  118. metadata +17 -6
  119. data/lib/dtas/compat_rbx.rb +0 -12
  120. data/test/test_trimfx.rb +0 -81
@@ -1,8 +1,12 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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 '../xs'
4
+ require_relative '../parse_time'
5
+
6
+ # client protocol handling for -player
4
7
  module DTAS::Player::ClientHandler # :nodoc:
5
8
  include DTAS::XS
9
+ include DTAS::ParseTime
6
10
 
7
11
  # returns true on success, wait_ctl arg on error
8
12
  def set_bool(io, kv, v)
@@ -69,7 +73,7 @@ module DTAS::Player::ClientHandler # :nodoc:
69
73
 
70
74
  def __sink_activate(sink)
71
75
  return if sink.pid
72
- @targets.concat(sink.spawn(@format))
76
+ @targets.concat(sink.sink_spawn(@format))
73
77
  @targets.sort_by! { |t| t.sink.prio }
74
78
  end
75
79
 
@@ -150,7 +154,7 @@ module DTAS::Player::ClientHandler # :nodoc:
150
154
  rv = set_bool(io, kv, v) { |b| sink.__send__("#{k}=", b) }
151
155
  rv == true or return rv
152
156
  when "pipe_size"
153
- rv = set_uint(io, kv, v, false) { |u| sink.pipe_size = u }
157
+ rv = set_uint(io, kv, v, true) { |u| sink.pipe_size = u }
154
158
  rv == true or return rv
155
159
  when "command" # nothing to validate, this could be "rm -rf /" :>
156
160
  sink.command = v.empty? ? DTAS::Sink::SINK_DEFAULTS["command"] : v
@@ -170,7 +174,7 @@ module DTAS::Player::ClientHandler # :nodoc:
170
174
  io.emit("OK")
171
175
  when "cat"
172
176
  sink = @sinks[name] or return io.emit("ERR #{name} not found")
173
- io.emit(sink.to_hsh.to_yaml)
177
+ io.emit(sink.to_hash.to_yaml)
174
178
  else
175
179
  io.emit("ERR unknown sink op #{msg[0]}")
176
180
  end
@@ -181,6 +185,7 @@ module DTAS::Player::ClientHandler # :nodoc:
181
185
  bytes = bytes < 0 ? 0 : bytes # maybe negative in case of sink errors
182
186
  end
183
187
 
188
+ # returns seek offset as an Integer in sample count
184
189
  def __seek_offset_adj(dir, offset)
185
190
  if offset.sub!(/s\z/, '')
186
191
  offset = offset.to_i
@@ -463,7 +468,11 @@ module DTAS::Player::ClientHandler # :nodoc:
463
468
  def source_handler(io, msg)
464
469
  map = @source_map
465
470
  op = msg.shift
466
- if op == "ls"
471
+ case op
472
+ when "restart"
473
+ __current_requeue
474
+ return io.emit("OK")
475
+ when "ls"
467
476
  s = map.keys.sort { |a,b| map[a].tryorder <=> map[b].tryorder }
468
477
  return io.emit(s.join(' '))
469
478
  end
@@ -474,7 +483,7 @@ module DTAS::Player::ClientHandler # :nodoc:
474
483
  when "cat"
475
484
  io.emit(src.to_source_cat.to_yaml)
476
485
  when "ed"
477
- before = src.to_state_hash
486
+ before = src.to_state_hash.inspect
478
487
  sd = src.source_defaults
479
488
  msg.each do |kv|
480
489
  k, v = kv.split(/=/, 2)
@@ -492,7 +501,7 @@ module DTAS::Player::ClientHandler # :nodoc:
492
501
  source_map_reload
493
502
  end
494
503
  end
495
- after = src.to_state_hash
504
+ after = src.to_state_hash.inspect
496
505
  __current_requeue if before != after && @current.class == src.class
497
506
  io.emit("OK")
498
507
  else
@@ -526,7 +535,7 @@ module DTAS::Player::ClientHandler # :nodoc:
526
535
  begin
527
536
  sf.dump(self)
528
537
  rescue => e
529
- return io.emit("ERR dumping to #{xs(Array(dest))} #{e.message}")
538
+ return io.emit("ERR dumping to #{xs(dest)} #{e.message}")
530
539
  end
531
540
  end
532
541
  io.emit("OK")
@@ -674,5 +683,31 @@ module DTAS::Player::ClientHandler # :nodoc:
674
683
  io.emit("NOCUE")
675
684
  end
676
685
  end
686
+
687
+ def trim_handler(io, msg)
688
+ case msg.size
689
+ when 0
690
+ io.emit({ 'trim' => @trim }.to_yaml)
691
+ when 1, 2
692
+ case msg[0]
693
+ when 'off'
694
+ @trim = nil
695
+ else
696
+ begin
697
+ tbeg = parse_time(msg[0])
698
+ if tlen = msg[1]
699
+ absolute = tlen.sub!(/\A=/, '') # 44:00 =44:55
700
+ tlen = parse_time(tlen)
701
+ tlen -= tbeg if absolute
702
+ end
703
+ @trim = [ tbeg, tlen ] # seconds as float, since we don't know rate
704
+ rescue => e
705
+ return io.emit("ERR #{e.message}")
706
+ end
707
+ end
708
+ __current_requeue
709
+ io.emit('OK')
710
+ end
711
+ end
677
712
  end
678
713
  # :startdoc:
@@ -1,12 +1,14 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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 'io/wait'
4
4
  require_relative '../dtas'
5
5
  require_relative 'xs'
6
6
 
7
+ # process management helpers
7
8
  module DTAS::Process # :nodoc:
8
9
  PIDS = {}
9
10
  include DTAS::XS
11
+ include DTAS::SpawnFix
10
12
 
11
13
  def self.reaper
12
14
  begin
@@ -47,12 +49,7 @@ module DTAS::Process # :nodoc:
47
49
  opts = { close_others: true, pgroup: true }.merge!(opts)
48
50
  env = env_expand(env, opts)
49
51
 
50
- pid = begin
51
- Process.spawn(env, cmd, opts)
52
- rescue Errno::EINTR
53
- # workaround for older Rubies https://bugs.ruby-lang.org/issues/8770
54
- retry
55
- end
52
+ pid = spawn(env, cmd, opts)
56
53
  warn [ :spawn, pid, cmd ].inspect if $DEBUG
57
54
  @spawn_at = Time.now.to_f
58
55
  PIDS[pid] = self
@@ -76,12 +73,7 @@ module DTAS::Process # :nodoc:
76
73
  opts[:err] = we
77
74
  end
78
75
  env = env_expand(env, opts)
79
- pid = begin
80
- Process.spawn(env, *cmd, opts)
81
- rescue Errno::EINTR
82
- # workaround for older Rubies https://bugs.ruby-lang.org/issues/8770
83
- retry
84
- end
76
+ pid = spawn(env, *cmd, opts)
85
77
  w.close
86
78
  if err_str
87
79
  we.close
@@ -107,6 +99,6 @@ module DTAS::Process # :nodoc:
107
99
  _, status = Process.waitpid2(pid)
108
100
  return res if status.success?
109
101
  return status if no_raise
110
- raise RuntimeError, "`#{xs(Array(cmd))}' failed: #{status.inspect}"
102
+ raise RuntimeError, "`#{xs(cmd)}' failed: #{status.inspect}"
111
103
  end
112
104
  end
@@ -1,7 +1,7 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
- #
4
- # Represents ReplayGain metadata for a DTAS::Source
3
+
4
+ # Represents ReplayGain metadata for a DTAS::Source, only used by -player
5
5
  # cleanup/validate values to prevent malicious files from making us
6
6
  # run arbitrary commands
7
7
  # *_peak values are 0..inf (1.0 being full scale, but >1 is possible
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
  #
4
4
  # provides support for generating appropriate effects for ReplayGain
@@ -1,5 +1,7 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
+
4
+ # used to serialize player state to the state file
3
5
  module DTAS::Serialize # :nodoc:
4
6
  def ivars_to_hash(ivars, rv = {})
5
7
  ivars.each { |k| rv[k] = instance_variable_get("@#{k}") }
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
  begin
4
4
  raise LoadError, "no eventfd with _DTAS_POSIX" if ENV["_DTAS_POSIX"]
@@ -1,10 +1,11 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
- class DTAS::Sigevent < SleepyPenguin::EventFD # :nodoc:
4
- include SleepyPenguin
5
3
 
4
+ # used in various places for safe wakeups from IO.select via signals
5
+ # This requires a modern Linux system and the "sleepy_penguin" RubyGem
6
+ class DTAS::Sigevent < SleepyPenguin::EventFD # :nodoc:
6
7
  def self.new
7
- super(0, EventFD::CLOEXEC)
8
+ super(0, :CLOEXEC)
8
9
  end
9
10
 
10
11
  def signal
@@ -1,5 +1,8 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
+
4
+ # used in various places for safe wakeups from IO.select via signals
5
+ # A fallback for non-Linux systems lacking the "sleepy_penguin" RubyGem
3
6
  class DTAS::Sigevent # :nodoc:
4
7
  attr_reader :to_io
5
8
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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_relative '../dtas'
@@ -8,7 +8,7 @@ require_relative 'command'
8
8
  require_relative 'format'
9
9
  require_relative 'serialize'
10
10
 
11
- # this is a sink (endpoint, audio enters but never leaves)
11
+ # this is a sink (endpoint, audio enters but never leaves), used by -player
12
12
  class DTAS::Sink # :nodoc:
13
13
  attr_accessor :prio # any Integer
14
14
  attr_accessor :active # boolean
@@ -66,8 +66,8 @@ class DTAS::Sink # :nodoc:
66
66
  super
67
67
  end
68
68
 
69
- def spawn(format, opts = {})
70
- raise "BUG: #{self.inspect}#spawn called twice" if @pid
69
+ def sink_spawn(format, opts = {})
70
+ raise "BUG: #{self.inspect}#sink_spawn called twice" if @pid
71
71
  rv = []
72
72
 
73
73
  pclass = @nonblock ? DTAS::PipeNB : DTAS::Pipe
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
 
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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 'av_ff_common'
@@ -10,7 +10,7 @@ class DTAS::Source::Av # :nodoc:
10
10
  AV_DEFAULTS = COMMAND_DEFAULTS.merge(
11
11
  "command" =>
12
12
  'avconv -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
13
- 'sox -p $SOXFMT - $RGFX',
13
+ 'sox -p $SOXFMT - $TRIMFX $RGFX',
14
14
 
15
15
  # this is above ffmpeg because this av is the Debian default and
16
16
  # it's easier for me to test av than ff
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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'
@@ -19,8 +19,8 @@ module DTAS::Source::AvFfCommon # :nodoc:
19
19
  attr_reader :precision # always 32
20
20
  attr_reader :format
21
21
 
22
- def try(infile, offset = nil)
23
- rv = source_file_dup(infile, offset)
22
+ def try(infile, offset = nil, trim = nil)
23
+ rv = source_file_dup(infile, offset, trim)
24
24
  rv.av_ff_ok? or return
25
25
  rv
26
26
  end
@@ -101,10 +101,20 @@ module DTAS::Source::AvFfCommon # :nodoc:
101
101
  ! @astreams.compact.empty?
102
102
  end
103
103
 
104
- def sspos(offset)
105
- offset =~ /\A(\d+)s\z/ or return "-ss #{offset}"
106
- samples = $1.to_f
107
- sprintf("-ss %0.9g", samples / @format.rate)
104
+ def sspos
105
+ return unless @offset || @trim
106
+ off = offset_samples / @format.rate.to_f
107
+ sprintf('-ss %0.9g', off)
108
+ end
109
+
110
+ def av_ff_trimfx # for sox
111
+ return unless @trim
112
+ tbeg, tlen = @trim # Floats
113
+ tend = tbeg + tlen
114
+ off = offset_samples / @format.rate.to_f
115
+ tlen = tend - off
116
+ tlen = 0 if tlen < 0
117
+ sprintf('trim 0 %0.9g', tlen)
108
118
  end
109
119
 
110
120
  def select_astream(as)
@@ -127,8 +137,8 @@ module DTAS::Source::AvFfCommon # :nodoc:
127
137
  raise "BUG: no audio stream in #@infile"
128
138
  end
129
139
 
130
- def spawn(player_format, rg_state, opts)
131
- raise "BUG: #{self.inspect}#spawn called twice" if @to_io
140
+ def src_spawn(player_format, rg_state, opts)
141
+ raise "BUG: #{self.inspect}#src_spawn called twice" if @to_io
132
142
  amap = nil
133
143
 
134
144
  # try to find an audio stream which matches our channel count
@@ -148,10 +158,11 @@ module DTAS::Source::AvFfCommon # :nodoc:
148
158
 
149
159
  e["PROBE"] = @probe_harder ? @probe_harder.join(' ') : nil
150
160
  # make sure these are visible to the source command...
151
- e["INFILE"] = @infile
161
+ e["INFILE"] = xs(@infile)
152
162
  e["AMAP"] = amap
153
- e["SSPOS"] = @offset ? sspos(@offset) : nil
163
+ e["SSPOS"] = sspos
154
164
  e["RGFX"] = rg_state.effect(self) || nil
165
+ e["TRIMFX"] = av_ff_trimfx
155
166
  e.merge!(@rg.to_env) if @rg
156
167
 
157
168
  @pid = dtas_spawn(e, command_string, opts)
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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'
@@ -31,8 +31,8 @@ class DTAS::Source::Cmd # :nodoc:
31
31
 
32
32
  alias to_hsh to_hash
33
33
 
34
- def spawn(format, rg_state, opts)
35
- raise "BUG: #{self.inspect}#spawn called twice" if @to_io
34
+ def src_spawn(format, rg_state, opts)
35
+ raise "BUG: #{self.inspect}#src_spawn called twice" if @to_io
36
36
  e = format.to_env
37
37
  @pid = dtas_spawn(e.merge!(@env), command_string, opts)
38
38
  end
@@ -1,10 +1,11 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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
  module DTAS::Source::Common # :nodoc:
4
- attr_reader :dst_zero_byte
4
+ attr_reader :dst_zero_byte # first byte this source object saw
5
5
  attr_reader :dst
6
6
  attr_accessor :requeued
7
7
 
8
+ # buf - a DTAS::Buffer object (pipe)
8
9
  def dst_assoc(buf)
9
10
  @dst = buf
10
11
  @dst_zero_byte = buf.bytes_xfer + buf.inflight
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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 'av_ff_common'
@@ -12,7 +12,7 @@ class DTAS::Source::Ff # :nodoc:
12
12
  FF_DEFAULTS = COMMAND_DEFAULTS.merge(
13
13
  "command" =>
14
14
  'ffmpeg -v error $SSPOS $PROBE -i "$INFILE" $AMAP -f sox - |' \
15
- 'sox -p $SOXFMT - $RGFX',
15
+ 'sox -p $SOXFMT - $TRIMFX $RGFX',
16
16
 
17
17
  # I haven't tested this much since av is in Debian stable and ff is not
18
18
  "tryorder" => 2,
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2014, Eric Wong <e@80x24.org> and all contributors
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'
@@ -21,17 +21,18 @@ module DTAS::Source::File # :nodoc:
21
21
  FILE_SIVS = %w(infile comments command env) # for the "current" command
22
22
  SRC_SIVS = %w(command env tryorder)
23
23
 
24
- def source_file_dup(infile, offset)
24
+ def source_file_dup(infile, offset, trim)
25
25
  rv = dup
26
- rv.__file_init(infile, offset)
26
+ rv.__file_init(infile, offset, trim)
27
27
  rv
28
28
  end
29
29
 
30
- def __file_init(infile, offset)
30
+ def __file_init(infile, offset, trim)
31
31
  @env = @env.dup
32
32
  @format = nil
33
33
  @infile = infile
34
34
  @offset = offset
35
+ @trim = trim
35
36
  @comments = nil
36
37
  @samples = nil
37
38
  @cuebp = nil
@@ -47,6 +48,13 @@ module DTAS::Source::File # :nodoc:
47
48
  # returns any offset in samples (relative to the original source file),
48
49
  # likely zero unless seek was used
49
50
  def offset_samples
51
+ off = __offset_samples
52
+ return off unless @trim
53
+ tbeg = @trim[0] * format.rate
54
+ tbeg < off ? off : tbeg
55
+ end
56
+
57
+ def __offset_samples
50
58
  return 0 unless @offset
51
59
  case @offset
52
60
  when /\A\d+s\z/
@@ -56,6 +64,16 @@ module DTAS::Source::File # :nodoc:
56
64
  end
57
65
  end
58
66
 
67
+ # creates the effect to fill the TRIMFX env
68
+ def trimfx
69
+ return unless @offset || @trim
70
+ fx = "trim #{offset_samples}s"
71
+ if @trim && @trim[1]
72
+ fx << sprintf(' =%0.9gs', (@trim[0] + @trim[1]) * format.rate)
73
+ end
74
+ fx
75
+ end
76
+
59
77
  # A user may be downloading the file and start playing
60
78
  # it before the download completes, this refreshes
61
79
  def samples!