roctave 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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +33 -0
  4. data/ext/roctave/cu8_file_reader.c +331 -0
  5. data/ext/roctave/cu8_file_reader.h +30 -0
  6. data/ext/roctave/extconf.rb +6 -0
  7. data/ext/roctave/fir_filter.c +795 -0
  8. data/ext/roctave/fir_filter.h +29 -0
  9. data/ext/roctave/freq_shifter.c +410 -0
  10. data/ext/roctave/freq_shifter.h +29 -0
  11. data/ext/roctave/iir_filter.c +462 -0
  12. data/ext/roctave/iir_filter.h +29 -0
  13. data/ext/roctave/roctave.c +38 -0
  14. data/ext/roctave/roctave.h +27 -0
  15. data/lib/roctave.rb +168 -0
  16. data/lib/roctave/bilinear.rb +92 -0
  17. data/lib/roctave/butter.rb +87 -0
  18. data/lib/roctave/cheby.rb +180 -0
  19. data/lib/roctave/cu8_file_reader.rb +45 -0
  20. data/lib/roctave/dft.rb +280 -0
  21. data/lib/roctave/filter.rb +64 -0
  22. data/lib/roctave/finite_difference_coefficients.rb +73 -0
  23. data/lib/roctave/fir.rb +121 -0
  24. data/lib/roctave/fir1.rb +134 -0
  25. data/lib/roctave/fir2.rb +246 -0
  26. data/lib/roctave/fir_design.rb +311 -0
  27. data/lib/roctave/firls.rb +380 -0
  28. data/lib/roctave/firpm.rb +499 -0
  29. data/lib/roctave/freq_shifter.rb +47 -0
  30. data/lib/roctave/freqz.rb +233 -0
  31. data/lib/roctave/iir.rb +80 -0
  32. data/lib/roctave/interp1.rb +78 -0
  33. data/lib/roctave/plot.rb +748 -0
  34. data/lib/roctave/poly.rb +46 -0
  35. data/lib/roctave/roots.rb +73 -0
  36. data/lib/roctave/sftrans.rb +157 -0
  37. data/lib/roctave/version.rb +3 -0
  38. data/lib/roctave/window.rb +116 -0
  39. data/roctave.gemspec +79 -0
  40. data/samples/butter.rb +12 -0
  41. data/samples/cheby.rb +28 -0
  42. data/samples/dft.rb +18 -0
  43. data/samples/differentiator.rb +48 -0
  44. data/samples/differentiator_frequency_scaling.rb +52 -0
  45. data/samples/fft.rb +40 -0
  46. data/samples/finite_difference_coefficient.rb +53 -0
  47. data/samples/fir1.rb +13 -0
  48. data/samples/fir2.rb +14 -0
  49. data/samples/fir2_windows.rb +29 -0
  50. data/samples/fir2bank.rb +30 -0
  51. data/samples/fir_low_pass.rb +44 -0
  52. data/samples/firls.rb +77 -0
  53. data/samples/firpm.rb +78 -0
  54. data/samples/hilbert_transformer.rb +20 -0
  55. data/samples/hilbert_transformer_frequency_scaling.rb +47 -0
  56. data/samples/plot.rb +45 -0
  57. data/samples/stem.rb +8 -0
  58. data/samples/type1.rb +25 -0
  59. data/samples/type3.rb +24 -0
  60. data/samples/windows.rb +25 -0
  61. metadata +123 -0
