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,33 @@
1
+
2
+ Roctave
3
+ =======
4
+
5
+ Roctave is a Ruby gem attempting to provide functions do design digital filters in Ruby.
6
+ Most algorithms are taken from [GNU Octave](https://www.gnu.org/software/octave/).
7
+
8
+ Installation
9
+ ------------
10
+
11
+ ```bash
12
+ git clone https://gitlab.com/theotime_bollengier/roctave.git
13
+ cd roctave
14
+ gem build roctave.gemspec
15
+ sudo gem install ./roctave-x.x.x.gem
16
+ ```
17
+
18
+
19
+ Documentation
20
+ -------------
21
+
22
+ You can generate the documentation with [YARD](https://yardoc.org/).
23
+
24
+ To get `yard` :
25
+ ```bash
26
+ sudo gem install yard
27
+ ```
28
+ Then, in this directory, generate the documentation :
29
+ ```bash
30
+ yard doc -o doc
31
+ firefox doc/index.html
32
+ ```
33
+
@@ -0,0 +1,331 @@
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 <errno.h>
21
+ #include <ruby.h>
22
+ #include "roctave.h"
23
+ #include "cu8_file_reader.h"
24
+
25
+
26
+ VALUE c_CU8FileReader;
27
+
28
+
29
+ struct cu8_file_reader {
30
+ FILE *file;
31
+ };
32
+
33
+
34
+ static void cu8_file_reader_free(void *p)
35
+ {
36
+ struct cu8_file_reader *fr = (struct cu8_file_reader*)p;
37
+
38
+ if (fr->file)
39
+ fclose(fr->file);
40
+
41
+ xfree(fr);
42
+ }
43
+
44
+
45
+ static size_t cu8_file_reader_size(const void* data)
46
+ {
47
+ (void)data;
48
+
49
+ return sizeof(struct cu8_file_reader);
50
+ }
51
+
52
+
53
+ static const rb_data_type_t cu8_file_reader_type = {
54
+ .wrap_struct_name = "roctave_cu8_file_reader_struct",
55
+ .function = {
56
+ .dmark = NULL,
57
+ .dfree = cu8_file_reader_free,
58
+ .dsize = cu8_file_reader_size,
59
+ },
60
+ .data = NULL,
61
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
62
+ };
63
+
64
+
65
+ static VALUE cu8_file_reader_alloc(VALUE klass)
66
+ {
67
+ VALUE obj;
68
+ struct cu8_file_reader *fr;
69
+
70
+ fr = ZALLOC(struct cu8_file_reader);
71
+
72
+ obj = TypedData_Wrap_Struct(klass, &cu8_file_reader_type, fr);
73
+
74
+ fr->file = NULL;
75
+
76
+ return obj;
77
+ }
78
+
79
+
80
+ /* Close the opened file
81
+ */
82
+ static VALUE cu8_file_reader_close(VALUE self)
83
+ {
84
+ struct cu8_file_reader *fr;
85
+
86
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
87
+
88
+ if (fr->file != NULL) {
89
+ fclose(fr->file);
90
+ fr->file = NULL;
91
+ }
92
+
93
+ return self;
94
+ }
95
+
96
+
97
+ /* @return [Boolean] whether the CU8FileReader has an opened file or not
98
+ */
99
+ static VALUE cu8_file_reader_closed(VALUE self)
100
+ {
101
+ struct cu8_file_reader *fr;
102
+ VALUE res = Qtrue;
103
+
104
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
105
+
106
+ if (fr->file != NULL)
107
+ res = Qfalse;
108
+
109
+ return res;
110
+ }
111
+
112
+
113
+ /* @param filename [String] the path of the file to open
114
+ */
115
+ static VALUE cu8_file_reader_open(VALUE self, VALUE filename)
116
+ {
117
+ struct cu8_file_reader *fr;
118
+ VALUE expanded_filename;
119
+ const char *expfname;
120
+
121
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
122
+
123
+ cu8_file_reader_close(self);
124
+
125
+ if (rb_class_of(filename) != rb_cString)
126
+ rb_raise(rb_eArgError, "Expecting the file path as a string");
127
+
128
+ expanded_filename = rb_funcallv(rb_cFile, rb_intern("expand_path"), 1, &filename);
129
+ expfname = StringValueCStr(expanded_filename);
130
+ if ((fr->file = fopen(expfname, "rb")) == NULL)
131
+ rb_raise(rb_eRuntimeError, "Cannot open file \"%s\": %s", expfname, strerror(errno));
132
+
133
+ return self;
134
+ }
135
+
136
+
137
+ /* @param filename [String] the path of the file to read
138
+ */
139
+ static VALUE cu8_file_reader_initialize(VALUE self, VALUE filename)
140
+ {
141
+ return cu8_file_reader_open(self, filename);
142
+ }
143
+
144
+
145
+ static inline void cu8_file_reader_get_size_and_position(struct cu8_file_reader *fr, long *position, long *size)
146
+ {
147
+ long pos;
148
+ long sz;
149
+
150
+ pos = ftell(fr->file);
151
+ if (position)
152
+ *position = pos/2;
153
+ if (size) {
154
+ fseek(fr->file, 0, SEEK_END);
155
+ sz = ftell(fr->file);
156
+ fseek(fr->file, pos, SEEK_SET);
157
+ *size = sz/2;
158
+ }
159
+ }
160
+
161
+
162
+ /* @return [Boolean, nil] if the end of the file has been reached, +nil+ if it is closed
163
+ */
164
+ static VALUE cu8_file_reader_eof(VALUE self)
165
+ {
166
+ struct cu8_file_reader *fr;
167
+ VALUE res = Qfalse;
168
+ long pos, size;
169
+
170
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
171
+
172
+ if (fr->file == NULL)
173
+ return Qnil;
174
+
175
+ cu8_file_reader_get_size_and_position(fr, &pos, &size);
176
+
177
+ if (pos >= size)
178
+ res = Qtrue;
179
+
180
+ return res;
181
+ }
182
+
183
+
184
+ static VALUE cu8_file_reader_rewind(VALUE self)
185
+ {
186
+ struct cu8_file_reader *fr;
187
+
188
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
189
+
190
+ if (fr->file == NULL)
191
+ return Qnil;
192
+
193
+ rewind(fr->file);
194
+
195
+ return self;
196
+ }
197
+
198
+
199
+ /* @return [Integer] the number of complex numbers in the file
200
+ */
201
+ static VALUE cu8_file_reader_length(VALUE self)
202
+ {
203
+ struct cu8_file_reader *fr;
204
+ long size;
205
+
206
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
207
+
208
+ if (fr->file == NULL)
209
+ return Qnil;
210
+
211
+ cu8_file_reader_get_size_and_position(fr, NULL, &size);
212
+
213
+ return LONG2NUM(size);
214
+ }
215
+
216
+
217
+ /* @return [Integer] the file position indicator in terms of complex numbers
218
+ */
219
+ static VALUE cu8_file_reader_position(VALUE self)
220
+ {
221
+ struct cu8_file_reader *fr;
222
+ long pos;
223
+
224
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
225
+
226
+ if (fr->file == NULL)
227
+ return Qnil;
228
+
229
+ cu8_file_reader_get_size_and_position(fr, &pos, NULL);
230
+
231
+ return LONG2NUM(pos);
232
+ }
233
+
234
+
235
+ /* @return [Float] the file position indicator divided by the file length
236
+ */
237
+ static VALUE cu8_file_reader_normalized_position(VALUE self)
238
+ {
239
+ struct cu8_file_reader *fr;
240
+ long pos, size;
241
+
242
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
243
+
244
+ if (fr->file == NULL)
245
+ return Qnil;
246
+
247
+ cu8_file_reader_get_size_and_position(fr, &pos, &size);
248
+
249
+ return DBL2NUM((double)pos / (double)size);
250
+ }
251
+
252
+
253
+ /* @overload read()
254
+ * Returns an array of complex numbers read from the file content
255
+ * @return [Array<Float>] array of complex numbers read from the file
256
+ * @overload read(len)
257
+ * Reads at most +len+ complex numbers from the file.
258
+ * @return [Array<Float>] array of at most +len+ complex numbers read from the file
259
+ */
260
+ static VALUE cu8_file_reader_read(int argc, VALUE *argv, VALUE self)
261
+ {
262
+ struct cu8_file_reader *fr;
263
+ VALUE rblen;
264
+ long pos, size;
265
+ int len, r, to_read, readden, i;
266
+ unsigned char *bufc;
267
+ VALUE res;
268
+
269
+ rb_scan_args(argc, argv, "01", &rblen);
270
+
271
+ TypedData_Get_Struct(self, struct cu8_file_reader, &cu8_file_reader_type, fr);
272
+
273
+ if (fr->file == NULL)
274
+ return Qnil;
275
+
276
+ cu8_file_reader_get_size_and_position(fr, &pos, &size);
277
+ len = (int)(size - pos);
278
+
279
+ if (rblen != Qnil) {
280
+ if (NUM2INT(rblen) < 0)
281
+ rb_raise(rb_eArgError, "Requested length cannot be less than 0");
282
+ if (len > NUM2INT(rblen))
283
+ len = NUM2INT(rblen);
284
+ }
285
+
286
+ if (len <= 0)
287
+ return rb_ary_new_capa(0);
288
+
289
+ bufc = malloc(len*2*sizeof(unsigned char));
290
+ if (bufc == NULL)
291
+ rb_raise(rb_eRuntimeError, "Failed to allocate %d bytes for internal buffer", len);
292
+
293
+ to_read = len*2;
294
+ readden = 0;
295
+ do {
296
+ r = fread(bufc + readden, sizeof(unsigned char), to_read, fr->file);
297
+ if (r < 0)
298
+ rb_raise(rb_eRuntimeError, "Error reading file: %s", strerror(errno));
299
+ readden += r;
300
+ to_read -= r;
301
+ } while (to_read > 0 && r > 0);
302
+ len = readden / 2;
303
+
304
+ res = rb_ary_new_capa(len);
305
+ for (i = 0; i < len; i++)
306
+ rb_ary_store(res, i, rb_complex_raw(DBL2NUM(((double)bufc[2*i+0] - 128.0)/128.0), DBL2NUM(((double)bufc[2*i+1] - 128.0)/128.0)));
307
+
308
+ free(bufc);
309
+
310
+ return res;
311
+ }
312
+
313
+
314
+ void Init_cu8_file_reader()
315
+ {
316
+ c_CU8FileReader = rb_define_class_under(m_Roctave, "CU8FileReader", rb_cData);
317
+
318
+ rb_define_alloc_func(c_CU8FileReader, cu8_file_reader_alloc);
319
+ rb_define_method(c_CU8FileReader, "initialize", cu8_file_reader_initialize, 1);
320
+ rb_define_method(c_CU8FileReader, "open", cu8_file_reader_open, 1);
321
+ rb_define_method(c_CU8FileReader, "close", cu8_file_reader_close, 0);
322
+ rb_define_method(c_CU8FileReader, "read", cu8_file_reader_read, -1);
323
+ rb_define_method(c_CU8FileReader, "eof?", cu8_file_reader_eof, 0);
324
+ rb_define_method(c_CU8FileReader, "rewind", cu8_file_reader_rewind, 0);
325
+ rb_define_method(c_CU8FileReader, "length", cu8_file_reader_length, 0);
326
+ rb_define_method(c_CU8FileReader, "position", cu8_file_reader_position, 0);
327
+ rb_define_method(c_CU8FileReader, "normalized_position", cu8_file_reader_normalized_position, 0);
328
+ rb_define_method(c_CU8FileReader, "closed?", cu8_file_reader_closed, 0);
329
+ }
330
+
331
+
@@ -0,0 +1,30 @@
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
+ #ifndef CU8_FILE_READER_H
21
+ #define CU8_FILE_READER_H
22
+
23
+ #include <ruby.h>
24
+
25
+ extern VALUE c_CU8FileReader;
26
+
27
+ void Init_cu8_file_reader();
28
+
29
+ #endif /* CU8_FILE_READER */
30
+
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+
3
+ #$CFLAGS += ' -O3 -mtune=native -ffast-math'
4
+
5
+ create_makefile 'roctave/roctave'
6
+
@@ -0,0 +1,795 @@
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 "fir_filter.h"
23
+
24
+ VALUE c_FIR;
25
+
26
+ static ID id_real;
27
+ static ID id_imag;
28
+
29
+
30
+ struct fir_filter {
31
+ double *b;
32
+ double *state;
33
+ double *stateim;
34
+ int nb_coefficients;
35
+ int state_length;
36
+ int index_mask;
37
+ int index;
38
+ int counter; // used for decimation
39
+ int max_counter; // used decimation/upsampling factor
40
+ int decimator_interpolator; // 0 simple filter, 1 decimator, 2 interpolator
41
+ void (*push_one_sample_func)(struct fir_filter*, VALUE);
42
+ VALUE (*filter_one_sample_func)(struct fir_filter*, VALUE, VALUE*, double);
43
+ VALUE zero; // Float or Complex ruby zero, used for interpolator
44
+ };
45
+
46
+
47
+ #define FIR_FILTER_DECIMATOR_FLAG 1
48
+ #define FIR_FILTER_INTERPOLATOR_FLAG 2
49
+ #define FIR_FILTER_IS_DECIMATOR(flt) ((flt)->decimator_interpolator & FIR_FILTER_DECIMATOR_FLAG)
50
+ #define FIR_FILTER_IS_INTERPOLATOR(flt) ((flt)->decimator_interpolator & FIR_FILTER_INTERPOLATOR_FLAG)
51
+ #define FIR_FILTER_IS_REGULAR(flt) ((flt)->decimator_interpolator == 0)
52
+
53
+
54
+ static void fir_filter_mark(void *p)
55
+ {
56
+ struct fir_filter *flt = (struct fir_filter*)p;
57
+ rb_gc_mark(flt->zero);
58
+ }
59
+
60
+
61
+ static void fir_filter_free(void *p)
62
+ {
63
+ struct fir_filter *flt = (struct fir_filter*)p;
64
+
65
+ if (flt->b)
66
+ free(flt->b);
67
+
68
+ if (flt->state)
69
+ free(flt->state);
70
+
71
+ if (flt->stateim)
72
+ free(flt->stateim);
73
+
74
+ xfree(flt);
75
+ }
76
+
77
+
78
+ static size_t fir_filter_size(const void* data)
79
+ {
80
+ const struct fir_filter *flt;
81
+ size_t res;
82
+
83
+ flt = (const struct fir_filter*)data;
84
+ res = sizeof(struct fir_filter);
85
+ res += flt->nb_coefficients*sizeof(double);
86
+ res += flt->state_length*sizeof(double);
87
+ if (flt->stateim)
88
+ res += flt->state_length*sizeof(double);
89
+
90
+ return res;
91
+ }
92
+
93
+
94
+ static const rb_data_type_t fir_filter_type = {
95
+ .wrap_struct_name = "roctave_fir_filter_struct",
96
+ .function = {
97
+ .dmark = fir_filter_mark,
98
+ .dfree = fir_filter_free,
99
+ .dsize = fir_filter_size,
100
+ },
101
+ .data = NULL,
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
103
+ };
104
+
105
+
106
+ /* @return [Integer] the filter order
107
+ */
108
+ static VALUE fir_filter_order(VALUE self)
109
+ {
110
+ struct fir_filter *flt;
111
+
112
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
113
+
114
+ return INT2NUM(flt->nb_coefficients - 1);
115
+ }
116
+
117
+
118
+ /* @return [Integer] number of coefficients (order + 1)
119
+ */
120
+ static VALUE fir_filter_nb_coefficients(VALUE self)
121
+ {
122
+ struct fir_filter *flt;
123
+
124
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
125
+
126
+ return INT2NUM(flt->nb_coefficients);
127
+ }
128
+
129
+
130
+ /* @param index [Integer] the coefficient index
131
+ * @param coef [Float] the coefficient value
132
+ * @return [Float, nil] the coefficient if the index is valid, or nil otherwise
133
+ */
134
+ static VALUE fir_filter_set_coefficient_at(VALUE self, VALUE index, VALUE coef)
135
+ {
136
+ struct fir_filter *flt;
137
+ int ind;
138
+ double val;
139
+ VALUE res = Qnil;
140
+
141
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
142
+
143
+ ind = NUM2INT(index);
144
+ if (ind >= 0 && ind < flt->nb_coefficients) {
145
+ val = NUM2DBL(coef);
146
+ flt->b[flt->nb_coefficients - 1 - ind] = val;
147
+ res = DBL2NUM(val);
148
+ }
149
+
150
+ return res;
151
+ }
152
+
153
+
154
+ /* @param index [Integer] the coefficient index
155
+ * @return [Float] the coefficient at index
156
+ */
157
+ static VALUE fir_filter_get_coefficient_at(VALUE self, VALUE index)
158
+ {
159
+ struct fir_filter *flt;
160
+ int ind;
161
+ VALUE res = Qnil;
162
+
163
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
164
+
165
+ ind = NUM2INT(index);
166
+ if (ind >= 0 && ind < flt->nb_coefficients)
167
+ res = DBL2NUM(flt->b[flt->nb_coefficients - 1 - ind]);
168
+ else
169
+ res = DBL2NUM(0.0);
170
+
171
+ return res;
172
+ }
173
+
174
+
175
+ /* This method noes nothing, it is there to be compatible with Roctave::IirFilter
176
+ * @param index [Integer] the coefficient index
177
+ * @param coef [Float] the denominator coefficient value
178
+ * @return [Float, nil] the denominator coefficient if the index is valid, or nil otherwise
179
+ */
180
+ static VALUE fir_filter_set_denominator_at(VALUE self, VALUE index, VALUE coef)
181
+ {
182
+ (void)self;
183
+ (void)index;
184
+ (void)coef;
185
+
186
+ return Qnil;
187
+ }
188
+
189
+
190
+ /* This method returns 1.0 if +index+ = 0, 0.0 otherwise. It is there to be compatible with Roctave::IirFilter
191
+ * @param index [Integer] the denominator coefficient index
192
+ * @return [Float] the denominator coefficient at index
193
+ */
194
+ static VALUE fir_filter_get_denominator_at(VALUE self, VALUE index)
195
+ {
196
+ int ind;
197
+ VALUE res;
198
+
199
+ ind = NUM2INT(index);
200
+ if (ind == 0)
201
+ res = DBL2NUM(1.0);
202
+ else
203
+ res = DBL2NUM(0.0);
204
+
205
+ return res;
206
+ }
207
+
208
+
209
+ /* Return a copy of the filter's numerator coefficients as an array.
210
+ * @return [Array<Float>] the filter coefficients
211
+ */
212
+ static VALUE fir_filter_coefficients(VALUE self)
213
+ {
214
+ struct fir_filter *flt;
215
+ VALUE res;
216
+ int i;
217
+
218
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
219
+
220
+ res = rb_ary_new_capa(flt->nb_coefficients);
221
+ for (i = 0; i < flt->nb_coefficients; i++)
222
+ rb_ary_store(res, i, DBL2NUM(flt->b[flt->nb_coefficients - 1 - i]));
223
+
224
+ return res;
225
+ }
226
+
227
+
228
+ /* Return a copy of the filter's denominator coefficients as an array, allways [1.0].
229
+ * @return [Array<Float>] the filter coefficients
230
+ */
231
+ static VALUE fir_filter_denominator(VALUE self)
232
+ {
233
+ VALUE res = rb_ary_new_capa(1);
234
+ rb_ary_store(res, 0, DBL2NUM(1.0));
235
+ return res;
236
+ }
237
+
238
+
239
+ static void push_one_sample_complex(struct fir_filter *flt, VALUE sample)
240
+ {
241
+ flt->state[flt->index] = NUM2DBL(rb_funcallv(sample, id_real, 0, NULL));
242
+ flt->stateim[flt->index] = NUM2DBL(rb_funcallv(sample, id_imag, 0, NULL));
243
+ flt->index = (flt->index + 1) & flt->index_mask;
244
+ }
245
+
246
+
247
+ static VALUE filter_one_sample_complex(struct fir_filter *flt, VALUE samp, VALUE *delayed, double post_mult)
248
+ {
249
+ const int flt_len = flt->nb_coefficients;
250
+ const int mask = flt->index_mask;
251
+ const int start_index = (flt->state_length + flt->index + 1 - flt_len) & mask;
252
+ double accr = 0.0;
253
+ double acci = 0.0;
254
+ double re, im;
255
+ int i, j;
256
+
257
+ flt->state[flt->index] = NUM2DBL(rb_funcallv(samp, id_real, 0, NULL));
258
+ flt->stateim[flt->index] = NUM2DBL(rb_funcallv(samp, id_imag, 0, NULL));
259
+
260
+ for (i = 0, j = start_index; i < flt_len; i++, j = (j + 1) & mask) {
261
+ accr += flt->b[i] * flt->state[j];
262
+ acci += flt->b[i] * flt->stateim[j];
263
+ }
264
+
265
+ flt->index = (flt->index + 1) & mask;
266
+
267
+ if (delayed) {
268
+ if (flt_len & 1) {
269
+ re = flt->state[(start_index + flt_len/2) & mask];
270
+ im = flt->stateim[(start_index + flt_len/2) & mask];
271
+ }
272
+ else {
273
+ re = (flt->state[(start_index + flt_len/2) & mask] + flt->state[(start_index + flt_len/2 - 1) & mask]) / 2.0;
274
+ im = (flt->stateim[(start_index + flt_len/2) & mask] + flt->stateim[(start_index + flt_len/2 - 1) & mask]) / 2.0;
275
+ }
276
+ *delayed = rb_complex_raw(DBL2NUM(re * post_mult), DBL2NUM(im * post_mult));
277
+ }
278
+
279
+ return rb_complex_raw(DBL2NUM(accr * post_mult), DBL2NUM(acci * post_mult));
280
+ }
281
+
282
+
283
+ static void make_accept_complex(struct fir_filter *flt)
284
+ {
285
+ if (!flt->stateim)
286
+ flt->stateim = calloc(flt->state_length, sizeof(double));
287
+ if (!flt->stateim)
288
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of state storage", flt->state_length*sizeof(double));
289
+ flt->push_one_sample_func = &push_one_sample_complex;
290
+ flt->filter_one_sample_func = &filter_one_sample_complex;
291
+ flt->zero = rb_complex_raw(DBL2NUM(0.0), DBL2NUM(0.0));
292
+ }
293
+
294
+
295
+ static void push_one_sample_float(struct fir_filter *flt, VALUE sample)
296
+ {
297
+ if (rb_class_of(sample) == rb_cComplex) {
298
+ make_accept_complex(flt);
299
+ push_one_sample_complex(flt, sample);
300
+ }
301
+ else {
302
+ flt->state[flt->index] = NUM2DBL(sample);
303
+ flt->index = (flt->index + 1) & flt->index_mask;
304
+ }
305
+ }
306
+
307
+
308
+ /* Not visible from the Ruby API.
309
+ * Process one Float sample, returning a Float sample.
310
+ * If the input sample is a Complex sample ane not a Float one,
311
+ * makes the filter accept Complex sample and returns filter_one_sample_complex().
312
+ * Once the filter has accepted one Complex, it will allways return complex samples.
313
+ */
314
+ static VALUE filter_one_sample_float(struct fir_filter *flt, VALUE samp, VALUE *delayed, double post_mult)
315
+ {
316
+ const int flt_len = flt->nb_coefficients;
317
+ const int mask = flt->index_mask;
318
+ const int start_index = (flt->state_length + flt->index + 1 - flt_len) & mask;
319
+ double acc = 0.0;
320
+ int i, j;
321
+
322
+ if (rb_class_of(samp) == rb_cComplex) {
323
+ make_accept_complex(flt);
324
+ return filter_one_sample_complex(flt, samp, delayed, post_mult);
325
+ }
326
+
327
+ flt->state[flt->index] = NUM2DBL(samp);
328
+
329
+ for (i = 0, j = start_index; i < flt_len; i++, j = (j + 1) & mask)
330
+ acc += flt->b[i] * flt->state[j];
331
+
332
+ flt->index = (flt->index + 1) & mask;
333
+
334
+ if (delayed) {
335
+ if (flt_len & 1)
336
+ *delayed = DBL2NUM(flt->state[(start_index + flt_len/2) & mask] * post_mult);
337
+ else
338
+ *delayed = DBL2NUM((flt->state[(start_index + flt_len/2) & mask] + flt->state[(start_index + flt_len/2 - 1) & mask]) * post_mult / 2.0);
339
+ }
340
+
341
+ return DBL2NUM(acc * post_mult);
342
+ }
343
+
344
+
345
+ /* @param sample [Numeric, Array<Numeric>] a single sample or an array of samples to process
346
+ * @return [Float, Complex, Array<Float, Complex>] a single or an array of processed samples
347
+ */
348
+ static VALUE fir_filter_filter(VALUE self, VALUE sample)
349
+ {
350
+ struct fir_filter *flt;
351
+ VALUE res = Qnil;
352
+ int i, j, len, outlen;
353
+
354
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
355
+
356
+ if (RB_FLOAT_TYPE_P(sample) || rb_obj_is_kind_of(sample, rb_cNumeric)) {
357
+ switch (flt->decimator_interpolator) {
358
+ case FIR_FILTER_DECIMATOR_FLAG:
359
+ if (flt->counter)
360
+ (*flt->push_one_sample_func)(flt, sample);
361
+ else
362
+ res = (*flt->filter_one_sample_func)(flt, sample, NULL, 1.0);
363
+ flt->counter = (flt->counter + 1) % flt->max_counter;
364
+ break;
365
+ case FIR_FILTER_INTERPOLATOR_FLAG:
366
+ len = flt->max_counter;
367
+ res = rb_ary_new_capa(len);
368
+ rb_ary_store(res, 0, (*flt->filter_one_sample_func)(flt, sample, NULL, (double)len));
369
+ for (i = 1; i < len; i++)
370
+ rb_ary_store(res, i, (*flt->filter_one_sample_func)(flt, flt->zero, NULL, (double)len));
371
+ break;
372
+ default:
373
+ res = (*flt->filter_one_sample_func)(flt, sample, NULL, 1.0);
374
+ }
375
+ }
376
+ else if (rb_class_of(sample) == rb_cArray) {
377
+ len = rb_array_len(sample);
378
+ switch (flt->decimator_interpolator) {
379
+ case FIR_FILTER_DECIMATOR_FLAG:
380
+ outlen = (int)ceil((double)(len - ((flt->max_counter - flt->counter) % flt->max_counter)) / (double)flt->max_counter);
381
+ res = rb_ary_new_capa(outlen);
382
+ for (i = 0, j = 0; i < len; i++) {
383
+ if (flt->counter)
384
+ (*flt->push_one_sample_func)(flt, rb_ary_entry(sample, i));
385
+ else
386
+ rb_ary_store(res, j++, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), NULL, 1.0));
387
+ flt->counter = (flt->counter + 1) % flt->max_counter;
388
+ }
389
+ break;
390
+ case FIR_FILTER_INTERPOLATOR_FLAG:
391
+ res = rb_ary_new_capa(len*flt->max_counter);
392
+ for (i = 0; i < len; i++) {
393
+ rb_ary_store(res, i*flt->max_counter, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), NULL, (double)flt->max_counter));
394
+ for (j = 1; j < flt->max_counter; j++)
395
+ rb_ary_store(res, i*flt->max_counter+j, (*flt->filter_one_sample_func)(flt, flt->zero, NULL, (double)flt->max_counter));
396
+ }
397
+ break;
398
+ default:
399
+ res = rb_ary_new_capa(len);
400
+ for (i = 0; i < len; i++)
401
+ rb_ary_store(res, i, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), NULL, 1.0));
402
+ }
403
+ }
404
+ else
405
+ rb_raise(rb_eArgError, "Expecting a single Numeric or an Array of Numeric");
406
+
407
+ return res;
408
+ }
409
+
410
+
411
+ /* @param sample [Float, Array<Float>] a single sample or an array of samples to process
412
+ * @return [Array<Float>, Array<Array{Float}>] an array containing the filtered signal, an the input signal delayed by n/2
413
+ */
414
+ static VALUE fir_filter_filter_with_delay(VALUE self, VALUE sample)
415
+ {
416
+ struct fir_filter *flt;
417
+ VALUE res = Qnil;
418
+ VALUE artwo;
419
+ VALUE delayed = Qnil;
420
+ VALUE rbval;
421
+ int i, j, len, outlen;
422
+
423
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
424
+
425
+ artwo = rb_ary_new_capa(2);
426
+
427
+ if (RB_FLOAT_TYPE_P(sample) || rb_obj_is_kind_of(sample, rb_cNumeric)) {
428
+ switch (flt->decimator_interpolator) {
429
+ case FIR_FILTER_DECIMATOR_FLAG:
430
+ if (flt->counter)
431
+ (*flt->push_one_sample_func)(flt, sample);
432
+ else
433
+ res = (*flt->filter_one_sample_func)(flt, sample, &delayed, 1.0);
434
+ flt->counter = (flt->counter + 1) % flt->max_counter;
435
+ break;
436
+ case FIR_FILTER_INTERPOLATOR_FLAG:
437
+ len = flt->max_counter;
438
+ res = rb_ary_new_capa(len);
439
+ delayed = rb_ary_new_capa(len);
440
+ rb_ary_store(res, 0, (*flt->filter_one_sample_func)(flt, sample, &rbval, (double)len));
441
+ rb_ary_store(delayed, 0, rbval);
442
+ for (i = 1; i < len; i++) {
443
+ rb_ary_store(res, i, (*flt->filter_one_sample_func)(flt, flt->zero, &rbval, (double)len));
444
+ rb_ary_store(delayed, i, rbval);
445
+ }
446
+ break;
447
+ default:
448
+ res = (*flt->filter_one_sample_func)(flt, sample, &delayed, 1.0);
449
+ }
450
+ }
451
+ else if (rb_class_of(sample) == rb_cArray) {
452
+ len = rb_array_len(sample);
453
+ switch (flt->decimator_interpolator) {
454
+ case FIR_FILTER_DECIMATOR_FLAG:
455
+ outlen = (int)ceil((double)(len - ((flt->max_counter - flt->counter) % flt->max_counter)) / (double)flt->max_counter);
456
+ res = rb_ary_new_capa(outlen);
457
+ delayed = rb_ary_new_capa(outlen);
458
+ for (i = 0, j = 0; i < len; i++) {
459
+ if (flt->counter)
460
+ (*flt->push_one_sample_func)(flt, rb_ary_entry(sample, i));
461
+ else {
462
+ rb_ary_store(res, j, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), &rbval, 1.0));
463
+ rb_ary_store(delayed, j++, rbval);
464
+ }
465
+ flt->counter = (flt->counter + 1) % flt->max_counter;
466
+ }
467
+ break;
468
+ case FIR_FILTER_INTERPOLATOR_FLAG:
469
+ res = rb_ary_new_capa(len*flt->max_counter);
470
+ delayed = rb_ary_new_capa(len*flt->max_counter);
471
+ for (i = 0; i < len; i++) {
472
+ rb_ary_store(res, i*flt->max_counter, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), &rbval, (double)flt->max_counter));
473
+ rb_ary_store(delayed, i*flt->max_counter, rbval);
474
+ for (j = 1; j < flt->max_counter; j++) {
475
+ rb_ary_store(res, i*flt->max_counter+j, (*flt->filter_one_sample_func)(flt, flt->zero, &rbval, (double)flt->max_counter));
476
+ rb_ary_store(delayed, i*flt->max_counter+j, rbval);
477
+ }
478
+ }
479
+ break;
480
+ default:
481
+ res = rb_ary_new_capa(len);
482
+ delayed = rb_ary_new_capa(len);
483
+ for (i = 0; i < len; i++) {
484
+ rb_ary_store(res, i, (*flt->filter_one_sample_func)(flt, rb_ary_entry(sample, i), &rbval, 1.0));
485
+ rb_ary_store(delayed, i, rbval);
486
+ }
487
+ }
488
+ }
489
+ else
490
+ rb_raise(rb_eArgError, "Expecting a single Numeric or an Array of Numeric");
491
+
492
+ rb_ary_store(artwo, 0, res);
493
+ rb_ary_store(artwo, 1, delayed);
494
+
495
+ return artwo;
496
+ }
497
+
498
+
499
+ /* Reset the filter state.
500
+ * @return self
501
+ */
502
+ static VALUE fir_filter_reset(VALUE self)
503
+ {
504
+ struct fir_filter *flt;
505
+ int i;
506
+
507
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
508
+ for (i = 0; i < flt->state_length; i++)
509
+ flt->state[i] = 0.0f;
510
+ if (flt->stateim) {
511
+ for (i = 0; i < flt->state_length; i++)
512
+ flt->stateim[i] = 0.0f;
513
+ }
514
+ flt->index = 0;
515
+ flt->counter = 0;
516
+ flt->push_one_sample_func = &push_one_sample_float;
517
+ flt->filter_one_sample_func = &filter_one_sample_float;
518
+
519
+ return self;
520
+ }
521
+
522
+
523
+ static VALUE fir_filter_alloc(VALUE klass)
524
+ {
525
+ VALUE obj;
526
+ struct fir_filter *flt;
527
+
528
+ flt = ZALLOC(struct fir_filter);
529
+
530
+ obj = TypedData_Wrap_Struct(klass, &fir_filter_type, flt);
531
+
532
+ flt->b = NULL;
533
+ flt->state = NULL;
534
+ flt->stateim = NULL;
535
+ flt->nb_coefficients = 0;
536
+ flt->state_length = 0;
537
+ flt->index_mask = 0;
538
+ flt->index = 0;
539
+ flt->counter = 0;
540
+ flt->max_counter = 1;
541
+ flt->decimator_interpolator = 0;
542
+ flt->push_one_sample_func = &push_one_sample_float;
543
+ flt->filter_one_sample_func = &filter_one_sample_float;
544
+ flt->zero = DBL2NUM(0.0);
545
+
546
+ return obj;
547
+ }
548
+
549
+
550
+ /* @overload initialize(n)
551
+ * @param n [Integer] the filter order
552
+ * @overload initialize(b)
553
+ * @param b [Array<Float>] the array of coefficients
554
+ */
555
+ static VALUE fir_filter_initialize(VALUE self, VALUE b_or_n)
556
+ {
557
+ struct fir_filter *flt;
558
+ int i, len;
559
+
560
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
561
+
562
+ if (rb_class_of(b_or_n) == rb_cInteger) {
563
+ flt->nb_coefficients = NUM2INT(b_or_n) + 1;
564
+ if (flt->nb_coefficients < 2)
565
+ rb_raise(rb_eArgError, "Order cannot be less than 1");
566
+ if (flt->nb_coefficients > 1048577)
567
+ rb_raise(rb_eArgError, "Order cannot be more than 1048576");
568
+ flt->b = calloc(flt->nb_coefficients, sizeof(double));
569
+ if (!flt->b)
570
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of coefficient storage", flt->nb_coefficients*sizeof(double));
571
+ }
572
+ else if (rb_class_of(b_or_n) == rb_cArray) {
573
+ len = rb_array_len(b_or_n);
574
+ if (len > 1048577)
575
+ rb_raise(rb_eArgError, "Cannot be more than 1048577 coefficients");
576
+ if (len < 1)
577
+ rb_raise(rb_eArgError, "Coefficient array is empty");
578
+ flt->nb_coefficients = len;
579
+ flt->b = calloc(flt->nb_coefficients, sizeof(double));
580
+ if (!flt->b)
581
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of coefficient storage", flt->nb_coefficients*sizeof(double));
582
+ for (i = 0; i < len; i++)
583
+ flt->b[flt->nb_coefficients - 1 - i] = (double)(NUM2DBL(rb_ary_entry(b_or_n, i)));
584
+ }
585
+ else
586
+ rb_raise(rb_eArgError, "Expecting the filter order (Integer) or the filter coefficients (Array<Float>)");
587
+
588
+ flt->state_length = (int)(pow(2.0, ceil(log2((double)flt->nb_coefficients))));
589
+ flt->index_mask = flt->state_length - 1;
590
+ flt->index = 0;
591
+ flt->state = calloc(flt->state_length, sizeof(double));
592
+ if (!flt->state)
593
+ rb_raise(rb_eRuntimeError, "Failed to allocate %lu bytes of state storage", flt->state_length*sizeof(double));
594
+ flt->stateim = NULL;
595
+ flt->push_one_sample_func = &push_one_sample_float;
596
+ flt->filter_one_sample_func = &filter_one_sample_float;
597
+
598
+ return self;
599
+ }
600
+
601
+
602
+ /* Push a sample in the filter state.
603
+ * Similar to +filter+, but does not produce an output sample.
604
+ * Used for sample per sample decimators.
605
+ *
606
+ * @param sample [Float, Array<Float>] a single sample or an array of samples to push
607
+ * @return self
608
+ */
609
+ static VALUE fir_filter_push(VALUE self, VALUE sample)
610
+ {
611
+ struct fir_filter *flt;
612
+ int i, len;
613
+
614
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
615
+
616
+ if (RB_FLOAT_TYPE_P(sample) || rb_obj_is_kind_of(sample, rb_cNumeric)) {
617
+ (*flt->push_one_sample_func)(flt, sample);
618
+ }
619
+ else if (rb_class_of(sample) == rb_cArray) {
620
+ len = rb_array_len(sample);
621
+ for (i = 0; i < len; i++)
622
+ (*flt->push_one_sample_func)(flt, rb_ary_entry(sample, i));
623
+ }
624
+ else
625
+ rb_raise(rb_eArgError, "Expecting a single Numeric or an Array of Numeric");
626
+
627
+ return self;
628
+ }
629
+
630
+
631
+ /* @return [Boolean] whether the filter is a decimator or not
632
+ */
633
+ static VALUE fir_filter_is_decimator(VALUE self)
634
+ {
635
+ struct fir_filter *flt;
636
+ VALUE res = Qfalse;
637
+
638
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
639
+
640
+ if (FIR_FILTER_IS_DECIMATOR(flt))
641
+ res = Qtrue;
642
+
643
+ return res;
644
+ }
645
+
646
+
647
+ /* @return [Boolean] whether the filter is an interpolator or not
648
+ */
649
+ static VALUE fir_filter_is_interpolator(VALUE self)
650
+ {
651
+ struct fir_filter *flt;
652
+ VALUE res = Qfalse;
653
+
654
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
655
+
656
+ if (FIR_FILTER_IS_INTERPOLATOR(flt))
657
+ res = Qtrue;
658
+
659
+ return res;
660
+ }
661
+
662
+
663
+ /* @return [Boolean] whether the filter is regular or not (not a decimator or interpolator)
664
+ */
665
+ static VALUE fir_filter_is_regular(VALUE self)
666
+ {
667
+ struct fir_filter *flt;
668
+ VALUE res = Qfalse;
669
+
670
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
671
+
672
+ if (FIR_FILTER_IS_REGULAR(flt))
673
+ res = Qtrue;
674
+
675
+ return res;
676
+ }
677
+
678
+
679
+ /* @return [Ingeger, nil] if the filter is a decimator, returns the decimation factor, +nil+ otherwise
680
+ */
681
+ static VALUE fir_filter_get_decimation_factor(VALUE self)
682
+ {
683
+ struct fir_filter *flt;
684
+ VALUE res = Qnil;
685
+
686
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
687
+
688
+ if (FIR_FILTER_IS_DECIMATOR(flt))
689
+ res = INT2NUM(flt->max_counter);
690
+
691
+ return res;
692
+ }
693
+
694
+
695
+ /* Make the filter a decimator
696
+ * @param factor [Ingeger] the decimation factor, must be > 1
697
+ */
698
+ static VALUE fir_filter_set_decimation_factor(VALUE self, VALUE factor)
699
+ {
700
+ struct fir_filter *flt;
701
+ int fctr;
702
+
703
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
704
+
705
+ fctr = NUM2INT(factor);
706
+ if (fctr > 1) {
707
+ flt->decimator_interpolator = FIR_FILTER_DECIMATOR_FLAG;
708
+ flt->max_counter = fctr;
709
+ }
710
+ else {
711
+ flt->decimator_interpolator = 0;
712
+ flt->max_counter = 1;
713
+ }
714
+ flt->counter = 0;
715
+
716
+ return self;
717
+ }
718
+
719
+
720
+ /* @return [Ingeger, nil] if the filter is an interpolator, returns the interpolation factor, +nil+ otherwise
721
+ */
722
+ static VALUE fir_filter_get_interpolation_factor(VALUE self)
723
+ {
724
+ struct fir_filter *flt;
725
+ VALUE res = Qnil;
726
+
727
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
728
+
729
+ if (FIR_FILTER_IS_INTERPOLATOR(flt))
730
+ res = INT2NUM(flt->max_counter);
731
+
732
+ return res;
733
+ }
734
+
735
+
736
+ /* Make the filter an interpolator
737
+ * @param factor [Ingeger] the interpolation factor, must be > 1
738
+ */
739
+ static VALUE fir_filter_set_interpolation_factor(VALUE self, VALUE factor)
740
+ {
741
+ struct fir_filter *flt;
742
+ int fctr;
743
+
744
+ TypedData_Get_Struct(self, struct fir_filter, &fir_filter_type, flt);
745
+
746
+ fctr = NUM2INT(factor);
747
+ if (fctr > 1) {
748
+ flt->decimator_interpolator = FIR_FILTER_INTERPOLATOR_FLAG;
749
+ flt->max_counter = fctr;
750
+ }
751
+ else {
752
+ flt->decimator_interpolator = 0;
753
+ flt->max_counter = 1;
754
+ }
755
+ flt->counter = 0;
756
+
757
+ return self;
758
+ }
759
+
760
+
761
+ void Init_fir_filter()
762
+ {
763
+ id_real = rb_intern("real");
764
+ id_imag = rb_intern("imag");
765
+
766
+ c_FIR = rb_define_class_under(m_Roctave, "FirFilter", rb_cData);
767
+
768
+ rb_define_alloc_func(c_FIR, fir_filter_alloc);
769
+ rb_define_method(c_FIR, "initialize", fir_filter_initialize, 1);
770
+ rb_define_method(c_FIR, "order", fir_filter_order, 0);
771
+ rb_define_method(c_FIR, "nb_coefficients", fir_filter_nb_coefficients, 0);
772
+ rb_define_method(c_FIR, "get_num_coefficient_at", fir_filter_get_coefficient_at, 1);
773
+ rb_define_method(c_FIR, "set_num_coefficient_at", fir_filter_set_coefficient_at, 2);
774
+ rb_define_method(c_FIR, "get_den_coefficient_at", fir_filter_get_denominator_at, 1);
775
+ rb_define_method(c_FIR, "set_den_coefficient_at", fir_filter_set_denominator_at, 2);
776
+ rb_define_alias(c_FIR, "get_coefficient_at", "get_num_coefficient_at");
777
+ rb_define_alias(c_FIR, "set_coefficient_at", "set_num_coefficient_at");
778
+ rb_define_method(c_FIR, "reset!", fir_filter_reset, 0);
779
+ rb_define_method(c_FIR, "numerator", fir_filter_coefficients, 0);
780
+ rb_define_method(c_FIR, "denominator", fir_filter_denominator, 0);
781
+ rb_define_alias(c_FIR, "coefficients", "numerator");
782
+ rb_define_method(c_FIR, "filter", fir_filter_filter, 1);
783
+ rb_define_method(c_FIR, "filter_with_delay", fir_filter_filter_with_delay, 1);
784
+ rb_define_method(c_FIR, "push", fir_filter_push, 1);
785
+ rb_define_alias(c_FIR, "<<", "push");
786
+ rb_define_method(c_FIR, "regular?", fir_filter_is_regular, 0);
787
+ rb_define_method(c_FIR, "decimator?", fir_filter_is_decimator, 0);
788
+ rb_define_method(c_FIR, "interpolator?", fir_filter_is_interpolator, 0);
789
+ rb_define_method(c_FIR, "decimation_factor", fir_filter_get_decimation_factor, 0);
790
+ rb_define_method(c_FIR, "decimation_factor=", fir_filter_set_decimation_factor, 1);
791
+ rb_define_method(c_FIR, "interpolation_factor", fir_filter_get_interpolation_factor, 0);
792
+ rb_define_method(c_FIR, "interpolation_factor=", fir_filter_set_interpolation_factor, 1);
793
+ }
794
+
795
+