ruby-audio 1.5.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.
@@ -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
+ }