ruby-audio 1.5.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS.gsub!("-arch i386", "")
4
+ $LDFLAGS.gsub!("-arch i386", "")
5
+
6
+ dir_config('sndfile')
7
+
8
+ # Mega-Nerd windows installer installs as libsndfile-1.dll
9
+ if RUBY_PLATFORM =~ /(mswin|mingw|cygwin)/
10
+ sndfile_lib = 'sndfile-1'
11
+ else
12
+ sndfile_lib = 'sndfile'
13
+ end
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']
17
+
18
+ # libsndfile requirements
19
+ find_header 'sndfile.h', *INCLUDE_DIRS
20
+ unless ['sndfile-1', 'sndfile'].any? {|lib| find_library lib, 'sf_open', *LIB_DIRS}
21
+ fail <<-EOM
22
+ Can't find libsndfile (http://www.mega-nerd.com/libsndfile/)
23
+
24
+ Try passing --with-sndfile-dir or --with-sndfile-lib and --with-sndfile-include
25
+ options to extconf. If there are spaces in the path on windows, it may not work.
26
+ EOM
27
+ end
28
+
29
+ # Check for format support
30
+ have_const('SF_FORMAT_OGG', 'sndfile.h')
31
+
32
+ create_makefile 'rubyaudio_ext'
@@ -0,0 +1,315 @@
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_define_alloc_func(cRABuffer, ra_buffer_allocate);
19
+ rb_define_method(cRABuffer, "initialize", ra_buffer_init, -1);
20
+ rb_define_method(cRABuffer, "initialize_copy", ra_buffer_init_copy, 1);
21
+ rb_define_method(cRABuffer, "channels", ra_buffer_channels, 0);
22
+ rb_define_method(cRABuffer, "size", ra_buffer_size, 0);
23
+ rb_define_method(cRABuffer, "real_size", ra_buffer_real_size, 0);
24
+ rb_define_method(cRABuffer, "real_size=", ra_buffer_real_size_set, 1);
25
+ rb_define_method(cRABuffer, "type", ra_buffer_type, 0);
26
+ rb_define_method(cRABuffer, "[]", ra_buffer_aref, 1);
27
+ rb_define_method(cRABuffer, "[]=", ra_buffer_aset, 2);
28
+
29
+ ra_short_sym = rb_intern("short");
30
+ ra_int_sym = rb_intern("int");
31
+ ra_float_sym = rb_intern("float");
32
+ ra_double_sym = rb_intern("double");
33
+ }
34
+
35
+ static VALUE ra_buffer_allocate(VALUE klass) {
36
+ RA_BUFFER *buf = ALLOC(RA_BUFFER);
37
+ memset(buf, 0, sizeof(RA_BUFFER));
38
+ VALUE self = Data_Wrap_Struct(klass, NULL, ra_buffer_free, buf);
39
+ return self;
40
+ }
41
+
42
+ static void ra_buffer_free(RA_BUFFER *buf) {
43
+ if(buf->data != NULL) xfree(buf->data);
44
+ xfree(buf);
45
+ }
46
+
47
+ /*
48
+ * Uses size, channels, and type to allocate a properly sized array and set data
49
+ * to the pointer for that data. Returns size.
50
+ */
51
+ static long ra_buffer_alloc_data(RA_BUFFER *buf) {
52
+ long size = 0;
53
+ switch(buf->type) {
54
+ case RA_BUFFER_TYPE_SHORT:
55
+ size = sizeof(short) * buf->size * buf->channels;
56
+ break;
57
+ case RA_BUFFER_TYPE_INT:
58
+ size = sizeof(int) * buf->size * buf->channels;
59
+ break;
60
+ case RA_BUFFER_TYPE_FLOAT:
61
+ size = sizeof(float) * buf->size * buf->channels;
62
+ break;
63
+ case RA_BUFFER_TYPE_DOUBLE:
64
+ size = sizeof(double) * buf->size * buf->channels;
65
+ break;
66
+ }
67
+ buf->data = (void*)xmalloc(size);
68
+ return size;
69
+ }
70
+
71
+ /*
72
+ * call-seq:
73
+ * RubyAudio::CBuffer.new(type, size, channels=1) => buf
74
+ *
75
+ * Returns a new <code>CBuffer</code> object which can contain the given number
76
+ * of audio frames of the given data type.
77
+ *
78
+ * buf = RubyAudio::CBuffer.new("float", 1000)
79
+ */
80
+ static VALUE ra_buffer_init(int argc, VALUE *argv, VALUE self) {
81
+ RA_BUFFER *buf;
82
+ Data_Get_Struct(self, RA_BUFFER, buf);
83
+
84
+ // Check args
85
+ if(argc < 2) rb_raise(rb_eArgError, "At least 2 arguments required");
86
+
87
+ // Get type of object
88
+ const char *buf_type;
89
+ switch(TYPE(argv[0])) {
90
+ case T_SYMBOL:
91
+ buf_type = rb_id2name(SYM2ID(argv[0]));
92
+ if(!buf_type) rb_raise(rb_eArgError, "bad type");
93
+ break;
94
+ case T_STRING:
95
+ buf_type = RSTRING_PTR(argv[0]);
96
+ break;
97
+ default:
98
+ rb_raise(rb_eArgError, "bad type");
99
+ break;
100
+ }
101
+
102
+ // Populate channels
103
+ buf->channels = (argc == 3) ? FIX2INT(argv[2]) : 1;
104
+
105
+ // Allocate data array based on type
106
+ buf->size = FIX2LONG(argv[1]);
107
+ buf->real_size = 0;
108
+ if(strcmp(buf_type, "short") == 0) buf->type = RA_BUFFER_TYPE_SHORT;
109
+ else if(strcmp(buf_type, "int") == 0) buf->type = RA_BUFFER_TYPE_INT;
110
+ else if(strcmp(buf_type, "float") == 0) buf->type = RA_BUFFER_TYPE_FLOAT;
111
+ else if(strcmp(buf_type, "double") == 0) buf->type = RA_BUFFER_TYPE_DOUBLE;
112
+ else rb_raise(rb_eArgError, "Invalid type: %s", buf_type);
113
+ ra_buffer_alloc_data(buf);
114
+
115
+ // Return self
116
+ return self;
117
+ }
118
+
119
+ /* :nodoc: */
120
+ static VALUE ra_buffer_init_copy(VALUE copy, VALUE buf) {
121
+ if (copy == buf) return copy;
122
+
123
+ // Checks
124
+ rb_check_frozen(copy);
125
+ if (!rb_obj_is_instance_of(buf, rb_obj_class(copy))) {
126
+ rb_raise(rb_eTypeError, "wrong argument class");
127
+ }
128
+
129
+ RA_BUFFER *copy_struct, *buf_struct;
130
+ Data_Get_Struct(copy, RA_BUFFER, copy_struct);
131
+ Data_Get_Struct(buf, RA_BUFFER, buf_struct);
132
+
133
+ // Clone data
134
+ memcpy(copy_struct, buf_struct, sizeof(RA_BUFFER));
135
+ long size = ra_buffer_alloc_data(copy_struct);
136
+ memcpy(copy_struct->data, buf_struct->data, size);
137
+
138
+ return copy;
139
+ }
140
+
141
+ /*
142
+ * call-seq:
143
+ * buf.channels => integer
144
+ *
145
+ * Returns the number of channels in a frame of the buffer.
146
+ */
147
+ static VALUE ra_buffer_channels(VALUE self) {
148
+ RA_BUFFER *buf;
149
+ Data_Get_Struct(self, RA_BUFFER, buf);
150
+ return INT2FIX(buf->channels);
151
+ }
152
+
153
+ /*
154
+ * call-seq:
155
+ * buf.size => integer
156
+ *
157
+ * Returns the number of frames the buffer can store.
158
+ */
159
+ static VALUE ra_buffer_size(VALUE self) {
160
+ RA_BUFFER *buf;
161
+ Data_Get_Struct(self, RA_BUFFER, buf);
162
+ return LONG2FIX(buf->size);
163
+ }
164
+
165
+ /*
166
+ * call-seq:
167
+ * buf.real_size => integer
168
+ *
169
+ * Returns the number of frames of actual data are currently stored in the
170
+ * buffer.
171
+ */
172
+ static VALUE ra_buffer_real_size(VALUE self) {
173
+ RA_BUFFER *buf;
174
+ Data_Get_Struct(self, RA_BUFFER, buf);
175
+ return LONG2FIX(buf->real_size);
176
+ }
177
+
178
+ /*:nodoc:*/
179
+ static VALUE ra_buffer_real_size_set(VALUE self, VALUE real_size) {
180
+ RA_BUFFER *buf;
181
+ Data_Get_Struct(self, RA_BUFFER, buf);
182
+
183
+ long new_real_size = FIX2LONG(real_size);
184
+ if(new_real_size > buf->size) {
185
+ buf->real_size = buf->size;
186
+ } else if(new_real_size < 0) {
187
+ buf->real_size = 0;
188
+ } else {
189
+ buf->real_size = new_real_size;
190
+ }
191
+
192
+ return LONG2FIX(buf->real_size);
193
+ }
194
+
195
+ /*
196
+ * call-seq:
197
+ * buf.type => symbol
198
+ *
199
+ * Returns the type of audio data being stored. <code>:short</code>,
200
+ * <code>:int</code>, <code>:float</code>, or <code>:double</code>.
201
+ */
202
+ static VALUE ra_buffer_type(VALUE self) {
203
+ RA_BUFFER *buf;
204
+ Data_Get_Struct(self, RA_BUFFER, buf);
205
+ switch(buf->type) {
206
+ case RA_BUFFER_TYPE_SHORT: return ID2SYM(ra_short_sym);
207
+ case RA_BUFFER_TYPE_INT: return ID2SYM(ra_int_sym);
208
+ case RA_BUFFER_TYPE_FLOAT: return ID2SYM(ra_float_sym);
209
+ case RA_BUFFER_TYPE_DOUBLE: return ID2SYM(ra_double_sym);
210
+ }
211
+ }
212
+
213
+ /*
214
+ * call-seq:
215
+ * buf[integer] => frame
216
+ *
217
+ * Returns a frame of audio data at the given offset.
218
+ *
219
+ * buf = snd.read(:float, 100) # Mono sound
220
+ * buf[5] #=> 0.4
221
+ *
222
+ * buf2 = snd2.read(:float, 100) # Stereo sound
223
+ * buf[5] #=> [0.4, 0.3]
224
+ */
225
+ static VALUE ra_buffer_aref(VALUE self, VALUE index) {
226
+ RA_BUFFER *buf;
227
+ Data_Get_Struct(self, RA_BUFFER, buf);
228
+
229
+ // Bounds check
230
+ long f = FIX2LONG(index);
231
+ if(f < 0 || f >= buf->real_size) return Qnil;
232
+ long i = f * buf->channels;
233
+
234
+ if(buf->channels == 1) {
235
+ return ra_buffer_index_get(buf, i);
236
+ } else {
237
+ VALUE frame = rb_ary_new();
238
+ long j;
239
+ for(j = 0; j < buf->channels; j++) {
240
+ rb_ary_push(frame, ra_buffer_index_get(buf, i+j));
241
+ }
242
+ return frame;
243
+ }
244
+ }
245
+
246
+ static VALUE ra_buffer_index_get(RA_BUFFER *buf, long i) {
247
+ switch(buf->type) {
248
+ case RA_BUFFER_TYPE_SHORT: return INT2FIX((int)((short*)buf->data)[i]);
249
+ case RA_BUFFER_TYPE_INT: return INT2FIX(((int*)buf->data)[i]);
250
+ case RA_BUFFER_TYPE_FLOAT: return rb_float_new((double)((float*)buf->data)[i]);
251
+ case RA_BUFFER_TYPE_DOUBLE: return rb_float_new(((double*)buf->data)[i]);
252
+ }
253
+ }
254
+
255
+ /*
256
+ * call-seq:
257
+ * buf[integer] = numeric => numeric
258
+ * buf[integer] = array => array
259
+ *
260
+ * Sets the frame of audio data at the given offset to the value. For
261
+ * multi-channel audio, pass in an array of values.
262
+ *
263
+ * buf = RubyAudio::Buffer.int(100, 1)
264
+ * buf[0] = 5
265
+ *
266
+ * buf = RubyAudio::Buffer.double(100, 2)
267
+ * buf[0] = [0.5, 0.3]
268
+ */
269
+ static VALUE ra_buffer_aset(VALUE self, VALUE index, VALUE val) {
270
+ RA_BUFFER *buf;
271
+ Data_Get_Struct(self, RA_BUFFER, buf);
272
+
273
+ // Bounds check
274
+ long f = FIX2LONG(index);
275
+ if(f < 0 || f >= buf->size) rb_raise(eRubyAudioError, "setting frame out of bounds");
276
+ long i = f * buf->channels;
277
+
278
+ // Set data
279
+ if(buf->channels == 1) {
280
+ ra_buffer_index_set(buf, i, val);
281
+ } else {
282
+ if(TYPE(val) != T_ARRAY) rb_raise(eRubyAudioError, "must pass in array for multi-channel buffer");
283
+ long length = RARRAY_LEN(val);
284
+ if(length != buf->channels) rb_raise(eRubyAudioError, "array length must match channel count");
285
+
286
+ long j;
287
+ for(j = 0; j < length; j++) {
288
+ ra_buffer_index_set(buf, i+j, rb_ary_entry(val, j));
289
+ }
290
+ }
291
+
292
+ // Bump real_size
293
+ if(f + 1 > buf->real_size) {
294
+ buf->real_size = f + 1;
295
+ }
296
+
297
+ return val;
298
+ }
299
+
300
+ static void ra_buffer_index_set(RA_BUFFER *buf, long i, VALUE val) {
301
+ switch(buf->type) {
302
+ case RA_BUFFER_TYPE_SHORT:
303
+ ((short*)buf->data)[i] = (short)FIX2INT(val);
304
+ break;
305
+ case RA_BUFFER_TYPE_INT:
306
+ ((int*)buf->data)[i] = FIX2INT(val);
307
+ break;
308
+ case RA_BUFFER_TYPE_FLOAT:
309
+ ((float*)buf->data)[i] = (float)RFLOAT_VALUE(val);
310
+ break;
311
+ case RA_BUFFER_TYPE_DOUBLE:
312
+ ((double*)buf->data)[i] = RFLOAT_VALUE(val);
313
+ break;
314
+ }
315
+ }
@@ -0,0 +1,40 @@
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_aref(VALUE self, VALUE index);
36
+ static VALUE ra_buffer_index_get(RA_BUFFER *buf, long i);
37
+ static VALUE ra_buffer_aset(VALUE self, VALUE index, VALUE val);
38
+ static void ra_buffer_index_set(RA_BUFFER *buf, long i, VALUE val);
39
+
40
+ #endif // #ifndef RA_BUFFER_H
@@ -0,0 +1,506 @@
1
+ #include "ra_sound.h"
2
+
3
+ extern VALUE eRubyAudioError;
4
+
5
+ /*
6
+ * Class <code>CSound</code> is a very light wrapper around the
7
+ * <code>SNDFILE</code> struct exposed by libsndfile.
8
+ */
9
+ void Init_ra_sound() {
10
+ VALUE mRubyAudio = rb_define_module("RubyAudio");
11
+ VALUE cRASound = rb_define_class_under(mRubyAudio, "CSound", rb_cObject);
12
+ rb_define_alloc_func(cRASound, ra_sound_allocate);
13
+ rb_define_singleton_method(cRASound, "open", ra_sound_s_open, -1);
14
+ rb_define_method(cRASound, "initialize", ra_sound_init, 3);
15
+ rb_define_method(cRASound, "info", ra_sound_info, 0);
16
+ rb_define_method(cRASound, "seek", ra_sound_seek, 2);
17
+ rb_define_method(cRASound, "read", ra_sound_read, 2);
18
+ rb_define_method(cRASound, "write", ra_sound_write, 1);
19
+ rb_define_method(cRASound, "<<", ra_sound_addbuf, 1);
20
+ rb_define_method(cRASound, "close", ra_sound_close, 0);
21
+ rb_define_method(cRASound, "closed?", ra_sound_closed, 0);
22
+ }
23
+
24
+ static VALUE ra_sound_allocate(VALUE klass) {
25
+ RA_SOUND *snd = ALLOC(RA_SOUND);
26
+ memset(snd, 0, sizeof(RA_SOUND));
27
+ VALUE self = Data_Wrap_Struct(klass, ra_sound_mark, ra_sound_free, snd);
28
+ return self;
29
+ }
30
+
31
+ static void ra_sound_mark(RA_SOUND *snd) {
32
+ if(snd) {
33
+ rb_gc_mark(snd->info);
34
+ }
35
+ }
36
+
37
+ static void ra_sound_free(RA_SOUND *snd) {
38
+ if(!snd->closed && snd->snd != NULL) sf_close(snd->snd);
39
+ xfree(snd);
40
+ }
41
+
42
+ /*
43
+ * call-seq:
44
+ * CSound.open(...) => snd
45
+ * CSound.open(...) {|snd| block } => obj
46
+ *
47
+ * With no associated block, <code>open</code> is a synonym for
48
+ * <code>CSound.new</code>. If the optional code block is given, it will be
49
+ * passed <i>snd</i> as an argument, and the CSound object will automatically be
50
+ * closed when the block terminates. In this instance, <code>CSound.open</code>
51
+ * returns the value of the block.
52
+ */
53
+ static VALUE ra_sound_s_open(int argc, VALUE *argv, VALUE klass) {
54
+ VALUE obj = rb_class_new_instance(argc, argv, klass);
55
+ if(!rb_block_given_p()) return obj;
56
+ return rb_ensure(rb_yield, obj, ra_sound_close_safe, obj);
57
+ }
58
+
59
+ /*
60
+ * call-seq:
61
+ * CSound.new(path, mode, info) => snd
62
+ *
63
+ * 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>.
66
+ */
67
+ static VALUE ra_sound_init(VALUE self, VALUE path, VALUE mode, VALUE info) {
68
+ RA_SOUND *snd;
69
+ Data_Get_Struct(self, RA_SOUND, snd);
70
+
71
+ // Get mode
72
+ const char *m = StringValueCStr(mode);
73
+ if(strcmp(m, "rw") == 0) snd->mode = SFM_RDWR;
74
+ else if(strcmp(m, "r") == 0) snd->mode = SFM_READ;
75
+ else if(strcmp(m, "w") == 0) snd->mode = SFM_WRITE;
76
+ else rb_raise(rb_eArgError, "invalid access mode %s", m);
77
+
78
+ // Set info
79
+ snd->info = info;
80
+
81
+ // Open sound file
82
+ const char *p = StringValueCStr(path);
83
+ SF_INFO *sf_info;
84
+ Data_Get_Struct(info, SF_INFO, sf_info);
85
+ snd->snd = sf_open(p, snd->mode, sf_info);
86
+ if(snd->snd == NULL) rb_raise(eRubyAudioError, sf_strerror(snd->snd));
87
+ snd->closed = 0;
88
+
89
+ return self;
90
+ }
91
+
92
+ /*
93
+ * call-seq:
94
+ * snd.info => CSoundInfo
95
+ *
96
+ * Returns the info object associated with the sound.
97
+ */
98
+ static VALUE ra_sound_info(VALUE self) {
99
+ RA_SOUND *snd;
100
+ Data_Get_Struct(self, RA_SOUND, snd);
101
+ return snd->info;
102
+ }
103
+
104
+ /*
105
+ * call-seq:
106
+ * snd.seek(frames, whence) => 0
107
+ *
108
+ * Seeks to a given offset <i>anInteger</i> in the sound according to the value
109
+ * of <i>whence</i>:
110
+ *
111
+ * IO::SEEK_CUR | Seeks to _frames_ plus current position
112
+ * --------------+----------------------------------------------------
113
+ * IO::SEEK_END | Seeks to _frames_ plus end of stream (you probably
114
+ * | want a negative value for _frames_)
115
+ * --------------+----------------------------------------------------
116
+ * IO::SEEK_SET | Seeks to the absolute location given by _frames_
117
+ */
118
+ static VALUE ra_sound_seek(VALUE self, VALUE frames, VALUE whence) {
119
+ RA_SOUND *snd;
120
+ Data_Get_Struct(self, RA_SOUND, snd);
121
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
122
+
123
+ if(sf_seek(snd->snd, (sf_count_t)NUM2OFFT(frames), FIX2INT(whence)) == -1) {
124
+ rb_raise(eRubyAudioError, "invalid seek");
125
+ }
126
+
127
+ return INT2FIX(0);
128
+ }
129
+
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;
364
+ }
365
+
366
+ /*
367
+ * call-seq:
368
+ * snd.read(buf, frames) => integer
369
+ *
370
+ * Tries to read the given number of frames into the buffer and returns the
371
+ * number of frames actually read.
372
+ */
373
+ static VALUE ra_sound_read(VALUE self, VALUE buf, VALUE frames) {
374
+ RA_SOUND *snd;
375
+ Data_Get_Struct(self, RA_SOUND, snd);
376
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
377
+
378
+ // Get buffer struct
379
+ RA_BUFFER *b;
380
+ Data_Get_Struct(buf, RA_BUFFER, b);
381
+
382
+ // Double-check frame count against buffer size
383
+ sf_count_t f = (sf_count_t)NUM2OFFT(frames);
384
+ if(f < 0 || f > b->size) {
385
+ rb_raise(eRubyAudioError, "frame count invalid");
386
+ }
387
+
388
+ // Shortcut for 0 frame reads
389
+ if(f == 0) {
390
+ b->real_size = 0;
391
+ return INT2FIX(b->real_size);;
392
+ }
393
+
394
+ // Read based on type
395
+ switch(b->type) {
396
+ case RA_BUFFER_TYPE_SHORT:
397
+ ra_sound_read_short(snd, b, f);
398
+ break;
399
+ case RA_BUFFER_TYPE_INT:
400
+ ra_sound_read_int(snd, b, f);
401
+ break;
402
+ case RA_BUFFER_TYPE_FLOAT:
403
+ ra_sound_read_float(snd, b, f);
404
+ break;
405
+ case RA_BUFFER_TYPE_DOUBLE:
406
+ ra_sound_read_double(snd, b, f);
407
+ break;
408
+ }
409
+
410
+ // Return read
411
+ return INT2FIX(b->real_size);
412
+ }
413
+
414
+ /*
415
+ * call-seq:
416
+ * snd.write(buf) => integer
417
+ *
418
+ * Writes the entire contents of the given buffer to the sound and returns the
419
+ * number of frames written.
420
+ */
421
+ static VALUE ra_sound_write(VALUE self, VALUE buf) {
422
+ RA_SOUND *snd;
423
+ Data_Get_Struct(self, RA_SOUND, snd);
424
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
425
+
426
+ // Get buffer struct
427
+ RA_BUFFER *b;
428
+ Data_Get_Struct(buf, RA_BUFFER, b);
429
+
430
+ // Get info struct
431
+ SF_INFO *info;
432
+ Data_Get_Struct(snd->info, SF_INFO, info);
433
+
434
+ // Check buffer channels matches actual channels
435
+ if(b->channels != info->channels) {
436
+ rb_raise(eRubyAudioError, "channel count mismatch: %d vs %d", b->channels, info->channels);
437
+ }
438
+
439
+ // Write data
440
+ sf_count_t written = 0;
441
+ switch(b->type) {
442
+ case RA_BUFFER_TYPE_SHORT:
443
+ written = sf_writef_short(snd->snd, b->data, b->real_size);
444
+ break;
445
+ case RA_BUFFER_TYPE_INT:
446
+ written = sf_writef_int(snd->snd, b->data, b->real_size);
447
+ break;
448
+ case RA_BUFFER_TYPE_FLOAT:
449
+ written = sf_writef_float(snd->snd, b->data, b->real_size);
450
+ break;
451
+ case RA_BUFFER_TYPE_DOUBLE:
452
+ written = sf_writef_double(snd->snd, b->data, b->real_size);
453
+ break;
454
+ }
455
+
456
+ return OFFT2NUM(written);
457
+ }
458
+
459
+ /*
460
+ * call-seq:
461
+ * snd << buf => snd
462
+ *
463
+ * Writes the given buffer to the string.
464
+ *
465
+ * snd << buf1 << buf2
466
+ */
467
+ static VALUE ra_sound_addbuf(VALUE self, VALUE buf) {
468
+ ra_sound_write(self, buf);
469
+ return self;
470
+ }
471
+
472
+ /*
473
+ * call-seq:
474
+ * snd.close => nil
475
+ *
476
+ * Closes <i>snd</i> and frees up all memory associated with the sound. The
477
+ * sound is unavailable for any further data operations; an error is raised if
478
+ * such an attempt is made. Sounds are automatically closed when they are claimed
479
+ * by the garbage collector.
480
+ */
481
+ static VALUE ra_sound_close(VALUE self) {
482
+ RA_SOUND *snd;
483
+ Data_Get_Struct(self, RA_SOUND, snd);
484
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
485
+
486
+ sf_close(snd->snd);
487
+ snd->snd = NULL;
488
+ snd->closed = 1;
489
+ return Qnil;
490
+ }
491
+
492
+ static VALUE ra_sound_close_safe(VALUE self) {
493
+ return rb_rescue(ra_sound_close, self, 0, 0);
494
+ }
495
+
496
+ /*
497
+ * call-seq:
498
+ * snd.closed? => true or false
499
+ *
500
+ * Whether or not the current sound is closed to further operations.
501
+ */
502
+ static VALUE ra_sound_closed(VALUE self) {
503
+ RA_SOUND *snd;
504
+ Data_Get_Struct(self, RA_SOUND, snd);
505
+ return snd->closed ? Qtrue : Qfalse;
506
+ }