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.
@@ -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 (x86)/Mega-Nerd/libsndfile/include']
16
- LIB_DIRS = ['/opt/local/lib', '/usr/local/lib', 'C:/Program Files/Mega-Nerd/libsndfile', 'C:/Program Files (x86)/Mega-Nerd/libsndfile']
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 are <code>"r"</code>, <code>"w"</code>, or
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 path, VALUE mode, VALUE info) {
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
- snd->snd = sf_open(p, snd->mode, sf_info);
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
- static void ra_sound_read_short(RA_SOUND *snd, RA_BUFFER *buf, sf_count_t frames) {
131
- static short temp[1024];
132
- int temp_len = 1024;
133
- short *data = (short*)buf->data;
134
- short mix_sum;
135
-
136
- // Get info struct
137
- SF_INFO *info;
138
- Data_Get_Struct(snd->info, SF_INFO, info);
139
-
140
- // Up/Downmix based on channel matching
141
- sf_count_t read = 0, r, amount;
142
- int i, k;
143
- if(buf->channels == info->channels) { // Simply read data without mix
144
- read = sf_readf_short(snd->snd, data, frames);
145
- } else if(buf->channels == 1) { // Downmix to mono
146
- sf_count_t max = temp_len / info->channels;
147
- int channels;
148
-
149
- while(read < frames) {
150
- // Calculate # of frames to read
151
- amount = frames - read;
152
- if(amount > max) amount = max;
153
-
154
- r = sf_readf_short(snd->snd, temp, amount);
155
- if(r == 0) break;
156
-
157
- // Mix channels together by averaging all channels and store to buffer
158
- for(i = 0; i < r; i++) {
159
- mix_sum = 0;
160
- for(k = 0; k < info->channels; k++) mix_sum += temp[i * info->channels + k];
161
- data[read] = mix_sum/info->channels;
162
- read++;
163
- }
164
- }
165
- } else if(info->channels == 1) { // Upmix from mono by copying channel
166
- while(read < frames) {
167
- // Calculate # of frames to read
168
- amount = frames - read;
169
- if(amount > temp_len) amount = temp_len;
170
-
171
- r = sf_readf_short(snd->snd, temp, amount);
172
- if(r == 0) break;
173
-
174
- // Write every frame channel times to the buffer
175
- for(i = 0; i < r; i++) {
176
- for(k = 0; k < buf->channels; k++) {
177
- data[read * buf->channels + k] = temp[i];
178
- }
179
- read++;
180
- }
181
- }
182
- } else {
183
- rb_raise(eRubyAudioError, "unsupported mix from %d to %d", buf->channels, info->channels);
184
- }
185
-
186
- buf->real_size = read;
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:
@@ -9,6 +9,7 @@
9
9
  typedef struct {
10
10
  SNDFILE *snd;
11
11
  VALUE info;
12
+ VALUE vio_source;
12
13
  int mode;
13
14
  int closed;
14
15
  } RA_SOUND;
Binary file
Binary file
@@ -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 is has the same channel
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|
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'ruby-audio'
5
- s.version = '1.5.0'
5
+ s.version = '1.6.0'
6
6
  s.platform = Gem::Platform::RUBY
7
7
  s.authors = ['Stephen Augenstein']
8
8
  s.email = ['perl.programmer@gmail.com']
@@ -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(0, 2)
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
@@ -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
@@ -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(OUT_WAV) if File.exists?(OUT_WAV)
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(MONO_TEST_WAV)
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(MONO_TEST_WAV, 'q')
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(MONO_TEST_WAV) do |snd|
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(TEST_MP3)
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(TEST_MP3+'.not')
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(MONO_TEST_WAV) do |snd|
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(MONO_TEST_WAV) do |snd|
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(MONO_TEST_WAV) {|snd| snd.seek(-1)}
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(MONO_TEST_WAV) {|snd| snd.seek(1000000)}
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(STEREO_TEST_WAV) do |snd|
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(MONO_TEST_WAV) do |snd|
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(MONO_TEST_WAV) do |snd|
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(STEREO_TEST_WAV, 'r') do |snd|
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(STEREO_TEST_WAV, 'r') do |snd|
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(STEREO_TEST_WAV) do |snd|
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(MONO_TEST_WAV) do |snd|
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(OUT_WAV, 'rw', out_info) do |snd|
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(MONO_TEST_WAV) do |snd|
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(OUT_WAV, 'rw', out_info) do |snd|
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(OUT_WAV, 'w', info) do |snd|
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")
@@ -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
- - 5
7
+ - 6
9
8
  - 0
10
- version: 1.5.0
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-07-06 00:00:00 -04:00
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.7
89
+ rubygems_version: 1.3.6
95
90
  signing_key:
96
91
  specification_version: 3
97
92
  summary: libsndfile wrapper for ruby