ruby-audio-heroku 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ module RubyAudio
2
+ # Class <code>Sound</code> wraps libsndfile to provide simple reading and
3
+ # writing for a wide variety of file formats
4
+ #
5
+ # Reading Example:
6
+ # RubyAudio::Sound.open('sound.wav') do |snd|
7
+ # buf = snd.read(:float, 100)
8
+ # puts buf.real_size #=> 100
9
+ # end
10
+ #
11
+ # Writing Example:
12
+ # buf = RubyAudio::Buffer.float(1000)
13
+ # out = nil
14
+ # ['snd1.wav', 'snd2.wav', 'snd3.wav'].each do |file|
15
+ # RubyAudio::Sound.open(file) do |snd|
16
+ # out = RubyAudio::Sound.open('out.wav', 'w', snd.info.clone) if out.nil?
17
+ #
18
+ # while snd.read(buf) != 0
19
+ # out.write(buf)
20
+ # end
21
+ # end
22
+ # end
23
+ # out.close if out
24
+ class Sound < CSound
25
+ # Creates a new <code>Sound</code> object for the audio file at the given path.
26
+ # Mode defaults to <code>"r"</code>, but valid modes are <code>"r"</code>,
27
+ # <code>"w"</code>, and <code>"rw"</code>.
28
+ #
29
+ # When creating a new sound, a valid <code>SoundInfo</code> object must be
30
+ # passed in, as libsndfile uses it to determine the output format.
31
+ # info = RubyAudio::SoundInfo.new :channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
32
+ # snd = RubyAudio::Sound.new "new.wav", 'r', info
33
+ def initialize(path, mode='r', info=nil)
34
+ info ||= SoundInfo.new
35
+ super(path, mode, info)
36
+ end
37
+
38
+ # Seeks to a given offset <i>anInteger</i> in the sound according to the value
39
+ # of <i>whence</i>:
40
+ #
41
+ # IO::SEEK_CUR | Seeks to _frames_ plus current position
42
+ # --------------+----------------------------------------------------
43
+ # IO::SEEK_END | Seeks to _frames_ plus end of stream (you probably
44
+ # | want a negative value for _frames_)
45
+ # --------------+----------------------------------------------------
46
+ # IO::SEEK_SET | Seeks to the absolute location given by _frames_
47
+ def seek(frames, whence=IO::SEEK_SET)
48
+ super(frames, whence)
49
+ end
50
+
51
+ # Reads a given number of frames from the sound into a buffer
52
+ #
53
+ # When given a buffer as the first argument, it reads data into that buffer
54
+ # reading an optional number of frames as the second argument. It returns
55
+ # the number of frames read.
56
+ #
57
+ # Example:
58
+ # buf = RubyAudio::Buffer.float(1000)
59
+ # snd.read(buf) #=> 1000
60
+ # snd.read(buf, 50) #=> 50
61
+ #
62
+ # When given a string or symbol as the first argument, it interprets this as
63
+ # the data type and creates a new buffer of the given size to read the data
64
+ # into. The buffer is correctly initialized with the proper number of channels
65
+ # to hold data from that sound.
66
+ #
67
+ # Example:
68
+ # buf = snd.read("int", 1000)
69
+ def read(*args)
70
+ case args[0]
71
+ when Buffer
72
+ buf = args[0]
73
+ size = args[1] || buf.size
74
+ return super(buf, size)
75
+ when Symbol, String
76
+ type = args[0]
77
+ buf = Buffer.new(type, args[1], info.channels)
78
+ super(buf, buf.size)
79
+ return buf
80
+ else
81
+ raise ArgumentError, "invalid arguments"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,83 @@
1
+ module RubyAudio
2
+ # Class <code>SoundInfo</code> provides information about open sound files'
3
+ # format, length, samplerate, channels, and other things.
4
+ #
5
+ # Example:
6
+ # snd = RubyAudio::Sound.open("snd.wav")
7
+ # puts snd.info.channels #=> 2
8
+ # puts snd.info.samplerate #=> 48000
9
+ # snd.close
10
+ #
11
+ # In addition it can be used to specify the format of new sound files:
12
+ #
13
+ # info = RubyAudio::SoundInfo.new :channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
14
+ # snd = RubyAudio::Sound.open("new.wav", 'w', info)
15
+ class SoundInfo < CSoundInfo
16
+ # Creates a new SoundInfo object and populates it using the given data
17
+ #
18
+ # Example:
19
+ # info = RubyAudio::SoundInfo.new :channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
20
+ def initialize options={}
21
+ # Populate from options if given
22
+ unless options.empty?
23
+ options.each {|key,value| send("#{key}=", value)}
24
+ end
25
+ end
26
+
27
+ # Returns a new <code>SoundInfo</code> object that has the same channel
28
+ # count, sample rate, and format. This is useful in creating a new sound with
29
+ # the same format as an already existing sound.
30
+ #
31
+ # Example:
32
+ # snd1 = RubyAudio::Sound.open("snd.wav")
33
+ # snd2 = RubyAudio::Sound.open("snd2.wav", 'w', snd1.info.clone)
34
+ def clone
35
+ SoundInfo.new(:channels => channels, :samplerate => samplerate, :format => format)
36
+ end
37
+
38
+ alias_method :seekable?, :seekable
39
+
40
+ # Returns the main format constant as a string
41
+ #
42
+ # Example:
43
+ # info = RubyAudio::SoundInfo.new :channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
44
+ # info.main_format
45
+ # #=> "FORMAT_WAV"
46
+ def main_format
47
+ calculate_format if @main_format.nil?
48
+ @main_format
49
+ end
50
+
51
+ # Returns the sub format constant as a string
52
+ #
53
+ # Example:
54
+ # info = RubyAudio::SoundInfo.new :channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
55
+ # info.sub_format
56
+ # #=> "FORMAT_PCM_16"
57
+ def sub_format
58
+ calculate_format if @sub_format.nil?
59
+ @sub_format
60
+ end
61
+
62
+ # Returns the length of the audio file in seconds
63
+ def length
64
+ frames / samplerate.to_f
65
+ end
66
+
67
+ private
68
+ def calculate_format
69
+ RubyAudio.constants.grep(/FORMAT_/).map(&:to_s).each do |f|
70
+ next if f.include?('MASK') # Skip mask constants
71
+
72
+ val = RubyAudio.const_get(f)
73
+ if val > RubyAudio::FORMAT_SUBMASK
74
+ # Main format
75
+ @main_format = f if format & RubyAudio::FORMAT_TYPEMASK == val
76
+ else
77
+ # Sub format
78
+ @sub_format = f if format & RubyAudio::FORMAT_SUBMASK == val
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
data/lib/ruby-audio.rb ADDED
@@ -0,0 +1,10 @@
1
+ begin
2
+ # Fat binaries for Windows
3
+ RUBY_VERSION =~ /(\d+.\d+)/
4
+ require "#{$1}/rubyaudio_ext"
5
+ rescue LoadError
6
+ require "rubyaudio_ext"
7
+ end
8
+ require 'ruby-audio/buffer'
9
+ require 'ruby-audio/sound_info'
10
+ require 'ruby-audio/sound'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'ruby-audio-heroku'
5
+ s.version = '1.6.1'
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ['Khurram Zaman']
8
+ s.email = ['khurram.zaman@gmail.com']
9
+ s.homepage = 'https://github.com/khurramzaman/ruby-audio-heroku'
10
+ s.summary = 'libsndfile wrapper for ruby that works on heroku'
11
+ s.description = 'ruby-audio-heroku wraps around libsndfile to provide simplified sound reading and writing support to ruby programs. it works on heroku.'
12
+
13
+ s.files = Dir['ruby-audio-heroku.gemspec', 'README.rdoc', 'LICENSE', 'Rakefile', 'lib/**/*.rb', 'spec/**/*.{rb,opts,wav,mp3}', 'ext/**/*.{c,h,rb}']
14
+ s.test_files = Dir['spec/**/*_spec.rb']
15
+ s.extensions = Dir["ext/**/extconf.rb"]
16
+
17
+ s.requirements << ''
18
+
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = Dir['README.rdoc', 'ext/**/*.c']
21
+ s.rdoc_options = ['--line-numbers', '--main', 'README.rdoc']
22
+ end
@@ -0,0 +1,107 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe RubyAudio::Buffer do
4
+ it "should initialize properly" do
5
+ buf = RubyAudio::Buffer.new('float', 100, 2)
6
+ buf.channels.should == 2
7
+ buf.size.should == 100
8
+ buf.real_size.should == 0
9
+ buf.type.should == :float
10
+ end
11
+
12
+ it "should support pretty typed constructors" do
13
+ lambda {
14
+ [:short, :int, :float, :double].each do |type|
15
+ buf = RubyAudio::Buffer.send(type, 100)
16
+ end
17
+ }.should_not raise_error
18
+ end
19
+
20
+ it "should allow [] access on integer single channel buffers" do
21
+ buf = RubyAudio::Buffer.int(100, 1)
22
+ buf[0] = 1.3
23
+ buf[0].should == 1
24
+
25
+ buf = RubyAudio::Buffer.short(100, 1)
26
+ buf[20] = 614
27
+ buf[20].should == 614
28
+ end
29
+
30
+ it "should allow [] access on floating point single channel buffers" do
31
+ buf = RubyAudio::Buffer.double(100, 1)
32
+ buf[30] = 1.375
33
+ buf[30].should == 1.375
34
+
35
+ buf = RubyAudio::Buffer.float(100, 1)
36
+ buf[12] = 5
37
+ buf[12].should == 5.0
38
+ end
39
+
40
+ it "should allow [] access on multi-channel buffers" do
41
+ buf = RubyAudio::Buffer.double(100, 2)
42
+ buf[0] = [0.5, 0.3]
43
+ buf[0].should == [0.5, 0.3]
44
+ end
45
+
46
+ it "should raise exception if channel count of set frame does not match buffer's" do
47
+ lambda {
48
+ buf = RubyAudio::Buffer.double(100, 2)
49
+ buf[0] = [0.4, 0.8, 0.8]
50
+ }.should raise_error(RubyAudio::Error, "array length must match channel count")
51
+ end
52
+
53
+ it "should return nil on out-of-bounds [] access" do
54
+ buf = RubyAudio::Buffer.float(100)
55
+ buf[101].should == nil
56
+ buf[-1].should == nil
57
+ end
58
+
59
+ it "should truncate invalid real size" do
60
+ buf = RubyAudio::Buffer.float(100)
61
+ buf.real_size = 101
62
+ buf.real_size.should == 100
63
+ end
64
+
65
+ it "should support cloning/duping" do
66
+ buf = RubyAudio::Buffer.int(100)
67
+ buf[4] = 100
68
+
69
+ buf2 = buf.dup
70
+ buf2.size.should == buf.size
71
+ buf2[4].should == 100
72
+
73
+ buf[4] = 140
74
+ buf2[4].should == 100
75
+ end
76
+
77
+ context ".each" do
78
+ before(:each) do
79
+ @buf = RubyAudio::Buffer.int(10, 2)
80
+ 10.times do |i|
81
+ @buf[i] = [i, i]
82
+ end
83
+ end
84
+
85
+ it "should yield the elements in order" do
86
+ i = 0
87
+ @buf.each do |left, right|
88
+ left.should == i
89
+ right.should == i
90
+ i += 1
91
+ end
92
+ end
93
+
94
+ it "shouldn't do anything on an empty buffer" do
95
+ buf = RubyAudio::Buffer.int(50, 2)
96
+
97
+ buf.each do
98
+ fail "This shouldn't be executed"
99
+ end
100
+ end
101
+
102
+ it "should support usage through returned enumerable" do
103
+ enum = @buf.each
104
+ enum.any? {|frame| frame[0] == 5}.should == true
105
+ end
106
+ end
107
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1,55 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe RubyAudio::SoundInfo do
4
+ it "should initialize with default properties" do
5
+ info = RubyAudio::SoundInfo.new
6
+ info.channels.should == 0
7
+ info.samplerate.should == 0
8
+ info.format.should == 0
9
+ end
10
+
11
+ it "should allow setting properties on initialize" do
12
+ info = RubyAudio::SoundInfo.new(:channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16)
13
+ info.channels.should == 1
14
+ info.samplerate.should == 48000
15
+ info.format.should == RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
16
+ end
17
+
18
+ it "should allow setting properties after initialize" do
19
+ info = RubyAudio::SoundInfo.new
20
+ info.channels = 3
21
+ info.channels.should == 3
22
+ end
23
+
24
+ it "should not be valid if properties invalid" do
25
+ info = RubyAudio::SoundInfo.new(:channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV)
26
+ info.valid?.should == false
27
+ info.format = RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
28
+ info.valid?.should == true
29
+ end
30
+
31
+ it "should allow cloning" do
32
+ info = RubyAudio::SoundInfo.new(:channels => 1, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16)
33
+ info2 = info.clone
34
+ info.object_id.should_not == info2.object_id
35
+ info.channels.should == info2.channels
36
+ info.samplerate.should == info2.samplerate
37
+ info.format.should == info2.format
38
+ end
39
+
40
+ it "should calculate main and sub format from format" do
41
+ info = RubyAudio::SoundInfo.new(:format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16)
42
+ info.main_format.should == "FORMAT_WAV"
43
+ info.sub_format.should == "FORMAT_PCM_16"
44
+
45
+ info = RubyAudio::SoundInfo.new(:format => RubyAudio::FORMAT_WAVEX|RubyAudio::FORMAT_MS_ADPCM)
46
+ info.main_format.should == "FORMAT_WAVEX"
47
+ info.sub_format.should == "FORMAT_MS_ADPCM"
48
+ end
49
+
50
+ it "should return the length of an audio file" do
51
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
52
+ snd.info.length.should == 26413 / 16000.0
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,220 @@
1
+ require "spec_helper.rb"
2
+
3
+ describe RubyAudio::Sound do
4
+ after :each do
5
+ File.delete(fixture('temp.wav')) if File.exists?(fixture('temp.wav'))
6
+ end
7
+
8
+ it "should open a standard wav without issues" do
9
+ lambda {
10
+ RubyAudio::Sound.open(fixture('what.wav'))
11
+ }.should_not raise_error
12
+ end
13
+
14
+ it "should open an IO conformer without issues" do
15
+ lambda {
16
+ RubyAudio::Sound.open(io_fixture('what.wav'))
17
+ }.should_not raise_error
18
+ end
19
+
20
+ it "should raise an exception if the mode is invalid" do
21
+ lambda {
22
+ RubyAudio::Sound.open(fixture('what.wav'), 'q')
23
+ }.should raise_error(ArgumentError, 'invalid access mode q')
24
+ end
25
+
26
+ it "should close the sound on block exit" do
27
+ s = nil
28
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
29
+ snd.closed?.should be_false
30
+ s = snd
31
+ end
32
+ s.closed?.should be_true
33
+ end
34
+
35
+ it "should raise an exception for an unsupported file" do
36
+ lambda {
37
+ RubyAudio::Sound.open(fixture('what.mp3'))
38
+ }.should raise_error(RubyAudio::Error, "File contains data in an unknown format.")
39
+ end
40
+
41
+ it "should raise an exception if file does not exist" do
42
+ lambda {
43
+ RubyAudio::Sound.open(fixture('what.mp3')+'.not')
44
+ }.should raise_error(RubyAudio::Error, "System error : No such file or directory.")
45
+ end
46
+
47
+ it "should have the proper sound info" do
48
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
49
+ snd.info.channels.should == 1
50
+ snd.info.samplerate.should == 16000
51
+ snd.info.format.should == RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
52
+ end
53
+ end
54
+
55
+ it "should allow seeking" do
56
+ lambda {
57
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
58
+ snd.seek(100)
59
+ buf = snd.read(:float, 100)
60
+ buf[0].should > 0
61
+ end
62
+ }.should_not raise_error
63
+ end
64
+
65
+ it "should allow seeking in IO conformers" do
66
+ lambda {
67
+ RubyAudio::Sound.open(io_fixture('what.wav')) do |snd|
68
+ snd.seek(100)
69
+ buf = snd.read(:float, 100)
70
+ buf[0].should > 0
71
+ end
72
+ }.should_not raise_error
73
+ end
74
+
75
+ it "should raise exceptions for invalid seeks" do
76
+ lambda {
77
+ RubyAudio::Sound.open(fixture('what.wav')) {|snd| snd.seek(-1)}
78
+ }.should raise_error(RubyAudio::Error, "invalid seek")
79
+ lambda {
80
+ RubyAudio::Sound.open(fixture('what.wav')) {|snd| snd.seek(1000000)}
81
+ }.should raise_error(RubyAudio::Error, "invalid seek")
82
+ end
83
+
84
+ it "should allow reading samples from the sound" do
85
+ RubyAudio::Sound.open(fixture('what2.wav')) do |snd|
86
+ buf = snd.read(:float, 1000)
87
+ buf.size.should == 1000
88
+ buf.real_size.should == 1000
89
+ buf[999].length.should == 2
90
+ end
91
+ end
92
+
93
+ it "should allow reading samples from IO conformers" do
94
+ RubyAudio::Sound.open(io_fixture('what2.wav')) do |snd|
95
+ buf = snd.read(:float, 1000)
96
+ buf.size.should == 1000
97
+ buf.real_size.should == 1000
98
+ buf[999].length.should == 2
99
+ end
100
+ end
101
+
102
+ it "should allow reading into an existing buffer" do
103
+ buf = RubyAudio::Buffer.float(1000)
104
+ buf.real_size.should == 0
105
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
106
+ snd.read(buf)
107
+ end
108
+ buf.real_size.should == 1000
109
+ buf[99].should > 0
110
+ end
111
+
112
+ it "should allow reading into an existing buffer partially" do
113
+ buf = RubyAudio::Buffer.float(1000)
114
+ buf.real_size.should == 0
115
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
116
+ snd.read(buf, 100)
117
+ end
118
+ buf.real_size.should == 100
119
+ buf[99].should > 0
120
+ buf[100].should == nil
121
+ end
122
+
123
+ it "should allow downmixing to mono on read" do
124
+ buf = RubyAudio::Buffer.int(100, 1)
125
+ buf2 = RubyAudio::Buffer.int(100, 2)
126
+ RubyAudio::Sound.open(fixture('what2.wav'), 'r') do |snd|
127
+ snd.read(buf)
128
+ snd.seek(0)
129
+ snd.read(buf2)
130
+
131
+ f = buf2[99]
132
+ buf[99].should == (f[0] + f[1]) / 2
133
+ end
134
+ end
135
+
136
+ it "should allow upmixing from mono on read" do
137
+ buf = RubyAudio::Buffer.int(100, 1)
138
+ buf2 = RubyAudio::Buffer.int(100, 2)
139
+ RubyAudio::Sound.open(fixture('what2.wav'), 'r') do |snd|
140
+ snd.read(buf2)
141
+ snd.seek(0)
142
+ snd.read(buf)
143
+
144
+ buf2[99].should == [buf[99], buf[99]]
145
+ end
146
+ end
147
+
148
+ it "should fail read on unsupported up/downmixing" do
149
+ buf = RubyAudio::Buffer.float(100, 5)
150
+ lambda {
151
+ RubyAudio::Sound.open(fixture('what2.wav')) do |snd|
152
+ snd.read(buf)
153
+ end
154
+ }.should raise_error(RubyAudio::Error, "unsupported mix from 5 to 2")
155
+ end
156
+
157
+ it "should allow writing to a new sound" do
158
+ in_buf = RubyAudio::Buffer.float(100)
159
+ out_buf = RubyAudio::Buffer.float(100)
160
+ out_info = nil
161
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
162
+ snd.read(in_buf)
163
+ out_info = snd.info.clone
164
+ end
165
+
166
+ RubyAudio::Sound.open(fixture('temp.wav'), 'rw', out_info) do |snd|
167
+ snd.write(in_buf)
168
+ snd.seek(0)
169
+ snd.read(out_buf)
170
+ end
171
+
172
+ out_buf[50].should == in_buf[50]
173
+ end
174
+
175
+ it "should allow writing to an IO conformer" do
176
+ in_buf = RubyAudio::Buffer.float(100)
177
+ out_buf = RubyAudio::Buffer.float(100)
178
+ out_info = nil
179
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
180
+ snd.read(in_buf)
181
+ out_info = snd.info.clone
182
+ end
183
+
184
+ RubyAudio::Sound.open(io_fixture, 'rw', out_info) do |snd|
185
+ snd.write(in_buf)
186
+ snd.seek(0)
187
+ snd.read(out_buf)
188
+ end
189
+
190
+ out_buf[50].should == in_buf[50]
191
+ end
192
+
193
+ it "should allow writing to a new sound using <<" do
194
+ in_buf = RubyAudio::Buffer.float(100)
195
+ out_buf = RubyAudio::Buffer.float(100)
196
+ out_info = nil
197
+ RubyAudio::Sound.open(fixture('what.wav')) do |snd|
198
+ snd.read(in_buf)
199
+ out_info = snd.info.clone
200
+ end
201
+
202
+ RubyAudio::Sound.open(fixture('temp.wav'), 'rw', out_info) do |snd|
203
+ snd << in_buf
204
+ snd.seek(0)
205
+ snd.read(out_buf)
206
+ end
207
+
208
+ out_buf[50].should == in_buf[50]
209
+ end
210
+
211
+ it "should fail write on channel mismatch" do
212
+ buf = RubyAudio::Buffer.float(100, 5)
213
+ info = RubyAudio::SoundInfo.new :channels => 2, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
214
+ lambda {
215
+ RubyAudio::Sound.open(fixture('temp.wav'), 'w', info) do |snd|
216
+ snd.write(buf)
217
+ end
218
+ }.should raise_error(RubyAudio::Error, "channel count mismatch: 5 vs 2")
219
+ end
220
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ -f nested
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+ require 'spec/autorun'
9
+
10
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ require 'ruby-audio'
12
+
13
+ def fixture file_name
14
+ File.join(File.dirname(__FILE__), 'data', file_name)
15
+ end
16
+
17
+ def io_fixture file_name=nil
18
+ if file_name
19
+ path = fixture(file_name)
20
+ StringIO.new(File.read(path))
21
+ else
22
+ StringIO.new
23
+ end
24
+ end