add oversampler

This commit is contained in:
2024-05-24 13:28:31 +02:00
parent e4a4a661a0
commit 989dba5a6b
484 changed files with 313937 additions and 0 deletions

View File

@@ -0,0 +1,257 @@
/*
FPUDownsampler2x.h
Copyright(c) 2005 Laurent de Soras
Downsamples the input signal by a factor 2, using FPU.
Template parameters:
- NC: number of coefficients, > 0
--- Legal stuff ---
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <array>
#include "FPUStageProc.h"
namespace hiir
{
template <int NC, typename T>
class Downsampler2xFPU
{
public:
enum { NBR_COEFS = NC };
Downsampler2xFPU();
/*
Name: set_coefs
Description:
Sets filter coefficients. Generate them with the PolyphaseIir2Designer
class.
Call this function before doing any processing.
Input parameters:
- coef_arr: Array of coefficients. There should be as many coefficients as
mentioned in the class template parameter.
*/
void set_coefs(const double coef_arr[]);
/*
Name: process_sample
Description:
Downsamples (x2) one pair of samples, to generate one output sample.
Input parameters:
- in_ptr: pointer on the two samples to decimate
Returns: Samplerate-reduced sample.
*/
inline T process_sample(const T in_ptr [2]);
/*
Name: process_block
Description:
Downsamples (x2) a block of samples.
Input and output blocks may overlap, see assert() for details.
Input parameters:
- in_ptr: Input array, containing nbr_spl * 2 samples.
- nbr_spl: Number of samples to output, > 0
Output parameters:
- out_ptr: Array for the output samples, capacity: nbr_spl samples.
*/
void process_block(T out_ptr[], const T in_ptr[], long nbr_spl);
/*
Name: process_sample_split
Description:
Split (spectrum-wise) in half a pair of samples. The lower part of the
spectrum is a classic downsampling, equivalent to the output of
process_sample().
The higher part is the complementary signal: original filter response
is flipped from left to right, becoming a high-pass filter with the same
cutoff frequency. This signal is then critically sampled (decimation by 2),
flipping the spectrum: Fs/4...Fs/2 becomes Fs/4...0.
Input parameters:
- in_ptr: pointer on the pair of input samples
Output parameters:
- low: output sample, lower part of the spectrum (downsampling)
- high: output sample, higher part of the spectrum.
*/
inline void process_sample_split(T &low, T &high, const T in_ptr[2]);
/*
Name: process_block_split
Description:
Split (spectrum-wise) in half a block of samples. The lower part of the
spectrum is a classic downsampling, equivalent to the output of
process_block().
The higher part is the complementary signal: original filter response
is flipped from left to right, becoming a high-pass filter with the same
cutoff frequency. This signal is then critically sampled (decimation by 2),
flipping the spectrum: Fs/4...Fs/2 becomes Fs/4...0.
Input and output blocks may overlap, see assert() for details.
Input parameters:
- in_ptr: Input array, containing nbr_spl * 2 samples.
- nbr_spl: Number of samples for each output, > 0
Output parameters:
- out_l_ptr: Array for the output samples, lower part of the spectrum
(downsampling). Capacity: nbr_spl samples.
- out_h_ptr: Array for the output samples, higher part of the spectrum.
Capacity: nbr_spl samples.
*/
void process_block_split(T out_l_ptr[], T out_h_ptr[], const T in_ptr[], long nbr_spl);
/*
Name: clear_buffers
Description:
Clears filter memory, as if it processed silence since an infinite amount
of time.
*/
void clear_buffers();
private:
std::array<T, NBR_COEFS> _coef;
std::array<T, NBR_COEFS> _x;
std::array<T, NBR_COEFS> _y;
private:
bool operator == (const Downsampler2xFPU &other);
bool operator != (const Downsampler2xFPU &other);
}; // class Downsampler2xFPU
template <int NC, typename T>
Downsampler2xFPU <NC, T>::Downsampler2xFPU ()
: _coef ()
, _x ()
, _y ()
{
for (int i = 0; i < NBR_COEFS; ++i)
{
_coef [i] = 0;
}
clear_buffers ();
}
template <int NC, typename T>
void Downsampler2xFPU <NC, T>::set_coefs (const double coef_arr[])
{
assert (coef_arr != 0);
for (int i = 0; i < NBR_COEFS; ++i)
{
_coef [i] = static_cast <T> (coef_arr [i]);
}
}
template <int NC, typename T>
T Downsampler2xFPU <NC, T>::process_sample (const T in_ptr [2])
{
assert (in_ptr != 0);
T spl_0 (in_ptr [1]);
T spl_1 (in_ptr [0]);
#if defined (_MSC_VER)
#pragma inline_depth (255)
#endif // _MSC_VER
StageProcFPU <NBR_COEFS, T>::process_sample_pos (
NBR_COEFS,
spl_0,
spl_1,
&_coef [0],
&_x [0],
&_y [0]
);
return (0.5f * (spl_0 + spl_1));
}
template <int NC, typename T>
void Downsampler2xFPU <NC, T>::process_block (T out_ptr[], const T in_ptr[], long nbr_spl)
{
assert (in_ptr != 0);
assert (out_ptr != 0);
assert (out_ptr <= in_ptr || out_ptr >= in_ptr + nbr_spl * 2);
assert (nbr_spl > 0);
long pos = 0;
do
{
out_ptr [pos] = process_sample (&in_ptr [pos * 2]);
++pos;
}
while (pos < nbr_spl);
}
template <int NC, typename T>
void Downsampler2xFPU <NC, T>::process_sample_split (T &low, T &high, const T in_ptr [2])
{
assert (&low != 0);
assert (&high != 0);
assert (in_ptr != 0);
T spl_0 = in_ptr [1];
T spl_1 = in_ptr [0];
#if defined (_MSC_VER)
#pragma inline_depth (255)
#endif // _MSC_VER
StageProcFPU <NBR_COEFS, T>::process_sample_pos (
NBR_COEFS,
spl_0,
spl_1,
&_coef [0],
&_x [0],
&_y [0]
);
low = (spl_0 + spl_1) * 0.5f;
high = spl_0 - low; // (spl_0 - spl_1) * 0.5f;
}
template <int NC, typename T>
void Downsampler2xFPU <NC, T>::process_block_split (T out_l_ptr[], T out_h_ptr[], const T in_ptr[], long nbr_spl)
{
assert (in_ptr != 0);
assert (out_l_ptr != 0);
assert (out_l_ptr <= in_ptr || out_l_ptr >= in_ptr + nbr_spl * 2);
assert (out_h_ptr != 0);
assert (out_h_ptr <= in_ptr || out_h_ptr >= in_ptr + nbr_spl * 2);
assert (out_h_ptr != out_l_ptr);
assert (nbr_spl > 0);
long pos = 0;
do
{
process_sample_split
(
out_l_ptr [pos],
out_h_ptr [pos],
&in_ptr [pos * 2]
);
++pos;
}
while (pos < nbr_spl);
}
template <int NC, typename T>
void Downsampler2xFPU <NC, T>::clear_buffers ()
{
for (int i = 0; i < NBR_COEFS; ++i)
{
_x [i] = 0;
_y [i] = 0;
}
}
} // namespace hiir

