openMSX
halfband.hh
Go to the documentation of this file.
1#ifndef HALFBAND_HH
2#define HALFBAND_HH
3
4#include <cassert>
5#include <span>
6
7// Remove the upper-half of the spectrum (low-pass filter)
8// and then halve the sample-rate.
9//
10// The input must contain twice the number of output samples plus some extra.
11// in.size() == 2 * out.size() + HALF_BAND_EXTRA
12//
13// About overlapping input and output (calculate in-place):
14// The output buffer is smaller than the input. Overlap is allowed, but only
15// when the beginning of the output is aligned with the beginning of the input.
16static constexpr size_t HALF_BAND_EXTRA = 13; // depends on the number of coefficients in the filter
17inline void halfBand(std::span<const float> in, std::span<float> out)
18{
19 // The low-pass filter is implemented via a 15-point FIR filter.
20 // [c1 0 c2 0 c3 0 c4 1 c4 0 c3 0 c2 0 c1]
21 // Note:
22 // * The filter is symmetrical.
23 // * The middle coefficient is 1.
24 // * Several coefficients are 0.
25 // -> This allows for an efficient implementation (only 4 multiplications per output)
26 // * The sum of the coefficients is 2.
27 // -> This implies an amplification by a factor 2. That's undesired,
28 // but saves 1 multiplication (and usually not a big problem).
29 //
30 // After filtering we drop every 2nd sample (decimate).
31 // (obviously we don't need to compute the outputs that will be dropped).
32 //
33 // Schematic representation:
34 // x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 x13 x14 x15 x16 x17 x18 ...
35 // y0 = [c1 0 c2 0 c3 0 c4 1 c4 0 c3 0 c2 0 c1]
36 // y1 = [c1 0 c2 0 c3 0 c4 1 c4 0 c3 0 c2 0 c1]
37 // y2 = [c1 0 c2 0 c3 0 c4 1 c4 0 c3 0 c2 0 c1]
38
39 // See script below for how to calculate these coefficients.
40 static constexpr float c1 = -0.076167110507847f;
41 static constexpr float c2 = 0.120096364504897f;
42 static constexpr float c3 = -0.215886324727246f;
43 static constexpr float c4 = 0.671957070730196f;
44
45 size_t n = out.size();
46 assert(in.size() == (2 * n + HALF_BAND_EXTRA));
47
48 for (size_t i = 0; i < n; ++i) {
49 out[i] = in[2 * i + 7]
50 + c1 * (in[2 * i + 0] + in[2 * i + 14])
51 + c2 * (in[2 * i + 2] + in[2 * i + 12])
52 + c3 * (in[2 * i + 4] + in[2 * i + 10])
53 + c4 * (in[2 * i + 6] + in[2 * i + 8]);
54 }
55}
56
57#endif
58
59// The filter coefficients were obtained via this octave (matlab) script:
60/* ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< -----
61
62pkg load signal
63
64ntaps = 4 * 3 + 3;
65beta = 1;
66resolution = 1024; % relates to the 'resolution' of the plot
67samplefreq = 100;
68
69
70N= ntaps-1;
71n= -N/2:N/2;
72sinc= sin(n*pi/2)./(n*pi+eps); % truncated impulse response; eps= 2E-16
73sinc(N/2 +1)= 1/2; % value for n --> 0
74win= kaiser(ntaps, beta); % window function
75b= sinc.*win';
76b(2:2:ntaps/2)=0;
77b((ntaps+5)/2:2:ntaps)=0;
78
79b2=b;
80b2((ntaps+1)/2)=0;
81b2 = b2 / (2 * sum(b2));
82b2((ntaps+1)/2)=0.5;
83b=b2;
84
85x = b;
86x(N+1:resolution) = 0;
87y = fft(x);
88
89xx = [0 : samplefreq/(resolution) : samplefreq/2];
90plot(xx, 20 * log(abs(y(1:resolution/2+1))));
91xlabel("frequency [kHz]");
92ylabel("attenuation [dB]");
93
94% print result
95format long
962 * b
97
98----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- 8< ----- */
void halfBand(std::span< const float > in, std::span< float > out)
Definition halfband.hh:17