rsox 0.0.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.
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
+ }/*}}}*/