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/buffer/splice.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
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 'io/nonblock'
|
4
4
|
require 'io/splice'
|
5
5
|
require_relative '../../dtas'
|
6
6
|
require_relative '../pipe'
|
7
7
|
|
8
|
+
# Used by -player on Linux systems with the "io-splice" RubyGem installed
|
8
9
|
module DTAS::Buffer::Splice # :nodoc:
|
9
10
|
MAX_AT_ONCE = 4096 # page size in Linux
|
10
11
|
MAX_AT_ONCE_1 = 65536
|
11
12
|
MAX_SIZE = File.read("/proc/sys/fs/pipe-max-size").to_i
|
12
13
|
DEVNULL = File.open("/dev/null", "r+")
|
13
14
|
F_MOVE = IO::Splice::F_MOVE
|
14
|
-
WAITALL = IO::Splice::WAITALL
|
15
15
|
|
16
16
|
def buffer_size
|
17
17
|
@to_io.pipe_size
|
@@ -28,9 +28,10 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
28
28
|
IO.splice(@to_io, nil, DEVNULL, nil, bytes)
|
29
29
|
end
|
30
30
|
|
31
|
-
def broadcast_one(targets)
|
31
|
+
def broadcast_one(targets, limit = nil)
|
32
32
|
# single output is always non-blocking
|
33
|
-
|
33
|
+
limit ||= MAX_AT_ONCE_1
|
34
|
+
s = IO.trysplice(@to_io, nil, targets[0], nil, limit, F_MOVE)
|
34
35
|
if Symbol === s
|
35
36
|
targets # our one and only target blocked on write
|
36
37
|
else
|
@@ -43,6 +44,26 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
43
44
|
nil # do not return error here, we already spewed an error message
|
44
45
|
end
|
45
46
|
|
47
|
+
def __tee_in_full(src, dst, bytes)
|
48
|
+
rv = 0
|
49
|
+
while bytes > 0
|
50
|
+
s = IO.tee(src, dst, bytes)
|
51
|
+
bytes -= s
|
52
|
+
rv += s
|
53
|
+
end
|
54
|
+
rv
|
55
|
+
end
|
56
|
+
|
57
|
+
def __splice_in_full(src, dst, bytes, flags)
|
58
|
+
rv = 0
|
59
|
+
while bytes > 0
|
60
|
+
s = IO.splice(src, nil, dst, nil, bytes, flags)
|
61
|
+
rv += s
|
62
|
+
bytes -= s
|
63
|
+
end
|
64
|
+
rv
|
65
|
+
end
|
66
|
+
|
46
67
|
# returns the largest value we teed
|
47
68
|
def __broadcast_tee(blocked, targets, chunk_size)
|
48
69
|
most_teed = 0
|
@@ -50,7 +71,7 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
50
71
|
begin
|
51
72
|
t = (dst.nonblock? || most_teed == 0) ?
|
52
73
|
IO.trytee(@to_io, dst, chunk_size) :
|
53
|
-
|
74
|
+
__tee_in_full(@to_io, dst, chunk_size)
|
54
75
|
if Integer === t
|
55
76
|
if t > most_teed
|
56
77
|
chunk_size = t if most_teed == 0
|
@@ -68,7 +89,7 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
68
89
|
most_teed
|
69
90
|
end
|
70
91
|
|
71
|
-
def broadcast_inf(targets)
|
92
|
+
def broadcast_inf(targets, limit = nil)
|
72
93
|
if targets.all?(&:ready_write_optimized?)
|
73
94
|
blocked = []
|
74
95
|
elsif targets.none?(&:nonblock?)
|
@@ -85,7 +106,7 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
85
106
|
end
|
86
107
|
|
87
108
|
# don't pin too much on one target
|
88
|
-
bytes = MAX_AT_ONCE
|
109
|
+
bytes = limit || MAX_AT_ONCE
|
89
110
|
last = targets.pop # we splice to the last one, tee to the rest
|
90
111
|
|
91
112
|
# this may return zero if all targets were non-blocking
|
@@ -116,7 +137,7 @@ module DTAS::Buffer::Splice # :nodoc:
|
|
116
137
|
end
|
117
138
|
else
|
118
139
|
# the blocking case is simple
|
119
|
-
s =
|
140
|
+
s = __splice_in_full(@to_io, last, bytes, F_MOVE)
|
120
141
|
end
|
121
142
|
@bytes_xfer += s
|
122
143
|
|
data/lib/dtas/command.rb
CHANGED
@@ -1,9 +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
|
-
# common code for wrapping SoX/ecasound/... commands
|
4
3
|
require_relative 'serialize'
|
5
4
|
require 'shellwords'
|
6
5
|
|
6
|
+
# common code for wrapping SoX/ecasound/... commands
|
7
7
|
module DTAS::Command # :nodoc:
|
8
8
|
include DTAS::Serialize
|
9
9
|
attr_reader :pid
|
data/lib/dtas/compat_onenine.rb
CHANGED
data/lib/dtas/cue_index.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
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
|
+
|
5
|
+
# embedded CUE sheet representation for -player
|
4
6
|
class DTAS::CueIndex
|
5
7
|
attr_reader :offset
|
6
8
|
attr_reader :index
|
data/lib/dtas/disclaimer.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# :enddoc:
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
3
3
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
4
|
DTAS_PROGNAME = File.basename($0)
|
5
5
|
DTAS_DISCLAIMER = <<EOF
|
data/lib/dtas/edit_client.rb
CHANGED
data/lib/dtas/fadefx.rb
ADDED
@@ -0,0 +1,100 @@
|
|
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
|
+
require_relative '../dtas'
|
4
|
+
require_relative 'parse_time'
|
5
|
+
require_relative 'xs'
|
6
|
+
|
7
|
+
# note: This is sox-specific
|
8
|
+
# --------- time --------->
|
9
|
+
# _____ _______ ______
|
10
|
+
# \ / \ /
|
11
|
+
# prev X cur X next
|
12
|
+
# _____/ \_______/ \______
|
13
|
+
#
|
14
|
+
# out_prev - controls the downward slope from prev
|
15
|
+
# in_cur - controls the upward slope into cur
|
16
|
+
# out_cur - controls the downward slope from cur
|
17
|
+
# in_next - controls the upward slope into next
|
18
|
+
class DTAS::FadeFX # :nodoc:
|
19
|
+
include DTAS::ParseTime
|
20
|
+
include DTAS::XS
|
21
|
+
|
22
|
+
attr_reader :out_prev, :in_cur, :out_cur, :in_next
|
23
|
+
F = Struct.new(:type, :flen)
|
24
|
+
|
25
|
+
def initialize(args)
|
26
|
+
args =~ /\A([^,]*),([^,]*);([^,]*),([^,]*)\z/ or
|
27
|
+
raise ArgumentError, "bad fade format"
|
28
|
+
fades = [ $1, $2, $3, $4 ]
|
29
|
+
%w(out_prev in_cur out_cur in_next).each do |iv|
|
30
|
+
instance_variable_set("@#{iv}", parse!(fades.shift))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def fade_cur_fx(format, tbeg, tlen, args = [])
|
35
|
+
fx = %W(trim #{tbeg}s #{tlen}s)
|
36
|
+
fx.concat(args)
|
37
|
+
if @in_cur && @out_cur && @in_cur.type == @out_cur.type
|
38
|
+
f = %W(fade #{@in_cur.type} #{@in_cur.flen} #{tlen}s #{@out_cur.flen})
|
39
|
+
fx.concat(f)
|
40
|
+
else # differing fade types for in/out, chain them:
|
41
|
+
fpart = @in_cur and
|
42
|
+
fx.concat(%W(fade #{fpart.type} #{fpart.flen} 0 0))
|
43
|
+
fpart = @out_cur and
|
44
|
+
fx.concat(%W(fade #{fpart.type} 0 #{tlen}s #{fpart.flen}))
|
45
|
+
end
|
46
|
+
fx
|
47
|
+
end
|
48
|
+
|
49
|
+
def fade_out_prev_fx(format, tbeg, tlen)
|
50
|
+
fx = %W(trim #{tbeg}s)
|
51
|
+
|
52
|
+
if fpart = @out_prev
|
53
|
+
out_len = format.hhmmss_to_samples(fpart.flen)
|
54
|
+
fx.concat(%W(fade #{fpart.type} 0 #{out_len}s #{out_len}s))
|
55
|
+
remain = tlen - out_len
|
56
|
+
|
57
|
+
# fade-out is longer than tlen, so truncate again:
|
58
|
+
remain < 0 and fx.concat(%W(trim 0 #{tlen}s))
|
59
|
+
|
60
|
+
# pad with silence, this is where fade_cur_fx goes
|
61
|
+
remain > 0 and fx.concat(%W(pad #{remain}s@#{out_len}s))
|
62
|
+
end
|
63
|
+
fx
|
64
|
+
end
|
65
|
+
|
66
|
+
def fade_in_next_fx(format, tbeg, tlen)
|
67
|
+
fpart = @in_next
|
68
|
+
flen = fpart ? fpart.flen : 0
|
69
|
+
nlen = format.hhmmss_to_samples(flen)
|
70
|
+
nbeg = tbeg + tlen - nlen
|
71
|
+
npad = nbeg - tbeg
|
72
|
+
if npad < 0
|
73
|
+
warn("in_next should not exceed range: #{inspect} @trim " \
|
74
|
+
"#{tbeg}s #{tlen}s\nclamping to #{tbeg}")
|
75
|
+
nbeg = tbeg
|
76
|
+
end
|
77
|
+
|
78
|
+
fx = %W(trim #{nbeg}s #{nlen}s)
|
79
|
+
nlen != 0 and
|
80
|
+
fx.concat(%W(fade #{fpart.type} #{nlen}s 0 0))
|
81
|
+
|
82
|
+
# likely, the pad section is where fade_cur_fx goes
|
83
|
+
npad > 0 and fx.concat(%W(pad #{npad}s@0s))
|
84
|
+
fx
|
85
|
+
end
|
86
|
+
|
87
|
+
# q - quarter of a sine wave
|
88
|
+
# h - half a sine wave
|
89
|
+
# t - linear (`triangular') slope
|
90
|
+
# l - logarithmic
|
91
|
+
# p - inverted parabola
|
92
|
+
# default is 't' (sox defaults to 'l', but triangular makes more sense
|
93
|
+
# when concatenating
|
94
|
+
def parse!(str)
|
95
|
+
return nil if str.empty?
|
96
|
+
type = "t"
|
97
|
+
str.sub!(/\A([a-z])/, "") and type = $1
|
98
|
+
F.new(type, parse_time(str))
|
99
|
+
end
|
100
|
+
end
|
data/lib/dtas/format.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
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
|
-
# class represents an audio format (type/bits/channels/sample rate/...)
|
4
3
|
require_relative '../dtas'
|
5
4
|
require_relative 'process'
|
6
5
|
require_relative 'serialize'
|
7
6
|
|
7
|
+
# class represents an audio format (type/bits/channels/sample rate/...)
|
8
|
+
# used throughout dtas
|
8
9
|
class DTAS::Format # :nodoc:
|
9
10
|
include DTAS::Process
|
10
11
|
include DTAS::Serialize
|
@@ -164,6 +165,7 @@ class DTAS::Format # :nodoc:
|
|
164
165
|
# HH:MM:SS.frac (don't bother with more complex times, too much code)
|
165
166
|
# part of me wants to drop this feature from playq, feels like bloat...
|
166
167
|
def hhmmss_to_samples(hhmmss)
|
168
|
+
Numeric === hhmmss and return hhmmss * @rate
|
167
169
|
time = hhmmss.dup
|
168
170
|
rv = 0
|
169
171
|
if time.sub!(/\.(\d+)\z/, "")
|
data/lib/dtas/parse_time.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_relative '../dtas'
|
4
|
+
|
4
5
|
module DTAS::ParseTime
|
6
|
+
# convert a string time to seconds, returning a Flot or Integer
|
5
7
|
def parse_time(time)
|
6
8
|
case time
|
7
9
|
when /\A\d+\z/
|
data/lib/dtas/partstats.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
# Copyright (C) 2013-
|
2
|
+
# Copyright (C) 2013-2015 all contributors <dtas-all@nongnu.org>
|
3
3
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
-
# Unlike the stuff for dtas-player, dtas-partstats is fairly tied to sox
|
5
4
|
require_relative '../dtas'
|
6
5
|
require_relative 'xs'
|
7
6
|
require_relative 'process'
|
8
7
|
require_relative 'sigevent'
|
9
8
|
|
9
|
+
# backend for the dtas-partstats(1) command
|
10
|
+
# Unlike the stuff for dtas-player, dtas-partstats is fairly tied to sox
|
10
11
|
class DTAS::PartStats
|
11
12
|
CMD = 'sox "$INFILE" -n $TRIMFX $SOXFX stats $STATSOPTS'
|
12
13
|
include DTAS::Process
|
14
|
+
include DTAS::SpawnFix
|
13
15
|
attr_reader :key_idx
|
14
16
|
attr_reader :key_width
|
15
17
|
|
@@ -49,18 +51,14 @@ class DTAS::PartStats
|
|
49
51
|
rv
|
50
52
|
end
|
51
53
|
|
52
|
-
def
|
54
|
+
def partstats_spawn(trim_part, opts)
|
53
55
|
rd, wr = IO.pipe
|
54
56
|
env = opts[:env]
|
55
57
|
env = env ? env.dup : {}
|
56
|
-
env["INFILE"] = @infile
|
58
|
+
env["INFILE"] = xs(@infile)
|
57
59
|
env["TRIMFX"] = "trim #{trim_part.tbeg}s #{trim_part.tlen}s"
|
58
60
|
opts = { pgroup: true, close_others: true, err: wr }
|
59
|
-
pid =
|
60
|
-
Process.spawn(env, CMD, opts)
|
61
|
-
rescue Errno::EINTR # Ruby bug?
|
62
|
-
retry
|
63
|
-
end
|
61
|
+
pid = spawn(env, CMD, opts)
|
64
62
|
wr.close
|
65
63
|
[ pid, rd ]
|
66
64
|
end
|
@@ -74,7 +72,7 @@ class DTAS::PartStats
|
|
74
72
|
stats = []
|
75
73
|
fails = []
|
76
74
|
do_spawn = lambda do |trim_part|
|
77
|
-
pid, rpipe =
|
75
|
+
pid, rpipe = partstats_spawn(trim_part, opts)
|
78
76
|
rset[rpipe] = [ trim_part, "" ]
|
79
77
|
pids[pid] = [ trim_part, rpipe ]
|
80
78
|
end
|
data/lib/dtas/pipe.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
|
begin
|
4
4
|
require 'io/splice'
|
@@ -7,6 +7,7 @@ end
|
|
7
7
|
require_relative '../dtas'
|
8
8
|
require_relative 'writable_iter'
|
9
9
|
|
10
|
+
# pipe wrapper for -player sinks
|
10
11
|
class DTAS::Pipe < IO # :nodoc:
|
11
12
|
include DTAS::WritableIter
|
12
13
|
attr_accessor :sink
|
data/lib/dtas/player.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 'yaml'
|
4
4
|
require 'shellwords'
|
@@ -8,6 +8,7 @@ require_relative 'source'
|
|
8
8
|
require_relative 'source/sox'
|
9
9
|
require_relative 'source/av'
|
10
10
|
require_relative 'source/ff'
|
11
|
+
require_relative 'source/splitfx'
|
11
12
|
require_relative 'source/cmd'
|
12
13
|
require_relative 'sink'
|
13
14
|
require_relative 'unix_server'
|
@@ -17,6 +18,7 @@ require_relative 'rg_state'
|
|
17
18
|
require_relative 'state_file'
|
18
19
|
require_relative 'tracklist'
|
19
20
|
|
21
|
+
# the core of dtas-player(1)
|
20
22
|
class DTAS::Player # :nodoc:
|
21
23
|
require_relative 'player/client_handler'
|
22
24
|
include DTAS::XS
|
@@ -43,10 +45,12 @@ class DTAS::Player # :nodoc:
|
|
43
45
|
@sink_buf = DTAS::Buffer.new
|
44
46
|
@current = nil
|
45
47
|
@watchers = {}
|
48
|
+
@trim = nil
|
46
49
|
@source_map = {
|
47
|
-
"sox" => DTAS::Source::Sox.new,
|
50
|
+
"sox" => (sox = DTAS::Source::Sox.new),
|
48
51
|
"av" => DTAS::Source::Av.new,
|
49
52
|
"ff" => DTAS::Source::Ff.new,
|
53
|
+
"splitfx" => DTAS::Source::SplitFX.new(sox),
|
50
54
|
}
|
51
55
|
source_map_reload
|
52
56
|
end
|
@@ -56,7 +60,7 @@ class DTAS::Player # :nodoc:
|
|
56
60
|
end
|
57
61
|
|
58
62
|
def wall(msg)
|
59
|
-
__wall(xs(
|
63
|
+
__wall(xs(msg))
|
60
64
|
end
|
61
65
|
|
62
66
|
def __wall(msg)
|
@@ -80,6 +84,7 @@ class DTAS::Player # :nodoc:
|
|
80
84
|
rv = {}
|
81
85
|
rv["socket"] = @socket
|
82
86
|
rv["paused"] = @paused if @paused
|
87
|
+
rv["trim"] = @trim if @trim
|
83
88
|
src_map = rv["source"] = {}
|
84
89
|
@source_map.each do |name, src|
|
85
90
|
src_hsh = src.to_state_hash
|
@@ -117,6 +122,10 @@ class DTAS::Player # :nodoc:
|
|
117
122
|
rv
|
118
123
|
end
|
119
124
|
|
125
|
+
def to_omap(hash)
|
126
|
+
YAML::Omap === hash ? hash : YAML::Omap.new.merge!(hash)
|
127
|
+
end
|
128
|
+
|
120
129
|
def self.load(hash)
|
121
130
|
rv = new
|
122
131
|
rv.instance_eval do
|
@@ -128,7 +137,7 @@ class DTAS::Player # :nodoc:
|
|
128
137
|
v = v["buffer_size"]
|
129
138
|
@sink_buf.buffer_size = v
|
130
139
|
end
|
131
|
-
%w(socket queue paused bypass).each do |k|
|
140
|
+
%w(socket queue paused bypass trim).each do |k|
|
132
141
|
v = hash[k] or next
|
133
142
|
instance_variable_set("@#{k}", v)
|
134
143
|
end
|
@@ -147,6 +156,7 @@ class DTAS::Player # :nodoc:
|
|
147
156
|
@source_map.each do |name, src|
|
148
157
|
src_hsh = v[name] or next
|
149
158
|
src.load!(src_hsh)
|
159
|
+
src.env = to_omap(src.env)
|
150
160
|
end
|
151
161
|
source_map_reload
|
152
162
|
end
|
@@ -158,6 +168,7 @@ class DTAS::Player # :nodoc:
|
|
158
168
|
if sinks = hash["sinks"]
|
159
169
|
sinks.each do |sink_hsh|
|
160
170
|
sink = DTAS::Sink.load(sink_hsh)
|
171
|
+
sink.env = to_omap(sink.env)
|
161
172
|
@sinks[sink.name] = sink
|
162
173
|
end
|
163
174
|
end
|
@@ -238,6 +249,8 @@ class DTAS::Player # :nodoc:
|
|
238
249
|
io.emit(Dir.pwd)
|
239
250
|
when "tl"
|
240
251
|
tl_handler(io, msg)
|
252
|
+
when "trim"
|
253
|
+
trim_handler(io, msg)
|
241
254
|
end
|
242
255
|
end
|
243
256
|
|
@@ -346,7 +359,7 @@ class DTAS::Player # :nodoc:
|
|
346
359
|
@sinks.each_value do |sink|
|
347
360
|
sink.active or next
|
348
361
|
next if sink.pid
|
349
|
-
@targets.concat(sink.
|
362
|
+
@targets.concat(sink.sink_spawn(@format))
|
350
363
|
end
|
351
364
|
if @targets[0]
|
352
365
|
@targets.sort_by! { |t| t.sink.prio }
|
@@ -358,23 +371,25 @@ class DTAS::Player # :nodoc:
|
|
358
371
|
end
|
359
372
|
end
|
360
373
|
|
361
|
-
def try_file(
|
374
|
+
def try_file(file, offset = nil)
|
362
375
|
@sources.each do |src|
|
363
|
-
rv = src.try(
|
376
|
+
rv = src.try(file, offset, @trim) and return rv
|
364
377
|
end
|
365
378
|
|
366
379
|
# keep going down the list until we find something
|
367
380
|
while source_spec = @queue.shift
|
381
|
+
path, off = source_spec
|
368
382
|
@sources.each do |src|
|
369
|
-
rv = src.try(
|
383
|
+
rv = src.try(path, off, @trim) and return rv
|
370
384
|
end
|
371
385
|
end
|
372
386
|
|
373
387
|
# don't get stuck in an infinite loop if @tl.repeat==true and we can't
|
374
388
|
# decode anything (FS errors, sox uninstalled, etc...)
|
375
389
|
while path_off = @tl.advance_track(false)
|
390
|
+
path, off = path_off
|
376
391
|
@sources.each do |src|
|
377
|
-
rv = src.try(
|
392
|
+
rv = src.try(path, off, @trim) and return rv
|
378
393
|
end
|
379
394
|
end
|
380
395
|
|
@@ -383,6 +398,7 @@ class DTAS::Player # :nodoc:
|
|
383
398
|
end
|
384
399
|
|
385
400
|
def next_source(source_spec)
|
401
|
+
@current.respond_to?(:watch_end) and @current.watch_end(@srv)
|
386
402
|
@current = nil
|
387
403
|
if source_spec
|
388
404
|
case source_spec
|
@@ -397,7 +413,7 @@ class DTAS::Player # :nodoc:
|
|
397
413
|
msg = %W(command #{pending.command_string})
|
398
414
|
end
|
399
415
|
|
400
|
-
|
416
|
+
if ! @bypass.empty? && pending.respond_to?(:format)
|
401
417
|
new_fmt = bypass_match!(@format.dup, pending.format)
|
402
418
|
if new_fmt != @format
|
403
419
|
stop_sinks # we may fail to start below
|
@@ -410,7 +426,13 @@ class DTAS::Player # :nodoc:
|
|
410
426
|
|
411
427
|
dst = @sink_buf
|
412
428
|
pending.dst_assoc(dst)
|
413
|
-
pending.
|
429
|
+
pending.src_spawn(@format, @rg, out: dst.wr, in: "/dev/null")
|
430
|
+
|
431
|
+
# watch and restart on modifications
|
432
|
+
pending.respond_to?(:watch_begin) and
|
433
|
+
@srv.wait_ctl(pending.watch_begin(method(:__current_requeue)),
|
434
|
+
:wait_readable)
|
435
|
+
|
414
436
|
@current = pending
|
415
437
|
@srv.wait_ctl(dst, :wait_readable)
|
416
438
|
wall(msg)
|