dtas 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +9 -0
  4. data/.rsync_doc +3 -0
  5. data/COPYING +674 -0
  6. data/Documentation/.gitignore +3 -0
  7. data/Documentation/GNUmakefile +46 -0
  8. data/Documentation/dtas-console.txt +42 -0
  9. data/Documentation/dtas-ctl.txt +64 -0
  10. data/Documentation/dtas-cueedit.txt +24 -0
  11. data/Documentation/dtas-enq.txt +29 -0
  12. data/Documentation/dtas-msinkctl.txt +45 -0
  13. data/Documentation/dtas-player.txt +110 -0
  14. data/Documentation/dtas-player_effects.txt +45 -0
  15. data/Documentation/dtas-player_protocol.txt +181 -0
  16. data/Documentation/dtas-sinkedit.txt +41 -0
  17. data/Documentation/dtas-sourceedit.txt +33 -0
  18. data/Documentation/dtas-xdelay.txt +57 -0
  19. data/Documentation/troubleshooting.txt +13 -0
  20. data/GIT-VERSION-GEN +30 -0
  21. data/GNUmakefile +9 -0
  22. data/HACKING +12 -0
  23. data/INSTALL +53 -0
  24. data/README +103 -0
  25. data/Rakefile +97 -0
  26. data/TODO +4 -0
  27. data/bin/dtas-console +160 -0
  28. data/bin/dtas-ctl +10 -0
  29. data/bin/dtas-cueedit +78 -0
  30. data/bin/dtas-enq +13 -0
  31. data/bin/dtas-msinkctl +51 -0
  32. data/bin/dtas-player +34 -0
  33. data/bin/dtas-sinkedit +58 -0
  34. data/bin/dtas-sourceedit +48 -0
  35. data/bin/dtas-xdelay +85 -0
  36. data/dtas-linux.gemspec +18 -0
  37. data/dtas-mpris.gemspec +16 -0
  38. data/examples/dtas_state.yml +18 -0
  39. data/lib/dtas.rb +7 -0
  40. data/lib/dtas/buffer.rb +90 -0
  41. data/lib/dtas/buffer/read_write.rb +102 -0
  42. data/lib/dtas/buffer/splice.rb +142 -0
  43. data/lib/dtas/command.rb +43 -0
  44. data/lib/dtas/compat_onenine.rb +18 -0
  45. data/lib/dtas/disclaimer.rb +18 -0
  46. data/lib/dtas/format.rb +151 -0
  47. data/lib/dtas/pipe.rb +39 -0
  48. data/lib/dtas/player.rb +393 -0
  49. data/lib/dtas/player/client_handler.rb +463 -0
  50. data/lib/dtas/process.rb +87 -0
  51. data/lib/dtas/replaygain.rb +41 -0
  52. data/lib/dtas/rg_state.rb +99 -0
  53. data/lib/dtas/serialize.rb +9 -0
  54. data/lib/dtas/sigevent.rb +10 -0
  55. data/lib/dtas/sigevent/efd.rb +20 -0
  56. data/lib/dtas/sigevent/pipe.rb +28 -0
  57. data/lib/dtas/sink.rb +121 -0
  58. data/lib/dtas/source.rb +147 -0
  59. data/lib/dtas/source/command.rb +40 -0
  60. data/lib/dtas/source/common.rb +14 -0
  61. data/lib/dtas/source/mp3.rb +37 -0
  62. data/lib/dtas/state_file.rb +33 -0
  63. data/lib/dtas/unix_accepted.rb +76 -0
  64. data/lib/dtas/unix_client.rb +51 -0
  65. data/lib/dtas/unix_server.rb +110 -0
  66. data/lib/dtas/util.rb +15 -0
  67. data/lib/dtas/writable_iter.rb +22 -0
  68. data/perl/dtas-graph +129 -0
  69. data/pkg.mk +26 -0
  70. data/setup.rb +1586 -0
  71. data/test/covshow.rb +30 -0
  72. data/test/helper.rb +76 -0
  73. data/test/player_integration.rb +121 -0
  74. data/test/test_buffer.rb +216 -0
  75. data/test/test_format.rb +61 -0
  76. data/test/test_format_change.rb +49 -0
  77. data/test/test_player.rb +47 -0
  78. data/test/test_player_client_handler.rb +86 -0
  79. data/test/test_player_integration.rb +220 -0
  80. data/test/test_rg_integration.rb +117 -0
  81. data/test/test_rg_state.rb +32 -0
  82. data/test/test_sink.rb +32 -0
  83. data/test/test_sink_tee_integration.rb +34 -0
  84. data/test/test_source.rb +102 -0
  85. data/test/test_unixserver.rb +66 -0
  86. data/test/test_util.rb +15 -0
  87. metadata +208 -0
