paddlec 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.
@@ -0,0 +1,28 @@
1
+ /* Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of PaddleC
4
+ *
5
+ * PaddleC 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
+ * PaddleC 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 PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef PADDLEC_COMPLEX_BUFFER_H
20
+ #define PADDLEC_COMPLEX_BUFFER_H
21
+
22
+ #include <ruby.h>
23
+ #include "libpaddlec.h"
24
+
25
+ void Init_paddlec_complex_buffer();
26
+ pdlc_complex_buffer_t* paddlec_complex_buffer_get_struct(VALUE obj);
27
+
28
+ #endif /* PADDLEC_COMPLEX_BUFFER_H */
@@ -0,0 +1,214 @@
1
+ /* Copyright (C) 2020 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of PaddleC
4
+ *
5
+ * PaddleC 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
+ * PaddleC 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 PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <ruby.h>
20
+ #include "libpaddlec.h"
21
+ #include "paddlec.h"
22
+ #include "float_buffer.h"
23
+ #include "complex_buffer.h"
24
+
25
+ /* Document-class: PaddleC::Delay
26
+ *
27
+ * A delay line. It can be used to balance delays brought by filters.
28
+ *
29
+ * To prevent incoherent data,
30
+ * a given {PaddleC::Delay} object must be used exclusively with {PaddleC::FloatBuffer} and Float,
31
+ * or {PaddleC::ComplexBuffer} and Complex.
32
+ */
33
+
34
+
35
+ VALUE c_Delay;
36
+
37
+
38
+ static void paddlec_delay_free(void *p)
39
+ {
40
+ pdlc_delay_t *del = (pdlc_delay_t*)p;
41
+ pdlc_delay_free(del);
42
+ }
43
+
44
+
45
+ static size_t paddlec_delay_size(const void* data)
46
+ {
47
+ const pdlc_delay_t *del = (pdlc_delay_t*)data;
48
+ return pdlc_delay_size(del);
49
+ }
50
+
51
+
52
+ static const rb_data_type_t paddlec_delay_type = {
53
+ .wrap_struct_name = "paddlec_delay_struct",
54
+ .function = {
55
+ .dmark = NULL,
56
+ .dfree = paddlec_delay_free,
57
+ .dsize = paddlec_delay_size,
58
+ },
59
+ .data = NULL,
60
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
61
+ };
62
+
63
+
64
+ static VALUE paddlec_delay_alloc(VALUE klass)
65
+ {
66
+ VALUE obj;
67
+ pdlc_delay_t *del;
68
+
69
+ del = pdlc_delay_new(0);
70
+ obj = TypedData_Wrap_Struct(klass, &paddlec_delay_type, del);
71
+
72
+ return obj;
73
+ }
74
+
75
+
76
+ pdlc_delay_t* paddlec_delay_get_struct(VALUE obj)
77
+ {
78
+ pdlc_delay_t *del;
79
+ TypedData_Get_Struct(obj, pdlc_delay_t, &paddlec_delay_type, del);
80
+ return del;
81
+ }
82
+
83
+
84
+ /* Return a new {PaddleC::Delay}.
85
+ * @param len [Integer] the number of samples the input will be delayed, must be strictly positive
86
+ * @return [PaddleC::Delay]
87
+ */
88
+ static VALUE paddlec_delay_initialize(VALUE self, VALUE len)
89
+ {
90
+ int dl;
91
+ pdlc_delay_t *del;
92
+
93
+ if (rb_class_of(len) != rb_cInteger)
94
+ rb_raise(rb_eTypeError, "expecting an integer, not a %"PRIsVALUE, rb_class_name(rb_class_of(len)));
95
+ dl = NUM2INT(len);
96
+ if (dl <= 0)
97
+ rb_raise(rb_eRangeError, "delay length must be strictly positive");
98
+
99
+ del = paddlec_delay_get_struct(self);
100
+ pdlc_delay_initialize(del, dl);
101
+
102
+ return self;
103
+ }
104
+
105
+
106
+ /* Reset the state of the delay line.
107
+ * @return [self]
108
+ */
109
+ static VALUE paddlec_delay_reset(VALUE self)
110
+ {
111
+ pdlc_delay_t *del = paddlec_delay_get_struct(self);
112
+ pdlc_delay_reset(del);
113
+ return self;
114
+ }
115
+
116
+
117
+ /* Return the length of the delay line.
118
+ * @return [Integer]
119
+ */
120
+ static VALUE paddlec_delay_length(VALUE self)
121
+ {
122
+ pdlc_delay_t *del = paddlec_delay_get_struct(self);
123
+ return UINT2NUM(del->delay);
124
+ }
125
+
126
+
127
+ /* Push new samples, pull delayed samples.
128
+ * If +outbuf+ is provided, it will receive the delayed data.
129
+ * @overload delay(float)
130
+ * @param float [Float]
131
+ * @return [Float]
132
+ * @overload delay(complex)
133
+ * @param complex [Complex]
134
+ * @return [Complex]
135
+ * @overload delay(fbuf, outbuf = nil)
136
+ * @param fbuf [PaddleC::FloatBuffer]
137
+ * @param outbuf [PaddleC::FloatBuffer, nil]
138
+ * @return [PaddleC::FloatBuffer]
139
+ * @overload delay(cbuf, outbuf = nil)
140
+ * @param cbuf [PaddleC::ComplexBuffer]
141
+ * @param outbuf [PaddleC::ComplexBuffer, nil]
142
+ * @return [PaddleC::ComplexBuffer]
143
+ */
144
+ static VALUE paddlec_delay_delay(int argc, VALUE *argv, VALUE self)
145
+ {
146
+ VALUE in, out;
147
+ pdlc_delay_t *del;
148
+ float flt;
149
+ pdlc_complex_t cmp;
150
+ const pdlc_buffer_t *ifbuf;
151
+ pdlc_buffer_t *ofbuf;
152
+ const pdlc_complex_buffer_t *icbuf;
153
+ pdlc_complex_buffer_t *ocbuf;
154
+
155
+ rb_scan_args(argc, argv, "11", &in, &out);
156
+
157
+ del = paddlec_delay_get_struct(self);
158
+
159
+ if (rb_class_of(in) == rb_cComplex) {
160
+ if (out != Qnil)
161
+ rb_raise(rb_eArgError, "outbuf argument is not used when a scalar is delayed");
162
+ cmp.real = (float)NUM2DBL(rb_funcallv(in, id_real, 0, NULL));
163
+ cmp.imag = (float)NUM2DBL(rb_funcallv(in, id_imag, 0, NULL));
164
+ cmp = pdlc_delay_delay_complex(del, cmp);
165
+ out = rb_complex_new(DBL2NUM((double)cmp.real), DBL2NUM((double)cmp.imag));
166
+ }
167
+ else if (rb_obj_is_kind_of(in, rb_cNumeric)) {
168
+ if (out != Qnil)
169
+ rb_raise(rb_eArgError, "outbuf argument is not used when a scalar is delayed");
170
+ flt = (float)NUM2DBL(in);
171
+ flt = pdlc_delay_delay_float(del, flt);
172
+ out = DBL2NUM((double)flt);
173
+ }
174
+ else if (rb_class_of(in) == c_FloatBuffer) {
175
+ if (out != Qnil) {
176
+ if (rb_class_of(out) != c_FloatBuffer)
177
+ rb_raise(rb_eTypeError, "outbuf must be the same type of buffer than the input");
178
+ }
179
+ else
180
+ out = rb_class_new_instance(0, NULL, c_FloatBuffer);
181
+ ifbuf = paddlec_float_buffer_get_struct(in);
182
+ ofbuf = paddlec_float_buffer_get_struct(out);
183
+ pdlc_delay_delay_float_buffer(del, ifbuf, ofbuf);
184
+ }
185
+ else if (rb_class_of(in) == c_ComplexBuffer) {
186
+ if (out != Qnil) {
187
+ if (rb_class_of(out) != c_ComplexBuffer)
188
+ rb_raise(rb_eTypeError, "outbuf must be the same type of buffer than the input");
189
+ }
190
+ else
191
+ out = rb_class_new_instance(0, NULL, c_ComplexBuffer);
192
+ icbuf = paddlec_complex_buffer_get_struct(in);
193
+ ocbuf = paddlec_complex_buffer_get_struct(out);
194
+ pdlc_delay_delay_complex_buffer(del, icbuf, ocbuf);
195
+ }
196
+ else
197
+ rb_raise(rb_eTypeError, "expecting a float, a complex, a %"PRIsVALUE" or a %"PRIsVALUE", not a %"PRIsVALUE,
198
+ rb_class_name(c_FloatBuffer), rb_class_name(c_ComplexBuffer), rb_class_name(rb_class_of(in)));
199
+
200
+ return out;
201
+ }
202
+
203
+
204
+ void Init_paddlec_delay()
205
+ {
206
+ c_Delay = rb_define_class_under(m_PaddleC, "Delay", rb_cObject);
207
+ rb_define_alloc_func(c_Delay, paddlec_delay_alloc);
208
+ rb_define_method(c_Delay, "initialize", paddlec_delay_initialize, 1);
209
+ rb_define_method(c_Delay, "lenght", paddlec_delay_length, 0);
210
+ rb_define_method(c_Delay, "reset", paddlec_delay_reset, 0);
211
+ rb_define_method(c_Delay, "delay", paddlec_delay_delay, -1);
212
+ }
213
+
214
+
@@ -0,0 +1,29 @@
1
+ /* Copyright (C) 2020 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of PaddleC
4
+ *
5
+ * PaddleC 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
+ * PaddleC 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 PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef PADDLEC_DELAY_H
20
+ #define PADDLEC_DELAY_H
21
+
22
+ #include <ruby.h>
23
+ #include "libpaddlec.h"
24
+
25
+ void Init_paddlec_delay();
26
+ pdlc_buffer_t* paddlec_delay_get_struct(VALUE obj);
27
+
28
+ #endif /* PADDLEC_DELAY_H */
29
+
@@ -0,0 +1,106 @@
1
+ require 'mkmf'
2
+
3
+ if have_library('pulse') and have_library('pulse-simple') then
4
+ $defs << '-DHAVE_PULSEAUDIO_L'
5
+ else
6
+ warn "Cannot find pulseaudio library, you should do 'sudo apt install libpulse-dev' and install this gem again'"
7
+ end
8
+
9
+ if have_library('fftw3f') then
10
+ $defs << '-DHAVE_FFTW3F_L'
11
+ else
12
+ warn "Cannot find fftw3f library, you should do 'sudo apt install libfftw3-dev libfftw3-single3' and install this gem again'"
13
+ end
14
+
15
+ if RUBY_PLATFORM =~ /linux/ then
16
+ cpuinfo = File.read '/proc/cpuinfo'
17
+ avx = not(not(cpuinfo =~ /\savx/i))
18
+ sse = not(not(cpuinfo =~ /\ssse/i))
19
+ neon = not(not(cpuinfo =~ /\sneon/i))
20
+ vfpv4 = not(not(cpuinfo =~ /\svfpv4/i))
21
+ else
22
+ avx = false
23
+ sse = false
24
+ neon = false
25
+ vfpv4 = false
26
+ end
27
+
28
+ vflgs = ''
29
+ if avx then
30
+ vflgs += ' -mavx'
31
+ elsif sse then
32
+ vflgs += ' -msse'
33
+ elsif neon then
34
+ vflgs += ' -mfpu=neon'
35
+ vflgs += '-vfpv4' if vfpv4
36
+ elsif vfpv4 then
37
+ vflgs += ' -mfpu=vfpv4'
38
+ end
39
+
40
+ File.open(File.expand_path(File.join('..', '..', 'libpaddlec', 'Makefile'), __FILE__), 'w') do |m|
41
+ m.puts <<EOF
42
+
43
+ CC = gcc
44
+ CFLAGS += -W -Wall
45
+ CFLAGS += -fPIC
46
+ CFLAGS += -O3 -march=native -ffast-math#{vflgs}
47
+
48
+ #SRC = $(wildcard *.c)
49
+ SRC += libpaddlec.c
50
+ SRC += fir_filter.c
51
+ SRC += delay.c
52
+ SRC += arithmetic.c
53
+ SRC += math.c
54
+ SRC += complex.c
55
+ SRC += comparison.c
56
+ SRC += rounding.c
57
+ SRC += no_fast_math.c
58
+ OBJS = $(SRC:.c=.o)
59
+
60
+ all: libpaddlec.a libpaddlec.so
61
+
62
+ libpaddlec.a: $(OBJS)
63
+ ar rcs $@ $^
64
+
65
+ libpaddlec.so: $(OBJS)
66
+ $(CC) $(CFLAGS) -shared -o $@ $^ -lm
67
+
68
+ fir_filter.o: fir_filter.c fir_filter_avx.c fir_filter_sse.c fir_filter_neon.c Makefile
69
+ $(CC) $(CFLAGS) -c -o $@ $<
70
+
71
+ no_fast_math.o: no_fast_math.c Makefile
72
+ $(CC) $(CFLAGS) -fno-fast-math -c -o $@ $<
73
+
74
+ %.o: %.c Makefile
75
+ $(CC) $(CFLAGS) -c -o $@ $<
76
+
77
+ clean:
78
+ @rm -vf *.o
79
+ @rm -vf libpaddlec.a
80
+ @rm -vf libpaddlec.so
81
+
82
+ .PHONY: clean
83
+
84
+ EOF
85
+ end
86
+
87
+ libpaddlec_dir = File.expand_path(File.join('..', '..', 'libpaddlec'), __FILE__)
88
+ $CFLAGS += " -I #{libpaddlec_dir}"
89
+ #$CFLAGS += ' -O3 -mtune=native -ffast-math'
90
+ #$CFLAGS += " -O0 -g "
91
+
92
+ libpaddlec_static = false
93
+ if libpaddlec_static then
94
+ $LDFLAGS += " -L #{libpaddlec_dir}"
95
+ #$LOCAL_LIBS << ' -Wl,--whole-archive -lpaddlec -Wl,--no-whole-archive '
96
+ $LOCAL_LIBS << '-lpaddlec'
97
+ abort "Cannot make libpaddlec.a" unless system "cd #{libpaddlec_dir} && make clean && make libpaddlec.a"
98
+ else
99
+ $LOCAL_LIBS += " -Wl,-rpath,#{libpaddlec_dir} -L#{libpaddlec_dir} -lpaddlec"
100
+ abort "Cannot make libpaddlec.so" unless system "cd #{libpaddlec_dir} && make clean && make libpaddlec.so"
101
+ end
102
+
103
+ create_header 'paddlec_defs.h'
104
+ create_makefile 'paddlec/paddlec'
105
+
106
+
@@ -0,0 +1,892 @@
1
+ /* Copyright (C) 2019 Théotime Bollengier <theotime.bollengier@gmail.com>
2
+ *
3
+ * This file is part of PaddleC
4
+ *
5
+ * PaddleC 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
+ * PaddleC 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 PaddleC. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include <ruby.h>
20
+ #include "libpaddlec.h"
21
+ #include "paddlec.h"
22
+ #include "fir_filter.h"
23
+ #include "float_buffer.h"
24
+ #include "complex_buffer.h"
25
+
26
+
27
+ VALUE c_FirFilter;
28
+ VALUE c_FirTransformer;
29
+ VALUE c_FirDecimator;
30
+ VALUE c_FirInterpolator;
31
+
32
+
33
+ static void paddlec_fir_filter_free(void *p)
34
+ {
35
+ pdlc_fir_filter_t *fir = (pdlc_fir_filter_t*)p;
36
+ pdlc_fir_filter_free(fir);
37
+ }
38
+
39
+
40
+ static size_t paddlec_fir_filter_size(const void* data)
41
+ {
42
+ pdlc_fir_filter_t *fir = (pdlc_fir_filter_t*)data;
43
+ return pdlc_fir_filter_size(fir);
44
+ }
45
+
46
+
47
+ static const rb_data_type_t paddlec_fir_filter_type = {
48
+ .wrap_struct_name = "paddlec_fir_filter_struct",
49
+ .function = {
50
+ .dmark = NULL,
51
+ .dfree = paddlec_fir_filter_free,
52
+ .dsize = paddlec_fir_filter_size,
53
+ },
54
+ .data = NULL,
55
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
56
+ };
57
+
58
+
59
+ static VALUE paddlec_fir_filter_alloc(VALUE klass)
60
+ {
61
+ VALUE obj;
62
+ pdlc_fir_filter_t *fir;
63
+
64
+ fir = pdlc_fir_filter_new(-1);
65
+ obj = TypedData_Wrap_Struct(klass, &paddlec_fir_filter_type, fir);
66
+
67
+ return obj;
68
+ }
69
+
70
+
71
+ static inline pdlc_fir_filter_t* paddlec_fir_filter_get_struct(VALUE obj)
72
+ {
73
+ pdlc_fir_filter_t *fir;
74
+ TypedData_Get_Struct(obj, pdlc_fir_filter_t, &paddlec_fir_filter_type, fir);
75
+ return fir;
76
+ }
77
+
78
+
79
+ /* @return [PaddleC::FirFilter]
80
+ * @overload initialize(n)
81
+ * @param n [Integer] the filter order
82
+ * @return [Paddlec::FirFilter]
83
+ * @overload initialize(b)
84
+ * @param b [Array<Float>, PaddleC::FloatBuffer] the coefficients, as an array or a {FloatBuffer}
85
+ * @return [Paddlec::FirFilter]
86
+ */
87
+ static VALUE paddlec_fir_filter_initialize(VALUE self, VALUE b_or_n)
88
+ {
89
+ pdlc_fir_filter_t *fir;
90
+ const pdlc_buffer_t *fbuf;
91
+ int order, i;
92
+
93
+ fir = paddlec_fir_filter_get_struct(self);
94
+
95
+ if (rb_class_of(b_or_n) == c_FloatBuffer) {
96
+ fbuf = paddlec_float_buffer_get_struct(b_or_n);
97
+ order = fbuf->length;
98
+ if (order < 1)
99
+ rb_raise(rb_eArgError, "Negative order");
100
+ pdlc_fir_filter_initialize(fir, order - 1);
101
+ for (i = 0; i < order; i++)
102
+ pdlc_fir_filter_set_coef_at(fir, i, fbuf->data[i]);
103
+ }
104
+ else if (rb_class_of(b_or_n) == rb_cArray) {
105
+ order = rb_array_len(b_or_n);
106
+ if (order < 1)
107
+ rb_raise(rb_eArgError, "Negative order");
108
+ pdlc_fir_filter_initialize(fir, order - 1);
109
+ for (i = 0; i < order; i++)
110
+ pdlc_fir_filter_set_coef_at(fir, i, (float)NUM2DBL(rb_ary_entry(b_or_n, i)));
111
+ }
112
+ else {
113
+ order = NUM2INT(b_or_n);
114
+ if (order < 0)
115
+ rb_raise(rb_eArgError, "Negative order");
116
+ pdlc_fir_filter_initialize(fir, order);
117
+ }
118
+
119
+ return self;
120
+ }
121
+
122
+
123
+ /* Reset the filter state.
124
+ * @return [self]
125
+ */
126
+ static VALUE paddlec_fir_filter_reset(VALUE self)
127
+ {
128
+ pdlc_fir_filter_t *fir;
129
+
130
+ fir = paddlec_fir_filter_get_struct(self);
131
+ pdlc_fir_filter_reset(fir);
132
+
133
+ return self;
134
+ }
135
+
136
+
137
+ /* @return [Integer] the filter order
138
+ */
139
+ static VALUE paddlec_fir_filter_order(VALUE self)
140
+ {
141
+ const pdlc_fir_filter_t *fir;
142
+
143
+ fir = paddlec_fir_filter_get_struct(self);
144
+
145
+ return INT2NUM((int)fir->nb_coefs - 1);
146
+ }
147
+
148
+
149
+ /* @return [Integer] number of coefficients (order + 1)
150
+ */
151
+ static VALUE paddlec_fir_filter_nb_coefficients(VALUE self)
152
+ {
153
+ const pdlc_fir_filter_t *fir;
154
+
155
+ fir = paddlec_fir_filter_get_struct(self);
156
+
157
+ return INT2NUM((int)fir->nb_coefs);
158
+ }
159
+
160
+
161
+ /* Return a copy of the filter's numerator coefficients.
162
+ * @return [PaddleC::FloatBuffer] the filter coefficients
163
+ */
164
+ static VALUE paddlec_fir_filter_coefficients(VALUE self)
165
+ {
166
+ const pdlc_fir_filter_t *fir;
167
+ pdlc_buffer_t *fbuf;
168
+ VALUE res;
169
+ unsigned int i;
170
+
171
+ fir = paddlec_fir_filter_get_struct(self);
172
+
173
+ res = UINT2NUM(fir->nb_coefs);
174
+ res = rb_class_new_instance(1, &res, c_FloatBuffer);
175
+ fbuf = paddlec_float_buffer_get_struct(res);
176
+
177
+ for (i = 0; i < fir->nb_coefs; i++)
178
+ pdlc_fir_filter_get_coef_at(fir, (int)i, fbuf->data + i);
179
+
180
+ return res;
181
+ }
182
+
183
+
184
+ /* Return a copy of the filter's denominator coefficients, allways [1].
185
+ * @return [PaddleC::FloatBuffer] the filter denominator coefficients
186
+ */
187
+ static VALUE paddlec_fir_filter_denominator(VALUE self)
188
+ {
189
+ pdlc_buffer_t *fbuf;
190
+ VALUE res;
191
+
192
+ res = UINT2NUM(1);
193
+ res = rb_class_new_instance(1, &res, c_FloatBuffer);
194
+ fbuf = paddlec_float_buffer_get_struct(res);
195
+
196
+ fbuf->data[0] = 1.0;
197
+
198
+ return res;
199
+ }
200
+
201
+
202
+ /* @param index [Integer] the coefficient index
203
+ * @return [Float] the coefficient at +index+
204
+ */
205
+ static VALUE paddlec_fir_filter_get_coefficient_at(VALUE self, VALUE index)
206
+ {
207
+ const pdlc_fir_filter_t *fir;
208
+ int ind;
209
+ float val;
210
+ VALUE res;
211
+
212
+ fir = paddlec_fir_filter_get_struct(self);
213
+
214
+ ind = NUM2INT(index);
215
+ if (pdlc_fir_filter_get_coef_at(fir, ind, &val) == 0)
216
+ res = DBL2NUM((double)val);
217
+ else
218
+ res = DBL2NUM(0.0);
219
+
220
+ return res;
221
+ }
222
+
223
+
224
+ /* @param index [Integer] the coefficient index
225
+ * @param coef [Float] the coefficient value
226
+ * @return [Float, nil] the coefficient if the index is valid, or +nil+ otherwise
227
+ */
228
+ static VALUE paddlec_fir_filter_set_coefficient_at(VALUE self, VALUE index, VALUE coef)
229
+ {
230
+ pdlc_fir_filter_t *fir;
231
+ int ind;
232
+ float val;
233
+ VALUE res = Qnil;
234
+
235
+ fir = paddlec_fir_filter_get_struct(self);
236
+ val = (float)NUM2DBL(coef);
237
+
238
+ ind = NUM2INT(index);
239
+ if (pdlc_fir_filter_set_coef_at(fir, ind, val) == 0)
240
+ res = DBL2NUM(val);
241
+
242
+ return res;
243
+ }
244
+
245
+
246
+ /* This method returns 1.0 if +index+ is 0, 0.0 otherwise. It is there to be compatible with {IirFilter}
247
+ * @param index [Integer] the denominator coefficient index
248
+ * @return [Float] the denominator coefficient at index
249
+ */
250
+ static VALUE paddlec_fir_filter_get_denominator_at(VALUE self, VALUE index)
251
+ {
252
+ int ind;
253
+ VALUE res;
254
+
255
+ ind = NUM2INT(index);
256
+ if (ind == 0)
257
+ res = DBL2NUM(1.0);
258
+ else
259
+ res = DBL2NUM(0.0);
260
+
261
+ return res;
262
+ }
263
+
264
+
265
+ /* This method does nothing, it is there to be compatible with {IirFilter}
266
+ * @param index [Integer] the coefficient index
267
+ * @param coef [Float] the denominator coefficient value
268
+ * @return [Float, nil] the denominator coefficient if the index is valid, or nil otherwise
269
+ */
270
+ static VALUE paddlec_fir_filter_set_denominator_at(VALUE self, VALUE index, VALUE coef)
271
+ {
272
+ (void)self;
273
+ (void)index;
274
+ (void)coef;
275
+
276
+ return Qnil;
277
+ }
278
+
279
+
280
+ /* Filter a single sample, complex or real, or a {FloatBuffer} or {ComplexBuffer}.
281
+ * The returned element is of the same type as the input element.
282
+ *
283
+ * If complex samples are fed to the filter, float samples must not be fed before a {FirFilter#reset}.
284
+ *
285
+ * The keyword argument +buffer:+ can be used to provide the filter with an already allocated buffer for the output.
286
+ *
287
+ * The keyword argument +delayed:+ can be used to provide the filter with an already allocated buffer filled
288
+ * with the input signal delayed by +order / 2+.
289
+ * If +delayed:+ is not +nil+, then the output is an array containing the filtered signal and the delayed signal.
290
+ *
291
+ * @overload filter(r_sample)
292
+ * @param r_sample [Float] a real input sample
293
+ * @return [Float] a real processed sample
294
+ *
295
+ * @overload filter(c_sample)
296
+ * @param c_sample [Complex] a complex input sample
297
+ * @return [Complex] a complex processed sample
298
+ *
299
+ * @overload filter(r_sample, delayed: true)
300
+ * @param r_sample [Float] a real input sample
301
+ * @param delayed [Boolean, nil] whether to output the delayed sample or not
302
+ * @return [Array<Float>] a two element array containing the processed sample and the delayed sample
303
+ *
304
+ * @overload filter(c_sample, delayed: true)
305
+ * @param c_sample [Complex] a complex input sample
306
+ * @param delayed [Boolean, nil] whether to output the delayed sample or not
307
+ * @return [Array<Complex>] a two element array containing the processed sample and the delayed sample
308
+ *
309
+ * @overload filter(float_buffer, buffer: obuf)
310
+ * @param float_buffer [PaddleC::FloatBuffer] a buffer of real input samples
311
+ * @param buffer [PaddleC::FloatBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
312
+ * @return [PaddleC::FloatBuffer] a buffer of real processed samples
313
+ *
314
+ * @overload filter(complex_buffer, buffer: obuf)
315
+ * @param complex_buffer [PaddleC::ComplexBuffer] a buffer of complex input samples
316
+ * @param buffer [PaddleC::ComplexBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
317
+ * @return [PaddleC::ComplexBuffer] a buffer of complex processed samples
318
+ *
319
+ * @overload filter(float_buffer, buffer: obuf, delayed: true)
320
+ * @param float_buffer [PaddleC::FloatBuffer] a buffer of real input samples
321
+ * @param buffer [PaddleC::FloatBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
322
+ * @param delayed [PaddleC::FloatBuffer, Boolean, nil] a boolean or an already allocated buffer
323
+ * @return [Array<PaddleC::FloatBuffer>] a two element array containing the processed samples and the delayed samples
324
+ *
325
+ * @overload filter(complex_buffer, buffer: obuf, delayed: true)
326
+ * @param complex_buffer [PaddleC::ComplexBuffer] a buffer of complex input samples
327
+ * @param buffer [PaddleC::ComplexBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
328
+ * @param delayed [PaddleC::ComplexBuffer, Boolean, nil] a boolean or an already allocated buffer
329
+ * @return [Array<PaddleC::ComplexBuffer>] a two element array containing the processed samples and the delayed samples
330
+ */
331
+ static VALUE paddlec_fir_filter_filter(int argc, VALUE *argv, VALUE self)
332
+ {
333
+ pdlc_fir_filter_t *fir;
334
+ VALUE rbSample, rbOptHash;
335
+ VALUE buffer_and_delayed[2] = {Qundef, Qundef};
336
+ const ID kwkeys[2] = {id_buffer, id_delayed};
337
+ VALUE obuf = Qnil, delayed = Qnil;
338
+ VALUE res = Qnil;
339
+ float delayed_float;
340
+ pdlc_complex_t delayed_complex;
341
+ float output_float;
342
+ pdlc_complex_t output_complex;
343
+ const pdlc_buffer_t *ifbuf;
344
+ const pdlc_complex_buffer_t *icbuf;
345
+ pdlc_buffer_t *ofbuf, *dfbuf;
346
+ pdlc_complex_buffer_t *ocbuf, *dcbuf;
347
+
348
+ rb_scan_args(argc, argv, "1:", &rbSample, &rbOptHash);
349
+
350
+ if (!NIL_P(rbOptHash))
351
+ rb_get_kwargs(rbOptHash, kwkeys, 0, 2, buffer_and_delayed);
352
+ if (buffer_and_delayed[0] != Qundef)
353
+ obuf = buffer_and_delayed[0];
354
+ if (buffer_and_delayed[1] != Qundef)
355
+ delayed = buffer_and_delayed[1];
356
+
357
+ fir = paddlec_fir_filter_get_struct(self);
358
+
359
+ if (rb_class_of(rbSample) == c_FloatBuffer) {
360
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_FloatBuffer)
361
+ rb_raise(rb_eArgError, "only a FloatBuffer is valid for buffer when a FloatBuffer is provided to the filter");
362
+ if (delayed != Qtrue && delayed != Qnil && delayed != Qfalse && rb_class_of(obuf) != c_FloatBuffer)
363
+ rb_raise(rb_eArgError, "only true, false, nil and a FloatBuffer are valid values for delayed when a FloatBuffer is provided to the filter");
364
+ ifbuf = paddlec_float_buffer_get_struct(rbSample);
365
+
366
+ if (rb_class_of(obuf) != c_FloatBuffer)
367
+ obuf = rb_class_new_instance(0, NULL, c_FloatBuffer);
368
+ ofbuf = paddlec_float_buffer_get_struct(obuf);
369
+
370
+ if (delayed == Qnil || delayed == Qfalse) {
371
+ pdlc_fir_filter_filter_float_buffer(fir, ifbuf, ofbuf, NULL);
372
+ res = obuf;
373
+ }
374
+ else {
375
+ if (rb_class_of(delayed) != c_FloatBuffer)
376
+ delayed = rb_class_new_instance(0, NULL, c_FloatBuffer);
377
+ dfbuf = paddlec_float_buffer_get_struct(delayed);
378
+ pdlc_fir_filter_filter_float_buffer(fir, ifbuf, ofbuf, dfbuf);
379
+ res = rb_ary_new_capa(2);
380
+ rb_ary_store(res, 0, obuf);
381
+ rb_ary_store(res, 1, delayed);
382
+ }
383
+ }
384
+ else if (rb_class_of(rbSample) == c_ComplexBuffer) {
385
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_ComplexBuffer)
386
+ rb_raise(rb_eArgError, "only a ComplexBuffer is valid for buffer when a ComplexBuffer is provided to the filter");
387
+ if (delayed != Qtrue && delayed != Qnil && delayed != Qfalse && rb_class_of(obuf) != c_ComplexBuffer)
388
+ rb_raise(rb_eArgError, "only true, false, nil and a ComplexBuffer are valid values for delayed when a ComplexBuffer is provided to the filter");
389
+ icbuf = paddlec_complex_buffer_get_struct(rbSample);
390
+
391
+ if (rb_class_of(obuf) != c_ComplexBuffer)
392
+ obuf = rb_class_new_instance(0, NULL, c_ComplexBuffer);
393
+ ocbuf = paddlec_complex_buffer_get_struct(obuf);
394
+
395
+ if (delayed == Qnil || delayed == Qfalse) {
396
+ pdlc_fir_filter_filter_complex_buffer(fir, icbuf, ocbuf, NULL);
397
+ res = obuf;
398
+ }
399
+ else {
400
+ if (rb_class_of(delayed) != c_ComplexBuffer)
401
+ delayed = rb_class_new_instance(0, NULL, c_ComplexBuffer);
402
+ dcbuf = paddlec_complex_buffer_get_struct(delayed);
403
+ pdlc_fir_filter_filter_complex_buffer(fir, icbuf, ocbuf, dcbuf);
404
+ res = rb_ary_new_capa(2);
405
+ rb_ary_store(res, 0, obuf);
406
+ rb_ary_store(res, 1, delayed);
407
+ }
408
+ }
409
+ else {
410
+ if (!NIL_P(obuf))
411
+ rb_raise(rb_eArgError, "output buffer must not be provided when a single sample is given to the filter");
412
+ if (delayed != Qtrue && delayed != Qnil && delayed != Qfalse)
413
+ rb_raise(rb_eArgError, "only true, false and nil are valid values for delayed when a single sample is provided to the filter");
414
+ if (rb_class_of(rbSample) == rb_cComplex) {
415
+ output_complex.real = (float)NUM2DBL(rb_funcallv(rbSample, id_real, 0, NULL));
416
+ output_complex.imag = (float)NUM2DBL(rb_funcallv(rbSample, id_real, 0, NULL));
417
+ if (delayed == Qtrue) {
418
+ output_complex = pdlc_fir_filter_filter_complex(fir, output_complex, &delayed_complex);
419
+ res = rb_ary_new_capa(2);
420
+ rb_ary_store(res, 0, rb_complex_raw(DBL2NUM((double)output_complex.real), DBL2NUM((double)output_complex.imag)));
421
+ rb_ary_store(res, 1, rb_complex_raw(DBL2NUM((double)delayed_complex.real), DBL2NUM((double)delayed_complex.imag)));
422
+ }
423
+ else {
424
+ output_complex = pdlc_fir_filter_filter_complex(fir, output_complex, NULL);
425
+ res = rb_complex_raw(DBL2NUM((double)output_complex.real), DBL2NUM((double)output_complex.imag));
426
+ }
427
+ }
428
+ else {
429
+ if (delayed == Qtrue) {
430
+ output_float = pdlc_fir_filter_filter_float(fir, (float)NUM2DBL(rbSample), &delayed_float);
431
+ res = rb_ary_new_capa(2);
432
+ rb_ary_store(res, 0, DBL2NUM((double)output_float));
433
+ rb_ary_store(res, 1, DBL2NUM((double)delayed_float));
434
+ }
435
+ else {
436
+ output_float = pdlc_fir_filter_filter_float(fir, (float)NUM2DBL(rbSample), NULL);
437
+ res = DBL2NUM((double)output_float);
438
+ }
439
+ }
440
+ }
441
+
442
+ return res;
443
+ }
444
+
445
+
446
+ /* @return [PaddleC::FirInterpolator]
447
+ * @overload initialize(n, interpolation_factor)
448
+ * @param n [Integer] the filter order
449
+ * @param interpolation_factor [Integer] the interpolation factor (>= 1)
450
+ * @return [PaddleC::FirInterpolator]
451
+ * @overload initialize(b, interpolation_factor)
452
+ * @param interpolation_factor [Integer] the interpolation factor (>= 1)
453
+ * @param b [Array<Float>, PaddleC::FloatBuffer] the coefficients, as an array or a {FloatBuffer}
454
+ * @return [PaddleC::FirInterpolator]
455
+ */
456
+ static VALUE paddlec_fir_filter_interpolator_initialize(VALUE self, VALUE b_or_n, VALUE factor)
457
+ {
458
+ pdlc_fir_filter_t *fir;
459
+ const pdlc_buffer_t *fbuf;
460
+ int order, i, ifactor;
461
+
462
+ ifactor = NUM2INT(factor);
463
+ if (ifactor < 1)
464
+ rb_raise(rb_eRangeError, "Interpolation factor cannot be less than one");
465
+ if (ifactor > 32)
466
+ rb_raise(rb_eRangeError, "Interpolation factor greater than 32 is too much");
467
+
468
+ fir = paddlec_fir_filter_get_struct(self);
469
+
470
+ if (rb_class_of(b_or_n) == c_FloatBuffer) {
471
+ fbuf = paddlec_float_buffer_get_struct(b_or_n);
472
+ order = fbuf->length;
473
+ if (order < 1)
474
+ rb_raise(rb_eArgError, "Negative order");
475
+ pdlc_fir_filter_interpolator_initialize(fir, order - 1, ifactor);
476
+ for (i = 0; i < order; i++)
477
+ pdlc_fir_filter_set_coef_at(fir, i, fbuf->data[i]);
478
+ }
479
+ else if (rb_class_of(b_or_n) == rb_cArray) {
480
+ order = rb_array_len(b_or_n);
481
+ if (order < 1)
482
+ rb_raise(rb_eArgError, "Negative order");
483
+ pdlc_fir_filter_interpolator_initialize(fir, order - 1, ifactor);
484
+ for (i = 0; i < order; i++)
485
+ pdlc_fir_filter_set_coef_at(fir, i, (float)NUM2DBL(rb_ary_entry(b_or_n, i)));
486
+ }
487
+ else {
488
+ order = NUM2INT(b_or_n);
489
+ if (order < 0)
490
+ rb_raise(rb_eArgError, "Negative order");
491
+ pdlc_fir_filter_interpolator_initialize(fir, order, ifactor);
492
+ }
493
+
494
+ return self;
495
+ }
496
+
497
+
498
+ /* @return [Integer] the interpolation factor
499
+ */
500
+ static VALUE paddlec_fir_filter_interpolator_interpolation_factor(VALUE self)
501
+ {
502
+ const pdlc_fir_filter_t *fir;
503
+
504
+ fir = paddlec_fir_filter_get_struct(self);
505
+
506
+ return INT2NUM(pdlc_fir_filter_get_factor(fir));
507
+ }
508
+
509
+
510
+ /* Interpolate a {FloatBuffer} or {ComplexBuffer}.
511
+ * The returned element is of the same type as the input element.
512
+ *
513
+ * If complex samples are fed to the interpolator, float samples must not be fed before a {FirFilter#reset}.
514
+ *
515
+ * The keyword argument +buffer:+ can be used to provide the interpolator with an already allocated buffer for the output.
516
+ *
517
+ * @overload interpolate(float_buffer, buffer: obuf)
518
+ * @param float_buffer [PaddleC::FloatBuffer] a buffer of real input samples
519
+ * @param buffer [PaddleC::FloatBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
520
+ * @return [PaddleC::FloatBuffer] a buffer of real processed samples of size +float_buffer.length * #interpolation_factor+
521
+ *
522
+ * @overload interpolate(complex_buffer, buffer: obuf)
523
+ * @param complex_buffer [PaddleC::ComplexBuffer] a buffer of complex input samples
524
+ * @param buffer [PaddleC::ComplexBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
525
+ * @return [PaddleC::ComplexBuffer] a buffer of complex processed samples of size +complex_buffer.length * #interpolation_factor+
526
+ */
527
+ static VALUE paddlec_fir_filter_interpolator_interpolate(int argc, VALUE *argv, VALUE self)
528
+ {
529
+ pdlc_fir_filter_t *fir;
530
+ VALUE rbSample, rbOptHash;
531
+ VALUE buffer_hash[2] = {Qundef};
532
+ const ID kwkeys[2] = {id_buffer};
533
+ VALUE obuf = Qnil;
534
+ VALUE res = Qnil;
535
+ const pdlc_buffer_t *ifbuf;
536
+ const pdlc_complex_buffer_t *icbuf;
537
+ pdlc_buffer_t *ofbuf;
538
+ pdlc_complex_buffer_t *ocbuf;
539
+
540
+ rb_scan_args(argc, argv, "1:", &rbSample, &rbOptHash);
541
+
542
+ if (!NIL_P(rbOptHash))
543
+ rb_get_kwargs(rbOptHash, kwkeys, 0, 1, buffer_hash);
544
+ if (buffer_hash[0] != Qundef)
545
+ obuf = buffer_hash[0];
546
+
547
+ fir = paddlec_fir_filter_get_struct(self);
548
+
549
+ if (rb_class_of(rbSample) == c_FloatBuffer) {
550
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_FloatBuffer)
551
+ rb_raise(rb_eArgError, "only a %"PRIsVALUE" is valid for buffer when a %"PRIsVALUE" is provided to the Interpolator", rb_class_name(c_FloatBuffer), rb_class_name(c_FloatBuffer));
552
+ ifbuf = paddlec_float_buffer_get_struct(rbSample);
553
+
554
+ if (rb_class_of(obuf) != c_FloatBuffer)
555
+ obuf = rb_class_new_instance(0, NULL, c_FloatBuffer);
556
+ ofbuf = paddlec_float_buffer_get_struct(obuf);
557
+
558
+ pdlc_fir_filter_interpolate_float_buffer(fir, ifbuf, ofbuf);
559
+ res = obuf;
560
+ }
561
+ else if (rb_class_of(rbSample) == c_ComplexBuffer) {
562
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_ComplexBuffer)
563
+ rb_raise(rb_eArgError, "only a %"PRIsVALUE" is valid for buffer when a %"PRIsVALUE" is provided to the Interpolator", rb_class_name(c_ComplexBuffer), rb_class_name(c_ComplexBuffer));
564
+ icbuf = paddlec_complex_buffer_get_struct(rbSample);
565
+
566
+ if (rb_class_of(obuf) != c_ComplexBuffer)
567
+ obuf = rb_class_new_instance(0, NULL, c_ComplexBuffer);
568
+ ocbuf = paddlec_complex_buffer_get_struct(obuf);
569
+
570
+ pdlc_fir_filter_interpolate_complex_buffer(fir, icbuf, ocbuf);
571
+ res = obuf;
572
+ }
573
+ else
574
+ rb_raise(rb_eTypeError, "expecting a %"PRIsVALUE" or a %"PRIsVALUE, rb_class_name(c_FloatBuffer), rb_class_name(c_ComplexBuffer));
575
+
576
+ return res;
577
+ }
578
+
579
+
580
+ /* @return [PaddleC::FirDecimator]
581
+ * @overload initialize(n, decimation_factor)
582
+ * @param n [Integer] the filter order
583
+ * @param decimation_factor [Integer] the decimation factor (>= 1)
584
+ * @return [PaddleC::FirDecimator]
585
+ * @overload initialize(b, decimation_factor)
586
+ * @param b [Array<Float>, PaddleC::FloatBuffer] the coefficients, as an array or a {FloatBuffer}
587
+ * @param decimation_factor [Integer] the decimation factor (>= 1)
588
+ * @return [PaddleC::FirDecimator]
589
+ */
590
+ static VALUE paddlec_fir_filter_decimator_initialize(VALUE self, VALUE b_or_n, VALUE factor)
591
+ {
592
+ pdlc_fir_filter_t *fir;
593
+ const pdlc_buffer_t *fbuf;
594
+ int order, i, ifactor;
595
+
596
+ ifactor = NUM2INT(factor);
597
+ if (ifactor < 1)
598
+ rb_raise(rb_eRangeError, "Decimation factor cannot be less than one");
599
+ if (ifactor > 32)
600
+ rb_raise(rb_eRangeError, "Decimation factor greater than 32 is too much");
601
+
602
+ fir = paddlec_fir_filter_get_struct(self);
603
+
604
+ if (rb_class_of(b_or_n) == c_FloatBuffer) {
605
+ fbuf = paddlec_float_buffer_get_struct(b_or_n);
606
+ order = fbuf->length;
607
+ if (order < 1)
608
+ rb_raise(rb_eArgError, "Negative order");
609
+ pdlc_fir_filter_decimator_initialize(fir, order - 1, ifactor);
610
+ for (i = 0; i < order; i++)
611
+ pdlc_fir_filter_set_coef_at(fir, i, fbuf->data[i]);
612
+ }
613
+ else if (rb_class_of(b_or_n) == rb_cArray) {
614
+ order = rb_array_len(b_or_n);
615
+ if (order < 1)
616
+ rb_raise(rb_eArgError, "Negative order");
617
+ pdlc_fir_filter_decimator_initialize(fir, order - 1, ifactor);
618
+ for (i = 0; i < order; i++)
619
+ pdlc_fir_filter_set_coef_at(fir, i, (float)NUM2DBL(rb_ary_entry(b_or_n, i)));
620
+ }
621
+ else {
622
+ order = NUM2INT(b_or_n);
623
+ if (order < 0)
624
+ rb_raise(rb_eArgError, "Negative order");
625
+ pdlc_fir_filter_decimator_initialize(fir, order, ifactor);
626
+ }
627
+
628
+ return self;
629
+ }
630
+
631
+
632
+ /* @return [Integer] the decimation factor
633
+ */
634
+ static VALUE paddlec_fir_filter_decimator_decimation_factor(VALUE self)
635
+ {
636
+ const pdlc_fir_filter_t *fir;
637
+
638
+ fir = paddlec_fir_filter_get_struct(self);
639
+
640
+ return INT2NUM(pdlc_fir_filter_get_factor(fir));
641
+ }
642
+
643
+
644
+ /* Decimate a {FloatBuffer} or {ComplexBuffer}.
645
+ * The returned element is of the same type as the input element.
646
+ *
647
+ * If complex samples are fed to the decimator, float samples must not be fed before a {FirFilter#reset}.
648
+ *
649
+ * The keyword argument +buffer:+ can be used to provide the decimator with an already allocated buffer for the output.
650
+ *
651
+ * @overload decimate(float_buffer, buffer: obuf)
652
+ * @param float_buffer [PaddleC::FloatBuffer] a buffer of real input samples
653
+ * @param buffer [PaddleC::FloatBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
654
+ * @return [PaddleC::FloatBuffer] a buffer of real processed samples of size ≈ +float_buffer.length / #decimation_factor+
655
+ *
656
+ * @overload decimate(complex_buffer, buffer: obuf)
657
+ * @param complex_buffer [PaddleC::ComplexBuffer] a buffer of complex input samples
658
+ * @param buffer [PaddleC::ComplexBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
659
+ * @return [PaddleC::ComplexBuffer] a buffer of complex processed samples of size ≈ +complex_buffer.length / #decimation_factor+
660
+ */
661
+ static VALUE paddlec_fir_filter_decimator_decimate(int argc, VALUE *argv, VALUE self)
662
+ {
663
+ pdlc_fir_filter_t *fir;
664
+ VALUE rbSample, rbOptHash;
665
+ VALUE buffer_hash[2] = {Qundef};
666
+ const ID kwkeys[2] = {id_buffer};
667
+ VALUE obuf = Qnil;
668
+ VALUE res = Qnil;
669
+ const pdlc_buffer_t *ifbuf;
670
+ const pdlc_complex_buffer_t *icbuf;
671
+ pdlc_buffer_t *ofbuf;
672
+ pdlc_complex_buffer_t *ocbuf;
673
+
674
+ rb_scan_args(argc, argv, "1:", &rbSample, &rbOptHash);
675
+
676
+ if (!NIL_P(rbOptHash))
677
+ rb_get_kwargs(rbOptHash, kwkeys, 0, 1, buffer_hash);
678
+ if (buffer_hash[0] != Qundef)
679
+ obuf = buffer_hash[0];
680
+
681
+ fir = paddlec_fir_filter_get_struct(self);
682
+
683
+ if (rb_class_of(rbSample) == c_FloatBuffer) {
684
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_FloatBuffer)
685
+ rb_raise(rb_eArgError, "only a %"PRIsVALUE" is valid for buffer when a %"PRIsVALUE" is provided to the decimator", rb_class_name(c_FloatBuffer), rb_class_name(c_FloatBuffer));
686
+ ifbuf = paddlec_float_buffer_get_struct(rbSample);
687
+
688
+ if (rb_class_of(obuf) != c_FloatBuffer)
689
+ obuf = rb_class_new_instance(0, NULL, c_FloatBuffer);
690
+ ofbuf = paddlec_float_buffer_get_struct(obuf);
691
+
692
+ pdlc_fir_filter_decimate_float_buffer(fir, ifbuf, ofbuf);
693
+ res = obuf;
694
+ }
695
+ else if (rb_class_of(rbSample) == c_ComplexBuffer) {
696
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_ComplexBuffer)
697
+ rb_raise(rb_eArgError, "only a %"PRIsVALUE" is valid for buffer when a %"PRIsVALUE" is provided to the decimator", rb_class_name(c_ComplexBuffer), rb_class_name(c_ComplexBuffer));
698
+ icbuf = paddlec_complex_buffer_get_struct(rbSample);
699
+
700
+ if (rb_class_of(obuf) != c_ComplexBuffer)
701
+ obuf = rb_class_new_instance(0, NULL, c_ComplexBuffer);
702
+ ocbuf = paddlec_complex_buffer_get_struct(obuf);
703
+
704
+ pdlc_fir_filter_decimate_complex_buffer(fir, icbuf, ocbuf);
705
+ res = obuf;
706
+ }
707
+ else
708
+ rb_raise(rb_eTypeError, "expecting a %"PRIsVALUE" or a %"PRIsVALUE, rb_class_name(c_FloatBuffer), rb_class_name(c_ComplexBuffer));
709
+
710
+ return res;
711
+ }
712
+
713
+
714
+ /* @return [PaddleC::FirTransformer]
715
+ * @overload initialize(n)
716
+ * @param n [Integer] the filter order
717
+ * @return [Paddlec::FirTransformer]
718
+ * @overload initialize(b)
719
+ * @param b [Array<Float>, PaddleC::FloatBuffer] the coefficients, as an array or a {FloatBuffer}
720
+ * @return [Paddlec::FirTransformer]
721
+ */
722
+ static VALUE paddlec_fir_filter_transformer_initialize(VALUE self, VALUE b_or_n)
723
+ {
724
+ pdlc_fir_filter_t *fir;
725
+ const pdlc_buffer_t *fbuf;
726
+ int order, i;
727
+
728
+ fir = paddlec_fir_filter_get_struct(self);
729
+
730
+ if (rb_class_of(b_or_n) == c_FloatBuffer) {
731
+ fbuf = paddlec_float_buffer_get_struct(b_or_n);
732
+ order = fbuf->length;
733
+ if (order < 1)
734
+ rb_raise(rb_eArgError, "Negative order");
735
+ pdlc_fir_filter_initialize(fir, order - 1);
736
+ for (i = 0; i < order; i++)
737
+ pdlc_fir_filter_set_coef_at(fir, i, fbuf->data[i]);
738
+ }
739
+ else if (rb_class_of(b_or_n) == rb_cArray) {
740
+ order = rb_array_len(b_or_n);
741
+ if (order < 1)
742
+ rb_raise(rb_eArgError, "Negative order");
743
+ pdlc_fir_filter_initialize(fir, order - 1);
744
+ for (i = 0; i < order; i++)
745
+ pdlc_fir_filter_set_coef_at(fir, i, (float)NUM2DBL(rb_ary_entry(b_or_n, i)));
746
+ }
747
+ else {
748
+ order = NUM2INT(b_or_n);
749
+ if (order < 0)
750
+ rb_raise(rb_eArgError, "Negative order");
751
+ pdlc_fir_filter_initialize(fir, order);
752
+ }
753
+
754
+ return self;
755
+ }
756
+
757
+
758
+ /* Transform a {FloatBuffer} into a {ComplexBuffer}.
759
+ * The real part of the output is the input signal delayed,
760
+ * and the imaginary part of the output is the input signal filtered.
761
+ *
762
+ * @overload transform(float_buffer, buffer: obuf)
763
+ * @param float_buffer [PaddleC::FloatBuffer] a buffer of real input samples
764
+ * @param buffer [PaddleC::ComplexBuffer, nil] if provided, the buffer is resized if needed and filled with output samples, then returned
765
+ * @return [PaddleC::ComplexBuffer] a complex buffer whose real part is the original signal delayed, and the imaginary part the signal filtered
766
+ */
767
+ static VALUE paddlec_fir_filter_transformer_transform(int argc, VALUE *argv, VALUE self)
768
+ {
769
+ pdlc_fir_filter_t *fir;
770
+ VALUE rbSample, rbOptHash;
771
+ VALUE buffer_hash = Qundef;
772
+ const ID kwkeys = id_buffer;
773
+ VALUE obuf = Qnil;
774
+ VALUE res = Qnil;
775
+ const pdlc_buffer_t *ifbuf;
776
+ pdlc_complex_buffer_t *ocbuf;
777
+
778
+ rb_scan_args(argc, argv, "1:", &rbSample, &rbOptHash);
779
+
780
+ if (!NIL_P(rbOptHash))
781
+ rb_get_kwargs(rbOptHash, &kwkeys, 0, 1, &buffer_hash);
782
+ if (buffer_hash != Qundef)
783
+ obuf = buffer_hash;
784
+
785
+ fir = paddlec_fir_filter_get_struct(self);
786
+
787
+ if (rb_class_of(rbSample) == c_FloatBuffer) {
788
+ if (!NIL_P(obuf) && rb_class_of(obuf) != c_ComplexBuffer)
789
+ rb_raise(rb_eArgError, "only a %"PRIsVALUE" is valid for buffer", rb_class_name(c_ComplexBuffer));
790
+ ifbuf = paddlec_float_buffer_get_struct(rbSample);
791
+
792
+ if (rb_class_of(obuf) != c_ComplexBuffer)
793
+ obuf = rb_class_new_instance(0, NULL, c_ComplexBuffer);
794
+ ocbuf = paddlec_complex_buffer_get_struct(obuf);
795
+
796
+ pdlc_fir_filter_transform(fir, ifbuf, ocbuf);
797
+ res = obuf;
798
+ }
799
+ else
800
+ rb_raise(rb_eTypeError, "expecting a %"PRIsVALUE, rb_class_name(c_FloatBuffer));
801
+
802
+ return res;
803
+ }
804
+
805
+
806
+
807
+ /* Document-class: PaddleC::FirTransformer
808
+ *
809
+ * PaddleC::FirTransformer is a FIR filter with the +#transform+ method. It is mostly implemented for Hilbert transformers.
810
+ *
811
+ * It can process {PaddleC::FloatBuffer} into {PaddleC::ComplexBuffer}.
812
+ */
813
+
814
+
815
+ static void Init_paddlec_fir_transformer()
816
+ {
817
+ c_FirTransformer = rb_define_class_under(m_PaddleC, "FirTransformer", c_FirFilter);
818
+ rb_define_method(c_FirTransformer, "initialize", paddlec_fir_filter_transformer_initialize, 1);
819
+ rb_define_method(c_FirTransformer, "transform", paddlec_fir_filter_transformer_transform, -1);
820
+ }
821
+
822
+
823
+ /* Document-class: PaddleC::FirDecimator
824
+ *
825
+ * A finite impulse response (FIR) decimator (filter + downsample), relying on {PaddleC::FirFilter}.
826
+ *
827
+ * It can process {PaddleC::FloatBuffer} and {PaddleC::ComplexBuffer}.
828
+ */
829
+
830
+
831
+ static void Init_paddlec_fir_decimator()
832
+ {
833
+ c_FirDecimator = rb_define_class_under(m_PaddleC, "FirDecimator", c_FirFilter);
834
+ rb_define_method(c_FirDecimator, "initialize", paddlec_fir_filter_decimator_initialize, 2);
835
+ rb_define_method(c_FirDecimator, "decimation_factor", paddlec_fir_filter_decimator_decimation_factor, 0);
836
+ rb_define_method(c_FirDecimator, "decimate", paddlec_fir_filter_decimator_decimate, -1);
837
+ }
838
+
839
+
840
+ /* Document-class: PaddleC::FirInterpolator
841
+ *
842
+ * A finite impulse response (FIR) interpolator (upsample + filter), relying on {PaddleC::FirFilter}.
843
+ *
844
+ * It can process {PaddleC::FloatBuffer} and {PaddleC::ComplexBuffer}.
845
+ */
846
+
847
+
848
+ static void Init_paddlec_fir_interpolator()
849
+ {
850
+ c_FirInterpolator = rb_define_class_under(m_PaddleC, "FirInterpolator", c_FirFilter);
851
+ rb_define_method(c_FirInterpolator, "initialize", paddlec_fir_filter_interpolator_initialize, 2);
852
+ rb_define_method(c_FirInterpolator, "interpolation_factor", paddlec_fir_filter_interpolator_interpolation_factor, 0);
853
+ rb_define_method(c_FirInterpolator, "interpolate", paddlec_fir_filter_interpolator_interpolate, -1);
854
+ }
855
+
856
+
857
+ /* Document-class: PaddleC::FirFilter
858
+ *
859
+ * A finite impulse response (FIR) filter.
860
+ *
861
+ * It can process +Float+, +Complex+, {PaddleC::FloatBuffer} and {PaddleC::ComplexBuffer}.
862
+ *
863
+ * Internal coefficients and state are hold using native arrays of single floats.
864
+ *
865
+ * Deppending on the host architecture, computation may be accelerated using +AVX+, +SSE+ or +NEON+ SIMD instructions.
866
+ */
867
+
868
+
869
+ void Init_paddlec_fir_filter()
870
+ {
871
+ c_FirFilter = rb_define_class_under(m_PaddleC, "FirFilter", rb_cObject);
872
+
873
+ rb_define_alloc_func(c_FirFilter, paddlec_fir_filter_alloc);
874
+ rb_define_method(c_FirFilter, "initialize", paddlec_fir_filter_initialize, 1);
875
+ rb_define_method(c_FirFilter, "filter", paddlec_fir_filter_filter, -1);
876
+ rb_define_method(c_FirFilter, "order", paddlec_fir_filter_order, 0);
877
+ rb_define_method(c_FirFilter, "nb_coefficients", paddlec_fir_filter_nb_coefficients, 0);
878
+ rb_define_method(c_FirFilter, "reset", paddlec_fir_filter_reset, 0);
879
+ rb_define_method(c_FirFilter, "coefficients", paddlec_fir_filter_coefficients, 0);
880
+ rb_define_alias(c_FirFilter, "numerator", "coefficients");
881
+ rb_define_method(c_FirFilter, "denominator", paddlec_fir_filter_denominator, 0);
882
+ rb_define_method(c_FirFilter, "get_numerator_at", paddlec_fir_filter_get_coefficient_at, 1);
883
+ rb_define_method(c_FirFilter, "set_numerator_at", paddlec_fir_filter_set_coefficient_at, 2);
884
+ rb_define_method(c_FirFilter, "get_denominator_at", paddlec_fir_filter_get_denominator_at, 1);
885
+ rb_define_method(c_FirFilter, "set_denominator_at", paddlec_fir_filter_set_denominator_at, 2);
886
+
887
+ Init_paddlec_fir_transformer();
888
+ Init_paddlec_fir_decimator();
889
+ Init_paddlec_fir_interpolator();
890
+ }
891
+
892
+