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.
@@ -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