add oversampler
This commit is contained in:
257
oversampling/HIIR/FPUDownsampler2x.h
Normal file
257
oversampling/HIIR/FPUDownsampler2x.h
Normal 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
|
||||
|
||||
162
oversampling/HIIR/FPUStageProc.h
Normal file
162
oversampling/HIIR/FPUStageProc.h
Normal 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
|
||||
168
oversampling/HIIR/FPUUpsampler2x.h
Executable file
168
oversampling/HIIR/FPUUpsampler2x.h
Executable 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
|
||||
267
oversampling/HIIR/PolyphaseIIR2Designer.cpp
Normal file
267
oversampling/HIIR/PolyphaseIIR2Designer.cpp
Normal 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
|
||||
|
||||
141
oversampling/HIIR/PolyphaseIIR2Designer.h
Normal file
141
oversampling/HIIR/PolyphaseIIR2Designer.h
Normal 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
13
oversampling/HIIR/license.txt
Executable 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
248
oversampling/HIIR/readme.txt
Executable 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
|
||||
|
||||
Reference in New Issue
Block a user