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,342 @@
|
|
1
|
+
#include "ra_buffer.h"
|
2
|
+
|
3
|
+
ID ra_short_sym, ra_int_sym, ra_float_sym, ra_double_sym;
|
4
|
+
extern VALUE eRubyAudioError;
|
5
|
+
|
6
|
+
// Before RFLOAT_VALUE, value was in a different place in the struct
|
7
|
+
#ifndef RFLOAT_VALUE
|
8
|
+
#define RFLOAT_VALUE(v) (RFLOAT(v)->value)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
/*
|
12
|
+
* Class <code>CBuffer</code> is a very light wrapper around a standard C array
|
13
|
+
* that can be read from and written to by libsndfile.
|
14
|
+
*/
|
15
|
+
void Init_ra_buffer() {
|
16
|
+
VALUE mRubyAudio = rb_define_module("RubyAudio");
|
17
|
+
VALUE cRABuffer = rb_define_class_under(mRubyAudio, "CBuffer", rb_cObject);
|
18
|
+
rb_include_module(cRABuffer, rb_mEnumerable);
|
19
|
+
rb_define_alloc_func(cRABuffer, ra_buffer_allocate);
|
20
|
+
rb_define_method(cRABuffer, "initialize", ra_buffer_init, -1);
|
21
|
+
rb_define_method(cRABuffer, "initialize_copy", ra_buffer_init_copy, 1);
|
22
|
+
rb_define_method(cRABuffer, "channels", ra_buffer_channels, 0);
|
23
|
+
rb_define_method(cRABuffer, "size", ra_buffer_size, 0);
|
24
|
+
rb_define_method(cRABuffer, "real_size", ra_buffer_real_size, 0);
|
25
|
+
rb_define_method(cRABuffer, "real_size=", ra_buffer_real_size_set, 1);
|
26
|
+
rb_define_method(cRABuffer, "type", ra_buffer_type, 0);
|
27
|
+
rb_define_method(cRABuffer, "each", ra_buffer_each, 0);
|
28
|
+
rb_define_method(cRABuffer, "[]", ra_buffer_aref, 1);
|
29
|
+
rb_define_method(cRABuffer, "[]=", ra_buffer_aset, 2);
|
30
|
+
|
31
|
+
ra_short_sym = rb_intern("short");
|
32
|
+
ra_int_sym = rb_intern("int");
|
33
|
+
ra_float_sym = rb_intern("float");
|
34
|
+
ra_double_sym = rb_intern("double");
|
35
|
+
}
|
36
|
+
|
37
|
+
static VALUE ra_buffer_allocate(VALUE klass) {
|
38
|
+
RA_BUFFER *buf = ALLOC(RA_BUFFER);
|
39
|
+
memset(buf, 0, sizeof(RA_BUFFER));
|
40
|
+
VALUE self = Data_Wrap_Struct(klass, NULL, ra_buffer_free, buf);
|
41
|
+
return self;
|
42
|
+
}
|
43
|
+
|
44
|
+
static void ra_buffer_free(RA_BUFFER *buf) {
|
45
|
+
if(buf->data != NULL) xfree(buf->data);
|
46
|
+
xfree(buf);
|
47
|
+
}
|
48
|
+
|
49
|
+
/*
|
50
|
+
* Uses size, channels, and type to allocate a properly sized array and set data
|
51
|
+
* to the pointer for that data. Returns size.
|
52
|
+
*/
|
53
|
+
static long ra_buffer_alloc_data(RA_BUFFER *buf) {
|
54
|
+
long size = 0;
|
55
|
+
switch(buf->type) {
|
56
|
+
case RA_BUFFER_TYPE_SHORT:
|
57
|
+
size = sizeof(short) * buf->size * buf->channels;
|
58
|
+
break;
|
59
|
+
case RA_BUFFER_TYPE_INT:
|
60
|
+
size = sizeof(int) * buf->size * buf->channels;
|
61
|
+
break;
|
62
|
+
case RA_BUFFER_TYPE_FLOAT:
|
63
|
+
size = sizeof(float) * buf->size * buf->channels;
|
64
|
+
break;
|
65
|
+
case RA_BUFFER_TYPE_DOUBLE:
|
66
|
+
size = sizeof(double) * buf->size * buf->channels;
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
buf->data = (void*)xmalloc(size);
|
70
|
+
memset(buf->data, 0, size);
|
71
|
+
return size;
|
72
|
+
}
|
73
|
+
|
74
|
+
/*
|
75
|
+
* call-seq:
|
76
|
+
* RubyAudio::CBuffer.new(type, size, channels=1) => buf
|
77
|
+
*
|
78
|
+
* Returns a new <code>CBuffer</code> object which can contain the given number
|
79
|
+
* of audio frames of the given data type.
|
80
|
+
*
|
81
|
+
* buf = RubyAudio::CBuffer.new("float", 1000)
|
82
|
+
*/
|
83
|
+
static VALUE ra_buffer_init(int argc, VALUE *argv, VALUE self) {
|
84
|
+
RA_BUFFER *buf;
|
85
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
86
|
+
|
87
|
+
// Check args
|
88
|
+
if(argc < 2) rb_raise(rb_eArgError, "At least 2 arguments required");
|
89
|
+
|
90
|
+
// Get type of object
|
91
|
+
const char *buf_type;
|
92
|
+
switch(TYPE(argv[0])) {
|
93
|
+
case T_SYMBOL:
|
94
|
+
buf_type = rb_id2name(SYM2ID(argv[0]));
|
95
|
+
if(!buf_type) rb_raise(rb_eArgError, "bad type");
|
96
|
+
break;
|
97
|
+
case T_STRING:
|
98
|
+
buf_type = RSTRING_PTR(argv[0]);
|
99
|
+
break;
|
100
|
+
default:
|
101
|
+
rb_raise(rb_eArgError, "bad type");
|
102
|
+
break;
|
103
|
+
}
|
104
|
+
|
105
|
+
// Populate channels
|
106
|
+
buf->channels = (argc == 3) ? FIX2INT(argv[2]) : 1;
|
107
|
+
|
108
|
+
// Allocate data array based on type
|
109
|
+
buf->size = FIX2LONG(argv[1]);
|
110
|
+
buf->real_size = 0;
|
111
|
+
if(strcmp(buf_type, "short") == 0) buf->type = RA_BUFFER_TYPE_SHORT;
|
112
|
+
else if(strcmp(buf_type, "int") == 0) buf->type = RA_BUFFER_TYPE_INT;
|
113
|
+
else if(strcmp(buf_type, "float") == 0) buf->type = RA_BUFFER_TYPE_FLOAT;
|
114
|
+
else if(strcmp(buf_type, "double") == 0) buf->type = RA_BUFFER_TYPE_DOUBLE;
|
115
|
+
else rb_raise(rb_eArgError, "Invalid type: %s", buf_type);
|
116
|
+
ra_buffer_alloc_data(buf);
|
117
|
+
|
118
|
+
// Return self
|
119
|
+
return self;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* :nodoc: */
|
123
|
+
static VALUE ra_buffer_init_copy(VALUE copy, VALUE buf) {
|
124
|
+
if (copy == buf) return copy;
|
125
|
+
|
126
|
+
// Checks
|
127
|
+
rb_check_frozen(copy);
|
128
|
+
if (!rb_obj_is_instance_of(buf, rb_obj_class(copy))) {
|
129
|
+
rb_raise(rb_eTypeError, "wrong argument class");
|
130
|
+
}
|
131
|
+
|
132
|
+
RA_BUFFER *copy_struct, *buf_struct;
|
133
|
+
Data_Get_Struct(copy, RA_BUFFER, copy_struct);
|
134
|
+
Data_Get_Struct(buf, RA_BUFFER, buf_struct);
|
135
|
+
|
136
|
+
// Clone data
|
137
|
+
memcpy(copy_struct, buf_struct, sizeof(RA_BUFFER));
|
138
|
+
long size = ra_buffer_alloc_data(copy_struct);
|
139
|
+
memcpy(copy_struct->data, buf_struct->data, size);
|
140
|
+
|
141
|
+
return copy;
|
142
|
+
}
|
143
|
+
|
144
|
+
/*
|
145
|
+
* call-seq:
|
146
|
+
* buf.channels => integer
|
147
|
+
*
|
148
|
+
* Returns the number of channels in a frame of the buffer.
|
149
|
+
*/
|
150
|
+
static VALUE ra_buffer_channels(VALUE self) {
|
151
|
+
RA_BUFFER *buf;
|
152
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
153
|
+
return INT2FIX(buf->channels);
|
154
|
+
}
|
155
|
+
|
156
|
+
/*
|
157
|
+
* call-seq:
|
158
|
+
* buf.size => integer
|
159
|
+
*
|
160
|
+
* Returns the number of frames the buffer can store.
|
161
|
+
*/
|
162
|
+
static VALUE ra_buffer_size(VALUE self) {
|
163
|
+
RA_BUFFER *buf;
|
164
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
165
|
+
return LONG2FIX(buf->size);
|
166
|
+
}
|
167
|
+
|
168
|
+
/*
|
169
|
+
* call-seq:
|
170
|
+
* buf.real_size => integer
|
171
|
+
*
|
172
|
+
* Returns the number of frames of actual data are currently stored in the
|
173
|
+
* buffer.
|
174
|
+
*/
|
175
|
+
static VALUE ra_buffer_real_size(VALUE self) {
|
176
|
+
RA_BUFFER *buf;
|
177
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
178
|
+
return LONG2FIX(buf->real_size);
|
179
|
+
}
|
180
|
+
|
181
|
+
/*:nodoc:*/
|
182
|
+
static VALUE ra_buffer_real_size_set(VALUE self, VALUE real_size) {
|
183
|
+
RA_BUFFER *buf;
|
184
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
185
|
+
|
186
|
+
long new_real_size = FIX2LONG(real_size);
|
187
|
+
if(new_real_size > buf->size) {
|
188
|
+
buf->real_size = buf->size;
|
189
|
+
} else if(new_real_size < 0) {
|
190
|
+
buf->real_size = 0;
|
191
|
+
} else {
|
192
|
+
buf->real_size = new_real_size;
|
193
|
+
}
|
194
|
+
|
195
|
+
return LONG2FIX(buf->real_size);
|
196
|
+
}
|
197
|
+
|
198
|
+
/*
|
199
|
+
* call-seq:
|
200
|
+
* buf.type => symbol
|
201
|
+
*
|
202
|
+
* Returns the type of audio data being stored. <code>:short</code>,
|
203
|
+
* <code>:int</code>, <code>:float</code>, or <code>:double</code>.
|
204
|
+
*/
|
205
|
+
static VALUE ra_buffer_type(VALUE self) {
|
206
|
+
RA_BUFFER *buf;
|
207
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
208
|
+
switch(buf->type) {
|
209
|
+
case RA_BUFFER_TYPE_SHORT: return ID2SYM(ra_short_sym);
|
210
|
+
case RA_BUFFER_TYPE_INT: return ID2SYM(ra_int_sym);
|
211
|
+
case RA_BUFFER_TYPE_FLOAT: return ID2SYM(ra_float_sym);
|
212
|
+
case RA_BUFFER_TYPE_DOUBLE: return ID2SYM(ra_double_sym);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
/*
|
217
|
+
* call-seq:
|
218
|
+
* buf.each {|frame| block } => buf
|
219
|
+
* buf.each => anEnumerator
|
220
|
+
*
|
221
|
+
* Iterates through each frame in the buffer. Each frame is either a number or
|
222
|
+
* an array of numbers if there are multiple channels.
|
223
|
+
*/
|
224
|
+
static VALUE ra_buffer_each(VALUE self) {
|
225
|
+
RA_BUFFER *buf;
|
226
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
227
|
+
|
228
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
229
|
+
|
230
|
+
long i;
|
231
|
+
for(i = 0; i < buf->real_size; i++) {
|
232
|
+
rb_yield(ra_buffer_aref(self, LONG2FIX(i)));
|
233
|
+
}
|
234
|
+
return self;
|
235
|
+
}
|
236
|
+
|
237
|
+
/*
|
238
|
+
* call-seq:
|
239
|
+
* buf[integer] => frame
|
240
|
+
*
|
241
|
+
* Returns a frame of audio data at the given offset.
|
242
|
+
*
|
243
|
+
* buf = snd.read(:float, 100) # Mono sound
|
244
|
+
* buf[5] #=> 0.4
|
245
|
+
*
|
246
|
+
* buf2 = snd2.read(:float, 100) # Stereo sound
|
247
|
+
* buf[5] #=> [0.4, 0.3]
|
248
|
+
*/
|
249
|
+
static VALUE ra_buffer_aref(VALUE self, VALUE index) {
|
250
|
+
RA_BUFFER *buf;
|
251
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
252
|
+
|
253
|
+
// Bounds check
|
254
|
+
long f = FIX2LONG(index);
|
255
|
+
if(f < 0 || f >= buf->real_size) return Qnil;
|
256
|
+
long i = f * buf->channels;
|
257
|
+
|
258
|
+
if(buf->channels == 1) {
|
259
|
+
return ra_buffer_index_get(buf, i);
|
260
|
+
} else {
|
261
|
+
VALUE frame = rb_ary_new();
|
262
|
+
long j;
|
263
|
+
for(j = 0; j < buf->channels; j++) {
|
264
|
+
rb_ary_push(frame, ra_buffer_index_get(buf, i+j));
|
265
|
+
}
|
266
|
+
return frame;
|
267
|
+
}
|
268
|
+
}
|
269
|
+
|
270
|
+
static VALUE ra_buffer_index_get(RA_BUFFER *buf, long i) {
|
271
|
+
switch(buf->type) {
|
272
|
+
case RA_BUFFER_TYPE_SHORT: return INT2FIX((int)((short*)buf->data)[i]);
|
273
|
+
case RA_BUFFER_TYPE_INT: return INT2FIX(((int*)buf->data)[i]);
|
274
|
+
case RA_BUFFER_TYPE_FLOAT: return rb_float_new((double)((float*)buf->data)[i]);
|
275
|
+
case RA_BUFFER_TYPE_DOUBLE: return rb_float_new(((double*)buf->data)[i]);
|
276
|
+
}
|
277
|
+
}
|
278
|
+
|
279
|
+
/*
|
280
|
+
* call-seq:
|
281
|
+
* buf[integer] = numeric => numeric
|
282
|
+
* buf[integer] = array => array
|
283
|
+
*
|
284
|
+
* Sets the frame of audio data at the given offset to the value. For
|
285
|
+
* multi-channel audio, pass in an array of values.
|
286
|
+
*
|
287
|
+
* buf = RubyAudio::Buffer.int(100, 1)
|
288
|
+
* buf[0] = 5
|
289
|
+
*
|
290
|
+
* buf = RubyAudio::Buffer.double(100, 2)
|
291
|
+
* buf[0] = [0.5, 0.3]
|
292
|
+
*/
|
293
|
+
static VALUE ra_buffer_aset(VALUE self, VALUE index, VALUE val) {
|
294
|
+
RA_BUFFER *buf;
|
295
|
+
Data_Get_Struct(self, RA_BUFFER, buf);
|
296
|
+
|
297
|
+
// Bounds check
|
298
|
+
long f = FIX2LONG(index);
|
299
|
+
if(f < 0 || f >= buf->size) rb_raise(eRubyAudioError, "setting frame out of bounds");
|
300
|
+
long i = f * buf->channels;
|
301
|
+
|
302
|
+
// Set data
|
303
|
+
if(buf->channels == 1) {
|
304
|
+
ra_buffer_index_set(buf, i, val);
|
305
|
+
} else {
|
306
|
+
if(TYPE(val) != T_ARRAY) rb_raise(eRubyAudioError, "must pass in array for multi-channel buffer");
|
307
|
+
long length = RARRAY_LEN(val);
|
308
|
+
if(length != buf->channels) rb_raise(eRubyAudioError, "array length must match channel count");
|
309
|
+
|
310
|
+
long j;
|
311
|
+
for(j = 0; j < length; j++) {
|
312
|
+
ra_buffer_index_set(buf, i+j, rb_ary_entry(val, j));
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
// Bump real_size
|
317
|
+
if(f + 1 > buf->real_size) {
|
318
|
+
buf->real_size = f + 1;
|
319
|
+
}
|
320
|
+
|
321
|
+
return val;
|
322
|
+
}
|
323
|
+
|
324
|
+
static void ra_buffer_index_set(RA_BUFFER *buf, long i, VALUE val) {
|
325
|
+
if(buf->type == RA_BUFFER_TYPE_SHORT || buf->type == RA_BUFFER_TYPE_INT) {
|
326
|
+
// Convert val to an integer
|
327
|
+
VALUE int_obj = rb_Integer(val);
|
328
|
+
if(TYPE(int_obj) != T_FIXNUM) rb_raise(eRubyAudioError, "could not convert frame value to an integer");
|
329
|
+
long int_val = FIX2LONG(int_obj);
|
330
|
+
|
331
|
+
// Set it
|
332
|
+
if(buf->type == RA_BUFFER_TYPE_SHORT) ((short*)buf->data)[i] = (short)int_val;
|
333
|
+
else ((int*)buf->data)[i] = (int)int_val;
|
334
|
+
} else {
|
335
|
+
// Convert val to a float
|
336
|
+
double float_val = RFLOAT_VALUE(rb_Float(val));
|
337
|
+
|
338
|
+
// Set it
|
339
|
+
if(buf->type == RA_BUFFER_TYPE_FLOAT) ((float*)buf->data)[i] = (float)float_val;
|
340
|
+
else ((double*)buf->data)[i] = float_val;
|
341
|
+
}
|
342
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#ifndef RA_BUFFER_H
|
2
|
+
#define RA_BUFFER_H
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
|
6
|
+
typedef enum {
|
7
|
+
RA_BUFFER_TYPE_SHORT,
|
8
|
+
RA_BUFFER_TYPE_INT,
|
9
|
+
RA_BUFFER_TYPE_FLOAT,
|
10
|
+
RA_BUFFER_TYPE_DOUBLE
|
11
|
+
} BUFFER_TYPE;
|
12
|
+
|
13
|
+
typedef struct {
|
14
|
+
BUFFER_TYPE type;
|
15
|
+
void *data;
|
16
|
+
long size;
|
17
|
+
long real_size;
|
18
|
+
int channels;
|
19
|
+
} RA_BUFFER;
|
20
|
+
|
21
|
+
void Init_ra_buffer();
|
22
|
+
|
23
|
+
/*** Initialization and Memory Manangement ***/
|
24
|
+
static VALUE ra_buffer_allocate(VALUE klass);
|
25
|
+
static void ra_buffer_free(RA_BUFFER *buf);
|
26
|
+
|
27
|
+
/*** Instance Methods ***/
|
28
|
+
static VALUE ra_buffer_init(int argc, VALUE *argv, VALUE self);
|
29
|
+
static VALUE ra_buffer_init_copy(VALUE copy, VALUE buf);
|
30
|
+
static VALUE ra_buffer_channels(VALUE self);
|
31
|
+
static VALUE ra_buffer_size(VALUE self);
|
32
|
+
static VALUE ra_buffer_real_size(VALUE self);
|
33
|
+
static VALUE ra_buffer_real_size_set(VALUE self, VALUE new_real_size);
|
34
|
+
static VALUE ra_buffer_type(VALUE self);
|
35
|
+
static VALUE ra_buffer_each(VALUE self);
|
36
|
+
static VALUE ra_buffer_aref(VALUE self, VALUE index);
|
37
|
+
static VALUE ra_buffer_index_get(RA_BUFFER *buf, long i);
|
38
|
+
static VALUE ra_buffer_aset(VALUE self, VALUE index, VALUE val);
|
39
|
+
static void ra_buffer_index_set(RA_BUFFER *buf, long i, VALUE val);
|
40
|
+
|
41
|
+
#endif // #ifndef RA_BUFFER_H
|