ruby-audio-heroku 1.6.1
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.
- data/LICENSE +340 -0
- data/README.rdoc +38 -0
- data/Rakefile +81 -0
- data/ext/rubyaudio_ext/extconf.rb +32 -0
- data/ext/rubyaudio_ext/ra_buffer.c +342 -0
- data/ext/rubyaudio_ext/ra_buffer.h +41 -0
- data/ext/rubyaudio_ext/ra_sound.c +403 -0
- data/ext/rubyaudio_ext/ra_sound.h +38 -0
- data/ext/rubyaudio_ext/ra_soundinfo.c +165 -0
- data/ext/rubyaudio_ext/ra_soundinfo.h +25 -0
- data/ext/rubyaudio_ext/rubyaudio_ext.c +92 -0
- data/ext/rubyaudio_ext/vendor/libsndfile/include/sndfile.h +666 -0
- data/lib/ruby-audio/buffer.rb +17 -0
- data/lib/ruby-audio/sound.rb +85 -0
- data/lib/ruby-audio/sound_info.rb +83 -0
- data/lib/ruby-audio.rb +10 -0
- data/ruby-audio-heroku.gemspec +22 -0
- data/spec/buffer_spec.rb +107 -0
- data/spec/data/what.mp3 +0 -0
- data/spec/data/what.wav +0 -0
- data/spec/data/what2.wav +0 -0
- data/spec/sound_info_spec.rb +55 -0
- data/spec/sound_spec.rb +220 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +24 -0
- metadata +84 -0
@@ -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,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
|
data/spec/buffer_spec.rb
ADDED
@@ -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
|
data/spec/data/what.mp3
ADDED
Binary file
|
data/spec/data/what.wav
ADDED
Binary file
|
data/spec/data/what2.wav
ADDED
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
|
data/spec/sound_spec.rb
ADDED
@@ -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
data/spec/spec_helper.rb
ADDED
@@ -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
|