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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +33 -0
- data/ext/roctave/cu8_file_reader.c +331 -0
- data/ext/roctave/cu8_file_reader.h +30 -0
- data/ext/roctave/extconf.rb +6 -0
- data/ext/roctave/fir_filter.c +795 -0
- data/ext/roctave/fir_filter.h +29 -0
- data/ext/roctave/freq_shifter.c +410 -0
- data/ext/roctave/freq_shifter.h +29 -0
- data/ext/roctave/iir_filter.c +462 -0
- data/ext/roctave/iir_filter.h +29 -0
- data/ext/roctave/roctave.c +38 -0
- data/ext/roctave/roctave.h +27 -0
- data/lib/roctave.rb +168 -0
- data/lib/roctave/bilinear.rb +92 -0
- data/lib/roctave/butter.rb +87 -0
- data/lib/roctave/cheby.rb +180 -0
- data/lib/roctave/cu8_file_reader.rb +45 -0
- data/lib/roctave/dft.rb +280 -0
- data/lib/roctave/filter.rb +64 -0
- data/lib/roctave/finite_difference_coefficients.rb +73 -0
- data/lib/roctave/fir.rb +121 -0
- data/lib/roctave/fir1.rb +134 -0
- data/lib/roctave/fir2.rb +246 -0
- data/lib/roctave/fir_design.rb +311 -0
- data/lib/roctave/firls.rb +380 -0
- data/lib/roctave/firpm.rb +499 -0
- data/lib/roctave/freq_shifter.rb +47 -0
- data/lib/roctave/freqz.rb +233 -0
- data/lib/roctave/iir.rb +80 -0
- data/lib/roctave/interp1.rb +78 -0
- data/lib/roctave/plot.rb +748 -0
- data/lib/roctave/poly.rb +46 -0
- data/lib/roctave/roots.rb +73 -0
- data/lib/roctave/sftrans.rb +157 -0
- data/lib/roctave/version.rb +3 -0
- data/lib/roctave/window.rb +116 -0
- data/roctave.gemspec +79 -0
- data/samples/butter.rb +12 -0
- data/samples/cheby.rb +28 -0
- data/samples/dft.rb +18 -0
- data/samples/differentiator.rb +48 -0
- data/samples/differentiator_frequency_scaling.rb +52 -0
- data/samples/fft.rb +40 -0
- data/samples/finite_difference_coefficient.rb +53 -0
- data/samples/fir1.rb +13 -0
- data/samples/fir2.rb +14 -0
- data/samples/fir2_windows.rb +29 -0
- data/samples/fir2bank.rb +30 -0
- data/samples/fir_low_pass.rb +44 -0
- data/samples/firls.rb +77 -0
- data/samples/firpm.rb +78 -0
- data/samples/hilbert_transformer.rb +20 -0
- data/samples/hilbert_transformer_frequency_scaling.rb +47 -0
- data/samples/plot.rb +45 -0
- data/samples/stem.rb +8 -0
- data/samples/type1.rb +25 -0
- data/samples/type3.rb +24 -0
- data/samples/windows.rb +25 -0
- metadata +123 -0
data/README.md
ADDED
@@ -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,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
|
+
|