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.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +56 -0
- data/ext/libpaddlec/arithmetic.c +2486 -0
- data/ext/libpaddlec/comparison.c +683 -0
- data/ext/libpaddlec/complex.c +349 -0
- data/ext/libpaddlec/delay.c +240 -0
- data/ext/libpaddlec/fir_filter.c +724 -0
- data/ext/libpaddlec/fir_filter_avx.c +2645 -0
- data/ext/libpaddlec/fir_filter_neon.c +1767 -0
- data/ext/libpaddlec/fir_filter_sse.c +1677 -0
- data/ext/libpaddlec/libpaddlec.c +933 -0
- data/ext/libpaddlec/libpaddlec.h +473 -0
- data/ext/libpaddlec/math.c +563 -0
- data/ext/libpaddlec/no_fast_math.c +955 -0
- data/ext/libpaddlec/rounding.c +503 -0
- data/ext/paddlec/complex_buffer.c +3555 -0
- data/ext/paddlec/complex_buffer.h +28 -0
- data/ext/paddlec/delay.c +214 -0
- data/ext/paddlec/delay.h +29 -0
- data/ext/paddlec/extconf.rb +106 -0
- data/ext/paddlec/fir_filter.c +892 -0
- data/ext/paddlec/fir_filter.h +28 -0
- data/ext/paddlec/float_buffer.c +4770 -0
- data/ext/paddlec/float_buffer.h +28 -0
- data/ext/paddlec/paddlec.c +788 -0
- data/ext/paddlec/paddlec.h +76 -0
- data/ext/paddlec/pulseaudio.c +6767 -0
- data/ext/paddlec/pulseaudio.h +30 -0
- data/lib/paddlec.rb +26 -0
- data/lib/paddlec/version.rb +3 -0
- data/paddlec.gemspec +55 -0
- data/samples/fmdemod.rb +121 -0
- data/samples/fmdemod_chunk.rb +120 -0
- data/samples/fmdemod_chunk_buffer.rb +144 -0
- data/samples/stereo_chunk.rb +161 -0
- metadata +99 -0
@@ -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 */
|
data/ext/paddlec/delay.c
ADDED
@@ -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
|
+
|
data/ext/paddlec/delay.h
ADDED
@@ -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
|
+
|