@@ -0,0 +1,462 @@
1
+ /* Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of Roctave
4
+ *
5
+ * Roctave is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * Roctave is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with Roctave. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+
20
+ #include <ruby.h>
21
+ #include "roctave.h"
22
+ #include "iir_filter.h"
23
+
24
+ VALUE c_IIR;
25
+
26
+ static ID id_real;
27
+ static ID id_imag;
28
+
29
+
30
+ struct iir_filter {
31
+ double *b;
32
+ double *a;
33
+ double *state;
34
+ double *stateim;
35
+ int nb_coefficients;
36
+ int state_length;
37
+ VALUE (*filter_one_sample_func)(struct iir_filter*, VALUE);
38
+ };
39
+
40
+
41
+ static void iir_filter_free(void *p)
42
+ {
43
+ struct iir_filter *flt = (struct iir_filter*)p;
44
+
45
+ if (flt->b)
46
+ free(flt->b);
47
+
48
+ if (flt->a)
49
+ free(flt->a);
50
+
51
+ if (flt->state)
52
+ free(flt->state);
53
+
54
+ if (flt->stateim)
55
+ free(flt->stateim);
56
+
57
+ xfree(flt);
58
+ }
59
+
60
+
61
+ static size_t iir_filter_size(const void* data)
62
+ {
63
+ const struct iir_filter *flt;
64
+ size_t res;
65
+
66
+ flt = (const struct iir_filter*)data;
67
+
68
+ res = sizeof(struct iir_filter);
69
+ res += 2*flt->nb_coefficients*sizeof(double);
70
+ res += flt->state_length*sizeof(double);
71
+ if (flt->stateim)
72
+ res += flt->state_length*sizeof(double);
73
+
74
+ return res;
75
+ }
76
+
77
+
78
+ static const rb_data_type_t iir_filter_type = {
79
+ .wrap_struct_name = "roctave_iir_filter_struct",
80
+ .function = {
81
+ .dmark = NULL,
82
+ .dfree = iir_filter_free,
83
+ .dsize = iir_filter_size,
84
+ },
85
+ .data = NULL,
86
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
87
+ };
88
+
89
+
90
+ /* @return [Integer] the filter order
91
+ */
92
+ static VALUE iir_filter_order(VALUE self)
93
+ {
94
+ struct iir_filter *flt;
95
+
96
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
97
+
98
+ return INT2NUM(flt->nb_coefficients - 1);
99
+ }
100
+
101
+
102
+ /* Set a numerator coefficient.
103
+ * @param index [Integer] the coefficient index
104
+ * @param coef [Float] the coefficient value
105
+ * @return [Float, nil] the coefficient if the index is valid, or nil otherwise
106
+ */
107
+ static VALUE iir_filter_set_b_coefficient_at(VALUE self, VALUE index, VALUE coef)
108
+ {
109
+ struct iir_filter *flt;
110
+ int ind;
111
+ double val;
112
+ VALUE res = Qnil;
113
+
114
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
115
+
116
+ ind = NUM2INT(index);
117
+ if (ind >= 0 && ind < flt->nb_coefficients) {
118
+ val = NUM2DBL(coef);
119
+ flt->b[ind] = val;
120
+ res = DBL2NUM(val);
121
+ }
122
+
123
+ return res;
124
+ }
125
+
126
+
127
+ /* Get a numerator coefficient.
128
+ * @param index [Integer] the coefficient index
129
+ * @return [Float] the coefficient at index
130
+ */
131
+ static VALUE iir_filter_get_b_coefficient_at(VALUE self, VALUE index)
132
+ {
133
+ struct iir_filter *flt;
134
+ int ind;
135
+ VALUE res = Qnil;
136
+
137
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
138
+
139
+ ind = NUM2INT(index);
140
+ if (ind >= 0 && ind < flt->nb_coefficients)
141
+ res = DBL2NUM(flt->b[ind]);
142
+ else
143
+ res = DBL2NUM(0.0);
144
+
145
+ return res;
146
+ }
147
+
148
+
149
+ /* Set a denominator coefficient.
150
+ * @param index [Integer] the coefficient index, must be > 0
151
+ * @param coef [Float] the coefficient value
152
+ * @return [Float, nil] the coefficient if the index is valid, or nil otherwise
153
+ */
154
+ static VALUE iir_filter_set_a_coefficient_at(VALUE self, VALUE index, VALUE coef)
155
+ {
156
+ struct iir_filter *flt;
157
+ int ind;
158
+ double val;
159
+ VALUE res = Qnil;
160
+
161
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
162
+
163
+ ind = NUM2INT(index);
164
+ if (ind > 0 && ind < flt->nb_coefficients) {
165
+ val = NUM2DBL(coef);
166
+ flt->a[ind] = val;
167
+ res = DBL2NUM(val);
168
+ }
169
+
170
+ return res;
171
+ }
172
+
173
+
174
+ /* Get a denominator coefficient.
175
+ * @param index [Integer] the coefficient index
176
+ * @return [Float] the coefficient at index
177
+ */
178
+ static VALUE iir_filter_get_a_coefficient_at(VALUE self, VALUE index)
179
+ {
180
+ struct iir_filter *flt;
181
+ int ind;
182
+ VALUE res = Qnil;
183
+
184
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
185
+
186
+ ind = NUM2INT(index);
187
+ if (ind >= 0 && ind < flt->nb_coefficients)
188
+ res = DBL2NUM(flt->a[ind]);
189
+ else
190
+ res = DBL2NUM(0.0);
191
+
192
+ return res;
193
+ }
194
+
195
+
196
+ /* Return a copy of the numerator coefficients as an array.
197
+ * @return [Array<Float>] the filter +b+ coefficients
198
+ */
199
+ static VALUE iir_filter_b_coefficients(VALUE self)
200
+ {
201
+ struct iir_filter *flt;
202
+ VALUE res;
203
+ int i;
204
+
205
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
206
+
207
+ res = rb_ary_new_capa(flt->nb_coefficients);
208
+ for (i = 0; i < flt->nb_coefficients; i++)
209
+ rb_ary_store(res, i, DBL2NUM(flt->b[i]));
210
+
211
+ return res;
212
+ }
213
+
214
+
215
+ /* Return a copy of the denominator coefficients as an array.
216
+ * @return [Array<Float>] the filter +a+ coefficients
217
+ */
218
+ static VALUE iir_filter_a_coefficients(VALUE self)
219
+ {
220
+ struct iir_filter *flt;
221
+ VALUE res;
222
+ int i;
223
+
224
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
225
+
226
+ res = rb_ary_new_capa(flt->nb_coefficients);
227
+ for (i = 0; i < flt->nb_coefficients; i++)
228
+ rb_ary_store(res, i, DBL2NUM(flt->a[i]));
229
+
230
+ return res;
231
+ }
232
+
233
+
234
+ static VALUE filter_one_sample_complex(struct iir_filter *flt, VALUE samp)
235
+ {
236
+ const int flt_len = flt->nb_coefficients - 1;
237
+ double inr, ini, outr, outi;
238
+ int i;
239
+
240
+ inr = NUM2DBL(rb_funcallv(samp, id_real, 0, NULL));
241
+ ini = NUM2DBL(rb_funcallv(samp, id_imag, 0, NULL));
242
+ outr = flt->b[0] * inr + flt->state[0];
243
+ outi = flt->b[0] * ini + flt->stateim[0];
244
+ for (i = 1; i < flt_len; i++) {
245
+ flt->state[i-1] = flt->b[i] * inr - flt->a[i] * outr + flt->state[i];
246
+ flt->stateim[i-1] = flt->b[i] * ini - flt->a[i] * outi + flt->stateim[i];
247
+ }
248
+ flt->state[i-1] = flt->b[i] * inr - flt->a[i] * outr;
249
+ flt->stateim[i-1] = flt->b[i] * ini - flt->a[i] * outi;
250
+
251
+ return rb_complex_raw(DBL2NUM(outr), DBL2NUM(outi));
252
+ }
253
+
254
+
255
+ static void make_accept_complex(struct iir_filter *flt)
256
+ {
257
+ if (!flt->stateim)
258
+ flt->stateim = calloc(flt->state_length, sizeof(double));
259
+ if (!flt->stateim)
260
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of state storage", flt->state_length*sizeof(double));
261
+ flt->filter_one_sample_func = &filter_one_sample_complex;
262
+ }
263
+
264
+
265
+ /* Not visible from the Ruby API.
266
+ * Process one Float sample, returning a Float sample.
267
+ * If the input sample is a Complex sample ane not a Float one,
268
+ * makes the filter accept Complex sample and returns filter_one_sample_complex().
269
+ * Once the filter has accepted one Complex, it will allways return complex samples.
270
+ */
271
+ static VALUE filter_one_sample_float(struct iir_filter *flt, VALUE samp)
272
+ {
273
+ const int flt_len = flt->nb_coefficients - 1;
274
+ double in, out;
275
+ int i;
276
+
277
+ if (rb_class_of(samp) == rb_cComplex) {
278
+ make_accept_complex(flt);
279
+ return filter_one_sample_complex(flt, samp);
280
+ }
281
+
282
+ /* Transposed direct form 2 */
283
+ in = NUM2DBL(samp);
284
+ out = flt->b[0] * in + flt->state[0];
285
+ for (i = 1; i < flt_len; i++)
286
+ flt->state[i-1] = flt->b[i] * in - flt->a[i] * out + flt->state[i];
287
+ flt->state[i-1] = flt->b[i] * in - flt->a[i] * out;
288
+
289
+ return DBL2NUM(out);
290
+ }
291
+
292
+
293
+ /* @param sample [Float, Array<Float>] a single sample or an array of samples to process
294
+ * @return [Float, Array<Float>] a single or an array of processed samples
295
+ */
296
+ static VALUE iir_filter_filter(VALUE self, VALUE sample)
297
+ {
298
+ struct iir_filter *flt;
299
+ VALUE res = Qnil;
300
+ int i, len;
301
+
302
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
303
+
304
+ if (RB_FLOAT_TYPE_P(sample) || rb_obj_is_kind_of(sample, rb_cNumeric))
305
+ res = (*flt->filter_one_sample_func)(flt, sample);
306
+ else if (rb_class_of(sample) == rb_cArray) {
307
+ len = rb_array_len(sample);
308
+ res = rb_ary_new_capa(len);
309
+ for (i = 0; i < len; i++)
310
+ rb_ary_store(res, i, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i)));
311
+ }
312
+ else
313
+ rb_raise(rb_eArgError, "Expecting a single Numeric or an Array of Numeric");
314
+
315
+ return res;
316
+ }
317
+
318
+
319
+ static VALUE iir_filter_alloc(VALUE klass)
320
+ {
321
+ VALUE obj;
322
+ struct iir_filter *flt;
323
+
324
+ flt = ZALLOC(struct iir_filter);
325
+
326
+ obj = TypedData_Wrap_Struct(klass, &iir_filter_type, flt);
327
+
328
+ flt->b = NULL;
329
+ flt->a = NULL;
330
+ flt->state = NULL;
331
+ flt->stateim = NULL;
332
+ flt->nb_coefficients = 0;
333
+ flt->state_length = 0;
334
+ flt->filter_one_sample_func = &filter_one_sample_float;
335
+
336
+ return obj;
337
+ }
338
+
339
+
340
+ /* Reset the filter state.
341
+ * @return self
342
+ */
343
+ static VALUE iir_filter_reset(VALUE self)
344
+ {
345
+ struct iir_filter *flt;
346
+ int i;
347
+
348
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
349
+
350
+ for (i = 0; i < flt->state_length; i++)
351
+ flt->state[i] = 0.0f;
352
+
353
+ if (flt->stateim) {
354
+ for (i = 0; i < flt->state_length; i++)
355
+ flt->stateim[i] = 0.0f;
356
+ }
357
+
358
+ flt->filter_one_sample_func = &filter_one_sample_float;
359
+
360
+ return self;
361
+ }
362
+
363
+
364
+ /* @overload initialize(n)
365
+ * @param n [Integer] the filter order
366
+ * @overload initialize(b, a)
367
+ * @param b [Array<Float>] the array of numerator coefficients
368
+ * @param a [Array<Float>] the array of denominator coefficients
369
+ */
370
+ static VALUE iir_filter_initialize(int argc, VALUE *argv, VALUE self)
371
+ {
372
+ struct iir_filter *flt;
373
+ VALUE b_or_n;
374
+ VALUE a;
375
+ double dv;
376
+ int i, lenb, lena = 0, len;
377
+
378
+ rb_scan_args(argc, argv, "11", &b_or_n, &a);
379
+
380
+ TypedData_Get_Struct(self, struct iir_filter, &iir_filter_type, flt);
381
+
382
+ if (rb_class_of(b_or_n) == rb_cInteger) {
383
+ if (argc > 1)
384
+ rb_raise(rb_eArgError, "If the order is given as the first argument, no other argument is expected");
385
+ flt->nb_coefficients = NUM2INT(b_or_n) + 1;
386
+ if (flt->nb_coefficients < 1)
387
+ rb_raise(rb_eArgError, "Order cannot be less than 0");
388
+ if (flt->nb_coefficients > 1048577)
389
+ rb_raise(rb_eArgError, "Order cannot be more than 1048576");
390
+ flt->b = calloc(flt->nb_coefficients, sizeof(double));
391
+ flt->a = calloc(flt->nb_coefficients, sizeof(double));
392
+ if (flt->b == NULL || flt->a == NULL)
393
+ rb_raise(rb_eRuntimeError, "Failed to allocate memory for coefficient storage");
394
+ flt->a[0] = 1.0;
395
+ }
396
+ else if (rb_class_of(b_or_n) == rb_cArray) {
397
+ lenb = rb_array_len(b_or_n);
398
+ len = lenb;
399
+ if (argc > 1) {
400
+ if (rb_class_of(a) == rb_cArray)
401
+ lena = rb_array_len(a);
402
+ else
403
+ rb_raise(rb_eArgError, "Expecting denominator coefficients (Array<Float>) as second argument");
404
+ if (len < lena)
405
+ len = lena;
406
+ }
407
+ if (len > 1048577)
408
+ rb_raise(rb_eArgError, "Cannot be more than 1048577 coefficients");
409
+ if (len < 1)
410
+ rb_raise(rb_eArgError, "Coefficient arrays are empty");
411
+ flt->nb_coefficients = len;
412
+ flt->b = calloc(flt->nb_coefficients, sizeof(double));
413
+ flt->a = calloc(flt->nb_coefficients, sizeof(double));
414
+ if (flt->b == NULL || flt->a == NULL)
415
+ rb_raise(rb_eRuntimeError, "Failed to allocate memory for coefficient storage");
416
+ flt->a[0] = 1.0;
417
+ if (lena > 1)
418
+ dv = NUM2DBL(rb_ary_entry(a, i));
419
+ else
420
+ dv = 1.0;
421
+ if (dv == 0.0)
422
+ rb_raise(rb_eArgError, "The first denominator coefficient may no be null");
423
+ for (i = 0; i < lenb; i++)
424
+ flt->b[i] = (double)(NUM2DBL(rb_ary_entry(b_or_n, i))) / dv;
425
+ for (i = 1; i < lena; i++)
426
+ flt->a[i] = (double)(NUM2DBL(rb_ary_entry(a, i))) / dv;
427
+ }
428
+ else
429
+ rb_raise(rb_eArgError, "Expecting the filter order (Integer) or the filter coefficients (two Array<Float>)");
430
+
431
+ flt->state_length = flt->nb_coefficients - 1;
432
+ flt->state = calloc(flt->state_length, sizeof(double));
433
+ if (!flt->state)
434
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of state storage", flt->state_length*sizeof(double));
435
+ flt->stateim = NULL;
436
+ flt->filter_one_sample_func = &filter_one_sample_float;
437
+
438
+ return self;
439
+ }
440
+
441
+
442
+ void Init_iir_filter()
443
+ {
444
+ id_real = rb_intern("real");
445
+ id_imag = rb_intern("imag");
446
+
447
+ c_IIR = rb_define_class_under(m_Roctave, "IirFilter", rb_cData);
448
+
449
+ rb_define_alloc_func(c_IIR, iir_filter_alloc);
450
+ rb_define_method(c_IIR, "initialize", iir_filter_initialize, -1);
451
+ rb_define_method(c_IIR, "order", iir_filter_order, 0);
452
+ rb_define_method(c_IIR, "reset!", iir_filter_reset, 0);
453
+ rb_define_method(c_IIR, "get_num_coefficient_at", iir_filter_get_b_coefficient_at, 1);
454
+ rb_define_method(c_IIR, "set_num_coefficient_at", iir_filter_set_b_coefficient_at, 2);
455
+ rb_define_method(c_IIR, "get_den_coefficient_at", iir_filter_get_a_coefficient_at, 1);
456
+ rb_define_method(c_IIR, "set_den_coefficient_at", iir_filter_set_a_coefficient_at, 2);
457
+ rb_define_method(c_IIR, "numerator", iir_filter_b_coefficients, 0);
458
+ rb_define_method(c_IIR, "denominator", iir_filter_a_coefficients, 0);
459
+ rb_define_method(c_IIR, "filter", iir_filter_filter, 1);
460
+ }
461
+
462
+