View File

@@ -0,0 +1,162 @@
/*
StageProcFPU.h
Copyright (c) 2005 Laurent de Soras
Template parameters:
- REMAINING: Number of remaining coefficients to process, >= 0
--- Legal stuff ---
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
namespace hiir
{
template <int REMAINING, typename T>
class StageProcFPU
{
public:
static inline void process_sample_pos (const int nbr_coefs, T &spl_0, T &spl_1, const T coef [], T x [], T y []);
static inline void process_sample_neg (const int nbr_coefs, T &spl_0, T &spl_1, const T coef [], T x [], T y []);
private:
StageProcFPU();
StageProcFPU(const StageProcFPU &other);
StageProcFPU& operator = (const StageProcFPU &other);
bool operator == (const StageProcFPU &other);
bool operator != (const StageProcFPU &other);
}; // class StageProcFPU
template <>
inline void StageProcFPU <1, double>::process_sample_pos (const int nbr_coefs, double &spl_0, double &/*spl_1*/, const double coef [], double x [], double y [])
{
const int last = nbr_coefs - 1;
const double temp = (spl_0 - y [last]) * coef [last] + x [last];
x [last] = spl_0;
y [last] = temp;
spl_0 = temp;
}
template <>
inline void StageProcFPU <0, double>::process_sample_pos (const int /*nbr_coefs*/, double &/*spl_0*/, double &/*spl_1*/, const double /*coef*/ [], double /*x*/ [], double /*y*/ [])
{
// Nothing (stops recursion)
}
template <>
inline void StageProcFPU <1, float>::process_sample_pos (const int nbr_coefs, float &spl_0, float &/*spl_1*/, const float coef [], float x [], float y [])
{
const int last = nbr_coefs - 1;
const float temp = (spl_0 - y [last]) * coef [last] + x [last];
x [last] = spl_0;
y [last] = temp;
spl_0 = temp;
}
template <>
inline void StageProcFPU <0, float>::process_sample_pos (const int /*nbr_coefs*/, float &/*spl_0*/, float &/*spl_1*/, const float /*coef*/ [], float /*x*/ [], float /*y*/ [])
{
// Nothing (stops recursion)
}
template <int REMAINING, typename T>
void StageProcFPU <REMAINING, T>::process_sample_pos (const int nbr_coefs, T &spl_0, T &spl_1, const T coef [], T x [], T y [])
{
const int cnt = nbr_coefs - REMAINING;
const T temp_0 =
(spl_0 - y [cnt + 0]) * coef [cnt + 0] + x [cnt + 0];
const T temp_1 =
(spl_1 - y [cnt + 1]) * coef [cnt + 1] + x [cnt + 1];
x [cnt + 0] = spl_0;
x [cnt + 1] = spl_1;
y [cnt + 0] = temp_0;
y [cnt + 1] = temp_1;
spl_0 = temp_0;
spl_1 = temp_1;
StageProcFPU <REMAINING - 2, T>::process_sample_pos (
nbr_coefs,
spl_0,
spl_1,
&coef [0],
&x [0],
&y [0]
);
}
template <>
inline void StageProcFPU <1, double>::process_sample_neg (const int nbr_coefs, double &spl_0, double &/*spl_1*/, const double coef [], double x [], double y [])
{
const int last = nbr_coefs - 1;
const double temp = (spl_0 + y [last]) * coef [last] - x [last];
x [last] = spl_0;
y [last] = temp;
spl_0 = temp;
}
template <>
inline void StageProcFPU <0, double>::process_sample_neg (const int /*nbr_coefs*/, double &/*spl_0*/, double &/*spl_1*/, const double /*coef*/ [], double /*x*/ [], double /*y*/ [])
{
// Nothing (stops recursion)
}
template <>
inline void StageProcFPU <1, float>::process_sample_neg (const int nbr_coefs, float &spl_0, float &/*spl_1*/, const float coef [], float x [], float y [])
{
const int last = nbr_coefs - 1;
const float temp = (spl_0 + y [last]) * coef [last] - x [last];
x [last] = spl_0;
y [last] = temp;
spl_0 = temp;
}
template <>
inline void StageProcFPU <0, float>::process_sample_neg (const int /*nbr_coefs*/, float &/*spl_0*/, float &/*spl_1*/, const float /*coef*/ [], float /*x*/ [], float /*y*/ [])
{
// Nothing (stops recursion)
}
template <int REMAINING, typename T>
void StageProcFPU <REMAINING, T>::process_sample_neg (const int nbr_coefs, T &spl_0, T &spl_1, const T coef [], T x [], T y [])
{
const int cnt = nbr_coefs - REMAINING;
const T temp_0 =
(spl_0 + y [cnt + 0]) * coef [cnt + 0] - x [cnt + 0];
const T temp_1 =
(spl_1 + y [cnt + 1]) * coef [cnt + 1] - x [cnt + 1];
x [cnt + 0] = spl_0;
x [cnt + 1] = spl_1;
y [cnt + 0] = temp_0;
y [cnt + 1] = temp_1;
spl_0 = temp_0;
spl_1 = temp_1;
StageProcFPU <REMAINING - 2, T>::process_sample_neg (
nbr_coefs,
spl_0,
spl_1,
&coef [0],
&x [0],
&y [0]
);
}
} // namespace hiir

