dtas 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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