ruby-audio-heroku 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,403 @@
1
+ #include "ra_sound.h"
2
+
3
+ extern VALUE eRubyAudioError;
4
+ ID id_size;
5
+ ID id_seek;
6
+ ID id_read;
7
+ ID id_write;
8
+ ID id_tell;
9
+
10
+ /*
11
+ * Class <code>CSound</code> is a very light wrapper around the
12
+ * <code>SNDFILE</code> struct exposed by libsndfile.
13
+ */
14
+ void Init_ra_sound() {
15
+ VALUE mRubyAudio = rb_define_module("RubyAudio");
16
+ VALUE cRASound = rb_define_class_under(mRubyAudio, "CSound", rb_cObject);
17
+ rb_define_alloc_func(cRASound, ra_sound_allocate);
18
+ rb_define_singleton_method(cRASound, "open", ra_sound_s_open, -1);
19
+ rb_define_method(cRASound, "initialize", ra_sound_init, 3);
20
+ rb_define_method(cRASound, "info", ra_sound_info, 0);
21
+ rb_define_method(cRASound, "seek", ra_sound_seek, 2);
22
+ rb_define_method(cRASound, "read", ra_sound_read, 2);
23
+ rb_define_method(cRASound, "write", ra_sound_write, 1);
24
+ rb_define_method(cRASound, "<<", ra_sound_addbuf, 1);
25
+ rb_define_method(cRASound, "close", ra_sound_close, 0);
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");
34
+ }
35
+
36
+ static VALUE ra_sound_allocate(VALUE klass) {
37
+ RA_SOUND *snd = ALLOC(RA_SOUND);
38
+ memset(snd, 0, sizeof(RA_SOUND));
39
+ VALUE self = Data_Wrap_Struct(klass, ra_sound_mark, ra_sound_free, snd);
40
+ return self;
41
+ }
42
+
43
+ static void ra_sound_mark(RA_SOUND *snd) {
44
+ if(snd) {
45
+ rb_gc_mark(snd->info);
46
+ if(snd->vio_source) rb_gc_mark(snd->vio_source);
47
+ }
48
+ }
49
+
50
+ static void ra_sound_free(RA_SOUND *snd) {
51
+ if(!snd->closed && snd->snd != NULL) sf_close(snd->snd);
52
+ xfree(snd);
53
+ }
54
+
55
+ /*
56
+ * call-seq:
57
+ * CSound.open(...) => snd
58
+ * CSound.open(...) {|snd| block } => obj
59
+ *
60
+ * With no associated block, <code>open</code> is a synonym for
61
+ * <code>CSound.new</code>. If the optional code block is given, it will be
62
+ * passed <i>snd</i> as an argument, and the CSound object will automatically be
63
+ * closed when the block terminates. In this instance, <code>CSound.open</code>
64
+ * returns the value of the block.
65
+ */
66
+ static VALUE ra_sound_s_open(int argc, VALUE *argv, VALUE klass) {
67
+ VALUE obj = rb_class_new_instance(argc, argv, klass);
68
+ if(!rb_block_given_p()) return obj;
69
+ return rb_ensure(rb_yield, obj, ra_sound_close_safe, obj);
70
+ }
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
+
109
+ /*
110
+ * call-seq:
111
+ * CSound.new(path, mode, info) => snd
112
+ * CSound.new(io, mode, info) => snd
113
+ *
114
+ * Returns a new <code>CSound</code> object for the audio file at the given path
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.
120
+ */
121
+ static VALUE ra_sound_init(VALUE self, VALUE source, VALUE mode, VALUE info) {
122
+ RA_SOUND *snd;
123
+ Data_Get_Struct(self, RA_SOUND, snd);
124
+
125
+ // Get mode
126
+ const char *m = StringValueCStr(mode);
127
+ if(strcmp(m, "rw") == 0) snd->mode = SFM_RDWR;
128
+ else if(strcmp(m, "r") == 0) snd->mode = SFM_READ;
129
+ else if(strcmp(m, "w") == 0) snd->mode = SFM_WRITE;
130
+ else rb_raise(rb_eArgError, "invalid access mode %s", m);
131
+
132
+ // Set info
133
+ snd->info = info;
134
+
135
+ // Open sound file
136
+ SF_INFO *sf_info;
137
+ Data_Get_Struct(info, SF_INFO, 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
+ }
155
+ if(snd->snd == NULL) rb_raise(eRubyAudioError, sf_strerror(snd->snd));
156
+ snd->closed = 0;
157
+
158
+ return self;
159
+ }
160
+
161
+ /*
162
+ * call-seq:
163
+ * snd.info => CSoundInfo
164
+ *
165
+ * Returns the info object associated with the sound.
166
+ */
167
+ static VALUE ra_sound_info(VALUE self) {
168
+ RA_SOUND *snd;
169
+ Data_Get_Struct(self, RA_SOUND, snd);
170
+ return snd->info;
171
+ }
172
+
173
+ /*
174
+ * call-seq:
175
+ * snd.seek(frames, whence) => 0
176
+ *
177
+ * Seeks to a given offset <i>anInteger</i> in the sound according to the value
178
+ * of <i>whence</i>:
179
+ *
180
+ * IO::SEEK_CUR | Seeks to _frames_ plus current position
181
+ * --------------+----------------------------------------------------
182
+ * IO::SEEK_END | Seeks to _frames_ plus end of stream (you probably
183
+ * | want a negative value for _frames_)
184
+ * --------------+----------------------------------------------------
185
+ * IO::SEEK_SET | Seeks to the absolute location given by _frames_
186
+ */
187
+ static VALUE ra_sound_seek(VALUE self, VALUE frames, VALUE whence) {
188
+ RA_SOUND *snd;
189
+ Data_Get_Struct(self, RA_SOUND, snd);
190
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
191
+
192
+ if(sf_seek(snd->snd, (sf_count_t)NUM2OFFT(frames), FIX2INT(whence)) == -1) {
193
+ rb_raise(eRubyAudioError, "invalid seek");
194
+ }
195
+
196
+ return INT2FIX(0);
197
+ }
198
+
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; \
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);
262
+
263
+ /*
264
+ * call-seq:
265
+ * snd.read(buf, frames) => integer
266
+ *
267
+ * Tries to read the given number of frames into the buffer and returns the
268
+ * number of frames actually read.
269
+ */
270
+ static VALUE ra_sound_read(VALUE self, VALUE buf, VALUE frames) {
271
+ RA_SOUND *snd;
272
+ Data_Get_Struct(self, RA_SOUND, snd);
273
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
274
+
275
+ // Get buffer struct
276
+ RA_BUFFER *b;
277
+ Data_Get_Struct(buf, RA_BUFFER, b);
278
+
279
+ // Double-check frame count against buffer size
280
+ sf_count_t f = (sf_count_t)NUM2OFFT(frames);
281
+ if(f < 0 || f > b->size) {
282
+ rb_raise(eRubyAudioError, "frame count invalid");
283
+ }
284
+
285
+ // Shortcut for 0 frame reads
286
+ if(f == 0) {
287
+ b->real_size = 0;
288
+ return INT2FIX(b->real_size);;
289
+ }
290
+
291
+ // Read based on type
292
+ switch(b->type) {
293
+ case RA_BUFFER_TYPE_SHORT:
294
+ ra_sound_read_short(snd, b, f);
295
+ break;
296
+ case RA_BUFFER_TYPE_INT:
297
+ ra_sound_read_int(snd, b, f);
298
+ break;
299
+ case RA_BUFFER_TYPE_FLOAT:
300
+ ra_sound_read_float(snd, b, f);
301
+ break;
302
+ case RA_BUFFER_TYPE_DOUBLE:
303
+ ra_sound_read_double(snd, b, f);
304
+ break;
305
+ }
306
+
307
+ // Return read
308
+ return INT2FIX(b->real_size);
309
+ }
310
+
311
+ /*
312
+ * call-seq:
313
+ * snd.write(buf) => integer
314
+ *
315
+ * Writes the entire contents of the given buffer to the sound and returns the
316
+ * number of frames written.
317
+ */
318
+ static VALUE ra_sound_write(VALUE self, VALUE buf) {
319
+ RA_SOUND *snd;
320
+ Data_Get_Struct(self, RA_SOUND, snd);
321
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
322
+
323
+ // Get buffer struct
324
+ RA_BUFFER *b;
325
+ Data_Get_Struct(buf, RA_BUFFER, b);
326
+
327
+ // Get info struct
328
+ SF_INFO *info;
329
+ Data_Get_Struct(snd->info, SF_INFO, info);
330
+
331
+ // Check buffer channels matches actual channels
332
+ if(b->channels != info->channels) {
333
+ rb_raise(eRubyAudioError, "channel count mismatch: %d vs %d", b->channels, info->channels);
334
+ }
335
+
336
+ // Write data
337
+ sf_count_t written = 0;
338
+ switch(b->type) {
339
+ case RA_BUFFER_TYPE_SHORT:
340
+ written = sf_writef_short(snd->snd, b->data, b->real_size);
341
+ break;
342
+ case RA_BUFFER_TYPE_INT:
343
+ written = sf_writef_int(snd->snd, b->data, b->real_size);
344
+ break;
345
+ case RA_BUFFER_TYPE_FLOAT:
346
+ written = sf_writef_float(snd->snd, b->data, b->real_size);
347
+ break;
348
+ case RA_BUFFER_TYPE_DOUBLE:
349
+ written = sf_writef_double(snd->snd, b->data, b->real_size);
350
+ break;
351
+ }
352
+
353
+ return OFFT2NUM(written);
354
+ }
355
+
356
+ /*
357
+ * call-seq:
358
+ * snd << buf => snd
359
+ *
360
+ * Writes the given buffer to the string.
361
+ *
362
+ * snd << buf1 << buf2
363
+ */
364
+ static VALUE ra_sound_addbuf(VALUE self, VALUE buf) {
365
+ ra_sound_write(self, buf);
366
+ return self;
367
+ }
368
+
369
+ /*
370
+ * call-seq:
371
+ * snd.close => nil
372
+ *
373
+ * Closes <i>snd</i> and frees up all memory associated with the sound. The
374
+ * sound is unavailable for any further data operations; an error is raised if
375
+ * such an attempt is made. Sounds are automatically closed when they are claimed
376
+ * by the garbage collector.
377
+ */
378
+ static VALUE ra_sound_close(VALUE self) {
379
+ RA_SOUND *snd;
380
+ Data_Get_Struct(self, RA_SOUND, snd);
381
+ if(snd->closed) rb_raise(eRubyAudioError, "closed sound");
382
+
383
+ sf_close(snd->snd);
384
+ snd->snd = NULL;
385
+ snd->closed = 1;
386
+ return Qnil;
387
+ }
388
+
389
+ static VALUE ra_sound_close_safe(VALUE self) {
390
+ return rb_rescue(ra_sound_close, self, 0, 0);
391
+ }
392
+
393
+ /*
394
+ * call-seq:
395
+ * snd.closed? => true or false
396
+ *
397
+ * Whether or not the current sound is closed to further operations.
398
+ */
399
+ static VALUE ra_sound_closed(VALUE self) {
400
+ RA_SOUND *snd;
401
+ Data_Get_Struct(self, RA_SOUND, snd);
402
+ return snd->closed ? Qtrue : Qfalse;
403
+ }
@@ -0,0 +1,38 @@
1
+ #ifndef RA_SOUND_H
2
+ #define RA_SOUND_H
3
+
4
+ #include <ruby.h>
5
+ #include <sndfile.h>
6
+ #include "ra_soundinfo.h"
7
+ #include "ra_buffer.h"
8
+
9
+ typedef struct {
10
+ SNDFILE *snd;
11
+ VALUE info;
12
+ VALUE vio_source;
13
+ int mode;
14
+ int closed;
15
+ } RA_SOUND;
16
+
17
+ void Init_ra_sound();
18
+
19
+ /*** Initialization and Memory Manangement ***/
20
+ static VALUE ra_sound_allocate(VALUE klass);
21
+ static void ra_sound_mark(RA_SOUND *snd);
22
+ static void ra_sound_free(RA_SOUND *snd);
23
+
24
+ /*** Singleton Methods ***/
25
+ static VALUE ra_sound_s_open(int argc, VALUE *argv, VALUE klass);
26
+
27
+ /*** Instance Methods ***/
28
+ static VALUE ra_sound_init(VALUE self, VALUE path, VALUE mode, VALUE info);
29
+ static VALUE ra_sound_info(VALUE self);
30
+ static VALUE ra_sound_seek(VALUE self, VALUE frames, VALUE whence);
31
+ static VALUE ra_sound_read(VALUE self, VALUE buf, VALUE frames);
32
+ static VALUE ra_sound_write(VALUE self, VALUE buf);
33
+ static VALUE ra_sound_addbuf(VALUE self, VALUE buf);
34
+ static VALUE ra_sound_close(VALUE self);
35
+ static VALUE ra_sound_close_safe(VALUE self);
36
+ static VALUE ra_sound_closed(VALUE self);
37
+
38
+ #endif // #ifndef RA_SOUND_H
@@ -0,0 +1,165 @@
1
+ #include "ra_soundinfo.h"
2
+
3
+ extern VALUE eRubyAudioError;
4
+
5
+ /*
6
+ * Class <code>CSoundInfo</code> is a very light wrapper around the
7
+ * <code>SF_INFO</code> struct exposed by libsndfile. It provides information
8
+ * about open sound files like format, length, samplerate, channels, and other
9
+ * things. In addition it can be used to specify the format of new sound files.
10
+ */
11
+ void Init_ra_soundinfo() {
12
+ VALUE mRubyAudio = rb_define_module("RubyAudio");
13
+ VALUE cRASoundInfo = rb_define_class_under(mRubyAudio, "CSoundInfo", rb_cObject);
14
+ rb_define_alloc_func(cRASoundInfo, ra_soundinfo_allocate);
15
+ rb_define_method(cRASoundInfo, "valid?", ra_soundinfo_valid, 0);
16
+ rb_define_method(cRASoundInfo, "frames", ra_soundinfo_frames, 0);
17
+ rb_define_method(cRASoundInfo, "samplerate", ra_soundinfo_samplerate, 0);
18
+ rb_define_method(cRASoundInfo, "samplerate=", ra_soundinfo_samplerate_set, 1);
19
+ rb_define_method(cRASoundInfo, "channels", ra_soundinfo_channels, 0);
20
+ rb_define_method(cRASoundInfo, "channels=", ra_soundinfo_channels_set, 1);
21
+ rb_define_method(cRASoundInfo, "format", ra_soundinfo_format, 0);
22
+ rb_define_method(cRASoundInfo, "format=", ra_soundinfo_format_set, 1);
23
+ rb_define_method(cRASoundInfo, "sections", ra_soundinfo_sections, 0);
24
+ rb_define_method(cRASoundInfo, "seekable", ra_soundinfo_seekable, 0);
25
+ }
26
+
27
+ static VALUE ra_soundinfo_allocate(VALUE klass) {
28
+ SF_INFO *info = ALLOC(SF_INFO);
29
+ memset(info, 0, sizeof(SF_INFO));
30
+ VALUE self = Data_Wrap_Struct(klass, NULL, ra_soundinfo_free, info);
31
+ return self;
32
+ }
33
+
34
+ static void ra_soundinfo_free(SF_INFO *info) {
35
+ xfree(info);
36
+ }
37
+
38
+ /*
39
+ * call-seq:
40
+ * info.valid? => true or false
41
+ *
42
+ * Calls <code>sf_format_check</code> on the underlying <code>SF_INFO</code>
43
+ * struct and returns true or false based on validity. Used when creating a new
44
+ * sound file to check that the format has enough information to properly create
45
+ * a new sound.
46
+ */
47
+ static VALUE ra_soundinfo_valid(VALUE self) {
48
+ SF_INFO *info;
49
+ Data_Get_Struct(self, SF_INFO, info);
50
+ return sf_format_check(info) ? Qtrue : Qfalse;
51
+ }
52
+
53
+ /*
54
+ * call-seq:
55
+ * info.frames => integer
56
+ *
57
+ * Returns the number of frames in the associated sound file.
58
+ */
59
+ static VALUE ra_soundinfo_frames(VALUE self) {
60
+ SF_INFO *info;
61
+ Data_Get_Struct(self, SF_INFO, info);
62
+ return OFFT2NUM(info->frames);
63
+ }
64
+
65
+ /*
66
+ * call-seq:
67
+ * info.samplerate => integer
68
+ *
69
+ * Returns the samplerate of the associated sound file.
70
+ */
71
+ static VALUE ra_soundinfo_samplerate(VALUE self) {
72
+ SF_INFO *info;
73
+ Data_Get_Struct(self, SF_INFO, info);
74
+ return INT2FIX(info->samplerate);
75
+ }
76
+
77
+ /*
78
+ * call-seq:
79
+ * info.samplerate = integer => integer
80
+ *
81
+ * Set the samplerate for a new sound created with the given info object.
82
+ */
83
+ static VALUE ra_soundinfo_samplerate_set(VALUE self, VALUE new_samplerate) {
84
+ SF_INFO *info;
85
+ Data_Get_Struct(self, SF_INFO, info);
86
+ info->samplerate = FIX2INT(new_samplerate);
87
+ return new_samplerate;
88
+ }
89
+
90
+ /*
91
+ * call-seq:
92
+ * info.channels => integer
93
+ *
94
+ * Returns the number of channels in the associated sound file.
95
+ */
96
+ static VALUE ra_soundinfo_channels(VALUE self) {
97
+ SF_INFO *info;
98
+ Data_Get_Struct(self, SF_INFO, info);
99
+ return INT2FIX(info->channels);
100
+ }
101
+
102
+ /*
103
+ * call-seq:
104
+ * info.channels = integer => integer
105
+ *
106
+ * Set the number of channels for a new sound created with the given info object.
107
+ */
108
+ static VALUE ra_soundinfo_channels_set(VALUE self, VALUE new_channels) {
109
+ SF_INFO *info;
110
+ Data_Get_Struct(self, SF_INFO, info);
111
+ info->channels = FIX2INT(new_channels);
112
+ return new_channels;
113
+ }
114
+
115
+ /*
116
+ * call-seq:
117
+ * info.format => integer
118
+ *
119
+ * Returns the format as a combination of binary flags of the associated sound file.
120
+ */
121
+ static VALUE ra_soundinfo_format(VALUE self) {
122
+ SF_INFO *info;
123
+ Data_Get_Struct(self, SF_INFO, info);
124
+ return INT2FIX(info->format);
125
+ }
126
+
127
+ /*
128
+ * call-seq:
129
+ * info.format = integer => integer
130
+ *
131
+ * Set the format for a new sound created with the given info object.
132
+ *
133
+ * info = RubyAudio::CSoundInfo.new
134
+ * info.format = RubyAudio::FORMAT_WAV|RubyAudio::FORMAT_PCM_16
135
+ */
136
+ static VALUE ra_soundinfo_format_set(VALUE self, VALUE new_format) {
137
+ SF_INFO *info;
138
+ Data_Get_Struct(self, SF_INFO, info);
139
+ info->format = FIX2INT(new_format);
140
+ return new_format;
141
+ }
142
+
143
+ /*
144
+ * call-seq:
145
+ * info.sections => integer
146
+ *
147
+ * Returns the number of sections in the associated sound file.
148
+ */
149
+ static VALUE ra_soundinfo_sections(VALUE self) {
150
+ SF_INFO *info;
151
+ Data_Get_Struct(self, SF_INFO, info);
152
+ return INT2FIX(info->sections);
153
+ }
154
+
155
+ /*
156
+ * call-seq:
157
+ * info.seekable => true or false
158
+ *
159
+ * Whether seeking is supported for the associated sound file.
160
+ */
161
+ static VALUE ra_soundinfo_seekable(VALUE self) {
162
+ SF_INFO *info;
163
+ Data_Get_Struct(self, SF_INFO, info);
164
+ return info->seekable ? Qtrue : Qfalse;
165
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef RA_SOUNDINFO_H
2
+ #define RA_SOUNDINFO_H
3
+
4
+ #include <ruby.h>
5
+ #include <sndfile.h>
6
+
7
+ void Init_ra_soundinfo();
8
+
9
+ /*** Initialization and Memory Manangement ***/
10
+ static VALUE ra_soundinfo_allocate(VALUE klass);
11
+ static void ra_soundinfo_free(SF_INFO *info);
12
+
13
+ /*** Instance Methods ***/
14
+ static VALUE ra_soundinfo_valid(VALUE self);
15
+ static VALUE ra_soundinfo_frames(VALUE self);
16
+ static VALUE ra_soundinfo_samplerate(VALUE self);
17
+ static VALUE ra_soundinfo_samplerate_set(VALUE self, VALUE new_samplerate);
18
+ static VALUE ra_soundinfo_channels(VALUE self);
19
+ static VALUE ra_soundinfo_channels_set(VALUE self, VALUE new_channels);
20
+ static VALUE ra_soundinfo_format(VALUE self);
21
+ static VALUE ra_soundinfo_format_set(VALUE self, VALUE new_format);
22
+ static VALUE ra_soundinfo_sections(VALUE self);
23
+ static VALUE ra_soundinfo_seekable(VALUE self);
24
+
25
+ #endif // #ifndef RA_SOUNDINFO_H