data/test/covshow.rb ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ #
5
+ # this works with the __covmerge method in test/helper.rb
6
+ # run this file after all tests are run
7
+
8
+ # load the merged dump data
9
+ res = Marshal.load(IO.binread("coverage.dump"))
10
+
11
+ # Dirty little text formatter. I tried simplecov but the default
12
+ # HTML+JS is unusable without a GUI (I hate GUIs :P) and it would've
13
+ # taken me longer to search the Internets to find a plain-text
14
+ # formatter I like...
15
+ res.keys.sort.each do |filename|
16
+ cov = res[filename]
17
+ puts "==> #{filename} <=="
18
+ File.readlines(filename).each_with_index do |line, i|
19
+ n = cov[i]
20
+ if n == 0 # BAD
21
+ print(" *** 0 #{line}")
22
+ elsif n
23
+ printf("% 7u %s", n, line)
24
+ elsif line =~ /\S/ # probably a line with just "end" in it
25
+ print(" #{line}")
26
+ else # blank line
27
+ print "\n" # don't output trailing whitespace on blank lines
28
+ end
29
+ end
30
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,76 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ $stdout.sync = $stderr.sync = Thread.abort_on_exception = true
5
+
6
+ # fork-aware coverage data gatherer, see also test/covshow.rb
7
+ if ENV["COVERAGE"]
8
+ require "coverage"
9
+ COVMATCH = %r{/lib/dtas\b.*rb\z}
10
+ COVTMP = File.open("coverage.dump", IO::CREAT|IO::RDWR)
11
+ COVTMP.binmode
12
+ COVTMP.sync = true
13
+
14
+ def __covmerge
15
+ res = Coverage.result
16
+
17
+ # we own this file (at least until somebody tries to use NFS :x)
18
+ COVTMP.flock(File::LOCK_EX)
19
+
20
+ COVTMP.rewind
21
+ prev = COVTMP.read
22
+ prev = prev.empty? ? {} : Marshal.load(prev)
23
+ res.each do |filename, counts|
24
+ # filter out stuff that's not in our project
25
+ COVMATCH =~ filename or next
26
+
27
+ merge = prev[filename] || []
28
+ merge = merge
29
+ counts.each_with_index do |count, i|
30
+ count or next
31
+ merge[i] = (merge[i] || 0) + count
32
+ end
33
+ prev[filename] = merge
34
+ end
35
+ COVTMP.rewind
36
+ COVTMP.truncate(0)
37
+ COVTMP.write(Marshal.dump(prev))
38
+ ensure
39
+ COVTMP.flock(File::LOCK_UN)
40
+ end
41
+
42
+ Coverage.start
43
+ at_exit { __covmerge }
44
+ end
45
+
46
+ gem 'minitest'
47
+ require 'minitest/autorun'
48
+ require "tempfile"
49
+
50
+ FIFOS = []
51
+ at_exit { FIFOS.each { |(pid,path)| File.unlink(path) if $$ == pid } }
52
+ def tmpfifo
53
+ tmp = Tempfile.new(%w(dtas-test .fifo))
54
+ path = tmp.path
55
+ tmp.close!
56
+ assert system(*%W(mkfifo #{path})), "mkfifo #{path}"
57
+ FIFOS << [ $$, path ]
58
+ path
59
+ end
60
+
61
+ require 'tmpdir'
62
+ class Dir
63
+ require 'fileutils'
64
+ def Dir.mktmpdir
65
+ begin
66
+ d = "#{Dir.tmpdir}/#$$.#{rand}"
67
+ Dir.mkdir(d)
68
+ rescue Errno::EEXIST
69
+ end while true
70
+ begin
71
+ yield d
72
+ ensure
73
+ FileUtils.remove_entry(d)
74
+ end
75
+ end
76
+ end unless Dir.respond_to?(:mktmpdir)
@@ -0,0 +1,121 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ require './test/helper'
5
+ require 'dtas/player'
6
+ require 'dtas/state_file'
7
+ require 'yaml'
8
+ require 'tempfile'
9
+ require 'shellwords'
10
+ require 'timeout'
11
+
12
+ module PlayerIntegration
13
+ def setup
14
+ sock_tmp = Tempfile.new(%w(dtas-test .sock))
15
+ @state_tmp = Tempfile.new(%w(dtas-test .yml))
16
+ @sock_path = sock_tmp.path
17
+ sock_tmp.close!
18
+ @player = DTAS::Player.new
19
+ @player.socket = @sock_path
20
+ @player.state_file = DTAS::StateFile.new(@state_tmp.path)
21
+ @player.bind
22
+ @out = Tempfile.new(%w(dtas-test .out))
23
+ @err = Tempfile.new(%w(dtas-test .err))
24
+ @out.sync = @err.sync = true
25
+ @pid = fork do
26
+ at_exit { @player.close }
27
+ ENV["SOX_OPTS"] = "#{ENV['SOX_OPTS']} -R"
28
+ unless $DEBUG
29
+ $stdout.reopen(@out)
30
+ $stderr.reopen(@err)
31
+ end
32
+ @player.run
33
+ end
34
+
35
+ # null playback device with delay to simulate a real device
36
+ @fmt = DTAS::Format.new
37
+ @period = 0.01
38
+ @period_size = @fmt.bytes_per_sample * @fmt.channels * @fmt.rate * @period
39
+ @cmd = "exec 2>/dev/null " \
40
+ "ruby -e " \
41
+ "\"b=%q();loop{STDIN.readpartial(#@period_size,b);sleep(#@period)}\""
42
+
43
+ # FIXME gross...
44
+ @player.instance_eval do
45
+ @sink_buf.close!
46
+ end
47
+ end
48
+
49
+ module PlayerClient
50
+ def preq(args)
51
+ args = Shellwords.join(args) if Array === args
52
+ send(args, Socket::MSG_EOR)
53
+ end
54
+ end
55
+
56
+ def client_socket
57
+ s = Socket.new(:AF_UNIX, :SOCK_SEQPACKET, 0)
58
+ s.connect(Socket.pack_sockaddr_un(@sock_path))
59
+ s.extend(PlayerClient)
60
+ s
61
+ end
62
+
63
+ def wait_pid_dead(pid, time = 5)
64
+ Timeout.timeout(time) do
65
+ begin
66
+ Process.kill(0, pid)
67
+ sleep(0.01)
68
+ rescue Errno::ESRCH
69
+ return
70
+ end while true
71
+ end
72
+ end
73
+
74
+ def wait_files_not_empty(*files)
75
+ files = Array(files)
76
+ Timeout.timeout(5) { sleep(0.01) until files.all? { |f| f.size > 0 } }
77
+ end
78
+
79
+ def default_sink_pid(s)
80
+ default_pid = Tempfile.new(%w(dtas-test .pid))
81
+ pf = "echo $$ >> #{default_pid.path}; "
82
+ s.send("sink ed default command='#{pf}#@cmd'", Socket::MSG_EOR)
83
+ assert_equal "OK", s.readpartial(666)
84
+ default_pid
85
+ end
86
+
87
+ def teardown
88
+ Process.kill(:TERM, @pid) if @pid
89
+ Process.waitall
90
+ refute File.exist?(@sock_path)
91
+ @state_tmp.close!
92
+ @out.close! if @out
93
+ @err.close! if @err
94
+ end
95
+
96
+ def read_pid_file(file)
97
+ file.rewind
98
+ pid = file.read.to_i
99
+ assert_operator pid, :>, 0
100
+ pid
101
+ end
102
+
103
+ def tmp_noise(len = 5)
104
+ noise = Tempfile.open(%w(junk .sox))
105
+ cmd = %W(sox -R -n -r44100 -c2 #{noise.path} synth #{len} pluck)
106
+ assert system(*cmd), cmd
107
+ [ noise, len ]
108
+ end
109
+
110
+ def dethrottle_decoder(s)
111
+ s.send("sink ed default active=false", Socket::MSG_EOR)
112
+ assert_equal "OK", s.readpartial(666)
113
+ end
114
+
115
+ def stop_playback(pid_file, s)
116
+ s.send("skip", Socket::MSG_EOR)
117
+ assert_equal "OK", s.readpartial(666)
118
+ pid = read_pid_file(pid_file)
119
+ wait_pid_dead(pid)
120
+ end
121
+ end
@@ -0,0 +1,216 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ require './test/helper'
5
+ require 'stringio'
6
+ require 'dtas/buffer'
7
+
8
+ class TestBuffer < Minitest::Unit::TestCase
9
+ def teardown
10
+ @to_close.each { |io| io.close unless io.closed? }
11
+ end
12
+
13
+ def setup
14
+ @to_close = []
15
+ end
16
+
17
+ def pipe
18
+ ret = IO.pipe
19
+ @to_close.concat(ret)
20
+ ret
21
+ end
22
+
23
+ def tmperr
24
+ olderr = $stderr
25
+ $stderr = newerr = StringIO.new
26
+ yield
27
+ newerr
28
+ ensure
29
+ $stderr = olderr
30
+ end
31
+
32
+ def new_buffer
33
+ buf = DTAS::Buffer.new
34
+ @to_close << buf.to_io
35
+ @to_close << buf.wr
36
+ buf
37
+ end
38
+
39
+ def test_set_buffer_size
40
+ buf = new_buffer
41
+ buf.buffer_size = DTAS::Buffer::MAX_SIZE
42
+ assert_equal DTAS::Buffer::MAX_SIZE, buf.buffer_size
43
+ end if defined?(DTAS::Buffer::MAX_SIZE)
44
+
45
+ def test_buffer_size
46
+ buf = new_buffer
47
+ assert_operator buf.buffer_size, :>, 128
48
+ buf.buffer_size = DTAS::Buffer::MAX_SIZE
49
+ assert_equal DTAS::Buffer::MAX_SIZE, buf.buffer_size
50
+ end if defined?(DTAS::Buffer::MAX_SIZE)
51
+
52
+ def test_broadcast_1
53
+ buf = new_buffer
54
+ r, w = IO.pipe
55
+ assert_equal :wait_readable, buf.broadcast([w])
56
+ assert_equal 0, buf.bytes_xfer
57
+ buf.wr.write "HIHI"
58
+ assert_equal :wait_readable, buf.broadcast([w])
59
+ assert_equal 4, buf.bytes_xfer
60
+ assert_equal :wait_readable, buf.broadcast([w])
61
+ assert_equal 4, buf.bytes_xfer
62
+ tmp = [w]
63
+ r.close
64
+ buf.wr.write "HIHI"
65
+ newerr = tmperr { assert_nil buf.broadcast(tmp) }
66
+ assert_equal [], tmp
67
+ assert_match(%r{dropping}, newerr.string)
68
+ end
69
+
70
+ def test_broadcast_tee
71
+ buf = new_buffer
72
+ return unless buf.respond_to?(:__broadcast_tee)
73
+ blocked = []
74
+ a = pipe
75
+ b = pipe
76
+ buf.wr.write "HELLO"
77
+ assert_equal 4, buf.__broadcast_tee(blocked, [a[1], b[1]], 4)
78
+ assert_empty blocked
79
+ assert_equal "HELL", a[0].read(4)
80
+ assert_equal "HELL", b[0].read(4)
81
+ assert_equal 5, buf.__broadcast_tee(blocked, [a[1], b[1]], 5)
82
+ assert_empty blocked
83
+ assert_equal "HELLO", a[0].read(5)
84
+ assert_equal "HELLO", b[0].read(5)
85
+ max = '*' * a[0].pipe_size
86
+ assert_equal max.size, a[1].write(max)
87
+ assert_equal a[0].nread, a[0].pipe_size
88
+ a[1].nonblock = true
89
+ assert_equal 5, buf.__broadcast_tee(blocked, [a[1], b[1]], 5)
90
+ assert_equal [a[1]], blocked
91
+ a[1].nonblock = false
92
+ b[0].read(b[0].nread)
93
+ b[1].write(max)
94
+ t = Thread.new do
95
+ sleep 0.005
96
+ [ a[0].read(max.size).size, b[0].read(max.size).size ]
97
+ end
98
+ assert_equal 5, buf.__broadcast_tee(blocked, [a[1], b[1]], 5)
99
+ assert_equal [a[1]], blocked
100
+ assert_equal [ max.size, max.size ], t.value
101
+ b[0].close
102
+ tmp = [a[1], b[1]]
103
+
104
+ newerr = tmperr { assert_equal 5, buf.__broadcast_tee(blocked, tmp, 5) }
105
+ assert_equal [a[1]], blocked
106
+ assert_match(%r{dropping}, newerr.string)
107
+ assert_equal [a[1]], tmp
108
+ end
109
+
110
+ def test_broadcast
111
+ a = pipe
112
+ b = pipe
113
+ buf = new_buffer
114
+ buf.wr.write "HELLO"
115
+ assert_equal :wait_readable, buf.broadcast([a[1], b[1]])
116
+ assert_equal 5, buf.bytes_xfer
117
+ assert_equal "HELLO", a[0].read(5)
118
+ assert_equal "HELLO", b[0].read(5)
119
+ assert_equal :wait_readable, buf.broadcast([a[1], b[1]])
120
+ assert_equal 5, buf.bytes_xfer
121
+
122
+ b[1].nonblock = true
123
+ b[1].write('*' * b[1].pipe_size)
124
+ buf.wr.write "BYE"
125
+ assert_equal :wait_readable, buf.broadcast([a[1], b[1]])
126
+ assert_equal 8, buf.bytes_xfer
127
+
128
+ buf.wr.write "DROP"
129
+ b[0].close
130
+ tmp = [a[1], b[1]]
131
+ newerr = tmperr { assert_equal :wait_readable, buf.broadcast(tmp) }
132
+ assert_equal 12, buf.bytes_xfer
133
+ assert_equal [a[1]], tmp
134
+ assert_match(%r{dropping}, newerr.string)
135
+ end
136
+
137
+ def test_broadcast_total_fail
138
+ a = pipe
139
+ b = pipe
140
+ buf = new_buffer
141
+ buf.wr.write "HELLO"
142
+ a[0].close
143
+ b[0].close
144
+ tmp = [a[1], b[1]]
145
+ newerr = tmperr { assert_nil buf.broadcast(tmp) }
146
+ assert_equal [], tmp
147
+ assert_match(%r{dropping}, newerr.string)
148
+ end
149
+
150
+ def test_broadcast_mostly_fail
151
+ a = pipe
152
+ b = pipe
153
+ c = pipe
154
+ buf = new_buffer
155
+ buf.wr.write "HELLO"
156
+ b[0].close
157
+ c[0].close
158
+ tmp = [a[1], b[1], c[1]]
159
+ newerr = tmperr { assert_equal :wait_readable, buf.broadcast(tmp) }
160
+ assert_equal 5, buf.bytes_xfer
161
+ assert_equal [a[1]], tmp
162
+ assert_match(%r{dropping}, newerr.string)
163
+ end
164
+
165
+ def test_broadcast_all_full
166
+ a = pipe
167
+ b = pipe
168
+ buf = new_buffer
169
+ a[1].write('*' * a[1].pipe_size)
170
+ b[1].write('*' * b[1].pipe_size)
171
+
172
+ a[1].nonblock = true
173
+ b[1].nonblock = true
174
+ tmp = [a[1], b[1]]
175
+
176
+ buf.wr.write "HELLO"
177
+ assert_equal tmp, buf.broadcast(tmp)
178
+ assert_equal [a[1], b[1]], tmp
179
+ end
180
+
181
+ def test_serialize
182
+ buf = new_buffer
183
+ hash = buf.to_hsh
184
+ assert_empty hash
185
+ buf.buffer_size = 4096
186
+ hash = buf.to_hsh
187
+ assert_equal %w(buffer_size), hash.keys
188
+ assert_kind_of Integer, hash["buffer_size"]
189
+ assert_operator hash["buffer_size"], :>, 0
190
+ end
191
+
192
+ def test_close
193
+ buf = DTAS::Buffer.new
194
+ buf.wr.write "HI"
195
+ assert_equal 2, buf.inflight
196
+ buf.close
197
+ assert_equal 0, buf.inflight
198
+ assert_nil buf.close!
199
+ end
200
+
201
+ def test_load_nil
202
+ buf = DTAS::Buffer.load(nil)
203
+ buf.close!
204
+ end
205
+
206
+ def test_load_empty
207
+ buf = DTAS::Buffer.load({})
208
+ buf.close!
209
+ end
210
+
211
+ def test_load_size
212
+ buf = DTAS::Buffer.load({"buffer_size" => 4096})
213
+ assert_equal 4096, buf.buffer_size
214
+ buf.close!
215
+ end
216
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ require './test/helper'
5
+ require 'tempfile'
6
+ require 'dtas/format'
7
+
8
+ class TestFormat < Minitest::Unit::TestCase
9
+ def test_initialize
10
+ fmt = DTAS::Format.new
11
+ assert_equal %w(-ts32 -c2 -r44100), fmt.to_sox_arg
12
+ hash = fmt.to_hsh
13
+ assert_equal({}, hash)
14
+ end
15
+
16
+ def test_nonstandard
17
+ fmt = DTAS::Format.new
18
+ fmt.type = "s16"
19
+ fmt.rate = 48000
20
+ fmt.channels = 4
21
+ hash = fmt.to_hsh
22
+ assert_kind_of Hash, hash
23
+ assert_equal %w(channels rate type), hash.keys.sort
24
+ assert_equal "s16", hash["type"]
25
+ assert_equal 48000, hash["rate"]
26
+ assert_equal 4, hash["channels"]
27
+
28
+ # back to stereo
29
+ fmt.channels = 2
30
+ hash = fmt.to_hsh
31
+ assert_equal %w(rate type), hash.keys.sort
32
+ assert_equal "s16", hash["type"]
33
+ assert_equal 48000, hash["rate"]
34
+ assert_nil hash["channels"]
35
+ end
36
+
37
+ def test_from_file
38
+ Tempfile.open(%w(tmp .wav)) do |tmp|
39
+ # generate an empty file with 1s of audio
40
+ cmd = %W(sox -r 96000 -b 24 -c 2 -n #{tmp.path} trim 0 1)
41
+ system(*cmd)
42
+ assert $?.success?, "#{cmd.inspect} failed: #$?"
43
+ fmt = DTAS::Format.new
44
+ fmt.from_file tmp.path
45
+ assert_equal 96000, fmt.rate
46
+ assert_equal 2, fmt.channels
47
+ tmp.unlink
48
+ end
49
+ end
50
+
51
+ def test_bytes_per_sample
52
+ fmt = DTAS::Format.new
53
+ assert_equal 4, fmt.bytes_per_sample
54
+ fmt.type = "f64"
55
+ assert_equal 8, fmt.bytes_per_sample
56
+ fmt.type = "f32"
57
+ assert_equal 4, fmt.bytes_per_sample
58
+ fmt.type = "s16"
59
+ assert_equal 2, fmt.bytes_per_sample
60
+ end
61
+ end