rsox 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ ## RSox is a libSoX binding for Ruby
2
+
3
+ ---
4
+
5
+ ### Usage
6
+
7
+ rewritten `example0.c` from [sox](http://sox.sourceforge.net/)
8
+
9
+ require 'rsox'
10
+
11
+ sox = RSox.new
12
+
13
+ input = sox.open_read 'file.mp3'
14
+ output = sox.open_write 'file.wav', input.signal
15
+
16
+ chain = sox.chain input, output
17
+ chain.add 'input', input
18
+ chain.add 'vol', '3dB'
19
+ chain.add 'flanger'
20
+ chain.add 'output', output
21
+
22
+ chain.flow
23
+
24
+ processing samples in Ruby
25
+
26
+ require 'rsox'
27
+
28
+ sox = RSox.new
29
+
30
+ input = sox.open_read 'file.mp3'
31
+ output = sox.open_write 'file.wav', input.signal
32
+
33
+ chain = sox.chain input, output
34
+ chain.add 'input', input
35
+ chain.add 'vol', '3dB'
36
+ chain.add 'flanger'
37
+ chain.add 'block' do |buffer|
38
+ # samples in buffer
39
+ buffer.size # or buffer.length
40
+
41
+ # access samples
42
+ buffer[0] # or buffer.at(0)
43
+ buffer[buffer.size-1] # last sample
44
+
45
+ # each sample is a 32bit signed integer converted to Fixnum
46
+ buffer[0].class == Fixnum
47
+ end
48
+
49
+ # all output data will be passed to `block` effect
50
+ # and processed by Ruby code block
51
+ chain.flow
52
+
53
+ ---
54
+
55
+ ### Thanks
56
+
57
+ [Roman Golomidov](https://github.com/golomidov) for idea and support
data/ext/extconf.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'mkmf'
2
+
3
+ LIBDIR = Config::CONFIG['libdir']
4
+ INCLUDEDIR = Config::CONFIG['includedir']
5
+
6
+ HEADER_DIRS = [
7
+ # First search /opt/local for macports
8
+ '/opt/local/include',
9
+
10
+ # Then search /usr/local for people that installed from source
11
+ '/usr/local/include',
12
+
13
+ # Check the ruby install locations
14
+ INCLUDEDIR,
15
+
16
+ # Finally fall back to /usr
17
+ '/usr/include',
18
+ ]
19
+
20
+ LIB_DIRS = [
21
+ # First search /opt/local for macports
22
+ '/opt/local/lib',
23
+
24
+ # Then search /usr/local for people that installed from source
25
+ '/usr/local/lib',
26
+
27
+ # Check the ruby install locations
28
+ LIBDIR,
29
+
30
+ # Finally fall back to /usr
31
+ '/usr/lib',
32
+ ]
33
+
34
+ dir_config('rsox', HEADER_DIRS, LIB_DIRS)
35
+
36
+ if have_library("sox", "sox_format_init") && have_header("sox.h")
37
+ create_makefile('rsox')
38
+ end
data/ext/rsox.c ADDED
@@ -0,0 +1,441 @@
1
+ #include <ruby.h>
2
+ #include <sox.h>
3
+
4
+ static VALUE RSox;
5
+
6
+ static VALUE RSoxFormat;
7
+ static VALUE RSoxEffect;
8
+ static VALUE RSoxEffectsChain;
9
+ static VALUE RSoxSignal;
10
+ static VALUE RSoxEncoding;
11
+ static VALUE RSoxBuffer;
12
+
13
+ static void rsox_destroy(void *instance) {/*{{{*/
14
+ int *instance_count;
15
+
16
+ instance_count = (int*)instance;
17
+ *instance_count -= 1;
18
+ if (*instance_count == 0)
19
+ sox_quit();
20
+ rb_iv_set(RSox, "@instance_count", INT2NUM(*instance_count));
21
+ }/*}}}*/
22
+
23
+ VALUE rsox_new(VALUE class) {/*{{{*/
24
+ static int rsox_instance_count = 0;
25
+ VALUE instance_num;
26
+
27
+ rsox_instance_count++;
28
+ instance_num = Data_Wrap_Struct(RSox, 0, rsox_destroy, &rsox_instance_count);
29
+
30
+ rb_iv_set(class, "@instance_count", INT2NUM(rsox_instance_count));
31
+ rb_obj_call_init(instance_num, 0, 0);
32
+
33
+ return instance_num;
34
+ }/*}}}*/
35
+
36
+ VALUE rsox_count(VALUE class) {/*{{{*/
37
+ return rb_iv_get(class, "@count");
38
+ }/*}}}*/
39
+
40
+ VALUE rsox_initialize(VALUE self) {/*{{{*/
41
+ int *instance_count;
42
+
43
+ Data_Get_Struct(self, int, instance_count);
44
+ if (*instance_count == 1)
45
+ sox_init();
46
+
47
+ return self;
48
+ }/*}}}*/
49
+
50
+ VALUE rsox_set_bufsize(VALUE self, VALUE bufsize) {
51
+ sox_globals.bufsiz = FIX2INT(bufsize);
52
+
53
+ return Qtrue;
54
+ }
55
+
56
+ VALUE rsox_get_bufsize(VALUE self) {
57
+ return INT2FIX(sox_globals.bufsiz);
58
+ }
59
+
60
+ VALUE rsox_format_init(VALUE self) {/*{{{*/
61
+ int i = sox_format_init();
62
+ return INT2NUM(i);
63
+ }/*}}}*/
64
+
65
+ VALUE rsox_format_quit(VALUE self) {/*{{{*/
66
+ sox_format_quit();
67
+ return Qnil;
68
+ }/*}}}*/
69
+
70
+ static void rsox_format_close(void *ptr) {/*{{{*/
71
+ sox_close(ptr);
72
+ }/*}}}*/
73
+
74
+ VALUE rsox_open_read(int argc, VALUE *argv, VALUE self) {/*{{{*/
75
+ VALUE path, signal, encoding, filetype;
76
+ sox_signalinfo_t *c_signal = NULL;
77
+ sox_encodinginfo_t *c_encoding = NULL;
78
+ sox_format_t *c_format;
79
+
80
+ rb_scan_args(argc, argv, "13", &path, &signal, &encoding, &filetype);
81
+
82
+ if (!NIL_P(signal)) Data_Get_Struct(signal, sox_signalinfo_t, c_signal);
83
+ if (!NIL_P(encoding)) Data_Get_Struct(encoding, sox_encodinginfo_t, c_encoding);
84
+
85
+ c_format = sox_open_read(StringValuePtr(path), c_signal, c_encoding, filetype == Qnil ? NULL : StringValuePtr(filetype));
86
+
87
+ return Data_Wrap_Struct(RSoxFormat, 0, rsox_format_close, c_format);
88
+ }/*}}}*/
89
+
90
+ sox_bool rsox_overwrite_callback(const char *filename) {/*{{{*/
91
+ return sox_false;
92
+ }/*}}}*/
93
+
94
+ VALUE rsox_open_write(int argc, VALUE *argv, VALUE self) {/*{{{*/
95
+ VALUE path, signal, encoding, filetype, oob;
96
+ sox_signalinfo_t *c_signal = NULL;
97
+ sox_encodinginfo_t *c_encoding = NULL;
98
+ sox_oob_t *c_oob = NULL;
99
+ sox_format_t *c_format;
100
+
101
+ rb_scan_args(argc, argv, "14", &path, &signal, &encoding, &filetype, &oob);
102
+
103
+ if (signal != Qnil) Data_Get_Struct(signal, sox_signalinfo_t, c_signal);
104
+ if (encoding != Qnil) Data_Get_Struct(encoding, sox_encodinginfo_t, c_encoding);
105
+ if (oob != Qnil) Data_Get_Struct(oob, sox_oob_t, c_oob);
106
+
107
+ c_format = sox_open_write(StringValuePtr(path),
108
+ c_signal,
109
+ c_encoding,
110
+ filetype == Qnil ? NULL : StringValuePtr(filetype),
111
+ c_oob,
112
+ rb_block_given_p() ? rsox_overwrite_callback : NULL);
113
+
114
+ return Data_Wrap_Struct(RSoxFormat, 0, 0, c_format);
115
+ }/*}}}*/
116
+
117
+ VALUE rsoxformat_signal(VALUE self) {/*{{{*/
118
+ sox_format_t *c_format;
119
+ sox_signalinfo_t *c_info;
120
+
121
+ Data_Get_Struct(self, sox_format_t, c_format);
122
+
123
+ return Data_Wrap_Struct(RSoxSignal, 0, 0, &c_format->signal);
124
+ }/*}}}*/
125
+
126
+ VALUE rsoxsignal_rate(VALUE self) {/*{{{*/
127
+ sox_signalinfo_t *c;
128
+
129
+ Data_Get_Struct(self, sox_signalinfo_t, c);
130
+
131
+ return DBL2NUM(c->rate);
132
+ }/*}}}*/
133
+
134
+ VALUE rsoxsignal_rate_set(VALUE self, VALUE rate) {/*{{{*/
135
+ sox_signalinfo_t *c;
136
+ double val = NUM2DBL(rate);
137
+
138
+ Data_Get_Struct(self, sox_signalinfo_t, c);
139
+ c->rate = val;
140
+
141
+ return rate;
142
+ }/*}}}*/
143
+
144
+ VALUE rsoxsignal_channels(VALUE self) {/*{{{*/
145
+ sox_signalinfo_t *c_signal;
146
+
147
+ Data_Get_Struct(self, sox_signalinfo_t, c_signal);
148
+
149
+ return UINT2NUM(c_signal->channels);
150
+ }/*}}}*/
151
+
152
+ VALUE rsoxsignal_channels_set(VALUE self, VALUE channels) {/*{{{*/
153
+ sox_signalinfo_t *c_signal;
154
+ unsigned int val = NUM2UINT(channels);
155
+
156
+ Data_Get_Struct(self, sox_signalinfo_t, c_signal);
157
+ c_signal->channels = val;
158
+
159
+ return channels;
160
+ }/*}}}*/
161
+
162
+ VALUE rsoxsignal_bits(VALUE self) {/*{{{*/
163
+ sox_signalinfo_t *c_signal;
164
+
165
+ Data_Get_Struct(self, sox_signalinfo_t, c_signal);
166
+
167
+ return UINT2NUM(c_signal->precision);
168
+ }/*}}}*/
169
+
170
+ VALUE rsoxsignal_bits_set(VALUE self, VALUE bits) {/*{{{*/
171
+ sox_signalinfo_t *c_signal;
172
+ unsigned int val = NUM2UINT(bits);
173
+
174
+ Data_Get_Struct(self, sox_signalinfo_t, c_signal);
175
+ c_signal->precision = val;
176
+
177
+ return bits;
178
+ }/*}}}*/
179
+
180
+ VALUE rsoxformat_encoding(VALUE self) {/*{{{*/
181
+ sox_format_t *c_format;
182
+
183
+ Data_Get_Struct(self, sox_format_t, c_format);
184
+
185
+ return Data_Wrap_Struct(RSoxEncoding, 0, 0, &c_format->encoding);
186
+ }/*}}}*/
187
+
188
+ VALUE rsoxformat_filename(VALUE self) {/*{{{*/
189
+ sox_format_t *c_format;
190
+
191
+ Data_Get_Struct(self, sox_format_t, c_format);
192
+
193
+ return rb_str_new2(c_format->filename);
194
+ }/*}}}*/
195
+
196
+ VALUE rsoxformat_read(VALUE self, VALUE buffer) { //, VALUE length) {/*{{{*/
197
+ sox_format_t *c_format;
198
+ sox_sample_t *c_buffer;
199
+
200
+ Data_Get_Struct(self, sox_format_t, c_format);
201
+ Data_Get_Struct(rb_iv_get(buffer, "@buffer"), sox_sample_t, c_buffer);
202
+
203
+ return INT2NUM(sox_read(c_format, c_buffer, NUM2INT(rb_iv_get(buffer, "@length"))));
204
+ }/*}}}*/
205
+
206
+ VALUE rsoxformat_write(VALUE self, VALUE buffer, VALUE length) {/*{{{*/
207
+ sox_format_t *c_format;
208
+ sox_sample_t *c_buffer;
209
+ int write_len = NUM2INT(length == Qnil ? rb_iv_get(buffer, "@length") : length);
210
+
211
+ Data_Get_Struct(self, sox_format_t, c_format);
212
+ Data_Get_Struct(rb_iv_get(buffer, "@buffer"), sox_sample_t, c_buffer);
213
+
214
+ return INT2NUM(sox_write(c_format, c_buffer, write_len));
215
+ }/*}}}*/
216
+
217
+ VALUE rsoxformat_seek(VALUE self, VALUE offset, VALUE whence){/*{{{*/
218
+ sox_format_t *c_format;
219
+
220
+ Data_Get_Struct(self, sox_format_t, c_format);
221
+
222
+ return INT2NUM(sox_seek(c_format, NUM2LONG(offset), NUM2INT(whence)));
223
+ }/*}}}*/
224
+
225
+ typedef struct {/*{{{ rsox_block_with_id_t */
226
+ VALUE block;
227
+ ID func;
228
+ } rsox_block_with_id_t;/*}}}*/
229
+
230
+ static int rsox_rubyblock_flow(sox_effect_t *effect, sox_sample_t const *ibuf, sox_sample_t *obuf UNUSED, size_t *isamp, size_t *osamp) {/*{{{*/
231
+ size_t i;
232
+ rsox_block_with_id_t *param = (rsox_block_with_id_t *)effect->priv;
233
+ VALUE buffer = Data_Wrap_Struct(RSoxBuffer, 0, 0, ibuf);
234
+ rb_iv_set(buffer, "@length", INT2NUM(*isamp));
235
+
236
+ if (*isamp > 0)
237
+ rb_funcall(param->block, param->func, 1, buffer);
238
+
239
+ *osamp = 0;
240
+
241
+ return SOX_SUCCESS;
242
+ }/*}}}*/
243
+
244
+ static sox_effect_handler_t const *rsox_rubyblock_handler(void) {/*{{{*/
245
+ static sox_effect_handler_t handler = {
246
+ "block", NULL, SOX_EFF_MCHAN, NULL, NULL, rsox_rubyblock_flow, NULL, NULL, NULL, sizeof(rsox_block_with_id_t)
247
+ };
248
+
249
+ return &handler;
250
+ }/*}}}*/
251
+
252
+ VALUE rsoxeffectschain_add(int argc, VALUE *argv, VALUE self) {/*{{{*/
253
+ sox_effects_chain_t *c_chain;
254
+ sox_effect_t *c_effect;
255
+ sox_format_t *c_input, *c_output, *c_tmp_format;
256
+ VALUE name, options, tmp, input, output;
257
+ sox_effect_handler_t const *c_handler;
258
+ char *c_options[10], *c_name;
259
+ int i, j, t;
260
+ rsox_block_with_id_t *block_param;
261
+
262
+ rb_scan_args(argc, argv, "1*", &name, &options);
263
+
264
+ c_name = StringValuePtr(name);
265
+
266
+ if (strncmp(c_name, "block", 5) == 0) {
267
+ if (!rb_block_given_p())
268
+ rb_raise(rb_eArgError, "no block given");
269
+
270
+ c_handler = rsox_rubyblock_handler();
271
+ c_effect = sox_create_effect(c_handler);
272
+
273
+ block_param = (rsox_block_with_id_t *)c_effect->priv;
274
+ block_param->block = rb_block_proc();
275
+ block_param->func = rb_intern("call");
276
+ } else {
277
+ c_handler = sox_find_effect(StringValuePtr(name));
278
+ if (c_handler == NULL)
279
+ rb_raise(rb_eArgError, "no such effect: %s", StringValuePtr(name));
280
+ c_effect = sox_create_effect(c_handler);
281
+
282
+ for (i = j = 0; i < RARRAY_LEN(options); i++) {
283
+ if (TYPE(RARRAY_PTR(options)[i]) == T_DATA) {
284
+ Data_Get_Struct(RARRAY_PTR(options)[i], sox_format_t, c_tmp_format);
285
+ c_options[j++] = (char *)c_tmp_format;
286
+ } else {
287
+ tmp = rb_check_string_type(RARRAY_PTR(options)[i]);
288
+ c_options[j++] = NIL_P(tmp) ? NULL : RSTRING_PTR(tmp);
289
+ }
290
+ }
291
+
292
+ i = sox_effect_options(c_effect, j, j > 0 ? c_options : NULL);
293
+ if (i != SOX_SUCCESS)
294
+ rb_raise(rb_eArgError, "wrong arguments (%d)", j);
295
+ }
296
+
297
+ Data_Get_Struct(self, sox_effects_chain_t, c_chain);
298
+ Data_Get_Struct(rb_iv_get(self, "@input"), sox_format_t, c_input);
299
+ Data_Get_Struct(rb_iv_get(self, "@output"), sox_format_t, c_output);
300
+
301
+ i = sox_add_effect(c_chain, c_effect, &c_input->signal, &c_output->signal);
302
+
303
+ return INT2NUM(i);
304
+ }/*}}}*/
305
+
306
+ VALUE rsoxbuffer_initialize(int argc, VALUE *argv, VALUE self) {/*{{{*/
307
+ sox_sample_t *buffer;
308
+ VALUE length;
309
+ int llen;
310
+
311
+ rb_scan_args(argc, argv, "01", &length);
312
+ llen = NIL_P(length) ? 2048 : NUM2INT(length);
313
+ buffer = ALLOC_N(sox_sample_t, llen);
314
+
315
+ rb_iv_set(self, "@buffer", Data_Wrap_Struct(RSoxBuffer, 0, free, buffer));
316
+ rb_iv_set(self, "@length", INT2NUM(llen));
317
+
318
+ return self;
319
+ }/*}}}*/
320
+
321
+ VALUE rsoxbuffer_at(VALUE self, VALUE index) {
322
+ sox_sample_t *c_buffer;
323
+
324
+ if (index < rb_iv_get(self, "@length")) {
325
+ Data_Get_Struct(self, sox_sample_t, c_buffer);
326
+ return INT2NUM(c_buffer[NUM2INT(index)]);
327
+ }
328
+
329
+ return Qnil;
330
+ }
331
+
332
+ VALUE rsoxbuffer_length(VALUE self) {/*{{{*/
333
+ return rb_iv_get(self, "@length");
334
+ }/*}}}*/
335
+
336
+ VALUE rsoxbuffer_buffer(VALUE self) {/*{{{*/
337
+ return rb_iv_get(self, "@buffer");
338
+ }/*}}}*/
339
+
340
+ VALUE rsoxencoding_bps(VALUE self) {/*{{{*/
341
+ sox_encodinginfo_t *c_enc;
342
+
343
+ Data_Get_Struct(self, sox_encodinginfo_t, c_enc);
344
+
345
+ return UINT2NUM(c_enc->bits_per_sample);
346
+ }/*}}}*/
347
+
348
+ VALUE rsoxencoding_bps_set(VALUE self, VALUE bps) {/*{{{*/
349
+ sox_encodinginfo_t *c_enc;
350
+ unsigned int val = NUM2UINT(bps);
351
+
352
+ Data_Get_Struct(self, sox_encodinginfo_t, c_enc);
353
+ c_enc->bits_per_sample = val;
354
+
355
+ return bps;
356
+ }/*}}}*/
357
+
358
+ static void rsoxeffectschain_free(void *ptr) {/*{{{*/
359
+ sox_delete_effects_chain(ptr);
360
+ }/*}}}*/
361
+
362
+ VALUE rsox_effectschain(VALUE self, VALUE input, VALUE output) {/*{{{*/
363
+ sox_format_t *c_input, *c_output;
364
+ sox_effects_chain_t *c_chain;
365
+ VALUE chain;
366
+
367
+ Data_Get_Struct(input, sox_format_t, c_input);
368
+ Data_Get_Struct(output, sox_format_t, c_output);
369
+
370
+ c_chain = sox_create_effects_chain(&c_input->encoding, &c_output->encoding);
371
+ chain = Data_Wrap_Struct(RSoxEffectsChain, 0, rsoxeffectschain_free, c_chain);
372
+ rb_iv_set(chain, "@input", input);
373
+ rb_iv_set(chain, "@output", output);
374
+
375
+ rb_iv_set(self, "@chain", chain);
376
+
377
+ return chain;
378
+ }/*}}}*/
379
+
380
+ int rsoxeffectschain_flow_callback(sox_bool all_done, void *data) {/*{{{*/
381
+ return SOX_SUCCESS;
382
+ }/*}}}*/
383
+
384
+ VALUE rsoxeffectschain_flow(int argc, VALUE *argv, VALUE self) {/*{{{*/
385
+ sox_effects_chain_t *c_chain;
386
+
387
+ Data_Get_Struct(self, sox_effects_chain_t, c_chain);
388
+
389
+ return INT2NUM(sox_flow_effects(c_chain, NULL, NULL));
390
+ }/*}}}*/
391
+
392
+ void Init_rsox(void) {/*{{{*/
393
+ rb_define_global_const("SOX_SUCCESS", INT2NUM(0));
394
+ rb_define_global_const("SOX_EOF", INT2NUM(-1));
395
+
396
+ RSox = rb_define_class("RSox", rb_cObject);
397
+ rb_define_singleton_method(RSox, "new", rsox_new, 0);
398
+ rb_define_singleton_method(RSox, "count", rsox_count, 0);
399
+ rb_define_method(RSox, "initialize", rsox_initialize, 0);
400
+ rb_define_method(RSox, "format_init", rsox_format_init, 0);
401
+ rb_define_method(RSox, "format_quit", rsox_format_quit, 0);
402
+ rb_define_method(RSox, "open_read", rsox_open_read, -1);
403
+ rb_define_method(RSox, "open_write", rsox_open_write, -1);
404
+ rb_define_method(RSox, "chain", rsox_effectschain, 2);
405
+ rb_define_method(RSox, "buffer_size", rsox_get_bufsize, 0);
406
+ rb_define_method(RSox, "buffer_size=", rsox_set_bufsize, 1);
407
+
408
+ RSoxFormat = rb_define_class("RSoxFormat", rb_cObject);
409
+ rb_define_method(RSoxFormat, "signal", rsoxformat_signal, 0);
410
+ rb_define_method(RSoxFormat, "encoding", rsoxformat_encoding, 0);
411
+ rb_define_method(RSoxFormat, "filename", rsoxformat_filename, 0);
412
+ rb_define_method(RSoxFormat, "read", rsoxformat_read, 1);
413
+ rb_define_method(RSoxFormat, "write", rsoxformat_write, 2);
414
+ rb_define_method(RSoxFormat, "seek", rsoxformat_seek, 2);
415
+
416
+ RSoxBuffer = rb_define_class("RSoxBuffer", rb_cObject);
417
+ rb_define_method(RSoxBuffer, "initialize", rsoxbuffer_initialize, -1);
418
+ rb_define_method(RSoxBuffer, "length", rsoxbuffer_length, 0);
419
+ rb_define_method(RSoxBuffer, "size", rsoxbuffer_length, 0);
420
+ rb_define_method(RSoxBuffer, "buffer", rsoxbuffer_buffer, 0);
421
+ rb_define_method(RSoxBuffer, "[]", rsoxbuffer_at, 1);
422
+ rb_define_method(RSoxBuffer, "at", rsoxbuffer_at, 1);
423
+
424
+ RSoxSignal = rb_define_class("RSoxSignal", rb_cObject);
425
+ rb_define_method(RSoxSignal, "rate", rsoxsignal_rate, 0);
426
+ rb_define_method(RSoxSignal, "channels", rsoxsignal_channels, 0);
427
+ rb_define_method(RSoxSignal, "bits", rsoxsignal_bits, 0);
428
+ rb_define_method(RSoxSignal, "rate=", rsoxsignal_rate_set, 1);
429
+ rb_define_method(RSoxSignal, "channels=", rsoxsignal_channels_set, 1);
430
+ rb_define_method(RSoxSignal, "bits=", rsoxsignal_bits_set, 1);
431
+
432
+ RSoxEncoding = rb_define_class("RSoxEncoding", rb_cObject);
433
+ rb_define_method(RSoxEncoding, "bps", rsoxencoding_bps, 0);
434
+ rb_define_method(RSoxEncoding, "bps=", rsoxencoding_bps, 1);
435
+
436
+ RSoxEffect = rb_define_class("RSoxEffect", rb_cObject);
437
+
438
+ RSoxEffectsChain = rb_define_class("RSoxEffectsChain", rb_cObject);
439
+ rb_define_method(RSoxEffectsChain, "add", rsoxeffectschain_add, -1);
440
+ rb_define_method(RSoxEffectsChain, "flow", rsoxeffectschain_flow, -1);
441
+ }/*}}}*/