ruby-audio 1.5.0-x86-mingw32 → 1.6.0-x86-mingw32
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/ext/rubyaudio_ext/extconf.rb +2 -2
- data/ext/rubyaudio_ext/ra_buffer.c +23 -0
- data/ext/rubyaudio_ext/ra_buffer.h +1 -0
- data/ext/rubyaudio_ext/ra_sound.c +136 -239
- data/ext/rubyaudio_ext/ra_sound.h +1 -0
- data/lib/1.8/rubyaudio_ext.so +0 -0
- data/lib/1.9/rubyaudio_ext.so +0 -0
- data/lib/ruby-audio/buffer.rb +0 -8
- data/lib/ruby-audio/sound_info.rb +18 -1
- data/ruby-audio.gemspec +1 -1
- data/spec/buffer_spec.rb +6 -1
- data/spec/sound_info_spec.rb +6 -0
- data/spec/sound_spec.rb +64 -26
- data/spec/spec_helper.rb +14 -1
- metadata +4 -9
@@ -12,8 +12,8 @@ else
|
|
12
12
|
sndfile_lib = 'sndfile'
|
13
13
|
end
|
14
14
|
|
15
|
-
INCLUDE_DIRS = ['/opt/local/include', '/usr/local/include', 'C:/Program Files/Mega-Nerd/libsndfile/include', 'C:/Program Files
|
16
|
-
LIB_DIRS = ['/opt/local/lib', '/usr/local/lib', 'C:/Program Files/Mega-Nerd/libsndfile', 'C:/Program Files
|
15
|
+
INCLUDE_DIRS = ['/opt/local/include', '/usr/local/include', 'C:/Program Files (x86)/Mega-Nerd/libsndfile/include', 'C:/Program Files/Mega-Nerd/libsndfile/include']
|
16
|
+
LIB_DIRS = ['/opt/local/lib', '/usr/local/lib', 'C:/Program Files (x86)/Mega-Nerd/libsndfile/bin', 'C:/Program Files/Mega-Nerd/libsndfile/bin']
|
17
17
|
|
18
18
|
# libsndfile requirements
|
19
19
|
find_header 'sndfile.h', *INCLUDE_DIRS
|
@@ -15,6 +15,7 @@ extern VALUE eRubyAudioError;
|
|
15
15
|
void Init_ra_buffer() {
|
16
16
|
VALUE mRubyAudio = rb_define_module("RubyAudio");
|
17
17
|
VALUE cRABuffer = rb_define_class_under(mRubyAudio, "CBuffer", rb_cObject);
|
18
|
+
rb_include_module(cRABuffer, rb_mEnumerable);
|
18
19
|
rb_define_alloc_func(cRABuffer, ra_buffer_allocate);
|
19
20
|
rb_define_method(cRABuffer, "initialize", ra_buffer_init, -1);
|
20
21
|
rb_define_method(cRABuffer, "initialize_copy", ra_buffer_init_copy, 1);
|
@@ -23,6 +24,7 @@ void Init_ra_buffer() {
|
|
23
24
|
rb_define_method(cRABuffer, "real_size", ra_buffer_real_size, 0);
|
24
25
|
rb_define_method(cRABuffer, "real_size=", ra_buffer_real_size_set, 1);
|
25
26
|
rb_define_method(cRABuffer, "type", ra_buffer_type, 0);
|
27
|
+
rb_define_method(cRABuffer, "each", ra_buffer_each, 0);
|
26
28
|
rb_define_method(cRABuffer, "[]", ra_buffer_aref, 1);
|
27
29
|
rb_define_method(cRABuffer, "[]=", ra_buffer_aset, 2);
|
28
30
|
|
@@ -210,6 +212,27 @@ static VALUE ra_buffer_type(VALUE self) {
|
|
210
212
|
}
|
211
213
|
}
|
212
214
|
|
215
|
+
/*
|
216
|
+
* call-seq:
|
217
|
+
* buf.each {|frame| block } => buf
|
218
|
+
* buf.each => anEnumerator
|
219
|
+
*
|
220
|
+
* Iterates through each frame in the buffer. Each frame is either a number or
|
221
|
+
* an array of numbers if there are multiple channels.
|
222
|
+
*/
|
223
|
+
static VALUE ra_buffer_each(VALUE self) {
|
224
|
+
RA_BUFFER *buf;
|
225
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
226
|
+
|
227
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
228
|
+
|
229
|
+
long i;
|
230
|
+
for(i = 0; i < buf->real_size; i++) {
|
231
|
+
rb_yield(ra_buffer_aref(self, LONG2FIX(i)));
|
232
|
+
}
|
233
|
+
return self;
|
234
|
+
}
|
235
|
+
|
213
236
|
/*
|
214
237
|
* call-seq:
|
215
238
|
* buf[integer] => frame
|
@@ -32,6 +32,7 @@ static VALUE ra_buffer_size(VALUE self);
|
|
32
32
|
static VALUE ra_buffer_real_size(VALUE self);
|
33
33
|
static VALUE ra_buffer_real_size_set(VALUE self, VALUE new_real_size);
|
34
34
|
static VALUE ra_buffer_type(VALUE self);
|
35
|
+
static VALUE ra_buffer_each(VALUE self);
|
35
36
|
static VALUE ra_buffer_aref(VALUE self, VALUE index);
|
36
37
|
static VALUE ra_buffer_index_get(RA_BUFFER *buf, long i);
|
37
38
|
static VALUE ra_buffer_aset(VALUE self, VALUE index, VALUE val);
|
@@ -1,6 +1,11 @@
|
|
1
1
|
#include "ra_sound.h"
|
2
2
|
|
3
3
|
extern VALUE eRubyAudioError;
|
4
|
+
ID id_size;
|
5
|
+
ID id_seek;
|
6
|
+
ID id_read;
|
7
|
+
ID id_write;
|
8
|
+
ID id_tell;
|
4
9
|
|
5
10
|
/*
|
6
11
|
* Class <code>CSound</code> is a very light wrapper around the
|
@@ -19,6 +24,13 @@ void Init_ra_sound() {
|
|
19
24
|
rb_define_method(cRASound, "<<", ra_sound_addbuf, 1);
|
20
25
|
rb_define_method(cRASound, "close", ra_sound_close, 0);
|
21
26
|
rb_define_method(cRASound, "closed?", ra_sound_closed, 0);
|
27
|
+
|
28
|
+
// Get refs to commonly used symbols and ids
|
29
|
+
id_size = rb_intern("size");
|
30
|
+
id_seek = rb_intern("seek");
|
31
|
+
id_read = rb_intern("read");
|
32
|
+
id_write = rb_intern("write");
|
33
|
+
id_tell = rb_intern("tell");
|
22
34
|
}
|
23
35
|
|
24
36
|
static VALUE ra_sound_allocate(VALUE klass) {
|
@@ -31,6 +43,7 @@ static VALUE ra_sound_allocate(VALUE klass) {
|
|
31
43
|
static void ra_sound_mark(RA_SOUND *snd) {
|
32
44
|
if(snd) {
|
33
45
|
rb_gc_mark(snd->info);
|
46
|
+
if(snd->vio_source) rb_gc_mark(snd->vio_source);
|
34
47
|
}
|
35
48
|
}
|
36
49
|
|
@@ -56,15 +69,56 @@ static VALUE ra_sound_s_open(int argc, VALUE *argv, VALUE klass) {
|
|
56
69
|
return rb_ensure(rb_yield, obj, ra_sound_close_safe, obj);
|
57
70
|
}
|
58
71
|
|
72
|
+
static sf_count_t ra_vir_size(void *user_data) {
|
73
|
+
VALUE io = (VALUE)user_data;
|
74
|
+
return NUM2OFFT(rb_funcall(io, id_size, 0));
|
75
|
+
}
|
76
|
+
|
77
|
+
static sf_count_t ra_vir_seek(sf_count_t offset, int whence, void *user_data) {
|
78
|
+
VALUE io = (VALUE)user_data;
|
79
|
+
rb_funcall(io, id_seek, 2, OFFT2NUM(offset), INT2FIX(whence));
|
80
|
+
return NUM2OFFT(rb_funcall(io, id_tell, 0));
|
81
|
+
}
|
82
|
+
|
83
|
+
static sf_count_t ra_vir_read(void *ptr, sf_count_t count, void *user_data) {
|
84
|
+
VALUE io = (VALUE)user_data;
|
85
|
+
if(count <= 0) return 0;
|
86
|
+
|
87
|
+
// It would be nice if we could create a fake buffer string with ptr as the target
|
88
|
+
VALUE read = rb_funcall(io, id_read, 1, OFFT2NUM(count));
|
89
|
+
sf_count_t len = RSTRING_LEN(read);
|
90
|
+
memcpy(ptr, RSTRING_PTR(read), RSTRING_LEN(read));
|
91
|
+
return len;
|
92
|
+
}
|
93
|
+
|
94
|
+
static sf_count_t ra_vir_write(const void *ptr, sf_count_t count, void *user_data) {
|
95
|
+
VALUE io = (VALUE)user_data;
|
96
|
+
if(count <= 0) return 0;
|
97
|
+
|
98
|
+
// It would be nice if we could create a fake string with ptr as the source
|
99
|
+
VALUE str = rb_str_new(ptr, count);
|
100
|
+
VALUE wrote = rb_funcall(io, id_write, 1, str);
|
101
|
+
return NUM2OFFT(wrote);
|
102
|
+
}
|
103
|
+
|
104
|
+
static sf_count_t ra_vir_tell(void *user_data) {
|
105
|
+
VALUE io = (VALUE)user_data;
|
106
|
+
return NUM2OFFT(rb_funcall(io, id_tell, 0));
|
107
|
+
}
|
108
|
+
|
59
109
|
/*
|
60
110
|
* call-seq:
|
61
111
|
* CSound.new(path, mode, info) => snd
|
112
|
+
* CSound.new(io, mode, info) => snd
|
62
113
|
*
|
63
114
|
* Returns a new <code>CSound</code> object for the audio file at the given path
|
64
|
-
* with the given mode. Valid modes
|
65
|
-
* <code>"rw"</code>.
|
115
|
+
* or using the given fixed-length IO-like object with the given mode. Valid modes
|
116
|
+
* are <code>"r"</code>, <code>"w"</code>, or <code>"rw"</code>.
|
117
|
+
* <code>StringIO</code> is the only valid IO-like object in the standard library,
|
118
|
+
* although any object that implements <code>size</code>, <code>seek</code>,
|
119
|
+
* <code>read</code>, <code>write</code>, and <code>tell</code> will work.
|
66
120
|
*/
|
67
|
-
static VALUE ra_sound_init(VALUE self, VALUE
|
121
|
+
static VALUE ra_sound_init(VALUE self, VALUE source, VALUE mode, VALUE info) {
|
68
122
|
RA_SOUND *snd;
|
69
123
|
Data_Get_Struct(self, RA_SOUND, snd);
|
70
124
|
|
@@ -79,10 +133,25 @@ static VALUE ra_sound_init(VALUE self, VALUE path, VALUE mode, VALUE info) {
|
|
79
133
|
snd->info = info;
|
80
134
|
|
81
135
|
// Open sound file
|
82
|
-
const char *p = StringValueCStr(path);
|
83
136
|
SF_INFO *sf_info;
|
84
137
|
Data_Get_Struct(info, SF_INFO, sf_info);
|
85
|
-
|
138
|
+
if(TYPE(source) == T_STRING) {
|
139
|
+
// Open sound file at the path
|
140
|
+
const char *p = StringValueCStr(source);
|
141
|
+
snd->snd = sf_open(p, snd->mode, sf_info);
|
142
|
+
} else {
|
143
|
+
// Check if the source implements the right methods
|
144
|
+
if(!rb_respond_to(source, id_size)) rb_raise(eRubyAudioError, "source does not implement size");
|
145
|
+
if(!rb_respond_to(source, id_seek)) rb_raise(eRubyAudioError, "source does not implement seek");
|
146
|
+
if(!rb_respond_to(source, id_read)) rb_raise(eRubyAudioError, "source does not implement read");
|
147
|
+
if(!rb_respond_to(source, id_write)) rb_raise(eRubyAudioError, "source does not implement write");
|
148
|
+
if(!rb_respond_to(source, id_tell)) rb_raise(eRubyAudioError, "source does not implement tell");
|
149
|
+
|
150
|
+
// Open sound using the virtual IO API
|
151
|
+
snd->vio_source = source;
|
152
|
+
SF_VIRTUAL_IO vir_io = {ra_vir_size, ra_vir_seek, ra_vir_read, ra_vir_write, ra_vir_tell};
|
153
|
+
snd->snd = sf_open_virtual(&vir_io, snd->mode, sf_info, (void*)source);
|
154
|
+
}
|
86
155
|
if(snd->snd == NULL) rb_raise(eRubyAudioError, sf_strerror(snd->snd));
|
87
156
|
snd->closed = 0;
|
88
157
|
|
@@ -127,241 +196,69 @@ static VALUE ra_sound_seek(VALUE self, VALUE frames, VALUE whence) {
|
|
127
196
|
return INT2FIX(0);
|
128
197
|
}
|
129
198
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
read
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
static void ra_sound_read_int(RA_SOUND *snd, RA_BUFFER *buf, sf_count_t frames) {
|
190
|
-
static int temp[1024];
|
191
|
-
int temp_len = 1024;
|
192
|
-
int *data = (int*)buf->data;
|
193
|
-
int mix_sum;
|
194
|
-
|
195
|
-
// Get info struct
|
196
|
-
SF_INFO *info;
|
197
|
-
Data_Get_Struct(snd->info, SF_INFO, info);
|
198
|
-
|
199
|
-
// Up/Downmix based on channel matching
|
200
|
-
sf_count_t read = 0, r, amount;
|
201
|
-
int i, k;
|
202
|
-
if(buf->channels == info->channels) { // Simply read data without mix
|
203
|
-
read = sf_readf_int(snd->snd, data, frames);
|
204
|
-
} else if(buf->channels == 1) { // Downmix to mono
|
205
|
-
sf_count_t max = temp_len / info->channels;
|
206
|
-
int channels;
|
207
|
-
|
208
|
-
while(read < frames) {
|
209
|
-
// Calculate # of frames to read
|
210
|
-
amount = frames - read;
|
211
|
-
if(amount > max) amount = max;
|
212
|
-
|
213
|
-
r = sf_readf_int(snd->snd, temp, amount);
|
214
|
-
if(r == 0) break;
|
215
|
-
|
216
|
-
// Mix channels together by averaging all channels and store to buffer
|
217
|
-
for(i = 0; i < r; i++) {
|
218
|
-
mix_sum = 0;
|
219
|
-
for(k = 0; k < info->channels; k++) mix_sum += temp[i * info->channels + k];
|
220
|
-
data[read] = mix_sum/info->channels;
|
221
|
-
read++;
|
222
|
-
}
|
223
|
-
}
|
224
|
-
} else if(info->channels == 1) { // Upmix from mono by copying channel
|
225
|
-
while(read < frames) {
|
226
|
-
// Calculate # of frames to read
|
227
|
-
amount = frames - read;
|
228
|
-
if(amount > temp_len) amount = temp_len;
|
229
|
-
|
230
|
-
r = sf_readf_int(snd->snd, temp, amount);
|
231
|
-
if(r == 0) break;
|
232
|
-
|
233
|
-
// Write every frame channel times to the buffer
|
234
|
-
for(i = 0; i < r; i++) {
|
235
|
-
for(k = 0; k < buf->channels; k++) {
|
236
|
-
data[read * buf->channels + k] = temp[i];
|
237
|
-
}
|
238
|
-
read++;
|
239
|
-
}
|
240
|
-
}
|
241
|
-
} else {
|
242
|
-
rb_raise(eRubyAudioError, "unsupported mix from %d to %d", buf->channels, info->channels);
|
243
|
-
}
|
244
|
-
|
245
|
-
buf->real_size = read;
|
246
|
-
}
|
247
|
-
|
248
|
-
static void ra_sound_read_float(RA_SOUND *snd, RA_BUFFER *buf, sf_count_t frames) {
|
249
|
-
static float temp[1024];
|
250
|
-
int temp_len = 1024;
|
251
|
-
float *data = (float*)buf->data;
|
252
|
-
float mix_sum;
|
253
|
-
|
254
|
-
// Get info struct
|
255
|
-
SF_INFO *info;
|
256
|
-
Data_Get_Struct(snd->info, SF_INFO, info);
|
257
|
-
|
258
|
-
// Up/Downmix based on channel matching
|
259
|
-
sf_count_t read = 0, r, amount;
|
260
|
-
int i, k;
|
261
|
-
if(buf->channels == info->channels) { // Simply read data without mix
|
262
|
-
read = sf_readf_float(snd->snd, data, frames);
|
263
|
-
} else if(buf->channels == 1) { // Downmix to mono
|
264
|
-
sf_count_t max = temp_len / info->channels;
|
265
|
-
int channels;
|
266
|
-
|
267
|
-
while(read < frames) {
|
268
|
-
// Calculate # of frames to read
|
269
|
-
amount = frames - read;
|
270
|
-
if(amount > max) amount = max;
|
271
|
-
|
272
|
-
r = sf_readf_float(snd->snd, temp, amount);
|
273
|
-
if(r == 0) break;
|
274
|
-
|
275
|
-
// Mix channels together by averaging all channels and store to buffer
|
276
|
-
for(i = 0; i < r; i++) {
|
277
|
-
mix_sum = 0;
|
278
|
-
for(k = 0; k < info->channels; k++) mix_sum += temp[i * info->channels + k];
|
279
|
-
data[read] = mix_sum/info->channels;
|
280
|
-
read++;
|
281
|
-
}
|
282
|
-
}
|
283
|
-
} else if(info->channels == 1) { // Upmix from mono by copying channel
|
284
|
-
while(read < frames) {
|
285
|
-
// Calculate # of frames to read
|
286
|
-
amount = frames - read;
|
287
|
-
if(amount > temp_len) amount = temp_len;
|
288
|
-
|
289
|
-
r = sf_readf_float(snd->snd, temp, amount);
|
290
|
-
if(r == 0) break;
|
291
|
-
|
292
|
-
// Write every frame channel times to the buffer
|
293
|
-
for(i = 0; i < r; i++) {
|
294
|
-
for(k = 0; k < buf->channels; k++) {
|
295
|
-
data[read * buf->channels + k] = temp[i];
|
296
|
-
}
|
297
|
-
read++;
|
298
|
-
}
|
299
|
-
}
|
300
|
-
} else {
|
301
|
-
rb_raise(eRubyAudioError, "unsupported mix from %d to %d", buf->channels, info->channels);
|
302
|
-
}
|
303
|
-
|
304
|
-
buf->real_size = read;
|
305
|
-
}
|
306
|
-
|
307
|
-
static void ra_sound_read_double(RA_SOUND *snd, RA_BUFFER *buf, sf_count_t frames) {
|
308
|
-
static double temp[1024];
|
309
|
-
int temp_len = 1024;
|
310
|
-
double *data = (double*)buf->data;
|
311
|
-
double mix_sum;
|
312
|
-
|
313
|
-
// Get info struct
|
314
|
-
SF_INFO *info;
|
315
|
-
Data_Get_Struct(snd->info, SF_INFO, info);
|
316
|
-
|
317
|
-
// Up/Downmix based on channel matching
|
318
|
-
sf_count_t read = 0, r, amount;
|
319
|
-
int i, k;
|
320
|
-
if(buf->channels == info->channels) { // Simply read data without mix
|
321
|
-
read = sf_readf_double(snd->snd, data, frames);
|
322
|
-
} else if(buf->channels == 1) { // Downmix to mono
|
323
|
-
sf_count_t max = temp_len / info->channels;
|
324
|
-
int channels;
|
325
|
-
|
326
|
-
while(read < frames) {
|
327
|
-
// Calculate # of frames to read
|
328
|
-
amount = frames - read;
|
329
|
-
if(amount > max) amount = max;
|
330
|
-
|
331
|
-
r = sf_readf_double(snd->snd, temp, amount);
|
332
|
-
if(r == 0) break;
|
333
|
-
|
334
|
-
// Mix channels together by averaging all channels and store to buffer
|
335
|
-
for(i = 0; i < r; i++) {
|
336
|
-
mix_sum = 0;
|
337
|
-
for(k = 0; k < info->channels; k++) mix_sum += temp[i * info->channels + k];
|
338
|
-
data[read] = mix_sum/info->channels;
|
339
|
-
read++;
|
340
|
-
}
|
341
|
-
}
|
342
|
-
} else if(info->channels == 1) { // Upmix from mono by copying channel
|
343
|
-
while(read < frames) {
|
344
|
-
// Calculate # of frames to read
|
345
|
-
amount = frames - read;
|
346
|
-
if(amount > temp_len) amount = temp_len;
|
347
|
-
|
348
|
-
r = sf_readf_double(snd->snd, temp, amount);
|
349
|
-
if(r == 0) break;
|
350
|
-
|
351
|
-
// Write every frame channel times to the buffer
|
352
|
-
for(i = 0; i < r; i++) {
|
353
|
-
for(k = 0; k < buf->channels; k++) {
|
354
|
-
data[read * buf->channels + k] = temp[i];
|
355
|
-
}
|
356
|
-
read++;
|
357
|
-
}
|
358
|
-
}
|
359
|
-
} else {
|
360
|
-
rb_raise(eRubyAudioError, "unsupported mix from %d to %d", buf->channels, info->channels);
|
361
|
-
}
|
362
|
-
|
363
|
-
buf->real_size = read;
|
199
|
+
#define DEFINE_RA_SOUND_READ_TYPE(itype) \
|
200
|
+
static void ra_sound_read_##itype(RA_SOUND *snd, RA_BUFFER *buf, sf_count_t frames) { \
|
201
|
+
static itype temp[1024]; \
|
202
|
+
int temp_len = 1024; \
|
203
|
+
itype *data = (itype*)buf->data; \
|
204
|
+
itype mix_sum; \
|
205
|
+
\
|
206
|
+
/* Get info struct */ \
|
207
|
+
SF_INFO *info; \
|
208
|
+
Data_Get_Struct(snd->info, SF_INFO, info); \
|
209
|
+
\
|
210
|
+
/* Up/Downmix based on channel matching */ \
|
211
|
+
sf_count_t read = 0, r, amount; \
|
212
|
+
int i, k; \
|
213
|
+
if(buf->channels == info->channels) { /* Simply read data without mix */ \
|
214
|
+
read = sf_readf_##itype(snd->snd, data, frames); \
|
215
|
+
} else if(buf->channels == 1) { /* Downmix to mono */ \
|
216
|
+
sf_count_t max = temp_len / info->channels; \
|
217
|
+
int channels; \
|
218
|
+
\
|
219
|
+
while(read < frames) { \
|
220
|
+
/* Calculate # of frames to read */ \
|
221
|
+
amount = frames - read; \
|
222
|
+
if(amount > max) amount = max; \
|
223
|
+
\
|
224
|
+
r = sf_readf_##itype(snd->snd, temp, amount); \
|
225
|
+
if(r == 0) break; \
|
226
|
+
\
|
227
|
+
/* Mix channels together by averaging all channels and store to buffer */ \
|
228
|
+
for(i = 0; i < r; i++) { \
|
229
|
+
mix_sum = 0; \
|
230
|
+
for(k = 0; k < info->channels; k++) mix_sum += temp[i * info->channels + k]; \
|
231
|
+
data[read] = mix_sum/info->channels; \
|
232
|
+
read++; \
|
233
|
+
} \
|
234
|
+
} \
|
235
|
+
} else if(info->channels == 1) { /* Upmix from mono by copying channel */ \
|
236
|
+
while(read < frames) { \
|
237
|
+
/* Calculate # of frames to read */ \
|
238
|
+
amount = frames - read; \
|
239
|
+
if(amount > temp_len) amount = temp_len; \
|
240
|
+
\
|
241
|
+
r = sf_readf_##itype(snd->snd, temp, amount); \
|
242
|
+
if(r == 0) break; \
|
243
|
+
\
|
244
|
+
/* Write every frame channel times to the buffer */ \
|
245
|
+
for(i = 0; i < r; i++) { \
|
246
|
+
for(k = 0; k < buf->channels; k++) { \
|
247
|
+
data[read * buf->channels + k] = temp[i]; \
|
248
|
+
} \
|
249
|
+
read++; \
|
250
|
+
} \
|
251
|
+
} \
|
252
|
+
} else { \
|
253
|
+
rb_raise(eRubyAudioError, "unsupported mix from %d to %d", buf->channels, info->channels); \
|
254
|
+
} \
|
255
|
+
\
|
256
|
+
buf->real_size = read; \
|
364
257
|
}
|
258
|
+
DEFINE_RA_SOUND_READ_TYPE(short);
|
259
|
+
DEFINE_RA_SOUND_READ_TYPE(int);
|
260
|
+
DEFINE_RA_SOUND_READ_TYPE(float);
|
261
|
+
DEFINE_RA_SOUND_READ_TYPE(double);
|
365
262
|
|
366
263
|
/*
|
367
264
|
* call-seq:
|
data/lib/1.8/rubyaudio_ext.so
CHANGED
Binary file
|
data/lib/1.9/rubyaudio_ext.so
CHANGED
Binary file
|
data/lib/ruby-audio/buffer.rb
CHANGED
@@ -10,14 +10,6 @@ module RubyAudio
|
|
10
10
|
# buf = RubyAudio::Buffer.float(1000)
|
11
11
|
# buf = RubyAudio::Buffer.new("float", 1000, 1)
|
12
12
|
class Buffer < CBuffer
|
13
|
-
include Enumerable
|
14
|
-
|
15
|
-
def each
|
16
|
-
self.size.times do |i|
|
17
|
-
yield self[i]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
13
|
[:short, :int, :float, :double].each do |type|
|
22
14
|
eval "def self.#{type}(frames, channels=1); self.new(:#{type}, frames, channels); end"
|
23
15
|
end
|
@@ -24,7 +24,7 @@ module RubyAudio
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
# Returns a new <code>SoundInfo</code> object that
|
27
|
+
# Returns a new <code>SoundInfo</code> object that has the same channel
|
28
28
|
# count, sample rate, and format. This is useful in creating a new sound with
|
29
29
|
# the same format as an already existing sound.
|
30
30
|
#
|
@@ -37,16 +37,33 @@ module RubyAudio
|
|
37
37
|
|
38
38
|
alias_method :seekable?, :seekable
|
39
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"
|
40
46
|
def main_format
|
41
47
|
calculate_format if @main_format.nil?
|
42
48
|
@main_format
|
43
49
|
end
|
44
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"
|
45
57
|
def sub_format
|
46
58
|
calculate_format if @sub_format.nil?
|
47
59
|
@sub_format
|
48
60
|
end
|
49
61
|
|
62
|
+
# Returns the length of the audio file in seconds
|
63
|
+
def length
|
64
|
+
frames / samplerate.to_f
|
65
|
+
end
|
66
|
+
|
50
67
|
private
|
51
68
|
def calculate_format
|
52
69
|
RubyAudio.constants.grep(/FORMAT_/).map(&:to_s).each do |f|
|
data/ruby-audio.gemspec
CHANGED
data/spec/buffer_spec.rb
CHANGED
@@ -78,11 +78,16 @@ describe RubyAudio::Buffer do
|
|
78
78
|
end
|
79
79
|
|
80
80
|
it "shouldn't do anything on an empty buffer" do
|
81
|
-
buf = RubyAudio::Buffer.int(
|
81
|
+
buf = RubyAudio::Buffer.int(50, 2)
|
82
82
|
|
83
83
|
buf.each do
|
84
84
|
fail "This shouldn't be executed"
|
85
85
|
end
|
86
86
|
end
|
87
|
+
|
88
|
+
it "should support usage through returned enumerable" do
|
89
|
+
enum = @buf.each
|
90
|
+
enum.any? {|frame| frame[0] == 5}.should == true
|
91
|
+
end
|
87
92
|
end
|
88
93
|
end
|
data/spec/sound_info_spec.rb
CHANGED
@@ -46,4 +46,10 @@ describe RubyAudio::SoundInfo do
|
|
46
46
|
info.main_format.should == "FORMAT_WAVEX"
|
47
47
|
info.sub_format.should == "FORMAT_MS_ADPCM"
|
48
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
|
49
55
|
end
|
data/spec/sound_spec.rb
CHANGED
@@ -1,30 +1,31 @@
|
|
1
1
|
require "spec_helper.rb"
|
2
2
|
|
3
3
|
describe RubyAudio::Sound do
|
4
|
-
MONO_TEST_WAV = File.dirname(__FILE__)+'/data/what.wav'
|
5
|
-
STEREO_TEST_WAV = File.dirname(__FILE__)+'/data/what2.wav'
|
6
|
-
TEST_MP3 = File.dirname(__FILE__)+'/data/what.mp3'
|
7
|
-
OUT_WAV = File.dirname(__FILE__)+'/data/temp.wav'
|
8
|
-
|
9
4
|
after :each do
|
10
|
-
File.delete(
|
5
|
+
File.delete(fixture('temp.wav')) if File.exists?(fixture('temp.wav'))
|
11
6
|
end
|
12
7
|
|
13
8
|
it "should open a standard wav without issues" do
|
14
9
|
lambda {
|
15
|
-
RubyAudio::Sound.open(
|
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'))
|
16
17
|
}.should_not raise_error
|
17
18
|
end
|
18
19
|
|
19
20
|
it "should raise an exception if the mode is invalid" do
|
20
21
|
lambda {
|
21
|
-
RubyAudio::Sound.open(
|
22
|
+
RubyAudio::Sound.open(fixture('what.wav'), 'q')
|
22
23
|
}.should raise_error(ArgumentError, 'invalid access mode q')
|
23
24
|
end
|
24
25
|
|
25
26
|
it "should close the sound on block exit" do
|
26
27
|
s = nil
|
27
|
-
RubyAudio::Sound.open(
|
28
|
+
RubyAudio::Sound.open(fixture('what.wav')) do |snd|
|
28
29
|
snd.closed?.should be_false
|
29
30
|
s = snd
|
30
31
|
end
|
@@ -33,18 +34,18 @@ describe RubyAudio::Sound do
|
|
33
34
|
|
34
35
|
it "should raise an exception for an unsupported file" do
|
35
36
|
lambda {
|
36
|
-
RubyAudio::Sound.open(
|
37
|
+
RubyAudio::Sound.open(fixture('what.mp3'))
|
37
38
|
}.should raise_error(RubyAudio::Error, "File contains data in an unknown format.")
|
38
39
|
end
|
39
40
|
|
40
41
|
it "should raise an exception if file does not exist" do
|
41
42
|
lambda {
|
42
|
-
RubyAudio::Sound.open(
|
43
|
+
RubyAudio::Sound.open(fixture('what.mp3')+'.not')
|
43
44
|
}.should raise_error(RubyAudio::Error, "System error : No such file or directory.")
|
44
45
|
end
|
45
46
|
|
46
47
|
it "should have the proper sound info" do
|
47
|
-
RubyAudio::Sound.open(
|
48
|
+
RubyAudio::Sound.open(fixture('what.wav')) do |snd|
|
48
49
|
snd.info.channels.should == 1
|
49
50
|
snd.info.samplerate.should == 16000
|
50
51
|
snd.info.format.should == RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
|
@@ -53,7 +54,17 @@ describe RubyAudio::Sound do
|
|
53
54
|
|
54
55
|
it "should allow seeking" do
|
55
56
|
lambda {
|
56
|
-
RubyAudio::Sound.open(
|
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|
|
57
68
|
snd.seek(100)
|
58
69
|
buf = snd.read(:float, 100)
|
59
70
|
buf[0].should > 0
|
@@ -63,15 +74,24 @@ describe RubyAudio::Sound do
|
|
63
74
|
|
64
75
|
it "should raise exceptions for invalid seeks" do
|
65
76
|
lambda {
|
66
|
-
RubyAudio::Sound.open(
|
77
|
+
RubyAudio::Sound.open(fixture('what.wav')) {|snd| snd.seek(-1)}
|
67
78
|
}.should raise_error(RubyAudio::Error, "invalid seek")
|
68
79
|
lambda {
|
69
|
-
RubyAudio::Sound.open(
|
80
|
+
RubyAudio::Sound.open(fixture('what.wav')) {|snd| snd.seek(1000000)}
|
70
81
|
}.should raise_error(RubyAudio::Error, "invalid seek")
|
71
82
|
end
|
72
83
|
|
73
84
|
it "should allow reading samples from the sound" do
|
74
|
-
RubyAudio::Sound.open(
|
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|
|
75
95
|
buf = snd.read(:float, 1000)
|
76
96
|
buf.size.should == 1000
|
77
97
|
buf.real_size.should == 1000
|
@@ -82,7 +102,7 @@ describe RubyAudio::Sound do
|
|
82
102
|
it "should allow reading into an existing buffer" do
|
83
103
|
buf = RubyAudio::Buffer.float(1000)
|
84
104
|
buf.real_size.should == 0
|
85
|
-
RubyAudio::Sound.open(
|
105
|
+
RubyAudio::Sound.open(fixture('what.wav')) do |snd|
|
86
106
|
snd.read(buf)
|
87
107
|
end
|
88
108
|
buf.real_size.should == 1000
|
@@ -92,7 +112,7 @@ describe RubyAudio::Sound do
|
|
92
112
|
it "should allow reading into an existing buffer partially" do
|
93
113
|
buf = RubyAudio::Buffer.float(1000)
|
94
114
|
buf.real_size.should == 0
|
95
|
-
RubyAudio::Sound.open(
|
115
|
+
RubyAudio::Sound.open(fixture('what.wav')) do |snd|
|
96
116
|
snd.read(buf, 100)
|
97
117
|
end
|
98
118
|
buf.real_size.should == 100
|
@@ -103,7 +123,7 @@ describe RubyAudio::Sound do
|
|
103
123
|
it "should allow downmixing to mono on read" do
|
104
124
|
buf = RubyAudio::Buffer.int(100, 1)
|
105
125
|
buf2 = RubyAudio::Buffer.int(100, 2)
|
106
|
-
RubyAudio::Sound.open(
|
126
|
+
RubyAudio::Sound.open(fixture('what2.wav'), 'r') do |snd|
|
107
127
|
snd.read(buf)
|
108
128
|
snd.seek(0)
|
109
129
|
snd.read(buf2)
|
@@ -116,7 +136,7 @@ describe RubyAudio::Sound do
|
|
116
136
|
it "should allow upmixing from mono on read" do
|
117
137
|
buf = RubyAudio::Buffer.int(100, 1)
|
118
138
|
buf2 = RubyAudio::Buffer.int(100, 2)
|
119
|
-
RubyAudio::Sound.open(
|
139
|
+
RubyAudio::Sound.open(fixture('what2.wav'), 'r') do |snd|
|
120
140
|
snd.read(buf2)
|
121
141
|
snd.seek(0)
|
122
142
|
snd.read(buf)
|
@@ -128,7 +148,7 @@ describe RubyAudio::Sound do
|
|
128
148
|
it "should fail read on unsupported up/downmixing" do
|
129
149
|
buf = RubyAudio::Buffer.float(100, 5)
|
130
150
|
lambda {
|
131
|
-
RubyAudio::Sound.open(
|
151
|
+
RubyAudio::Sound.open(fixture('what2.wav')) do |snd|
|
132
152
|
snd.read(buf)
|
133
153
|
end
|
134
154
|
}.should raise_error(RubyAudio::Error, "unsupported mix from 5 to 2")
|
@@ -138,12 +158,30 @@ describe RubyAudio::Sound do
|
|
138
158
|
in_buf = RubyAudio::Buffer.float(100)
|
139
159
|
out_buf = RubyAudio::Buffer.float(100)
|
140
160
|
out_info = nil
|
141
|
-
RubyAudio::Sound.open(
|
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|
|
142
180
|
snd.read(in_buf)
|
143
181
|
out_info = snd.info.clone
|
144
182
|
end
|
145
183
|
|
146
|
-
RubyAudio::Sound.open(
|
184
|
+
RubyAudio::Sound.open(io_fixture, 'rw', out_info) do |snd|
|
147
185
|
snd.write(in_buf)
|
148
186
|
snd.seek(0)
|
149
187
|
snd.read(out_buf)
|
@@ -156,12 +194,12 @@ describe RubyAudio::Sound do
|
|
156
194
|
in_buf = RubyAudio::Buffer.float(100)
|
157
195
|
out_buf = RubyAudio::Buffer.float(100)
|
158
196
|
out_info = nil
|
159
|
-
RubyAudio::Sound.open(
|
197
|
+
RubyAudio::Sound.open(fixture('what.wav')) do |snd|
|
160
198
|
snd.read(in_buf)
|
161
199
|
out_info = snd.info.clone
|
162
200
|
end
|
163
201
|
|
164
|
-
RubyAudio::Sound.open(
|
202
|
+
RubyAudio::Sound.open(fixture('temp.wav'), 'rw', out_info) do |snd|
|
165
203
|
snd << in_buf
|
166
204
|
snd.seek(0)
|
167
205
|
snd.read(out_buf)
|
@@ -174,7 +212,7 @@ describe RubyAudio::Sound do
|
|
174
212
|
buf = RubyAudio::Buffer.float(100, 5)
|
175
213
|
info = RubyAudio::SoundInfo.new :channels => 2, :samplerate => 48000, :format => RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
|
176
214
|
lambda {
|
177
|
-
RubyAudio::Sound.open(
|
215
|
+
RubyAudio::Sound.open(fixture('temp.wav'), 'w', info) do |snd|
|
178
216
|
snd.write(buf)
|
179
217
|
end
|
180
218
|
}.should raise_error(RubyAudio::Error, "channel count mismatch: 5 vs 2")
|
data/spec/spec_helper.rb
CHANGED
@@ -8,4 +8,17 @@ end
|
|
8
8
|
require 'spec/autorun'
|
9
9
|
|
10
10
|
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
|
-
require 'ruby-audio'
|
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
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-audio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 3
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 1
|
8
|
-
-
|
7
|
+
- 6
|
9
8
|
- 0
|
10
|
-
version: 1.
|
9
|
+
version: 1.6.0
|
11
10
|
platform: x86-mingw32
|
12
11
|
authors:
|
13
12
|
- Stephen Augenstein
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date: 2011-
|
17
|
+
date: 2011-11-19 00:00:00 -05:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
@@ -71,27 +70,23 @@ rdoc_options:
|
|
71
70
|
require_paths:
|
72
71
|
- lib
|
73
72
|
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
73
|
requirements:
|
76
74
|
- - ">="
|
77
75
|
- !ruby/object:Gem::Version
|
78
|
-
hash: 3
|
79
76
|
segments:
|
80
77
|
- 0
|
81
78
|
version: "0"
|
82
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
80
|
requirements:
|
85
81
|
- - ">="
|
86
82
|
- !ruby/object:Gem::Version
|
87
|
-
hash: 3
|
88
83
|
segments:
|
89
84
|
- 0
|
90
85
|
version: "0"
|
91
86
|
requirements:
|
92
87
|
- libsndfile (http://www.mega-nerd.com/libsndfile/)
|
93
88
|
rubyforge_project:
|
94
|
-
rubygems_version: 1.3.
|
89
|
+
rubygems_version: 1.3.6
|
95
90
|
signing_key:
|
96
91
|
specification_version: 3
|
97
92
|
summary: libsndfile wrapper for ruby
|