ruby-audio 0.2.1 → 1.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.
- data/README.rdoc +7 -6
- data/Rakefile +14 -21
- data/ext/extconf.rb +12 -0
- data/ext/ra_buffer.c +277 -0
- data/ext/ra_buffer.h +39 -0
- data/ext/ra_sound.c +274 -0
- data/ext/ra_sound.h +37 -0
- data/ext/ra_soundinfo.c +165 -0
- data/ext/ra_soundinfo.h +25 -0
- data/ext/rubyaudio_ext.c +91 -0
- data/lib/ruby-audio.rb +4 -0
- data/lib/ruby-audio/buffer.rb +17 -0
- data/lib/ruby-audio/sound.rb +85 -0
- data/lib/ruby-audio/sound_info.rb +40 -0
- data/spec/buffer_spec.rb +50 -0
- data/spec/data/what.mp3 +0 -0
- data/{test → spec/data}/what.wav +0 -0
- data/{test → spec/data}/what2.wav +0 -0
- data/spec/sound_info_spec.rb +39 -0
- data/spec/sound_spec.rb +147 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +12 -0
- metadata +37 -31
- data/TODO +0 -5
- data/examples/load_samples.rb +0 -27
- data/examples/t.rb +0 -10
- data/ext/sndfile/extconf.rb +0 -31
- data/ext/sndfile/sndfile.i +0 -90
- data/lib/audio.rb +0 -134
- data/lib/audio/sndfile.rb +0 -156
- data/test/test_audio.rb +0 -82
- data/test/test_sndfile.rb +0 -191
data/ext/sndfile/sndfile.i
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
// TODO: sf_command,
|
2
|
-
%module "sndfile"
|
3
|
-
%{
|
4
|
-
#include <sndfile.h>
|
5
|
-
#include <narray.h>
|
6
|
-
%}
|
7
|
-
|
8
|
-
// this bit brought to you by stdio.h
|
9
|
-
#ifndef SEEK_SET
|
10
|
-
#define SEEK_SET 0 /* set file offset to offset */
|
11
|
-
#endif
|
12
|
-
#ifndef SEEK_CUR
|
13
|
-
#define SEEK_CUR 1 /* set file offset to current plus offset */
|
14
|
-
#endif
|
15
|
-
#ifndef SEEK_END
|
16
|
-
#define SEEK_END 2 /* set file offset to EOF plus offset */
|
17
|
-
#endif
|
18
|
-
|
19
|
-
%include "typemaps.i"
|
20
|
-
%typemap(out) int sf_format_check {
|
21
|
-
if ($1)
|
22
|
-
$result = Qtrue;
|
23
|
-
else
|
24
|
-
$result = Qfalse;
|
25
|
-
}
|
26
|
-
|
27
|
-
%typemap(out) sf_count_t {
|
28
|
-
$result = INT2NUM($1);
|
29
|
-
}
|
30
|
-
%typemap(in) sf_count_t {
|
31
|
-
$1 = (sf_count_t) NUM2INT($input);
|
32
|
-
}
|
33
|
-
|
34
|
-
// We have to do this or when a function like sf_get_string returns NULL it
|
35
|
-
// will raise an exception when trying to create the string.
|
36
|
-
%typemap(out) const char *{
|
37
|
-
if ($1 == 0)
|
38
|
-
$result = Qnil;
|
39
|
-
else
|
40
|
-
$result = rb_str_new2($1);
|
41
|
-
}
|
42
|
-
|
43
|
-
%typemap(in) (short *ptr, sf_count_t),
|
44
|
-
(const short *ptr, sf_count_t)
|
45
|
-
{
|
46
|
-
if (!(NA_IsNArray($input) && NA_TYPE($input) == NA_SINT))
|
47
|
-
rb_raise(rb_eArgError, "Expected NArray.sint");
|
48
|
-
$1 = NA_PTR_TYPE($input, $1_type);
|
49
|
-
$2 = NA_TOTAL($input);
|
50
|
-
}
|
51
|
-
%typemap(in) (int *ptr, sf_count_t),
|
52
|
-
(const int *ptr, sf_count_t)
|
53
|
-
{
|
54
|
-
if (!(NA_IsNArray($input) && NA_TYPE($input) == NA_LINT))
|
55
|
-
rb_raise(rb_eArgError, "Expected NArray.int");
|
56
|
-
$1 = NA_PTR_TYPE($input, $1_type);
|
57
|
-
$2 = NA_TOTAL($input);
|
58
|
-
}
|
59
|
-
%typemap(in) (float *ptr, sf_count_t),
|
60
|
-
(const float *ptr, sf_count_t)
|
61
|
-
{
|
62
|
-
if (!(NA_IsNArray($input) && NA_TYPE($input) == NA_SFLOAT))
|
63
|
-
rb_raise(rb_eArgError, "Expected NArray.sfloat");
|
64
|
-
$1 = NA_PTR_TYPE($input, $1_type);
|
65
|
-
$2 = NA_TOTAL($input);
|
66
|
-
}
|
67
|
-
%typemap(in) (double *ptr, sf_count_t),
|
68
|
-
(const double *ptr, sf_count_t)
|
69
|
-
{
|
70
|
-
if (!(NA_IsNArray($input) && NA_TYPE($input) == NA_DFLOAT))
|
71
|
-
rb_raise(rb_eArgError, "Expected NArray.float");
|
72
|
-
$1 = NA_PTR_TYPE($input, $1_type);
|
73
|
-
$2 = NA_TOTAL($input);
|
74
|
-
}
|
75
|
-
|
76
|
-
%typemap(in) (void *data, int datasize), (void *ptr, sf_count_t),
|
77
|
-
(const void *ptr, sf_count_t)
|
78
|
-
{
|
79
|
-
if (!NA_IsNArray($input))
|
80
|
-
rb_raise(rb_eArgError, "Expected NArray");
|
81
|
-
$1 = NA_STRUCT($input)->ptr;
|
82
|
-
$2 = NA_TOTAL($input) * na_sizeof[NA_TYPE($input)];
|
83
|
-
}
|
84
|
-
|
85
|
-
|
86
|
-
// sf_perror and sf_error_str are deprecated. sf_error_str probably doesn't
|
87
|
-
// work at all.
|
88
|
-
|
89
|
-
%include "sndfile.h"
|
90
|
-
// vim: filetype=c
|
data/lib/audio.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'narray'
|
3
|
-
|
4
|
-
# TODO: libsamplerate, portaudio
|
5
|
-
module Audio
|
6
|
-
|
7
|
-
# A Sound is a NArray with some audio convenience methods. It should always
|
8
|
-
# have the shape <tt>[frames,channels]</tt>. (Even when m=1)
|
9
|
-
#
|
10
|
-
# *Notice* for NArray users: because most audio libraries do not agree with
|
11
|
-
# NArray on type names, Sound.float has a different meaning than
|
12
|
-
# NArray.float. Also, the shape of a new Sound is different than you might
|
13
|
-
# expect:
|
14
|
-
# s = Sound.float(10)
|
15
|
-
# n = NArray.float(10)
|
16
|
-
# s.shape #=> [10,1]
|
17
|
-
# n.shape #=> [10]
|
18
|
-
# s.typecode #=> 4
|
19
|
-
# n.typecode #=> 5
|
20
|
-
class Sound < NArray
|
21
|
-
|
22
|
-
# Mapping from typecode to type symbols
|
23
|
-
TYPES = [nil,:char,:short,:long,:float,:double]
|
24
|
-
|
25
|
-
# typecode:: Same as NArray, or a member of TYPES
|
26
|
-
# frames:: Number of frames
|
27
|
-
# channels:: Number of channels
|
28
|
-
#
|
29
|
-
# Note that the resulting shape is always [frames,channels].
|
30
|
-
def self.new(typecode,frames,channels=1)
|
31
|
-
case typecode
|
32
|
-
when String, Symbol
|
33
|
-
typecode = TYPES.index(typecode.to_sym)
|
34
|
-
end
|
35
|
-
super(typecode,frames,channels)
|
36
|
-
end
|
37
|
-
|
38
|
-
# One of TYPES
|
39
|
-
def type
|
40
|
-
TYPES[typecode]
|
41
|
-
end
|
42
|
-
|
43
|
-
# The number of frames
|
44
|
-
def frames
|
45
|
-
self.shape[0]
|
46
|
-
end
|
47
|
-
|
48
|
-
# The number of channels
|
49
|
-
def channels
|
50
|
-
self.shape[1]
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns a new Sound with the data from channel i
|
54
|
-
def channel(i)
|
55
|
-
self[true,i]
|
56
|
-
end
|
57
|
-
|
58
|
-
# A frame, i.e. an array containing the samples at position i from each
|
59
|
-
# channel. For a two-channel sound:
|
60
|
-
# sound.frame(i) #=> [0.42, 0.12]
|
61
|
-
#
|
62
|
-
# You may prefer to do this the NArray way: s[i,false]
|
63
|
-
def frame(i)
|
64
|
-
unless (0...frames).include? i
|
65
|
-
raise IndexError, "Index out of range"
|
66
|
-
end
|
67
|
-
self[i,false]
|
68
|
-
end
|
69
|
-
|
70
|
-
# For a two-channel sound:
|
71
|
-
# s.set_frame(i,[0.42,0.24])
|
72
|
-
# s.set_frame(i,0.42) #=> s.set_frame(i,[0.42,0.42])
|
73
|
-
#
|
74
|
-
# You may prefer to do this the NArray way: s[i,false] = val
|
75
|
-
def set_frame(i,val)
|
76
|
-
self[i,false] = val
|
77
|
-
end
|
78
|
-
|
79
|
-
def each_frame
|
80
|
-
frames.times do |i|
|
81
|
-
yield frame(i)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Return a Sound with the channels interleaved.
|
86
|
-
# Sound[[0,1],[2,3]].interleaved #=> Sound[[0,2,1,3]]
|
87
|
-
def interleave
|
88
|
-
self.transpose(1,0).reshape!(self.size,1)
|
89
|
-
end
|
90
|
-
alias_method :interleaved, :interleave
|
91
|
-
|
92
|
-
# Fill this Sound's channels with deinterleaved data.
|
93
|
-
# Sound[[0,0],[0,0]].interleaved = NArray[0,2,1,3] #=> Sound[[0,1],[2,3]]
|
94
|
-
def interleave=(o)
|
95
|
-
self[] = o.reshape(channels,frames).transpose(1,0)
|
96
|
-
self
|
97
|
-
end
|
98
|
-
alias_method :interleaved=, :interleave=
|
99
|
-
|
100
|
-
|
101
|
-
# Creates a new Sound with the specified number of channels from the
|
102
|
-
# interleaved data in narray. narray should be evenly divisible by
|
103
|
-
# channels.
|
104
|
-
def self.deinterleave(narray,channels)
|
105
|
-
unless narray.size % channels == 0
|
106
|
-
raise ArgumentError, "narray not evenly divisible by channels"
|
107
|
-
end
|
108
|
-
frames = narray.size/channels
|
109
|
-
s = Sound.new(narray.typecode,frames,channels)
|
110
|
-
s.interleaved = narray
|
111
|
-
s
|
112
|
-
end
|
113
|
-
def deinterleave(channels)
|
114
|
-
self.class.deinterleave(self,channels)
|
115
|
-
end
|
116
|
-
def deinterleave!(channels)
|
117
|
-
s = deinterleave(channels)
|
118
|
-
reshape!(*s.shape)
|
119
|
-
self[] = s
|
120
|
-
end
|
121
|
-
|
122
|
-
%w{char short long float double}.each_with_index do |t,i|
|
123
|
-
eval "def self.#{t}(frames,channels=1); self.new(#{i+1},frames,channels); end"
|
124
|
-
end
|
125
|
-
|
126
|
-
# alias class methods
|
127
|
-
class << self
|
128
|
-
alias_method :byte, :char
|
129
|
-
alias_method :sint, :short
|
130
|
-
alias_method :int, :long
|
131
|
-
alias_method :sfloat, :float
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
data/lib/audio/sndfile.rb
DELETED
@@ -1,156 +0,0 @@
|
|
1
|
-
require 'audio'
|
2
|
-
require 'sndfile.so'
|
3
|
-
|
4
|
-
module Audio
|
5
|
-
# libsndfile[http://www.mega-nerd.com/libsndfile/]
|
6
|
-
#
|
7
|
-
# = Synopsis
|
8
|
-
# require 'audio/sndfile'
|
9
|
-
#
|
10
|
-
# Audio::Soundfile.open('chunky_bacon.wav') do |sf|
|
11
|
-
# sound = sf.readf_float(sf.frames)
|
12
|
-
# puts "Maximum amplitude: #{sound.abs.max}"
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# = Details
|
16
|
-
# Refer to the libsndfile api[http://www.mega-nerd.com/libsndfile/api.html].
|
17
|
-
#
|
18
|
-
# Usage is quite straightforward: drop the +sf_+ prefix, omit the
|
19
|
-
# <tt>SNDFILE*</tt> parameter, and use Sound or Numeric instead of
|
20
|
-
# (pointer, size) pairs. So, if you have a Soundfile object named +sf+, then
|
21
|
-
# sf_read_float(SNDFILE, float *ptr, sf_count_t items)
|
22
|
-
# becomes
|
23
|
-
# buf = Sound.float(items)
|
24
|
-
# sf.read_float(buf)
|
25
|
-
# or
|
26
|
-
# buf = sf.read_float(items) # creates a new Sound
|
27
|
-
#
|
28
|
-
# Exceptions to this pattern are documented below.
|
29
|
-
#
|
30
|
-
# Constants are accessed as <tt>Soundfile::SF_FORMAT_WAV</tt>
|
31
|
-
class Soundfile
|
32
|
-
# SF_INFO
|
33
|
-
attr :info
|
34
|
-
attr_reader :mode
|
35
|
-
|
36
|
-
# mode:: One of %w{r w rw}
|
37
|
-
# info:: Instance of SF_INFO or nil
|
38
|
-
def initialize(path, mode='r', info=nil)
|
39
|
-
if info.nil?
|
40
|
-
info = SF_INFO.new
|
41
|
-
end
|
42
|
-
|
43
|
-
modes = {:r => SFM_READ, :w => SFM_WRITE, :rw => SFM_RDWR}
|
44
|
-
unless Numeric === mode
|
45
|
-
mode = modes[mode.to_sym]
|
46
|
-
end
|
47
|
-
unless [SFM_READ, SFM_WRITE, SFM_RDWR].include? mode
|
48
|
-
raise ArgumentError, "Invalid mode"
|
49
|
-
end
|
50
|
-
|
51
|
-
sf = Sndfile.sf_open(path.to_s, mode, info)
|
52
|
-
@sf = sf
|
53
|
-
@info = info
|
54
|
-
@mode = modes.invert[mode]
|
55
|
-
if block_given?
|
56
|
-
yield self
|
57
|
-
self.close
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class << self
|
62
|
-
alias_method :open, :new
|
63
|
-
end
|
64
|
-
|
65
|
-
def frames
|
66
|
-
@info.frames
|
67
|
-
end
|
68
|
-
def samplerate
|
69
|
-
@info.samplerate
|
70
|
-
end
|
71
|
-
def channels
|
72
|
-
@info.channels
|
73
|
-
end
|
74
|
-
def format
|
75
|
-
@info.format
|
76
|
-
end
|
77
|
-
def sections
|
78
|
-
@info.sections
|
79
|
-
end
|
80
|
-
def seekable
|
81
|
-
@info.seekable
|
82
|
-
end
|
83
|
-
|
84
|
-
# The following are equivalent:
|
85
|
-
# sf_format_check(info) /* C */
|
86
|
-
# sf.format_check # ruby
|
87
|
-
def format_check
|
88
|
-
Sndfile.sf_format_check(@info)
|
89
|
-
end
|
90
|
-
|
91
|
-
TYPES = [nil,:char,:short,:int,:float,:double] #:nodoc:
|
92
|
-
|
93
|
-
# Automagic read method. Type is autodetected.
|
94
|
-
def read(na)
|
95
|
-
sym = "read_#{TYPES[na.typecode]}".to_sym
|
96
|
-
self.send sym, na
|
97
|
-
end
|
98
|
-
|
99
|
-
# Automagic write method. Type is autodetected.
|
100
|
-
def write(na)
|
101
|
-
sym = "write_#{TYPES[na.typecode]}".to_sym
|
102
|
-
self.send sym, na
|
103
|
-
end
|
104
|
-
|
105
|
-
%w{read readf}.each do |r|
|
106
|
-
%w{short int float double}.each do |t|
|
107
|
-
tc = TYPES.index(t.to_sym)
|
108
|
-
c = ', channels' if r == 'readf'
|
109
|
-
cmd = "#{r}_#{t}"
|
110
|
-
eval <<-EOF
|
111
|
-
def #{cmd}(arg)
|
112
|
-
if Numeric === arg
|
113
|
-
na = NArray.new(#{tc}, arg#{c})
|
114
|
-
n = Sndfile.sf_#{cmd}(@sf, na)
|
115
|
-
Sound.deinterleave(na, channels)
|
116
|
-
else
|
117
|
-
na = arg
|
118
|
-
n = Sndfile.sf_#{cmd}(@sf, na)
|
119
|
-
na.deinterleave!(channels)
|
120
|
-
n
|
121
|
-
end
|
122
|
-
end
|
123
|
-
EOF
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
%w{write writef}.each do |w|
|
128
|
-
%w{short int float double}.each do |t|
|
129
|
-
cmd = "#{w}_#{t}"
|
130
|
-
eval <<-EOF
|
131
|
-
def #{cmd}(sound)
|
132
|
-
raise "Format check failed: \#{perror}" unless format_check
|
133
|
-
Sndfile.sf_#{cmd}(@sf, sound.interleave)
|
134
|
-
end
|
135
|
-
EOF
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def method_missing(name, *args) #:nodoc:
|
140
|
-
begin
|
141
|
-
Sndfile.send "sf_#{name}".to_sym, @sf, *args
|
142
|
-
rescue NameError
|
143
|
-
super
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def self.const_missing(sym) #:nodoc:
|
148
|
-
begin
|
149
|
-
Sndfile.const_get(sym)
|
150
|
-
rescue NameError
|
151
|
-
super
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
data/test/test_audio.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'audio'
|
3
|
-
|
4
|
-
class AudioTest < Test::Unit::TestCase
|
5
|
-
include Audio
|
6
|
-
def test_sound
|
7
|
-
%w{char short long float double}.each do |t|
|
8
|
-
eval "s = Sound.#{t}(10); assert_equal :#{t}, s.type"
|
9
|
-
end
|
10
|
-
s = Sound.float(6).indgen!
|
11
|
-
assert_equal [6,1], s.shape
|
12
|
-
assert_equal 1, s.channels
|
13
|
-
assert_equal 6, s.frames
|
14
|
-
s.reshape!(3,2)
|
15
|
-
assert_equal 2, s.channels
|
16
|
-
assert_equal 3, s.frames
|
17
|
-
assert_equal [[0.0,3.0,1.0,4.0,2.0,5.0]], s.interleave.to_a
|
18
|
-
assert_equal 6, s.size
|
19
|
-
|
20
|
-
s = Sound.byte(10); assert_equal :char, s.type
|
21
|
-
s = Sound.sint(10); assert_equal :short, s.type
|
22
|
-
s = Sound.int(10); assert_equal :long, s.type
|
23
|
-
s = Sound.sfloat(10); assert_equal :float, s.type
|
24
|
-
|
25
|
-
s = Sound.sfloat(10)
|
26
|
-
assert_equal 10, s.size
|
27
|
-
assert_equal 10, s.frames
|
28
|
-
assert_equal 1, s.channels
|
29
|
-
assert_equal :float, s.type
|
30
|
-
assert_kind_of NArray, s
|
31
|
-
s.channels.times do |i|
|
32
|
-
c = s.channel(i)
|
33
|
-
assert_kind_of NArray, c
|
34
|
-
assert_instance_of Sound, c
|
35
|
-
assert_equal 10, c.size
|
36
|
-
end
|
37
|
-
|
38
|
-
s = Sound.double(10,2)
|
39
|
-
assert_equal 2, s.channels
|
40
|
-
assert_equal 10, s.frames
|
41
|
-
|
42
|
-
c = s.channel(1)
|
43
|
-
assert_instance_of Sound, c
|
44
|
-
assert_equal 10, c.size
|
45
|
-
|
46
|
-
s.each_frame do |a|
|
47
|
-
assert_instance_of Sound, a
|
48
|
-
assert_kind_of Numeric, a[0]
|
49
|
-
assert_kind_of Numeric, a[1]
|
50
|
-
end
|
51
|
-
|
52
|
-
# test new
|
53
|
-
s = Sound.new(4,10)
|
54
|
-
assert_equal [10,1], s.shape
|
55
|
-
assert_equal 10, s.size
|
56
|
-
assert_equal 4, s.typecode
|
57
|
-
assert_equal :float, s.type
|
58
|
-
assert_equal 10, s.frames
|
59
|
-
assert_equal 1, s.channels
|
60
|
-
|
61
|
-
s = Sound.new(:long,10)
|
62
|
-
assert_equal [10,1], s.shape
|
63
|
-
assert_equal 10, s.size
|
64
|
-
assert_equal 3, s.typecode
|
65
|
-
assert_equal :long, s.type
|
66
|
-
assert_equal 10, s.frames
|
67
|
-
assert_equal 1, s.channels
|
68
|
-
end
|
69
|
-
|
70
|
-
def test_interleave
|
71
|
-
s = Sound.float(2,2).indgen!
|
72
|
-
i = s.interleave
|
73
|
-
assert_equal s[0,0], i[0]
|
74
|
-
assert_equal s[0,1], i[1]
|
75
|
-
|
76
|
-
s2 = Sound.deinterleave(i,2)
|
77
|
-
assert_equal s, s2
|
78
|
-
|
79
|
-
s2.interleave = i
|
80
|
-
assert_equal s, s2
|
81
|
-
end
|
82
|
-
end
|