View File

@@ -0,0 +1,168 @@
/*
FPUUpsampler2x.h
Copyright (c) 2005 Laurent de Soras
Upsamples by a factor 2 the input signal, using FPU.
Template parameters:
- NC: number of coefficients, > 0
--- Legal stuff ---
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
#include <array>
#include "FPUStageProc.h"
namespace hiir
{
template <int NC, typename T>
class Upsampler2xFPU
{
public:
enum { NBR_COEFS = NC };
Upsampler2xFPU ();
/*
Name: set_coefs
Description:
Sets filter coefficients. Generate them with the PolyphaseIir2Designer
class.
Call this function before doing any processing.
Input parameters:
- coef_arr: Array of coefficients. There should be as many coefficients as
mentioned in the class template parameter.
*/
void set_coefs (const double coef_arr [NBR_COEFS]);
/*
Name: process_sample
Description:
Upsamples (x2) the input sample, generating two output samples.
Input parameters:
- input: The input sample.
Output parameters:
- out_0: First output sample.
- out_1: Second output sample.
*/
inline void process_sample (T &out_0, T &out_1, T input);
/*
Name: process_block
Description:
Upsamples (x2) the input sample block.
Input and output blocks may overlap, see assert() for details.
Input parameters:
- in_ptr: Input array, containing nbr_spl samples.
- nbr_spl: Number of input samples to process, > 0
Output parameters:
- out_0_ptr: Output sample array, capacity: nbr_spl * 2 samples.
*/
void process_block (T out_ptr [], const T in_ptr [], long nbr_spl);
/*
Name: clear_buffers
Description:
Clears filter memory, as if it processed silence since an infinite amount
of time.
*/
void clear_buffers ();
private:
std::array<T, NBR_COEFS> _coef;
std::array<T, NBR_COEFS> _x;
std::array<T, NBR_COEFS> _y;
private:
bool operator == (const Upsampler2xFPU &other);
bool operator != (const Upsampler2xFPU &other);
}; // class Upsampler2xFPU
template <int NC, typename T>
Upsampler2xFPU <NC, T>::Upsampler2xFPU ()
: _coef ()
, _x ()
, _y ()
{
for (int i = 0; i < NBR_COEFS; ++i)
{
_coef [i] = 0;
}
clear_buffers ();
}
template <int NC, typename T>
void Upsampler2xFPU <NC, T>::set_coefs (const double coef_arr [NBR_COEFS])
{
assert (coef_arr != 0);
for (int i = 0; i < NBR_COEFS; ++i)
{
_coef [i] = static_cast <T> (coef_arr [i]);
}
}
template <int NC, typename T>
void Upsampler2xFPU <NC, T>::process_sample (T &out_0, T &out_1, T input)
{
// assert (&out_0 != 0);
// assert (&out_1 != 0);
T even = input;
T odd = input;
StageProcFPU <NBR_COEFS, T>::process_sample_pos (
NBR_COEFS,
even,
odd,
&_coef [0],
&_x [0],
&_y [0]
);
out_0 = even;
out_1 = odd;
}
template <int NC, typename T>
void Upsampler2xFPU <NC, T>::process_block (T out_ptr [], const T in_ptr [], long nbr_spl)
{
// assert (out_ptr != 0);
// assert (in_ptr != 0);
// assert (out_ptr >= in_ptr + nbr_spl || in_ptr >= out_ptr + nbr_spl);
// assert (nbr_spl > 0);
long pos = 0;
do
{
process_sample (
out_ptr [pos * 2],
out_ptr [pos * 2 + 1],
in_ptr [pos]
);
++ pos;
}
while (pos < nbr_spl);
}
template <int NC, typename T>
void Upsampler2xFPU <NC, T>::clear_buffers ()
{
for (int i = 0; i < NBR_COEFS; ++i)
{
_x [i] = 0;
_y [i] = 0;
}
}
} // namespace hiir

View File

@@ -0,0 +1,267 @@
/*
PolyphaseIIR2Designer.cpp
Copyright (c) 2005 Laurent de Soras
--- Legal stuff ---
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#if defined (_MSC_VER)
#pragma warning (1 : 4130) // "'operator' : logical operation on address of string constant"
#pragma warning (1 : 4223) // "nonstandard extension used : non-lvalue array converted to pointer"
#pragma warning (1 : 4705) // "statement has no effect"
#pragma warning (1 : 4706) // "assignment within conditional expression"
#pragma warning (4 : 4786) // "identifier was truncated to '255' characters in the debug information"
#pragma warning (4 : 4800) // "forcing value to bool 'true' or 'false' (performance warning)"
#pragma warning (4 : 4355) // "'this' : used in base member initializer list"
#endif
#include "PolyphaseIIR2Designer.h"
#include <cassert>
#include <cmath>
namespace hiir
{
static const double PI = 3.141592653589793238;
int round_int (double x)
{
return (static_cast <int> (std::floor (x + 0.5)));
}
int ceil_int (double x)
{
return (static_cast <int> (std::ceil (x)));
}
template <class T>
T ipowp (T x, long n)
{
assert (n >= 0);
T z (1);
while (n != 0)
{
if ((n & 1) != 0)
{
z *= x;
}
n >>= 1;
x *= x;
}
return (z);
}
int PolyphaseIIR2Designer::compute_nbr_coefs_from_proto (double attenuation, double transition)
{
assert (attenuation > 0);
assert (transition > 0);
assert (transition < 0.5);
double k;
double q;
compute_transition_param (k, q, transition);
const int order = compute_order (attenuation, q);
const int nbr_coefs = (order - 1) / 2;
return (nbr_coefs);
}
double PolyphaseIIR2Designer::compute_atten_from_order_tbw (int nbr_coefs, double transition)
{
assert (nbr_coefs > 0);
assert (transition > 0);
assert (transition < 0.5);
double k;
double q;
compute_transition_param (k, q, transition);
const int order = nbr_coefs * 2 + 1;
const double attenuation = compute_atten (q, order);
return (attenuation);
}
int PolyphaseIIR2Designer::compute_coefs (double coef_arr[], double attenuation, double transition)
{
assert (&coef_arr != 0);
assert (attenuation > 0);
assert (transition > 0);
assert (transition < 0.5);
double k;
double q;
compute_transition_param (k, q, transition);
// Computes number of required coefficients
const int order = compute_order (attenuation, q);
const int nbr_coefs = (order - 1) / 2;
// Coefficient calculation
for (int index = 0; index < nbr_coefs; ++index)
{
coef_arr [index] = compute_coef (index, k, q, order);
}
return (nbr_coefs);
}
void PolyphaseIIR2Designer::compute_coefs_spec_order_tbw (double coef_arr[], int nbr_coefs, double transition)
{
assert (&coef_arr != 0);
assert (nbr_coefs > 0);
assert (transition > 0);
assert (transition < 0.5);
double k;
double q;
compute_transition_param (k, q, transition);
const int order = nbr_coefs * 2 + 1;
// Coefficient calculation
for (int index = 0; index < nbr_coefs; ++index)
{
coef_arr [index] = compute_coef (index, k, q, order);
}
}
void PolyphaseIIR2Designer::compute_transition_param (double &k, double &q, double transition)
{
assert (&k != 0);
assert (&q != 0);
assert (transition > 0);
assert (transition < 0.5);
using namespace std;
k = tan ((1 - transition * 2) * hiir::PI / 4);
k *= k;
assert (k < 1);
assert (k > 0);
double kksqrt = pow (1 - k * k, 0.25);
const double e = 0.5 * (1 - kksqrt) / (1 + kksqrt);
const double e2 = e * e;
const double e4 = e2 * e2;
q = e * (1 + e4 * (2 + e4 * (15 + 150 * e4)));
assert (q > 0);
}
int PolyphaseIIR2Designer::compute_order (double attenuation, double q)
{
assert (attenuation > 0);
assert (q > 0);
using namespace std;
const double attn_p2 = pow (10.0, -attenuation / 10);
const double a = attn_p2 / (1 - attn_p2);
int order = hiir::ceil_int (log (a * a / 16) / log (q));
if ((order & 1) == 0)
{
++ order;
}
if (order == 1)
{
order = 3;
}
return (order);
}
double PolyphaseIIR2Designer::compute_atten (double q, int order)
{
assert (q > 0);
assert (order > 0);
assert ((order & 1) == 1);
using namespace std;
const double a = 4 * exp (order * 0.5 * log (q));
assert (a != -1.0);
const double attn_p2 = a / (1 + a);
const double attenuation = -10 * log10 (attn_p2);
assert (attenuation > 0);
return (attenuation);
}
double PolyphaseIIR2Designer::compute_coef (int index, double k, double q, int order)
{
assert (index >= 0);
assert (index * 2 < order);
using namespace std;
const int c = index + 1;
const double num = compute_acc_num (q, order, c) * pow (q, 0.25);
const double den = compute_acc_den (q, order, c) + 0.5;
const double ww = num / den;
const double wwsq = ww * ww;
const double x = sqrt ((1 - wwsq * k) * (1 - wwsq / k)) / (1 + wwsq);
const double coef = (1 - x) / (1 + x);
return (coef);
}
double PolyphaseIIR2Designer::compute_acc_num (double q, int order, int c)
{
assert (c >= 1);
assert (c < order * 2);
using namespace std;
int i = 0;
int j = 1;
double acc = 0;
double q_ii1;
do
{
q_ii1 = hiir::ipowp (q, i * (i + 1));
q_ii1 *= sin ((i * 2 + 1) * c * hiir::PI / order) * j;
acc += q_ii1;
j = -j;
++i;
}
while (fabs (q_ii1) > 1e-100);
return (acc);
}
double PolyphaseIIR2Designer::compute_acc_den (double q, int order, int c)
{
assert (c >= 1);
assert (c < order * 2);
using namespace std;
int i = 1;
int j = -1;
double acc = 0;
double q_i2;
do
{
q_i2 = hiir::ipowp (q, i * i);
q_i2 *= cos (i * 2 * c * hiir::PI / order) * j;
acc += q_i2;
j = -j;
++i;
}
while (fabs (q_i2) > 1e-100);
return (acc);
}
} // namespace hiir

View File

@@ -0,0 +1,141 @@
/*
PolyphaseIIR2Designer.h
Copyright (c) 2005 Laurent de Soras
Compute coefficients for 2-path polyphase IIR filter, half-band filter or
Pi/2 phaser.
-2
a + z
N/2-1 2k+1
A0 (z) = Prod ------------
k = 0 -2
1 + a z
2k+1
-2
a + z
-1 (N-1)/2 2k
A1 (z) = z . Prod ----------
k = 0 -2
1 + a z
2k
1
H (z) = - (A0 (z) + A1 (z))
2
Sum of A0 and A1 gives a low-pass filter.
Difference of A0 and A1 gives the complementary high-pass filter.
For the Pi/2 phaser, product form is (a - z^-2) / (1 - az^-2)
Sum and difference of A0 and A1 have a Pi/2 phase difference.
References:
* Polyphase Two-Path Filter Designer in Java
Artur Krukowski
http://www.cmsa.wmin.ac.uk/~artur/Poly.html
* Digital Signal Processing Schemes for Efficient Interpolation and Decimation
Valenzuela and Constantinides
IEE Proceedings, Dec 1983
* A Hilbert-Transformer Frequency Shifter for Audio
Scott Wardle
http://www.iua.upf.es/dafx98/papers/WAR19.PS
--- Legal stuff ---
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#pragma once
namespace hiir
{
class PolyphaseIIR2Designer
{
public:
/*
Name: compute_nbr_coefs_from_proto
Description:
Finds the minimum number of coefficients for a given filter specification
Input parameters:
- attenuation: stop-band attenuation, dB. > 0.
- transition: normalized transition bandwidth. Range ]0 ; 1/2[
Returns: Number of coefficients, > 0
*/
static int compute_nbr_coefs_from_proto (double attenuation, double transition);
/*
Name: compute_atten_from_order_tbw
Description:
Compute the attenuation corresponding to a given number of coefficients
and the transition bandwidth.
Input parameters:
- nbr_coefs: Number of desired coefficients. > 0.
- transition: normalized transition bandwidth. Range ]0 ; 1/2[
Returns: stop-band attenuation, dB. > 0.
*/
static double compute_atten_from_order_tbw (int nbr_coefs, double transition);
/*
Name: compute_coefs
Description:
Computes coefficients for a half-band polyphase IIR filter, function of a
given stop-band gain / transition bandwidth specification.
Order is automatically calculated.
Input parameters:
- attenuation: stop-band attenuation, dB. > 0.
- transition: normalized transition bandwidth. Range ]0 ; 1/2[
Output parameters:
- coef_arr: Coefficient list, must be large enough to store all the
coefficients. Filter order = nbr_coefs * 2 + 1
Returns: number of coefficients
*/
static int compute_coefs (double coef_arr[], double attenuation, double transition);
/*
Name: compute_coefs_spec_order_tbw
Description:
Computes coefficients for a half-band polyphase IIR filter, function of a
given transition bandwidth and desired filter order. Bandstop attenuation
is set to the maximum value for these constraints.
Input parameters:
- nbr_coefs: Number of desired coefficients. > 0.
- transition: normalized transition bandwidth. Range ]0 ; 1/2[
Output parameters:
- coef_arr: Coefficient list, must be large enough to store all the
coefficients.
*/
static void compute_coefs_spec_order_tbw (double coef_arr[], int nbr_coefs, double transition);
private:
static void compute_transition_param (double &k, double &q, double transition);
static int compute_order (double attenuation, double q);
static double compute_atten (double q, int order);
static double compute_coef (int index, double k, double q, int order);
static double compute_acc_num (double q, int order, int c);
static double compute_acc_den (double q, int order, int c);
private:
PolyphaseIIR2Designer();
~PolyphaseIIR2Designer();
PolyphaseIIR2Designer(const PolyphaseIIR2Designer &other);
PolyphaseIIR2Designer&
operator = (const PolyphaseIIR2Designer &other);
bool operator == (const PolyphaseIIR2Designer &other);
bool operator != (const PolyphaseIIR2Designer &other);
}; // class PolyphaseIIR2Designer
} // namespace hiir

13
oversampling/HIIR/license.txt Executable file
View File

@@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

248
oversampling/HIIR/readme.txt Executable file
View File

@@ -0,0 +1,248 @@
==============================================================================
hiir
Version 1.11
An oversampling and Hilbert transform library in C++
By Laurent de Soras, 2005-2013
==============================================================================
Contents:
1. Legal
2. What is hiir ?
3. Using hiir
4. Compilation
5. Oversampling to higher ratios
6. History
7. Contact
1. Legal
--------
Check the file license.txt to get full information about the license.
2. What is hiir ?
-----------------
hiir is a DSP (digital signal processing) library in C++, with two purposes:
- Changing the sampling rate of a signal by a factor two, in both
directions (upsampling and downsampling).
- Obtaining two signals with a pi/2 phase difference (Hilbert transform)
These distinct operations are actually sharing the same filter design method
and processing kernel, that is why they are included in the same package. The
filter (a two-path polyphase IIR) is very efficient and can be used to achieve
high-quality oversampling or phase shift at a low CPU cost. It is made of a
symetric half-band elliptic low-pass filter, hence having an extremely flat
frequency response in the passband.
Various implementations are supplied with the library, using special CPU
instructions to optimise the calculation speed. Currently SSE and 3DNow!
instruction sets are supported, as well as the classic and portable FPU
implementation.
Source code may be downloaded from this webpage:
http://ldesoras.free.fr/prod.html
3. Using hiir
-------------
To avoid any collision, names have been encapsulated in the namespace "hiir".
So you have to prefix every name of this library with hiir:: or put a line
"using namespace hiir;" into your code.
The filter design class is PolyphaseIir2Designer. It generates coefficients
for the filters from a specification: stopband attenuation, transition
bandwidth and/or number of coefficients.
The main processing classes are Downsampler2x*, Upsampler2x* and PhaseHalfPi*.
The suffix indicates the implementation. Choose "Fpu" if you are not sure
about the right one to use. All implementations of a class category have the
same function syntax, so you can use them with C++ templates.
The implementations should have a consistent behaviour, based on the FPU one.
Some of them have specific requirement, like object alignment in memory or
delay in processing. See the header file (.h) of the class for details about
the constraints and inconsistencies, and the code file (.cpp/.hpp) for details
about function calls.
As you can see, almost all classes are templates based on number of
coefficients. This means it is not possible to change this number at run-time.
This is the most important constraint of this library. However the reward is
the high speed of the execution. Anyway, you could build a wrapper to support
variable number of coefficients, althought it means that you will have
probably to compile a large number of variations on the same code.
The library processes only 32-bit floating point data.
hiir is intended to be portable, but has little architecture-dependant pieces
of code. So far, it has been built and tested on:
- MS Windows / MS Visual C++ 6.0 (FPU/SSE/3DNow)
- MS Windows / MS Visual C++ 2005 (FPU/SSE/3DNow)
- MS Windows / MS Visual C++ 2010 (FPU/SSE/3DNow)
- MS Windows / GCC 4.5.3 (FPU/SSE only)
- MS Windows / Clang 3.1 (FPU/SSE only)
- MacOS 10.5 / GCC 4 (FPU/SSE only)
If you happen to have another system and tweak it to make it run successfully,
pleeeeease send me your modification so I can include it to the main
distribution. Run main.cpp in Debug mode before, then in Release mode, in
order to be sure that everything is fine. I would also be glad to include
implementations for other processors/compilers.
References for filter use and design:
- Scott Wardle, "A Hilbert-Transformer Frequency Shifter for Audio"
http://www.iua.upf.es/dafx98/papers/
- Valenzuela and Constantinides, "Digital Signal Processing Schemes for
Efficient Interpolation and Decimation", IEEE Proceedings, Dec 1983
- Artur Krukowski, Izzet Kale, "The design of arbitrary-band multi-path
polyphase IIR filters", ISCAS 2, page 741-744. IEEE, 2001
4. Compilation and testing
--------------------------
Drop the following files into your project or makefile :
hiir/Array.*
hiir/def.h
hiir/Downsampler2x*.*
hiir/fnc.*
hiir/PhaseHalfPi*.*
hiir/PolyphaseIir2Designer.*
hiir/Stage*.*
hiir/Upsampler2x*.*
Other files (in the hiir/test directory) are for testing purpose only, do not
include them if you just need to use the library; they are not needed to use
hiir in your own programs.
hiir may be compiled in two versions: release and debug. Debug version
has checks that could slow down the code. Define NDEBUG to set the Release
mode. For example, the command line to compile the test bench on GCC or
Clang would look like:
Debug mode:
g++ -msse -I. -o ./hiir_debug.exe hiir/*.cpp hiir/test/*.cpp
clang++ -D_X86_ -msse -I. -o ./hiir_debug.exe hiir/*.cpp hiir/test/*.cpp
Release mode:
g++ -msse -I. -o ./hiir_release.exe -DNDEBUG -O3 hiir/*.cpp hiir/test/*.cpp
clang++ -D_X86_ -msse -I. -o ./hiir_release.exe -DNDEBUG -O3 hiir/*.cpp hiir/test/*.cpp
The "-msse" option enables the compilation of the SSE intrinsics.
Notes for MS VC++ 6.0 users:
- You'll need the Processor Pack in order to be able to compile 3DNow! and
SSE code.
- The intensive use of recursive templates may slow down a bit the compilation,
especially if you use many different filter sizes (number of coefficients).
On MS Visual C++, you will probably have to use the /Zm option to increase
the memory reserved to the compiler. /Zm500 should be enough to compile the
test bench.
- Also, MS Visual C++ issues a lot of warning related to the use of the EBX
register or lack of FEMMS instruction at the end of a function. This is normal
and you can safely disable these warning while using hiir classes.
The included test bench checks roughly the accuracy of the filters. It also
tests the speed of every available function. Therefore, implementing new
instruction set should be facilitated.
If you want to compile and run the test bench, please first edit the
test/conf.h file, in order to select the instruction sets available for your
CPU (there is currently no automatic detection). If you are not sure, disable
all of them.
In the same file, you have also testing options. You can save on the disk all
the samples generated during tests in order to check them in a sample editor.
However the files may take a lot of space on the disk, so it is recommended to
disable this option if it is not required. The "long tests" options are
intended to provide extensive checks on various filter sizes (it takes longer
to compile, but is safer if you want to change anything in the lib).
5. Oversampling to higher ratios
--------------------------------
It is possible to oversample a signal at a higher ratio than 2. You just have
to cascade up/downsamplers to achieve a power-of-2 ratio. Depending on your
requirements, you can reduce the filter order as the sampling rate is getting
bigger by reducing the transition bandwidth (TBW).
For example, let's suppose one wants 16x downsampling, with 96 dB of stopband
attenuation and a 0.49*Fs passband. You'll need the following specifications
for each stage:
2x -> 1x: TBW = 0.01
4x -> 2x: TBW = 0.01/2 + 1/4 = 0.255
8x -> 4x: TBW = 0.01/4 + 1/8 + 1/4 = 0.3775
16x -> 8x: TBW = 0.01/8 + 1/16 + 1/8 + 1/4 = 0.43865
The reason is that you do not need to preserve spectrum parts that will be
wiped out by subsequent stages. Only the spectrum part present after the
final stage has to be perserved.
More generally:
TBW[stage] = (TBW[stage-1] + 0.5) / 2
or
TBW[stage] = TBW[0] * (0.5^stage) + 0.5 * (1 - 0.5^stage)
So transition bandwidth requirement is significatively low until the last
stage (0). Thus, the optimal performance would be reached by using hiir
downsampler for the last stage because the requirement on the transition
bandwidth is important, and by using a classic FIR filtering for other
stages. Of course, it's possible to use hiir at every stage, but a well-
optimised polyphase FIR routine is probably more efficient than a 1- or 2-
coefficent IIR downsampler. Indeed, these IIR SIMD implementations have
little or no benefit for low-order filters, whereas small FIR filters can
benefit from SIMD. Check the speed test results to make your mind.
6. History
----------
v1.11 (2012.06.26)
- Changed the license to the WTFPL
- Fixed some compilation warnings
v1.10 (2008.05.28)
- Changed directory structure
- Test code is now in its own namespace (hiir::test)
- Uses intrinsics for SSE code, making the code compilable on GCC.
v1.00 (2005.03.29)
- Initial release
7. Contact
----------
Please address any comment, bug report or flame to:
Laurent de Soras
http://ldesoras.free.fr