init
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Bessel.h"
|
||||
#include "RootFinder.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace Bessel
|
||||
{
|
||||
|
||||
// returns fact(n) = n!
|
||||
static double fact(const int n)
|
||||
{
|
||||
if (n == 0) { return 1; }
|
||||
double res = 1;
|
||||
for (int i = 2; i <= n; ++i) { res *= i; }
|
||||
return res;
|
||||
}
|
||||
|
||||
// returns the k-th zero based coefficient of the reverse bessel polynomial of degree n
|
||||
static double reversebessel(const int k, const int n) { return fact(2 * n - k) / ((fact(n - k) * fact(k)) * pow(2., n - k)); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void AnalogLowPass::design(const int numPoles, WorkspaceBase* w)
|
||||
{
|
||||
if (m_numPoles != numPoles)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
|
||||
reset();
|
||||
|
||||
RootFinderBase& solver(w->roots);
|
||||
for (int i = 0; i < numPoles + 1; ++i) { solver.coef()[i] = reversebessel(i, numPoles); }
|
||||
solver.solve(numPoles);
|
||||
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
complex_t c = solver.root()[i];
|
||||
addPoleZeroConjugatePairs(c, infinity());
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(solver.root()[pairs].real(), infinity()); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AnalogLowShelf::AnalogLowShelf()
|
||||
: m_numPoles(-1) { setNormal(doublePi, 1); }
|
||||
|
||||
void AnalogLowShelf::design(int numPoles,
|
||||
double gainDb,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
if (m_numPoles != numPoles ||
|
||||
m_gainDb != gainDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_gainDb = gainDb;
|
||||
|
||||
reset();
|
||||
|
||||
const double G = pow(10., gainDb / 20) - 1;
|
||||
|
||||
RootFinderBase& poles(w->roots);
|
||||
for (int i = 0; i < numPoles + 1; ++i) { poles.coef()[i] = reversebessel(i, numPoles); }
|
||||
poles.solve(numPoles);
|
||||
|
||||
RootFinder<50> zeros;
|
||||
for (int i = 0; i < numPoles + 1; ++i) { zeros.coef()[i] = reversebessel(i, numPoles); }
|
||||
double a0 = reversebessel(0, numPoles);
|
||||
zeros.coef()[0] += G * a0;
|
||||
zeros.solve(numPoles);
|
||||
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
complex_t p = poles.root()[i];
|
||||
complex_t z = zeros.root()[i];
|
||||
addPoleZeroConjugatePairs(p, z);
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(poles.root()[pairs].real(), zeros.root()[pairs].real()); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
HighPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
BandPassTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandStopBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
BandStopTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void LowShelfBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double gainDb,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, w);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace Bessel
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,350 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_BESSEL_H
|
||||
#define DSPFILTERS_BESSEL_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
#include "RootFinder.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Bessel response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Bessel
|
||||
{
|
||||
|
||||
// A Workspace is necessary to find roots
|
||||
|
||||
struct WorkspaceBase
|
||||
{
|
||||
WorkspaceBase(RootFinderBase* rootsBase)
|
||||
: roots(*rootsBase) { }
|
||||
|
||||
RootFinderBase& roots;
|
||||
|
||||
private:
|
||||
WorkspaceBase(WorkspaceBase&);
|
||||
WorkspaceBase& operator=(WorkspaceBase&);
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct Workspace : WorkspaceBase
|
||||
{
|
||||
Workspace() : WorkspaceBase(&m_roots) { }
|
||||
|
||||
private:
|
||||
RootFinder<MaxOrder> m_roots;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Half-band analog prototypes (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass() : m_numPoles(-1) { setNormal(0, 1); }
|
||||
|
||||
void design(int numPoles, WorkspaceBase* w);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class AnalogLowShelf : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowShelf();
|
||||
|
||||
void design(int numPoles, double gainDb, WorkspaceBase* w);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_gainDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct LowShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
LowPassBase::setup(order, sampleRate, cutoffFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
HighPassBase::setup(order, sampleRate, cutoffFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
BandPassBase::setup(order, sampleRate, centerFrequency, widthFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
BandStopBase::setup(order, sampleRate, centerFrequency, widthFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : PoleFilter<LowShelfBase, MaxOrder, MaxOrder * 2>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
LowShelfBase::setup(order, sampleRate, cutoffFrequency, gainDb, &w);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 3
|
||||
};
|
||||
|
||||
static int getNumParams() { return 3; }
|
||||
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultGainParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIII : TypeIIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIVBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultGainParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIV : TypeIVBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Bessel Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Bessel High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Bessel Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Bessel Band Stop"; }
|
||||
};
|
||||
|
||||
struct LowShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowShelf; }
|
||||
static const char* getName() { return "Bessel Low Shelf"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
const ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, Bessel::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, Bessel::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, Bessel::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, Bessel::BandStop>, BandStopDescription {};
|
||||
|
||||
/*
|
||||
* NOT IMPLEMENTED
|
||||
*
|
||||
*/
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : OrderBase<MaxOrder, TypeIII, Bessel::LowShelf>, LowShelfDescription {};
|
||||
} // namespace Design
|
||||
} // namespace Bessel
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
|
||||
/* This is a test of svn:external */
|
||||
@@ -0,0 +1,226 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
#include "Biquad.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
BiquadPoleState::BiquadPoleState(const BiquadBase& s)
|
||||
{
|
||||
const double a0 = s.getA0();
|
||||
const double a1 = s.getA1();
|
||||
const double a2 = s.getA2();
|
||||
const double b0 = s.getB0();
|
||||
const double b1 = s.getB1();
|
||||
const double b2 = s.getB2();
|
||||
|
||||
if (a2 == 0 && b2 == 0)
|
||||
{
|
||||
// single pole
|
||||
poles.first = -a1;
|
||||
zeros.first = -b0 / b1;
|
||||
poles.second = 0;
|
||||
zeros.second = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
const complex_t c = sqrt(complex_t(a1 * a1 - 4 * a0 * a2, 0));
|
||||
double d = 2. * a0;
|
||||
poles.first = -(a1 + c) / d;
|
||||
poles.second = (c - a1) / d;
|
||||
assert(!poles.is_nan());
|
||||
}
|
||||
|
||||
{
|
||||
const complex_t c = sqrt(complex_t(b1 * b1 - 4 * b0 * b2, 0));
|
||||
double d = 2. * b0;
|
||||
zeros.first = -(b1 + c) / d;
|
||||
zeros.second = (c - b1) / d;
|
||||
assert(!zeros.is_nan());
|
||||
}
|
||||
}
|
||||
|
||||
gain = b0 / a0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
complex_t BiquadBase::response(double normalizedFrequency) const
|
||||
{
|
||||
const double a0 = getA0();
|
||||
const double a1 = getA1();
|
||||
const double a2 = getA2();
|
||||
const double b0 = getB0();
|
||||
const double b1 = getB1();
|
||||
const double b2 = getB2();
|
||||
|
||||
const double w = 2 * doublePi * normalizedFrequency;
|
||||
const complex_t czn1 = std::polar(1., -w);
|
||||
const complex_t czn2 = std::polar(1., -2 * w);
|
||||
complex_t ch(1);
|
||||
complex_t cbot(1);
|
||||
|
||||
complex_t ct(b0 / a0);
|
||||
complex_t cb(1);
|
||||
ct = addmul(ct, b1 / a0, czn1);
|
||||
ct = addmul(ct, b2 / a0, czn2);
|
||||
cb = addmul(cb, a1 / a0, czn1);
|
||||
cb = addmul(cb, a2 / a0, czn2);
|
||||
ch *= ct;
|
||||
cbot *= cb;
|
||||
|
||||
return ch / cbot;
|
||||
}
|
||||
|
||||
std::vector<PoleZeroPair> BiquadBase::getPoleZeros() const
|
||||
{
|
||||
std::vector<PoleZeroPair> vpz;
|
||||
BiquadPoleState bps(*this);
|
||||
vpz.push_back(bps);
|
||||
return vpz;
|
||||
}
|
||||
|
||||
void BiquadBase::setCoefficients(double a0, double a1, double a2,
|
||||
double b0, double b1, double b2)
|
||||
{
|
||||
assert(!Dsp::is_nan (a0) && !Dsp::is_nan (a1) && !Dsp::is_nan (a2) &&
|
||||
!Dsp::is_nan (b0) && !Dsp::is_nan (b1) && !Dsp::is_nan (b2));
|
||||
|
||||
m_a0 = a0;
|
||||
m_a1 = a1 / a0;
|
||||
m_a2 = a2 / a0;
|
||||
m_b0 = b0 / a0;
|
||||
m_b1 = b1 / a0;
|
||||
m_b2 = b2 / a0;
|
||||
}
|
||||
|
||||
void BiquadBase::setOnePole(complex_t pole, complex_t zero)
|
||||
{
|
||||
#if 0
|
||||
pole = adjust_imag (pole);
|
||||
zero = adjust_imag (zero);
|
||||
#else
|
||||
assert(pole.imag() == 0);
|
||||
assert(zero.imag() == 0);
|
||||
#endif
|
||||
|
||||
const double a0 = 1;
|
||||
const double a1 = -pole.real();
|
||||
const double a2 = 0;
|
||||
const double b0 = 1;
|
||||
const double b1 = -zero.real();
|
||||
const double b2 = 0;
|
||||
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BiquadBase::setTwoPole(complex_t pole1, complex_t zero1,
|
||||
complex_t pole2, complex_t zero2)
|
||||
{
|
||||
#if 0
|
||||
pole1 = adjust_imag (pole1);
|
||||
pole2 = adjust_imag (pole2);
|
||||
zero1 = adjust_imag (zero1);
|
||||
zero2 = adjust_imag (zero2);
|
||||
#endif
|
||||
|
||||
const double a0 = 1;
|
||||
double a1;
|
||||
double a2;
|
||||
|
||||
if (pole1.imag() != 0)
|
||||
{
|
||||
assert(pole2 == std::conj (pole1));
|
||||
|
||||
a1 = -2 * pole1.real();
|
||||
a2 = std::norm(pole1);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(pole2.imag() == 0);
|
||||
|
||||
a1 = -(pole1.real() + pole2.real());
|
||||
a2 = pole1.real() * pole2.real();
|
||||
}
|
||||
|
||||
const double b0 = 1;
|
||||
double b1;
|
||||
double b2;
|
||||
|
||||
if (zero1.imag() != 0)
|
||||
{
|
||||
assert(zero2 == std::conj (zero1));
|
||||
|
||||
b1 = -2 * zero1.real();
|
||||
b2 = std::norm(zero1);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(zero2.imag() == 0);
|
||||
|
||||
b1 = -(zero1.real() + zero2.real());
|
||||
b2 = zero1.real() * zero2.real();
|
||||
}
|
||||
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BiquadBase::setPoleZeroForm(const BiquadPoleState& bps)
|
||||
{
|
||||
setPoleZeroPair(bps);
|
||||
applyScale(bps.gain);
|
||||
}
|
||||
|
||||
void BiquadBase::setIdentity() { setCoefficients(1, 0, 0, 1, 0, 0); }
|
||||
|
||||
void BiquadBase::applyScale(double scale)
|
||||
{
|
||||
m_b0 *= scale;
|
||||
m_b1 *= scale;
|
||||
m_b2 *= scale;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Biquad::Biquad() {}
|
||||
|
||||
// Construct a second order section from a pair of poles and zeroes
|
||||
Biquad::Biquad(const BiquadPoleState& bps) { setPoleZeroForm(bps); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,206 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_BIQUAD_H
|
||||
#define DSPFILTERS_BIQUAD_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
struct BiquadPoleState;
|
||||
|
||||
/*
|
||||
* Holds coefficients for a second order Infinite Impulse Response
|
||||
* digital filter. This is the building block for all IIR filters.
|
||||
*
|
||||
*/
|
||||
|
||||
// Factored interface to prevent outsiders from fiddling
|
||||
class BiquadBase
|
||||
{
|
||||
public:
|
||||
template <class StateType>
|
||||
struct State : StateType, private DenormalPrevention
|
||||
{
|
||||
template <typename Sample>
|
||||
Sample process(const Sample in, const BiquadBase& b) { return Sample(StateType::process1(in, b, ac())); }
|
||||
};
|
||||
|
||||
// Calculate filter response at the given normalized frequency.
|
||||
complex_t response(double normalizedFrequency) const;
|
||||
|
||||
std::vector<PoleZeroPair> getPoleZeros() const;
|
||||
|
||||
double getA0() const { return m_a0; }
|
||||
double getA1() const { return m_a1 * m_a0; }
|
||||
double getA2() const { return m_a2 * m_a0; }
|
||||
double getB0() const { return m_b0 * m_a0; }
|
||||
double getB1() const { return m_b1 * m_a0; }
|
||||
double getB2() const { return m_b2 * m_a0; }
|
||||
|
||||
// Process a block of samples in the given form
|
||||
template <class StateType, typename Sample>
|
||||
void process(int nSamples, Sample* dest, StateType& state) const
|
||||
{
|
||||
while (--nSamples >= 0)
|
||||
{
|
||||
*dest = state.process(*dest, *this);
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
//
|
||||
// These are protected so you can't mess with RBJ biquads
|
||||
//
|
||||
|
||||
void setCoefficients(double a0, double a1, double a2, double b0, double b1, double b2);
|
||||
|
||||
void setOnePole(complex_t pole, complex_t zero);
|
||||
|
||||
void setTwoPole(complex_t pole1, complex_t zero1, complex_t pole2, complex_t zero2);
|
||||
|
||||
void setPoleZeroPair(const PoleZeroPair& pair)
|
||||
{
|
||||
if (pair.isSinglePole()) { setOnePole(pair.poles.first, pair.zeros.first); }
|
||||
else { setTwoPole(pair.poles.first, pair.zeros.first, pair.poles.second, pair.zeros.second); }
|
||||
}
|
||||
|
||||
void setPoleZeroForm(const BiquadPoleState& bps);
|
||||
|
||||
void setIdentity();
|
||||
|
||||
void applyScale(double scale);
|
||||
|
||||
public:
|
||||
double m_a0 = 0;
|
||||
double m_a1 = 0;
|
||||
double m_a2 = 0;
|
||||
double m_b1 = 0;
|
||||
double m_b2 = 0;
|
||||
double m_b0 = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Expresses a biquad as a pair of pole/zeros, with gain
|
||||
// values so that the coefficients can be reconstructed precisely.
|
||||
struct BiquadPoleState : PoleZeroPair
|
||||
{
|
||||
BiquadPoleState() { }
|
||||
|
||||
explicit BiquadPoleState(const BiquadBase& s);
|
||||
|
||||
double gain = 0;
|
||||
};
|
||||
|
||||
// More permissive interface for fooling around
|
||||
class Biquad : public BiquadBase
|
||||
{
|
||||
public:
|
||||
Biquad();
|
||||
|
||||
explicit Biquad(const BiquadPoleState& bps);
|
||||
|
||||
// Process a block of samples, interpolating from the old section's coefficients
|
||||
// to this section's coefficients, over nSamples. This implements smooth
|
||||
// parameter changes.
|
||||
|
||||
template <class StateType, typename Sample>
|
||||
void smoothProcess1(int nSamples, Sample* dest, StateType& state, Biquad sectionPrev) const
|
||||
{
|
||||
double t = 1. / nSamples;
|
||||
double da1 = (m_a1 - sectionPrev.m_a1) * t;
|
||||
double da2 = (m_a2 - sectionPrev.m_a2) * t;
|
||||
double db0 = (m_b0 - sectionPrev.m_b0) * t;
|
||||
double db1 = (m_b1 - sectionPrev.m_b1) * t;
|
||||
double db2 = (m_b2 - sectionPrev.m_b2) * t;
|
||||
|
||||
while (--nSamples >= 0)
|
||||
{
|
||||
sectionPrev.m_a1 += da1;
|
||||
sectionPrev.m_a2 += da2;
|
||||
sectionPrev.m_b0 += db0;
|
||||
sectionPrev.m_b1 += db1;
|
||||
sectionPrev.m_b2 += db2;
|
||||
|
||||
*dest = state.process(*dest, sectionPrev);
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Process a block of samples, interpolating from the old section's pole/zeros
|
||||
// to this section's pole/zeros, over nSamples. The interpolation is done
|
||||
// in the z-plane using polar coordinates.
|
||||
template <class StateType, typename Sample>
|
||||
void smoothProcess2(int nSamples,
|
||||
Sample* dest,
|
||||
StateType& state,
|
||||
BiquadPoleState zPrev) const
|
||||
{
|
||||
BiquadPoleState z(*this);
|
||||
double t = 1. / nSamples;
|
||||
complex_t dp0 = (z.poles.first - zPrev.poles.first) * t;
|
||||
complex_t dp1 = (z.poles.second - zPrev.poles.second) * t;
|
||||
complex_t dz0 = (z.zeros.first - zPrev.zeros.first) * t;
|
||||
complex_t dz1 = (z.zeros.second - zPrev.zeros.second) * t;
|
||||
double dg = (z.gain - zPrev.gain) * t;
|
||||
|
||||
while (--nSamples >= 0)
|
||||
{
|
||||
zPrev.poles.first += dp0;
|
||||
zPrev.poles.second += dp1;
|
||||
zPrev.zeros.first += dz0;
|
||||
zPrev.zeros.second += dz1;
|
||||
zPrev.gain += dg;
|
||||
|
||||
*dest = state.process(*dest, Biquad(zPrev));
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Export these as public
|
||||
|
||||
void setOnePole(complex_t pole, complex_t zero) { BiquadBase::setOnePole(pole, zero); }
|
||||
void setTwoPole(complex_t pole1, complex_t zero1, complex_t pole2, complex_t zero2) { BiquadBase::setTwoPole(pole1, zero1, pole2, zero2); }
|
||||
void setPoleZeroPair(const PoleZeroPair& pair) { BiquadBase::setPoleZeroPair(pair); }
|
||||
void applyScale(double scale) { BiquadBase::applyScale(scale); }
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Butterworth.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace Butterworth
|
||||
{
|
||||
AnalogLowPass::AnalogLowPass()
|
||||
: m_numPoles(-1) { setNormal(0, 1); }
|
||||
|
||||
void AnalogLowPass::design(int numPoles)
|
||||
{
|
||||
if (m_numPoles != numPoles)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
|
||||
reset();
|
||||
|
||||
const double n2 = 2 * numPoles;
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
complex_t c = std::polar(1., doublePi_2 + (2 * i + 1) * doublePi / n2);
|
||||
addPoleZeroConjugatePairs(c, infinity());
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(-1, infinity()); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AnalogLowShelf::AnalogLowShelf()
|
||||
: m_numPoles(-1) { setNormal(doublePi, 1); }
|
||||
|
||||
void AnalogLowShelf::design(int numPoles, double gainDb)
|
||||
{
|
||||
if (m_numPoles != numPoles ||
|
||||
m_gainDb != gainDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_gainDb = gainDb;
|
||||
|
||||
reset();
|
||||
|
||||
const double n2 = numPoles * 2;
|
||||
const double g = pow(pow(10., gainDb / 20), 1. / n2);
|
||||
const double gp = -1. / g;
|
||||
const double gz = -g;
|
||||
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 1; i <= pairs; ++i)
|
||||
{
|
||||
const double theta = doublePi * (0.5 - (2 * i - 1) / n2);
|
||||
addPoleZeroConjugatePairs(std::polar(gp, theta), std::polar(gz, theta));
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(gp, gz); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency)
|
||||
{
|
||||
m_analogProto.design(order);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency)
|
||||
{
|
||||
m_analogProto.design(order);
|
||||
|
||||
HighPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency)
|
||||
{
|
||||
m_analogProto.design(order);
|
||||
|
||||
BandPassTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
#include "ButterworthSynthesisCpp.inl"
|
||||
|
||||
void BandStopBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency)
|
||||
{
|
||||
m_analogProto.design(order);
|
||||
|
||||
BandStopTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void LowShelfBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double gainDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighShelfBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double gainDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb);
|
||||
|
||||
HighPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandShelfBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
double gainDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb);
|
||||
|
||||
BandPassTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
// HACK!
|
||||
m_digitalProto.setNormal(((centerFrequency / sampleRate) < 0.25) ? doublePi : 0, 1);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace Butterworth
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,314 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Butterworth response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Butterworth
|
||||
{
|
||||
|
||||
// Half-band analog prototypes (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass();
|
||||
|
||||
void design(int numPoles);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class AnalogLowShelf : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowShelf();
|
||||
|
||||
void design(int numPoles, double gainDb);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_gainDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency);
|
||||
|
||||
#include "ButterworthSynthesisH.inl"
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency);
|
||||
};
|
||||
|
||||
struct LowShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb);
|
||||
};
|
||||
|
||||
struct HighShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb);
|
||||
};
|
||||
|
||||
struct BandShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : PoleFilter<LowShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : PoleFilter<HighShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : PoleFilter<BandShelfBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 3
|
||||
};
|
||||
|
||||
static int getNumParams() { return 3; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
|
||||
#include "ButterworthSynthesisH2.inl"
|
||||
};
|
||||
|
||||
struct TypeIIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultGainParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIII : TypeIIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIVBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultGainParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIV : TypeIVBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Butterworth Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Butterworth High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Butterworth Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Butterworth Band Stop"; }
|
||||
};
|
||||
|
||||
struct LowShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowShelf; }
|
||||
static const char* getName() { return "Butterworth Low Shelf"; }
|
||||
};
|
||||
|
||||
struct HighShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighShelf; }
|
||||
static const char* getName() { return "Butterworth High Shelf"; }
|
||||
};
|
||||
|
||||
struct BandShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindBandShelf; }
|
||||
static const char* getName() { return "Butterworth Band Shelf"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
const ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, Butterworth::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, Butterworth::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, Butterworth::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, Butterworth::BandStop>, BandStopDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : OrderBase<MaxOrder, TypeIII, Butterworth::LowShelf>, LowShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : OrderBase<MaxOrder, TypeIII, Butterworth::HighShelf>, HighShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : OrderBase<MaxOrder, TypeIV, Butterworth::BandShelf>, BandShelfDescription {};
|
||||
} // namespace Design
|
||||
} // namespace Butterworth
|
||||
} // namespace Dsp
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
|
||||
void BandPassBase::setupSynthesis (int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency)
|
||||
{
|
||||
m_analogProto.design (order);
|
||||
|
||||
BandPassTransform (centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
|
||||
// inversion of the numerator and denominator digital coefficients
|
||||
Cascade::setLayoutSynthesis (m_digitalProto);
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
|
||||
void setupSynthesis (int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency);
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
|
||||
void setParamsSynthesis (const Params& params)
|
||||
{
|
||||
FilterClass::setupSynthesis (int(params[1]), params[0], params[2], params[3]);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
//#include <sstream>
|
||||
//#include <iostream>
|
||||
//#include <iomanip>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
Cascade::Cascade() {}
|
||||
|
||||
void Cascade::setCascadeStorage(const Storage& storage)
|
||||
{
|
||||
m_numStages = 0;
|
||||
m_maxStages = storage.maxStages;
|
||||
m_stageArray = storage.stageArray;
|
||||
}
|
||||
|
||||
complex_t Cascade::response(double normalizedFrequency) const
|
||||
{
|
||||
double w = 2 * doublePi * normalizedFrequency;
|
||||
const complex_t czn1 = std::polar(1., -w);
|
||||
const complex_t czn2 = std::polar(1., -2 * w);
|
||||
complex_t ch(1);
|
||||
complex_t cbot(1);
|
||||
|
||||
const Biquad* stage = m_stageArray;
|
||||
for (int i = m_numStages; --i >= 0; ++stage)
|
||||
{
|
||||
complex_t cb(1);
|
||||
complex_t ct(stage->getB0() / stage->getA0());
|
||||
ct = addmul(ct, stage->getB1() / stage->getA0(), czn1);
|
||||
ct = addmul(ct, stage->getB2() / stage->getA0(), czn2);
|
||||
cb = addmul(cb, stage->getA1() / stage->getA0(), czn1);
|
||||
cb = addmul(cb, stage->getA2() / stage->getA0(), czn2);
|
||||
ch *= ct;
|
||||
cbot *= cb;
|
||||
}
|
||||
|
||||
return ch / cbot;
|
||||
}
|
||||
|
||||
std::vector<PoleZeroPair> Cascade::getPoleZeros() const
|
||||
{
|
||||
std::vector<PoleZeroPair> vpz;
|
||||
vpz.reserve(m_numStages);
|
||||
|
||||
const Stage* stage = m_stageArray;
|
||||
for (int i = m_numStages; --i >= 0;)
|
||||
{
|
||||
BiquadPoleState bps(*stage++);
|
||||
assert(!bps.isSinglePole() || i == 0);
|
||||
vpz.push_back(bps);
|
||||
}
|
||||
|
||||
return vpz;
|
||||
}
|
||||
|
||||
void Cascade::applyScale(double scale)
|
||||
{
|
||||
// For higher order filters it might be helpful
|
||||
// to spread this factor between all the stages.
|
||||
assert(m_numStages > 0);
|
||||
m_stageArray->applyScale(scale);
|
||||
}
|
||||
|
||||
void Cascade::setLayout(const LayoutBase& proto)
|
||||
{
|
||||
const int numPoles = proto.getNumPoles();
|
||||
m_numStages = (numPoles + 1) / 2;
|
||||
assert(m_numStages <= m_maxStages);
|
||||
|
||||
Biquad* stage = m_stageArray;
|
||||
for (int i = 0; i < m_numStages; ++i, ++stage) { stage->setPoleZeroPair(proto[i]); }
|
||||
|
||||
applyScale(proto.getNormalGain() /
|
||||
std::abs(response(proto.getNormalW() / (2 * doublePi))));
|
||||
}
|
||||
|
||||
#include "CascadeSynthesisCpp.inl"
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,169 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_CASCADE_H
|
||||
#define DSPFILTERS_CASCADE_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Biquad.h"
|
||||
#include "Filter.h"
|
||||
#include "Layout.h"
|
||||
#include "MathSupplement.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Holds coefficients for a cascade of second order sections.
|
||||
*
|
||||
*/
|
||||
|
||||
// Factored implementation to reduce template instantiations
|
||||
class Cascade
|
||||
{
|
||||
public:
|
||||
template <class StateType>
|
||||
class StateBase : DenormalPrevention
|
||||
{
|
||||
public:
|
||||
template <typename Sample>
|
||||
Sample process(const Sample in, const Cascade& c)
|
||||
{
|
||||
double out = in;
|
||||
StateType* state = m_stateArray;
|
||||
Biquad const* stage = c.m_stageArray;
|
||||
const double vsa = ac();
|
||||
int i = c.m_numStages - 1;
|
||||
out = (state++)->process1(out, *stage++, vsa);
|
||||
for (; --i >= 0;) { out = (state++)->process1(out, *stage++, 0); }
|
||||
//for (int i = c.m_numStages; --i >= 0; ++state, ++stage)
|
||||
// out = state->process1 (out, *stage, vsa);
|
||||
return Sample(out);
|
||||
}
|
||||
|
||||
#include "StateBaseSynthesisH.inl"
|
||||
|
||||
protected:
|
||||
StateBase(StateType* stateArray) : m_stateArray(stateArray) { }
|
||||
|
||||
StateType* m_stateArray = nullptr;
|
||||
};
|
||||
|
||||
struct Stage : Biquad { };
|
||||
|
||||
struct Storage
|
||||
{
|
||||
Storage(int maxStages_, Stage* stageArray_) : maxStages(maxStages_), stageArray(stageArray_) { }
|
||||
|
||||
int maxStages;
|
||||
Stage* stageArray;
|
||||
};
|
||||
|
||||
int getNumStages() const { return m_numStages; }
|
||||
|
||||
const Stage& operator[](int index)
|
||||
{
|
||||
assert(index >= 0 && index <= m_numStages);
|
||||
return m_stageArray[index];
|
||||
}
|
||||
|
||||
// Calculate filter response at the given normalized frequency.
|
||||
complex_t response(double normalizedFrequency) const;
|
||||
|
||||
std::vector<PoleZeroPair> getPoleZeros() const;
|
||||
|
||||
// Process a block of samples in the given form
|
||||
template <class StateType, typename Sample>
|
||||
void process(int nSamples, Sample* dest, StateType& state) const
|
||||
{
|
||||
while (--nSamples >= 0)
|
||||
{
|
||||
*dest = state.process(*dest, *this);
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
|
||||
#include "CascadeSynthesisH.inl"
|
||||
|
||||
protected:
|
||||
Cascade();
|
||||
|
||||
void setCascadeStorage(const Storage& storage);
|
||||
|
||||
void applyScale(double scale);
|
||||
void setLayout(const LayoutBase& proto);
|
||||
|
||||
private:
|
||||
int m_numStages = 0;
|
||||
int m_maxStages = 0;
|
||||
Stage* m_stageArray = nullptr;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Storage for Cascade
|
||||
template <int MaxStages>
|
||||
class CascadeStages
|
||||
{
|
||||
public:
|
||||
template <class StateType>
|
||||
class State : public Cascade::StateBase<StateType>
|
||||
{
|
||||
public:
|
||||
State() : Cascade::StateBase<StateType>(m_states)
|
||||
{
|
||||
Cascade::StateBase<StateType>::m_stateArray = m_states;
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
StateType* state = m_states;
|
||||
for (int i = MaxStages; --i >= 0; ++state) { state->reset(); }
|
||||
}
|
||||
|
||||
private:
|
||||
StateType m_states[MaxStages];
|
||||
};
|
||||
|
||||
/*@Internal*/
|
||||
Cascade::Storage getCascadeStorage() { return Cascade::Storage(MaxStages, m_stages); }
|
||||
|
||||
private:
|
||||
Cascade::Stage m_stages[MaxStages];
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
|
||||
//#include <sstream>
|
||||
//#include <iostream>
|
||||
//#include <iomanip>
|
||||
|
||||
void Cascade::setLayoutSynthesis (const LayoutBase& proto)
|
||||
{
|
||||
const int numPoles = proto.getNumPoles();
|
||||
m_numStages = (numPoles + 1)/ 2;
|
||||
assert (m_numStages <= m_maxStages);
|
||||
|
||||
Biquad* stage = m_stageArray;
|
||||
for (int i = 0; i < m_numStages; ++i, ++stage)
|
||||
stage->setPoleZeroPair (proto[i]);
|
||||
|
||||
applyScale (proto.getNormalGain() /
|
||||
std::abs (response (proto.getNormalW() / (2 * doublePi))));
|
||||
|
||||
/*
|
||||
// visu Analysis Filter
|
||||
stage-=m_numStages;
|
||||
for (int i=0; i<m_numStages; ++i, ++stage)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cascade::setLayout : " << i << "\n"
|
||||
<< "Gain = " << proto.getNormalGain() / std::abs (response (proto.getNormalW() / (2 * doublePi))) << "\n"
|
||||
<< "a0[0] = " << stage->m_a0 << "\n"
|
||||
<< "a1[0] = " << stage->m_a1 << "\n"
|
||||
<< "a2[0] = " << stage->m_a2 << "\n"
|
||||
<< "b0[0] = " << stage->m_b0 << "\n"
|
||||
<< "b1[0] = " << stage->m_b1 << "\n"
|
||||
<< "b2[0] = " << stage->m_b2 << "\n";
|
||||
std::cout << os.str();
|
||||
}
|
||||
*/
|
||||
|
||||
//manual inversion of digital coefficients
|
||||
stage-=m_numStages;
|
||||
double a0,a1,a2,b0,b1,b2;
|
||||
for (int i=0; i<m_numStages; ++i, ++stage)
|
||||
{
|
||||
a0 = stage->m_a0;
|
||||
a1 = stage->m_a1;
|
||||
a2 = stage->m_a2;
|
||||
b0 = stage->m_b0;
|
||||
b1 = stage->m_b1;
|
||||
b2 = stage->m_b2;
|
||||
stage->m_a0 = b0;
|
||||
stage->m_a1 = b1;
|
||||
stage->m_a2 = b2;
|
||||
stage->m_b0 = a0;
|
||||
stage->m_b1 = a1;
|
||||
stage->m_b2 = a2;
|
||||
}
|
||||
|
||||
applyScale (proto.getNormalGain() / std::abs (response (proto.getNormalW() / (2 * doublePi))));
|
||||
|
||||
/*
|
||||
// visu Synthesis Filter
|
||||
stage-=m_numStages;
|
||||
double zeros_discr, poles_discr;
|
||||
for (int i=0; i<m_numStages; ++i, ++stage)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cascade::setLayoutSynthesis : " << i << "\n"
|
||||
<< "Gain = " << proto.getNormalGain() / std::abs (response (proto.getNormalW() / (2 * doublePi))) << "\n"
|
||||
<< "a0[0] = " << stage->m_a0 << "\n"
|
||||
<< "a1[0] = " << stage->m_a1 << "\n"
|
||||
<< "a2[0] = " << stage->m_a2 << "\n"
|
||||
<< "b0[0] = " << stage->m_b0 << "\n"
|
||||
<< "b1[0] = " << stage->m_b1 << "\n"
|
||||
<< "b2[0] = " << stage->m_b2 << "\n";
|
||||
|
||||
zeros_discr = stage->m_b1*stage->m_b1 - 4*stage->m_b0*stage->m_b2;
|
||||
poles_discr = stage->m_a1*stage->m_a1 - 4*stage->m_a0*stage->m_a2;
|
||||
os << "zeros discriminant = " << zeros_discr << "\n";
|
||||
if(zeros_discr>=0)
|
||||
{
|
||||
os << "zeros = " << (-stage->m_b1 + std::sqrt(zeros_discr))/(2*stage->m_b0) << " et " << (-stage->m_b1 - std::sqrt(zeros_discr))/(2*stage->m_b0) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "zeros = " << (-stage->m_b1)/(2*stage->m_b0) << "+/-i" << std::sqrt(std::abs(zeros_discr))/(2*stage->m_b0) << "\n"
|
||||
<< "zeros abs = " << std::sqrt( (-stage->m_b1)/(2*stage->m_b0)*(-stage->m_b1)/(2*stage->m_b0) + std::sqrt(std::abs(zeros_discr))/(2*stage->m_b0)*std::sqrt(std::abs(zeros_discr))/(2*stage->m_b0) ) << "\n";
|
||||
}
|
||||
os << "poles discriminant = " << poles_discr << "\n";
|
||||
if(poles_discr>=0)
|
||||
{
|
||||
os << "poles = " << (-stage->m_a1 + std::sqrt(poles_discr))/(2*stage->m_a0) << " et " << (-stage->m_a1 - std::sqrt(poles_discr))/(2*stage->m_a0) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "poles = " << (-stage->m_a1)/(2*stage->m_a0) << "+/-i" << std::sqrt(std::abs(poles_discr))/(2*stage->m_a0) << "\n"
|
||||
<< "poles abs = " << std::sqrt( (-stage->m_a1)/(2*stage->m_a0)*(-stage->m_a1)/(2*stage->m_a0) + std::sqrt(std::abs(poles_discr))/(2*stage->m_a0)*std::sqrt(std::abs(poles_discr))/(2*stage->m_a0) ) << "\n";
|
||||
}
|
||||
std::cout << os.str();
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
|
||||
// Process a block of samples in the given form
|
||||
template <class StateType, typename Sample>
|
||||
void processSynthesis (int nSamples, Sample* dest, StateType& state) const
|
||||
{
|
||||
while (--nSamples >= 0)
|
||||
{
|
||||
*dest = state.processSynthesis (*dest, *this);
|
||||
dest++;
|
||||
}
|
||||
}
|
||||
|
||||
void setLayoutSynthesis (const LayoutBase& proto);
|
||||
|
||||
void setupSynthesis (int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency) { assert(false); }
|
||||
@@ -0,0 +1,190 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "ChebyshevI.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace ChebyshevI
|
||||
{
|
||||
AnalogLowPass::AnalogLowPass() : m_numPoles(-1) {}
|
||||
|
||||
void AnalogLowPass::design(int numPoles, double rippleDb)
|
||||
{
|
||||
if (m_numPoles != numPoles || m_rippleDb != rippleDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_rippleDb = rippleDb;
|
||||
|
||||
reset();
|
||||
|
||||
const double eps = std::sqrt(1. / std::exp(-rippleDb * 0.1 * doubleLn10) - 1);
|
||||
const double v0 = asinh(1 / eps) / numPoles;
|
||||
const double sinh_v0 = -sinh(v0);
|
||||
const double cosh_v0 = cosh(v0);
|
||||
|
||||
const double n2 = 2 * numPoles;
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
const int k = 2 * i + 1 - numPoles;
|
||||
double a = sinh_v0 * cos(k * doublePi / n2);
|
||||
double b = cosh_v0 * sin(k * doublePi / n2);
|
||||
|
||||
//addPoleZero (complex_t (a, b), infinity());
|
||||
//addPoleZero (complex_t (a, -b), infinity());
|
||||
addPoleZeroConjugatePairs(complex_t(a, b), infinity());
|
||||
}
|
||||
|
||||
if (numPoles & 1)
|
||||
{
|
||||
add(complex_t(sinh_v0, 0), infinity());
|
||||
setNormal(0, 1);
|
||||
}
|
||||
else { setNormal(0, pow(10, -rippleDb / 20.)); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Chebyshev Type I low pass shelf prototype
|
||||
// From "High-Order Digital Parametric Equalizer Design"
|
||||
// Sophocles J. Orfanidis
|
||||
// http://www.ece.rutgers.edu/~orfanidi/ece521/hpeq.pdf
|
||||
//
|
||||
|
||||
AnalogLowShelf::AnalogLowShelf() { setNormal(doublePi, 1); }
|
||||
|
||||
void AnalogLowShelf::design(int numPoles, double gainDb, double rippleDb)
|
||||
{
|
||||
if (m_numPoles != numPoles || m_rippleDb != rippleDb || m_gainDb != gainDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_rippleDb = rippleDb;
|
||||
m_gainDb = gainDb;
|
||||
|
||||
reset();
|
||||
|
||||
gainDb = -gainDb;
|
||||
|
||||
if (rippleDb >= fabs(gainDb)) { rippleDb = fabs(gainDb); }
|
||||
if (gainDb < 0) { rippleDb = -rippleDb; }
|
||||
|
||||
const double G = std::pow(10., gainDb / 20.0);
|
||||
const double Gb = std::pow(10., (gainDb - rippleDb) / 20.0);
|
||||
const double G0 = 1;
|
||||
const double g0 = pow(G0, 1. / numPoles);
|
||||
|
||||
double eps;
|
||||
if (Gb != G0) { eps = sqrt((G * G - Gb * Gb) / (Gb * Gb - G0 * G0)); }
|
||||
else { eps = G - 1; } // This is surely wrong
|
||||
|
||||
const double b = pow(G / eps + Gb * sqrt(1 + 1 / (eps * eps)), 1. / numPoles);
|
||||
const double u = log(b / g0);
|
||||
const double v = log(pow(1. / eps + sqrt(1 + 1 / (eps * eps)), 1. / numPoles));
|
||||
|
||||
const double sinh_u = sinh(u);
|
||||
const double sinh_v = sinh(v);
|
||||
const double cosh_u = cosh(u);
|
||||
const double cosh_v = cosh(v);
|
||||
const double n2 = 2 * numPoles;
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 1; i <= pairs; ++i)
|
||||
{
|
||||
const double a = doublePi * (2 * i - 1) / n2;
|
||||
const double sn = sin(a);
|
||||
const double cs = cos(a);
|
||||
addPoleZeroConjugatePairs(complex_t(-sn * sinh_u, cs * cosh_u), complex_t(-sn * sinh_v, cs * cosh_v));
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(-sinh_u, -sinh_v); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order, double sampleRate, double cutoffFrequency, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb);
|
||||
LowPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order, double sampleRate, double cutoffFrequency, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb);
|
||||
HighPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb);
|
||||
BandPassTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandStopBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb);
|
||||
BandStopTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void LowShelfBase::setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, rippleDb);
|
||||
LowPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighShelfBase::setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, rippleDb);
|
||||
HighPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandShelfBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, double rippleDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, rippleDb);
|
||||
BandPassTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
m_digitalProto.setNormal(((centerFrequency / sampleRate) < 0.25) ? doublePi : 0, 1);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace ChebyshevI
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,316 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Chebyshev response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ChebyshevI
|
||||
{
|
||||
|
||||
// Half-band analog prototypes (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass();
|
||||
|
||||
void design(int numPoles, double rippleDb);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_rippleDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class AnalogLowShelf : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowShelf();
|
||||
|
||||
void design(int numPoles, double gainDb, double rippleDb);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_rippleDb = 0;
|
||||
double m_gainDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double rippleDb);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double rippleDb);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb);
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb);
|
||||
};
|
||||
|
||||
struct LowShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb);
|
||||
};
|
||||
|
||||
struct HighShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double rippleDb);
|
||||
};
|
||||
|
||||
struct BandShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, double rippleDb);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : PoleFilter<LowShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : PoleFilter<HighShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : PoleFilter<BandShelfBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultRippleDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultRippleDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
struct TypeIIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultRippleDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIII : TypeIIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
struct TypeIVBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 6
|
||||
};
|
||||
|
||||
static int getNumParams() { return 6; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_5() { return ParamInfo::defaultRippleDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIV : TypeIVBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4], params[5]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Chebyshev I Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev I High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev I Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev I Band Stop"; }
|
||||
};
|
||||
|
||||
struct LowShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowShelf; }
|
||||
static const char* getName() { return "Chebyshev I Low Shelf"; }
|
||||
};
|
||||
|
||||
struct HighShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighShelf; }
|
||||
static const char* getName() { return "Chebyshev I High Shelf"; }
|
||||
};
|
||||
|
||||
struct BandShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindBandShelf; }
|
||||
static const char* getName() { return "Chebyshev I Band Shelf"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, ChebyshevI::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, ChebyshevI::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, ChebyshevI::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, ChebyshevI::BandStop>, BandStopDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : OrderBase<MaxOrder, TypeIII, ChebyshevI::LowShelf>, LowShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : OrderBase<MaxOrder, TypeIII, ChebyshevI::HighShelf>, HighShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : OrderBase<MaxOrder, TypeIV, ChebyshevI::BandShelf>, BandShelfDescription {};
|
||||
} // namespace Design
|
||||
} // namespace ChebyshevI
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,187 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "ChebyshevII.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace ChebyshevII
|
||||
{
|
||||
|
||||
// "Chebyshev Filter Properties"
|
||||
// http://cnx.org/content/m16906/latest/
|
||||
|
||||
AnalogLowPass::AnalogLowPass() : m_numPoles(-1) { setNormal(0, 1); }
|
||||
|
||||
void AnalogLowPass::design(int numPoles, double stopBandDb)
|
||||
{
|
||||
if (m_numPoles != numPoles || m_stopBandDb != stopBandDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_stopBandDb = stopBandDb;
|
||||
|
||||
reset();
|
||||
|
||||
const double eps = std::sqrt(1. / (std::exp(stopBandDb * 0.1 * doubleLn10) - 1));
|
||||
const double v0 = asinh(1 / eps) / numPoles;
|
||||
const double sinh_v0 = -sinh(v0);
|
||||
const double cosh_v0 = cosh(v0);
|
||||
const double fn = doublePi / (2 * numPoles);
|
||||
|
||||
int k = 1;
|
||||
for (int i = numPoles / 2; --i >= 0; k += 2)
|
||||
{
|
||||
const double a = sinh_v0 * cos((k - numPoles) * fn);
|
||||
const double b = cosh_v0 * sin((k - numPoles) * fn);
|
||||
const double d2 = a * a + b * b;
|
||||
const double im = 1 / cos(k * fn);
|
||||
addPoleZeroConjugatePairs(complex_t(a / d2, b / d2), complex_t(0, im));
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(1 / sinh_v0, infinity()); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Chebyshev Type I low pass shelf prototype
|
||||
// From "High-Order Digital Parametric Equalizer Design"
|
||||
// Sophocles J. Orfanidis
|
||||
// http://www.ece.rutgers.edu/~orfanidi/ece521/hpeq.pdf
|
||||
//
|
||||
|
||||
AnalogLowShelf::AnalogLowShelf() : m_numPoles(-1) { setNormal(doublePi, 1); }
|
||||
|
||||
void AnalogLowShelf::design(int numPoles, double gainDb, double stopBandDb)
|
||||
{
|
||||
if (m_numPoles != numPoles || m_stopBandDb != stopBandDb || m_gainDb != gainDb)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_stopBandDb = stopBandDb;
|
||||
m_gainDb = gainDb;
|
||||
|
||||
reset();
|
||||
|
||||
gainDb = -gainDb;
|
||||
|
||||
if (stopBandDb >= fabs(gainDb)) { stopBandDb = fabs(gainDb); }
|
||||
if (gainDb < 0) { stopBandDb = -stopBandDb; }
|
||||
|
||||
const double G = std::pow(10., gainDb / 20.0);
|
||||
const double Gb = std::pow(10., (gainDb - stopBandDb) / 20.0);
|
||||
const double G0 = 1;
|
||||
const double g0 = pow(G0, 1. / numPoles);
|
||||
|
||||
double eps;
|
||||
if (Gb != G0) { eps = sqrt((G * G - Gb * Gb) / (Gb * Gb - G0 * G0)); }
|
||||
else { eps = G - 1; } // This is surely wrong
|
||||
|
||||
const double b = pow(G / eps + Gb * sqrt(1 + 1 / (eps * eps)), 1. / numPoles);
|
||||
const double u = log(b / g0);
|
||||
const double v = log(pow(1. / eps + sqrt(1 + 1 / (eps * eps)), 1. / numPoles));
|
||||
|
||||
const double sinh_u = sinh(u);
|
||||
const double sinh_v = sinh(v);
|
||||
const double cosh_u = cosh(u);
|
||||
const double cosh_v = cosh(v);
|
||||
const double n2 = 2 * numPoles;
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 1; i <= pairs; ++i)
|
||||
{
|
||||
const double a = doublePi * (2 * i - 1) / n2;
|
||||
const double sn = sin(a);
|
||||
const double cs = cos(a);
|
||||
addPoleZeroConjugatePairs(complex_t(-sn * sinh_u, cs * cosh_u), complex_t(-sn * sinh_v, cs * cosh_v));
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(-sinh_u, -sinh_v); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, stopBandDb);
|
||||
LowPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, stopBandDb);
|
||||
HighPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, stopBandDb);
|
||||
BandPassTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandStopBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, stopBandDb);
|
||||
BandStopTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void LowShelfBase::setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, stopBandDb);
|
||||
LowPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighShelfBase::setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, stopBandDb);
|
||||
HighPassTransform(cutoffFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandShelfBase::setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, double stopBandDb)
|
||||
{
|
||||
m_analogProto.design(order, gainDb, stopBandDb);
|
||||
BandPassTransform(centerFrequency / sampleRate, widthFrequency / sampleRate, m_digitalProto, m_analogProto);
|
||||
m_digitalProto.setNormal(((centerFrequency / sampleRate) < 0.25) ? doublePi : 0, 1);
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace ChebyshevII
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,317 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Inverse Chebyshev response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace ChebyshevII
|
||||
{
|
||||
|
||||
// Half-band analog prototypes (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass();
|
||||
|
||||
void design(int numPoles, double stopBandDb);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_stopBandDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class AnalogLowShelf : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowShelf();
|
||||
|
||||
void design(int numPoles, double gainDb, double stopBandDb);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
double m_stopBandDb = 0;
|
||||
double m_gainDb = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double stopBandDb);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb);
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double stopBandDb);
|
||||
};
|
||||
|
||||
struct LowShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb);
|
||||
};
|
||||
|
||||
struct HighShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double gainDb, double stopBandDb);
|
||||
};
|
||||
|
||||
struct BandShelfBase : PoleFilterBase<AnalogLowShelf>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double gainDb, double stopBandDb);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : PoleFilter<LowShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : PoleFilter<HighShelfBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : PoleFilter<BandShelfBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultStopDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultStopDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
struct TypeIIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultStopDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIII : TypeIIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
struct TypeIVBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 6
|
||||
};
|
||||
|
||||
static int getNumParams() { return 6; }
|
||||
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_5() { return ParamInfo::defaultStopDbParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIV : TypeIVBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4], params[5]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Chebyshev II Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev II High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev II Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Chebyshev II Band Stop"; }
|
||||
};
|
||||
|
||||
struct LowShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowShelf; }
|
||||
static const char* getName() { return "Chebyshev II Low Shelf"; }
|
||||
};
|
||||
|
||||
struct HighShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighShelf; }
|
||||
static const char* getName() { return "Chebyshev II High Shelf"; }
|
||||
};
|
||||
|
||||
struct BandShelfDescription
|
||||
{
|
||||
static Kind getKind() { return kindBandShelf; }
|
||||
static const char* getName() { return "Chebyshev II Band Shelf"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design Filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, ChebyshevII::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, ChebyshevII::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, ChebyshevII::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, ChebyshevII::BandStop>, BandStopDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowShelf : OrderBase<MaxOrder, TypeIII, ChebyshevII::LowShelf>, LowShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighShelf : OrderBase<MaxOrder, TypeIII, ChebyshevII::HighShelf>, HighShelfDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandShelf : OrderBase<MaxOrder, TypeIV, ChebyshevII::BandShelf>, BandShelfDescription {};
|
||||
} // namespace Design
|
||||
} // namespace ChebyshevII
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_COMMON_H
|
||||
#define DSPFILTERS_COMMON_H
|
||||
|
||||
//
|
||||
// This must be the first file included in every DspFilters header and source
|
||||
//
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning (disable: 4100)
|
||||
#endif
|
||||
|
||||
//#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <complex>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
namespace tr1 = std::tr1;
|
||||
#else
|
||||
namespace tr1 = std;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Custom.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace Custom
|
||||
{
|
||||
void OnePole::setup(double scale,
|
||||
double pole,
|
||||
double zero)
|
||||
{
|
||||
setOnePole(pole, zero);
|
||||
applyScale(scale);
|
||||
}
|
||||
|
||||
void TwoPole::setup(double scale,
|
||||
double poleRho,
|
||||
double poleTheta,
|
||||
double zeroRho,
|
||||
double zeroTheta)
|
||||
{
|
||||
complex_t pole = std::polar(poleRho, poleTheta);
|
||||
complex_t zero = std::polar(zeroRho, zeroTheta);
|
||||
|
||||
setTwoPole(pole, zero, std::conj(pole), std::conj(zero));
|
||||
applyScale(scale);
|
||||
}
|
||||
} // namespace Custom
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Biquad.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Single pole and Biquad with parameters allowing
|
||||
* for directly setting the poles and zeros
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Custom
|
||||
{
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
struct OnePole : Biquad
|
||||
{
|
||||
void setup(double scale, double pole, double zero);
|
||||
};
|
||||
|
||||
struct TwoPole : Biquad
|
||||
{
|
||||
void setup(double scale, double poleRho, double poleTheta, double zeroRho, double zeroTheta);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct OnePole : DesignBase, Custom::OnePole
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultPoleRealParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultZeroRealParam(); }
|
||||
static Kind getKind() { return kindOther; }
|
||||
static const char* getName() { return "Custom One-Pole"; }
|
||||
void setParams(const Params& params) { setup(pow(10., params[1] / 20), params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TwoPole : DesignBase, Custom::TwoPole
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 6
|
||||
};
|
||||
|
||||
static int getNumParams() { return 6; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultPoleRhoParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultPoleThetaParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultZeroRhoParam(); }
|
||||
static ParamInfo getParamInfo_5() { return ParamInfo::defaultZeroThetaParam(); }
|
||||
static Kind getKind() { return kindOther; }
|
||||
static const char* getName() { return "Custom Two-Pole"; }
|
||||
void setParams(const Params& params) { setup(pow(10., params[1] / 20), params[2], params[3], params[4], params[5]); }
|
||||
};
|
||||
} // namespace Design
|
||||
} // namespace Custom
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Design.h"
|
||||
|
||||
namespace Dsp {}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Params.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
struct DesignBase
|
||||
{
|
||||
#include "DesignSynthesisH.inl"
|
||||
|
||||
// Sampling rate is the first param for every Design filter
|
||||
static ParamInfo getParamInfo_0() { return ParamInfo::defaultSampleRateParam(); }
|
||||
|
||||
// These should never get called
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_5() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_6() { return ParamInfo(); }
|
||||
static ParamInfo getParamInfo_7() { return ParamInfo(); }
|
||||
};
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
void setParamsSynthesis (const Params& parameters) { assert(false); }
|
||||
@@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_DSP_H
|
||||
#define DSPFILTERS_DSP_H
|
||||
|
||||
//
|
||||
// Include this file in your application to get everything
|
||||
//
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#include "Biquad.h"
|
||||
#include "Cascade.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
#include "SmoothedFilter.h"
|
||||
#include "State.h"
|
||||
#include "Utilities.h"
|
||||
|
||||
#include "Bessel.h"
|
||||
#include "Butterworth.h"
|
||||
#include "ChebyshevI.h"
|
||||
#include "ChebyshevII.h"
|
||||
#include "Custom.h"
|
||||
#include "Elliptic.h"
|
||||
#include "Legendre.h"
|
||||
#include "RBJ.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,352 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Elliptic.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace Elliptic
|
||||
{
|
||||
|
||||
// shit ton of math in here
|
||||
|
||||
// approximation to complete elliptic integral of the first kind.
|
||||
// fast convergence, peak error less than 2e-16.
|
||||
double Solver::ellipticK(double k)
|
||||
{
|
||||
double m = k * k;
|
||||
double a = 1;
|
||||
double b = sqrt(1 - m);
|
||||
double c = a - b;
|
||||
double co;
|
||||
do
|
||||
{
|
||||
co = c;
|
||||
c = (a - b) / 2;
|
||||
double ao = (a + b) / 2;
|
||||
b = sqrt(a * b);
|
||||
a = ao;
|
||||
} while (c < co);
|
||||
|
||||
return doublePi / (a + a);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AnalogLowPass::AnalogLowPass()
|
||||
: m_numPoles(-1) { setNormal(0, 1); }
|
||||
|
||||
void AnalogLowPass::design(int numPoles,
|
||||
double rippleDb,
|
||||
double rolloff)
|
||||
{
|
||||
if (m_numPoles != numPoles ||
|
||||
m_rippleDb != rippleDb ||
|
||||
m_rolloff != rolloff)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
m_rippleDb = rippleDb;
|
||||
m_rolloff = rolloff;
|
||||
|
||||
reset();
|
||||
|
||||
// calculate
|
||||
//const double ep = rippleDb; // passband ripple
|
||||
|
||||
const int n = numPoles;
|
||||
|
||||
double e2 = pow(10., rippleDb / 10) - 1;
|
||||
//double xi = rolloff + 1;
|
||||
double xi = 5 * exp(rolloff - 1) + 1;
|
||||
|
||||
m_K = Solver::ellipticK(1 / xi);
|
||||
m_Kprime = Solver::ellipticK(sqrt(1 - 1 / (xi * xi)));
|
||||
|
||||
int ni = ((n & 1) == 1) ? 0 : 1;
|
||||
int i;
|
||||
double f[100]; // HACK!!!
|
||||
for (i = 1; i <= n / 2; ++i)
|
||||
{
|
||||
double u = (2 * i - ni) * m_K / n;
|
||||
double sn = calcsn(u);
|
||||
sn *= 2 * doublePi / m_K;
|
||||
f[i] = m_zeros[i - 1] = 1 / sn;
|
||||
}
|
||||
m_zeros[n / 2] = std::numeric_limits<double>::infinity();
|
||||
double fb = 1 / (2 * doublePi);
|
||||
m_nin = n % 2;
|
||||
m_n2 = n / 2;
|
||||
for (i = 1; i <= m_n2; ++i)
|
||||
{
|
||||
double x = f[m_n2 + 1 - i];
|
||||
m_z1[i] = sqrt(1 - 1 / (x * x));
|
||||
}
|
||||
double ee = e2;//pow(10., rippleDb/20)-1;
|
||||
m_e = sqrt(ee);
|
||||
double fbb = fb * fb;
|
||||
m_m = m_nin + 2 * m_n2;
|
||||
m_em = 2 * (m_m / 2);
|
||||
double tp = 2 * doublePi;
|
||||
calcfz();
|
||||
calcqz();
|
||||
if (m_m > m_em) { m_c1[2 * m_m] = 0; }
|
||||
for (i = 0; i <= 2 * m_m; i += 2) { m_a1[m_m - i / 2] = m_c1[i] + m_d1[i]; }
|
||||
double a0 = findfact(m_m);
|
||||
int r = 0;
|
||||
while (r < m_em / 2)
|
||||
{
|
||||
r++;
|
||||
m_p[r] /= 10;
|
||||
m_q1[r] /= 100;
|
||||
double d = 1 + m_p[r] + m_q1[r];
|
||||
m_b1[r] = (1 + m_p[r] / 2) * fbb / d;
|
||||
m_zf1[r] = fb / pow(d, .25);
|
||||
m_zq1[r] = 1 / sqrt(fabs(2 * (1 - m_b1[r] / (m_zf1[r] * m_zf1[r]))));
|
||||
m_zw1[r] = tp * m_zf1[r];
|
||||
|
||||
m_rootR[r] = -.5 * m_zw1[r] / m_zq1[r];
|
||||
m_rootR[r + m_em / 2] = m_rootR[r];
|
||||
m_rootI[r] = .5 * sqrt(fabs(m_zw1[r] * m_zw1[r] / (m_zq1[r] * m_zq1[r]) - 4 * m_zw1[r] * m_zw1[r]));
|
||||
m_rootI[r + m_em / 2] = -m_rootI[r];
|
||||
|
||||
complex_t pole(-.5 * m_zw1[r] / m_zq1[r], .5 * sqrt(fabs(m_zw1[r] * m_zw1[r] / (m_zq1[r] * m_zq1[r]) - 4 * m_zw1[r] * m_zw1[r])));
|
||||
|
||||
complex_t zero(0, m_zeros[r - 1]);
|
||||
|
||||
addPoleZeroConjugatePairs(pole, zero);
|
||||
}
|
||||
|
||||
if (a0 != 0)
|
||||
{
|
||||
m_rootR[r + 1 + m_em / 2] = -sqrt(fbb / (.1 * a0 - 1)) * tp;
|
||||
m_rootI[r + 1 + m_em / 2] = 0;
|
||||
|
||||
add(-sqrt(fbb / (.1 * a0 - 1)) * tp, infinity());
|
||||
}
|
||||
|
||||
setNormal(0, (numPoles & 1) ? 1. : pow(10., -rippleDb / 20.0));
|
||||
}
|
||||
}
|
||||
|
||||
// generate the product of (z+s1[i]) for i = 1 .. sn and store it in b1[]
|
||||
// (i.e. f[z] = b1[0] + b1[1] z + b1[2] z^2 + ... b1[sn] z^sn)
|
||||
void AnalogLowPass::prodpoly(int sn)
|
||||
{
|
||||
m_b1[0] = m_s1[1];
|
||||
m_b1[1] = 1;
|
||||
int i;
|
||||
for (int j = 2; j <= sn; ++j)
|
||||
{
|
||||
m_a1[0] = m_s1[j] * m_b1[0];
|
||||
for (i = 1; i <= j - 1; ++i) { m_a1[i] = m_b1[i - 1] + m_s1[j] * m_b1[i]; }
|
||||
for (i = 0; i != j; ++i) { m_b1[i] = m_a1[i]; }
|
||||
m_b1[j] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// determine f(z)^2
|
||||
void AnalogLowPass::calcfz2(int i)
|
||||
{
|
||||
int ji = 0;
|
||||
int jf = 0;
|
||||
if (i < m_em + 2)
|
||||
{
|
||||
ji = 0;
|
||||
jf = i;
|
||||
}
|
||||
if (i > m_em)
|
||||
{
|
||||
ji = i - m_em;
|
||||
jf = m_em;
|
||||
}
|
||||
m_c1[i] = 0;
|
||||
for (int j = ji; j <= jf; j += 2) { m_c1[i] += m_a1[j] * (m_a1[i - j] * pow(10., m_m - i / 2)); }
|
||||
}
|
||||
|
||||
// calculate f(z)
|
||||
void AnalogLowPass::calcfz()
|
||||
{
|
||||
int i = 1;
|
||||
if (m_nin == 1) { m_s1[i++] = 1; }
|
||||
for (; i <= m_nin + m_n2; ++i) { m_s1[i] = m_s1[i + m_n2] = m_z1[i - m_nin]; }
|
||||
prodpoly(m_nin + 2 * m_n2);
|
||||
for (i = 0; i <= m_em; i += 2) { m_a1[i] = m_e * m_b1[i]; }
|
||||
for (i = 0; i <= 2 * m_em; i += 2) { calcfz2(i); }
|
||||
}
|
||||
|
||||
// determine q(z)
|
||||
void AnalogLowPass::calcqz()
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i <= m_nin; ++i) { m_s1[i] = -10; }
|
||||
for (; i <= m_nin + m_n2; ++i) { m_s1[i] = -10 * m_z1[i - m_nin] * m_z1[i - m_nin]; }
|
||||
for (; i <= m_nin + 2 * m_n2; ++i) { m_s1[i] = m_s1[i - m_n2]; }
|
||||
prodpoly(m_m);
|
||||
int dd = ((m_nin & 1) == 1) ? -1 : 1;
|
||||
for (i = 0; i <= 2 * m_m; i += 2) { m_d1[i] = dd * m_b1[i / 2]; }
|
||||
}
|
||||
|
||||
// compute factors
|
||||
double AnalogLowPass::findfact(int t)
|
||||
{
|
||||
int i;
|
||||
double a = 0;
|
||||
for (i = 1; i <= t; ++i) { m_a1[i] /= m_a1[0]; }
|
||||
m_a1[0] = m_b1[0] = m_c1[0] = 1;
|
||||
int i1 = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (t <= 2) { break; }
|
||||
double p0 = 0, q0 = 0;
|
||||
i1++;
|
||||
for (;;)
|
||||
{
|
||||
m_b1[1] = m_a1[1] - p0;
|
||||
m_c1[1] = m_b1[1] - p0;
|
||||
for (i = 2; i <= t; ++i) { m_b1[i] = m_a1[i] - p0 * m_b1[i - 1] - q0 * m_b1[i - 2]; }
|
||||
for (i = 2; i < t; ++i) { m_c1[i] = m_b1[i] - p0 * m_c1[i - 1] - q0 * m_c1[i - 2]; }
|
||||
int x1 = t - 1;
|
||||
int x2 = t - 2;
|
||||
int x3 = t - 3;
|
||||
double x4 = m_c1[x2] * m_c1[x2] + m_c1[x3] * (m_b1[x1] - m_c1[x1]);
|
||||
if (x4 == 0) { x4 = 1e-3; }
|
||||
double ddp = (m_b1[x1] * m_c1[x2] - m_b1[t] * m_c1[x3]) / x4;
|
||||
p0 += ddp;
|
||||
double dq = (m_b1[t] * m_c1[x2] - m_b1[x1] * (m_c1[x1] - m_b1[x1])) / x4;
|
||||
q0 += dq;
|
||||
if (fabs(ddp + dq) < 1e-6) { break; }
|
||||
}
|
||||
m_p[i1] = p0;
|
||||
m_q1[i1] = q0;
|
||||
m_a1[1] = m_a1[1] - p0;
|
||||
t -= 2;
|
||||
for (i = 2; i <= t; ++i) { m_a1[i] -= p0 * m_a1[i - 1] + q0 * m_a1[i - 2]; }
|
||||
if (t <= 2) { break; }
|
||||
}
|
||||
|
||||
if (t == 2)
|
||||
{
|
||||
i1++;
|
||||
m_p[i1] = m_a1[1];
|
||||
m_q1[i1] = m_a1[2];
|
||||
}
|
||||
if (t == 1) { a = -m_a1[1]; }
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
double AnalogLowPass::calcsn(double u)
|
||||
{
|
||||
double sn = 0;
|
||||
// q = modular constant
|
||||
double q = exp(-doublePi * m_Kprime / m_K);
|
||||
double v = doublePi * .5 * u / m_K;
|
||||
for (int j = 0; ; ++j)
|
||||
{
|
||||
double w = pow(q, j + .5);
|
||||
sn += w * sin((2 * j + 1) * v) / (1 - w * w);
|
||||
if (w < 1e-7) { break; }
|
||||
}
|
||||
return sn;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double rippleDb,
|
||||
double rolloff)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb, rolloff);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double rippleDb,
|
||||
double rolloff)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb, rolloff);
|
||||
|
||||
HighPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
double rippleDb,
|
||||
double rolloff)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb, rolloff);
|
||||
|
||||
BandPassTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandStopBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
double rippleDb,
|
||||
double rolloff)
|
||||
{
|
||||
m_analogProto.design(order, rippleDb, rolloff);
|
||||
|
||||
BandStopTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace Elliptic
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,267 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Elliptic response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Elliptic
|
||||
{
|
||||
|
||||
// Solves for Jacobi elliptics
|
||||
class Solver
|
||||
{
|
||||
public:
|
||||
static double ellipticK(double k);
|
||||
};
|
||||
|
||||
// Half-band analog prototype (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass();
|
||||
|
||||
void design(int numPoles, double rippleDb, double rolloff);
|
||||
|
||||
private:
|
||||
void prodpoly(int sn);
|
||||
void calcfz2(int i);
|
||||
void calcfz();
|
||||
void calcqz();
|
||||
double findfact(int t);
|
||||
double calcsn(double u);
|
||||
|
||||
#if 0
|
||||
template<int n>
|
||||
struct CalcArray
|
||||
{
|
||||
double& operator[](size_t index)
|
||||
{
|
||||
assert( index<n );
|
||||
return m_a[index];
|
||||
}
|
||||
private:
|
||||
double m_a[n];
|
||||
};
|
||||
#else
|
||||
#endif
|
||||
|
||||
double m_p0 = 0;
|
||||
double m_q = 0;
|
||||
double m_K = 0;
|
||||
double m_Kprime = 0;
|
||||
double m_e = 0;
|
||||
int m_nin = 0;
|
||||
int m_m = 0;
|
||||
int m_n2 = 0;
|
||||
int m_em = 0;
|
||||
double m_zeros[100];
|
||||
double m_c1[100];
|
||||
double m_b1[100];
|
||||
double m_a1[100];
|
||||
double m_d1[100];
|
||||
double m_q1[100];
|
||||
double m_z1[100];
|
||||
double m_f1[100];
|
||||
double m_s1[100];
|
||||
double m_p [100];
|
||||
double m_zw1[100];
|
||||
double m_zf1[100];
|
||||
double m_zq1[100];
|
||||
double m_rootR[100];
|
||||
double m_rootI[100];
|
||||
|
||||
int m_numPoles = 0;
|
||||
double m_rippleDb = 0;
|
||||
double m_rolloff = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double rippleDb, double rolloff);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, double rippleDb, double rolloff);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb, double rolloff);
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, double rippleDb, double rolloff);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2> {};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 5
|
||||
};
|
||||
|
||||
static int getNumParams() { return 5; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultRippleDbParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultRolloffParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 6
|
||||
};
|
||||
|
||||
static int getNumParams() { return 6; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
static ParamInfo getParamInfo_4() { return ParamInfo::defaultRippleDbParam(); }
|
||||
static ParamInfo getParamInfo_5() { return ParamInfo::defaultRolloffParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3], params[4], params[5]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Elliptic Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Elliptic High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Elliptic Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Elliptic Band Stop"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
const ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, Elliptic::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, Elliptic::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, Elliptic::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, Elliptic::BandStop>, BandStopDescription {};
|
||||
} // namespace Design
|
||||
} // namespace Elliptic
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,113 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Filter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
Params Filter::getDefaultParams() const
|
||||
{
|
||||
Params params;
|
||||
|
||||
params.clear();
|
||||
|
||||
for (int i = 0; i < getNumParams(); ++i) { params[i] = getParamInfo(i).getDefaultValue(); }
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
Filter::~Filter() {}
|
||||
|
||||
int Filter::findParamId(int paramId)
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
for (int i = getNumParams(); --i >= 0;)
|
||||
{
|
||||
if (getParamInfo(i).getId() == paramId)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void Filter::setParamById(int paramId, double nativeValue)
|
||||
{
|
||||
for (int i = getNumParams(); --i >= 0;)
|
||||
{
|
||||
if (getParamInfo(i).getId() == paramId)
|
||||
{
|
||||
setParam(i, nativeValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(0);
|
||||
}
|
||||
|
||||
void Filter::copyParamsFrom(Filter const* other)
|
||||
{
|
||||
// first, set reasonable defaults
|
||||
m_params = getDefaultParams();
|
||||
|
||||
if (other)
|
||||
{
|
||||
// now loop
|
||||
for (int i = 0; i < getNumParams(); ++i)
|
||||
{
|
||||
const ParamInfo& paramInfo = getParamInfo(i);
|
||||
|
||||
// find a match
|
||||
for (int j = 0; j < other->getNumParams(); ++j)
|
||||
{
|
||||
const ParamInfo& otherParamInfo = other->getParamInfo(j);
|
||||
|
||||
if (paramInfo.getId() == otherParamInfo.getId())
|
||||
{
|
||||
// match!
|
||||
m_params[i] = paramInfo.clamp(other->getParam(j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doSetParams(m_params);
|
||||
}
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,219 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_FILTER_H
|
||||
#define DSPFILTERS_FILTER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
#include "Params.h"
|
||||
#include "State.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filter
|
||||
*
|
||||
* Full abstraction of a digital IIR filter.
|
||||
* Supports run-time introspection and modulation of filter
|
||||
* parameters.
|
||||
*
|
||||
*/
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
virtual ~Filter();
|
||||
|
||||
virtual Kind getKind() const = 0;
|
||||
|
||||
virtual const std::string getName() const = 0;
|
||||
|
||||
virtual int getNumParams() const = 0;
|
||||
|
||||
virtual ParamInfo getParamInfo(int index) const = 0;
|
||||
|
||||
Params getDefaultParams() const;
|
||||
|
||||
const Params& getParams() const { return m_params; }
|
||||
|
||||
double getParam(int paramIndex) const
|
||||
{
|
||||
assert(paramIndex >= 0 && paramIndex <= getNumParams());
|
||||
return m_params[paramIndex];
|
||||
}
|
||||
|
||||
void setParam(int paramIndex, double nativeValue)
|
||||
{
|
||||
assert(paramIndex >= 0 && paramIndex <= getNumParams());
|
||||
m_params[paramIndex] = nativeValue;
|
||||
doSetParams(m_params);
|
||||
}
|
||||
|
||||
int findParamId(int paramId);
|
||||
|
||||
void setParamById(int paramId, double nativeValue);
|
||||
|
||||
void setParams(const Params& parameters)
|
||||
{
|
||||
m_params = parameters;
|
||||
doSetParams(parameters);
|
||||
}
|
||||
|
||||
#include "FilterSynthesisH.inl"
|
||||
|
||||
// This makes a best-effort to pick up the values
|
||||
// of matching parameters from another set. It uses
|
||||
// the ParamID information to make the match.
|
||||
void copyParamsFrom(Filter const* other);
|
||||
|
||||
virtual std::vector<PoleZeroPair> getPoleZeros() const = 0;
|
||||
|
||||
virtual complex_t response(double normalizedFrequency) const = 0;
|
||||
|
||||
virtual int getNumChannels() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void process(int nSamples, float* const* arrayOfChannels) = 0;
|
||||
virtual void process(int nSamples, double* const* arrayOfChannels) = 0;
|
||||
|
||||
protected:
|
||||
virtual void doSetParams(const Params& parameters) = 0;
|
||||
|
||||
private:
|
||||
Params m_params;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* FilterDesign
|
||||
*
|
||||
* This container holds a filter Design (Gui-friendly layer) and
|
||||
* optionally combines it with the necessary state information to
|
||||
* process channel data.
|
||||
*
|
||||
*/
|
||||
|
||||
// Factored to reduce template instantiations
|
||||
template <class DesignClass>
|
||||
class FilterDesignBase : public Filter
|
||||
{
|
||||
public:
|
||||
Kind getKind() const override { return m_design.getKind(); }
|
||||
|
||||
const std::string getName() const override { return m_design.getName(); }
|
||||
|
||||
int getNumParams() const override { return DesignClass::NumParams; }
|
||||
|
||||
Params getDefaultParams() const { return m_design.getDefaultParams(); }
|
||||
|
||||
ParamInfo getParamInfo(int index) const override
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return m_design.getParamInfo_0();
|
||||
case 1: return m_design.getParamInfo_1();
|
||||
case 2: return m_design.getParamInfo_2();
|
||||
case 3: return m_design.getParamInfo_3();
|
||||
case 4: return m_design.getParamInfo_4();
|
||||
case 5: return m_design.getParamInfo_5();
|
||||
case 6: return m_design.getParamInfo_6();
|
||||
case 7: return m_design.getParamInfo_7();
|
||||
default: return ParamInfo();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PoleZeroPair> getPoleZeros() const override { return m_design.getPoleZeros(); }
|
||||
|
||||
complex_t response(double normalizedFrequency) const override { return m_design.response(normalizedFrequency); }
|
||||
|
||||
protected:
|
||||
void doSetParams(const Params& parameters) override { m_design.setParams(parameters); }
|
||||
|
||||
#include "FilterSynthesisH2.inl"
|
||||
|
||||
DesignClass m_design;
|
||||
};
|
||||
|
||||
|
||||
template <class DesignClass, int Channels = 0, class StateType = DirectFormII>
|
||||
class FilterDesign : public FilterDesignBase<DesignClass>
|
||||
{
|
||||
public:
|
||||
FilterDesign() { }
|
||||
|
||||
int getNumChannels() override { return Channels; }
|
||||
void reset() override { m_state.reset(); }
|
||||
|
||||
void process(int nSamples, float* const* arrayOfChannels) override
|
||||
{
|
||||
m_state.process(nSamples, arrayOfChannels, FilterDesignBase<DesignClass>::m_design);
|
||||
}
|
||||
|
||||
void process(int nSamples, double* const* arrayOfChannels) override
|
||||
{
|
||||
m_state.process(nSamples, arrayOfChannels, FilterDesignBase<DesignClass>::m_design);
|
||||
}
|
||||
|
||||
protected:
|
||||
ChannelsState<Channels, typename DesignClass::template State<StateType>> m_state;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* This container combines a raw filter with state information
|
||||
* so it can process channels. In order to set up the filter you
|
||||
* must call a setup function directly. Smooth changes are
|
||||
* not supported, but this class has a smaller footprint.
|
||||
*
|
||||
*/
|
||||
template <class FilterClass, int Channels = 0, class StateType = DirectFormII>
|
||||
class SimpleFilter : public FilterClass
|
||||
{
|
||||
public:
|
||||
int getNumChannels() { return Channels; }
|
||||
void reset() { m_state.reset(); }
|
||||
|
||||
template <typename Sample>
|
||||
void process(int nSamples, Sample* const* arrayOfChannels) { m_state.process(nSamples, arrayOfChannels, *static_cast<FilterClass*>(this)); }
|
||||
|
||||
protected:
|
||||
ChannelsState<Channels, typename FilterClass::template State<StateType>> m_state;
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
void setParamsSynthesis (const Params& parameters)
|
||||
{
|
||||
m_params = parameters;
|
||||
doSetParamsSynthesis (parameters);
|
||||
}
|
||||
|
||||
virtual void processSynthesis (int nSamples, double* const* arrayOfChannels) = 0;
|
||||
|
||||
virtual void doSetParamsSynthesis (const Params& parameters) = 0;
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
void doSetParamsSynthesis (const Params& parameters)
|
||||
{
|
||||
m_design.setParamsSynthesis (parameters);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_LAYOUT_H
|
||||
#define DSPFILTERS_LAYOUT_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
//
|
||||
// Describes a filter as a collection of poles and zeros along with
|
||||
// normalization information to achieve a specified gain at a specified
|
||||
// frequency. The poles and zeros may lie either in the s or the z plane.
|
||||
//
|
||||
|
||||
// Base uses pointers to reduce template instantiations
|
||||
class LayoutBase
|
||||
{
|
||||
public:
|
||||
LayoutBase() { }
|
||||
|
||||
LayoutBase(int maxPoles, PoleZeroPair* pairs) : m_maxPoles(maxPoles), m_pair(pairs) { }
|
||||
|
||||
void setStorage(const LayoutBase& other)
|
||||
{
|
||||
m_numPoles = 0;
|
||||
m_maxPoles = other.m_maxPoles;
|
||||
m_pair = other.m_pair;
|
||||
}
|
||||
|
||||
void reset() { m_numPoles = 0; }
|
||||
|
||||
int getNumPoles() const { return m_numPoles; }
|
||||
|
||||
int getMaxPoles() const { return m_maxPoles; }
|
||||
|
||||
void add(const complex_t& pole, const complex_t& zero)
|
||||
{
|
||||
assert(!(m_numPoles&1)); // single comes last
|
||||
assert(!Dsp::is_nan (pole));
|
||||
m_pair[m_numPoles / 2] = PoleZeroPair(pole, zero);
|
||||
++m_numPoles;
|
||||
}
|
||||
|
||||
void addPoleZeroConjugatePairs(const complex_t pole, const complex_t zero)
|
||||
{
|
||||
assert(!(m_numPoles&1)); // single comes last
|
||||
assert(!Dsp::is_nan (pole));
|
||||
m_pair[m_numPoles / 2] = PoleZeroPair(pole, zero, std::conj(pole), std::conj(zero));
|
||||
m_numPoles += 2;
|
||||
}
|
||||
|
||||
void add(const ComplexPair& poles, const ComplexPair& zeros)
|
||||
{
|
||||
assert(!(m_numPoles&1)); // single comes last
|
||||
assert(poles.isMatchedPair ());
|
||||
assert(zeros.isMatchedPair ());
|
||||
m_pair[m_numPoles / 2] = PoleZeroPair(poles.first, zeros.first, poles.second, zeros.second);
|
||||
m_numPoles += 2;
|
||||
}
|
||||
|
||||
const PoleZeroPair& getPair(int pairIndex) const
|
||||
{
|
||||
assert(pairIndex >= 0 && pairIndex < (m_numPoles+1)/2);
|
||||
return m_pair[pairIndex];
|
||||
}
|
||||
|
||||
const PoleZeroPair& operator[](int pairIndex) const { return getPair(pairIndex); }
|
||||
|
||||
double getNormalW() const { return m_normalW; }
|
||||
|
||||
double getNormalGain() const { return m_normalGain; }
|
||||
|
||||
void setNormal(double w, double g)
|
||||
{
|
||||
m_normalW = w;
|
||||
m_normalGain = g;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
int m_maxPoles = 0;
|
||||
PoleZeroPair* m_pair = nullptr;
|
||||
double m_normalW = 0.0;
|
||||
double m_normalGain = 0.0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Storage for Layout
|
||||
template <int MaxPoles>
|
||||
class Layout
|
||||
{
|
||||
public:
|
||||
operator LayoutBase() { return LayoutBase(MaxPoles, m_pairs); }
|
||||
|
||||
private:
|
||||
PoleZeroPair m_pairs[(MaxPoles + 1) / 2];
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,308 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Legendre.h"
|
||||
#include "RootFinder.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace Legendre
|
||||
{
|
||||
static double m_sqrt2() { return 1.41421356237309504880; }
|
||||
|
||||
// Optimum 'L' Filter algorithm.
|
||||
// (C) 2004, C. Bond.
|
||||
//
|
||||
// Based on discussion in Kuo, "Network Analysis and Synthesis",
|
||||
// pp. 379-383. Original method due to A.Papoulis."On Monotonic
|
||||
// Response Filters", Proc. IRE, 47, Feb. 1959.
|
||||
//
|
||||
// Rewritten by Vinnie Falco to change the way temporary
|
||||
// storage is allocated
|
||||
//
|
||||
|
||||
//
|
||||
// This routine calculates the coefficients of the Legendre polynomial
|
||||
// of the 1st kind. It uses a recursion relation. The first few polynomials
|
||||
// are hard coded and the rest are found by recursion.
|
||||
//
|
||||
// (n+1)Pn+1 = (2n+1)xPn - nPn-1 Recursion relation.
|
||||
//
|
||||
void PolynomialFinderBase::legendre(double* p, int n)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
p[0] = 1.0;
|
||||
return;
|
||||
}
|
||||
if (n == 1)
|
||||
{
|
||||
p[0] = 0.0;
|
||||
p[1] = 1.0;
|
||||
return;
|
||||
}
|
||||
p[0] = -0.5;
|
||||
p[1] = 0.0;
|
||||
p[2] = 1.5;
|
||||
|
||||
if (n == 2) { return; }
|
||||
|
||||
for (i = 0; i <= n; ++i) { m_aa[i] = m_bb[i] = 0.0; }
|
||||
m_bb[1] = 1.0;
|
||||
|
||||
for (i = 3; i <= n; ++i)
|
||||
{
|
||||
for (j = 0; j <= i; ++j)
|
||||
{
|
||||
m_aa[j] = m_bb[j];
|
||||
m_bb[j] = p[j];
|
||||
p[j] = 0.0;
|
||||
}
|
||||
for (j = i - 2; j >= 0; j -= 2) { p[j] -= (i - 1) * m_aa[j] / i; }
|
||||
for (j = i - 1; j >= 0; j -= 2) { p[j + 1] += (2 * i - 1) * m_bb[j] / i; }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// In the following routine n = 2k + 1 for odd 'n' and n = 2k + 2 for
|
||||
// even 'n'.
|
||||
//
|
||||
//
|
||||
// n k
|
||||
// -----
|
||||
// 1 0
|
||||
// 2 0
|
||||
// 3 1
|
||||
// 4 1
|
||||
// 5 2
|
||||
// 6 2
|
||||
//
|
||||
|
||||
void PolynomialFinderBase::solve(int n)
|
||||
{
|
||||
assert(n <= m_maxN);
|
||||
|
||||
double c1;
|
||||
int i, j;
|
||||
|
||||
int k = (n - 1) / 2;
|
||||
//
|
||||
// form vector of 'a' constants
|
||||
//
|
||||
if (n & 1)
|
||||
{ // odd
|
||||
for (i = 0; i <= k; ++i) { m_a[i] = (2.0 * i + 1.0) / (m_sqrt2() * (k + 1.0)); }
|
||||
} // even
|
||||
else
|
||||
{
|
||||
for (i = 0; i < k + 1; ++i) { m_a[i] = 0.0; }
|
||||
if (k & 1) { for (i = 1; i <= k; i += 2) { m_a[i] = (2 * i + 1) / sqrt(double((k + 1) * (k + 2))); } }
|
||||
else { for (i = 0; i <= k; i += 2) { m_a[i] = (2 * i + 1) / sqrt(double((k + 1) * (k + 2))); } }
|
||||
}
|
||||
for (i = 0; i <= n; ++i)
|
||||
{
|
||||
m_s[i] = 0.0;
|
||||
m_w[i] = 0.0;
|
||||
}
|
||||
//
|
||||
// form s[] = sum of a[i]*P[i]
|
||||
//
|
||||
m_s[0] = m_a[0];
|
||||
m_s[1] = m_a[1];
|
||||
for (i = 2; i <= k; ++i)
|
||||
{
|
||||
legendre(m_p, i);
|
||||
for (j = 0; j <= i; ++j) { m_s[j] += m_a[i] * m_p[j]; }
|
||||
}
|
||||
//
|
||||
// form v[] = square of s[]
|
||||
//
|
||||
for (i = 0; i <= 2 * k + 2; ++i) { m_v[i] = 0.0; }
|
||||
for (i = 0; i <= k; ++i) { for (j = 0; j <= k; ++j) { m_v[i + j] += m_s[i] * m_s[j]; } }
|
||||
//
|
||||
// modify integrand for even 'n'
|
||||
//
|
||||
m_v[2 * k + 1] = 0.0;
|
||||
if ((n & 1) == 0) { for (i = n; i >= 0; i--) { m_v[i + 1] += m_v[i]; } }
|
||||
//
|
||||
// form integral of v[]
|
||||
//
|
||||
for (i = n + 1; i >= 0; i--) { m_v[i + 1] = m_v[i] / double(i + 1.0); }
|
||||
m_v[0] = 0.0;
|
||||
//
|
||||
// clear s[] for use in computing definite integral
|
||||
//
|
||||
for (i = 0; i < (n + 2); ++i) { m_s[i] = 0.0; }
|
||||
m_s[0] = -1.0;
|
||||
m_s[1] = 2.0;
|
||||
//
|
||||
// calculate definite integral
|
||||
//
|
||||
for (i = 1; i <= n; ++i)
|
||||
{
|
||||
if (i > 1)
|
||||
{
|
||||
double c0 = -m_s[0];
|
||||
for (j = 1; j < i + 1; ++j)
|
||||
{
|
||||
c1 = -m_s[j] + 2.0 * m_s[j - 1];
|
||||
m_s[j - 1] = c0;
|
||||
c0 = c1;
|
||||
}
|
||||
c1 = 2.0 * m_s[i];
|
||||
m_s[i] = c0;
|
||||
m_s[i + 1] = c1;
|
||||
}
|
||||
for (j = i; j > 0; j--) { m_w[j] += (m_v[i] * m_s[j]); }
|
||||
}
|
||||
if ((n & 1) == 0) { m_w[1] = 0.0; }
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AnalogLowPass::AnalogLowPass()
|
||||
: m_numPoles(-1) { setNormal(0, 1); }
|
||||
|
||||
void AnalogLowPass::design(int numPoles,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
if (m_numPoles != numPoles)
|
||||
{
|
||||
m_numPoles = numPoles;
|
||||
|
||||
reset();
|
||||
|
||||
PolynomialFinderBase& poly(w->poly);
|
||||
RootFinderBase& poles(w->roots);
|
||||
|
||||
poly.solve(numPoles);
|
||||
int degree = numPoles * 2;
|
||||
|
||||
poles.coef()[0] = 1 + poly.coef()[0];
|
||||
poles.coef()[1] = 0;
|
||||
for (int i = 1; i <= degree; ++i)
|
||||
{
|
||||
poles.coef()[2 * i] = poly.coef()[i] * ((i & 1) ? -1 : 1);
|
||||
poles.coef()[2 * i + 1] = 0;
|
||||
}
|
||||
poles.solve(degree);
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < degree; ++i) { if (poles.root()[i].real() <= 0) { poles.root()[j++] = poles.root()[i]; } }
|
||||
// sort descending imag() and cut degree in half
|
||||
poles.sort(degree / 2);
|
||||
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
complex_t c = poles.root()[i];
|
||||
addPoleZeroConjugatePairs(c, infinity());
|
||||
}
|
||||
|
||||
if (numPoles & 1) { add(poles.root()[pairs].real(), infinity()); }
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void LowPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
LowPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void HighPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double cutoffFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
HighPassTransform(cutoffFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandPassBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
BandPassTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
|
||||
void BandStopBase::setup(int order,
|
||||
double sampleRate,
|
||||
double centerFrequency,
|
||||
double widthFrequency,
|
||||
WorkspaceBase* w)
|
||||
{
|
||||
m_analogProto.design(order, w);
|
||||
|
||||
BandStopTransform(centerFrequency / sampleRate,
|
||||
widthFrequency / sampleRate,
|
||||
m_digitalProto,
|
||||
m_analogProto);
|
||||
|
||||
setLayout(m_digitalProto);
|
||||
}
|
||||
} // namespace Legendre
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,320 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Cascade.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
#include "PoleFilter.h"
|
||||
#include "RootFinder.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filters with Legendre / "Optimum-L" response characteristics
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Legendre
|
||||
{
|
||||
|
||||
// Numerical computation of Legendre "Optimum-L" polynomials
|
||||
|
||||
class PolynomialFinderBase
|
||||
{
|
||||
public:
|
||||
void solve(int n);
|
||||
|
||||
double* coef() { return m_w; }
|
||||
|
||||
private:
|
||||
void legendre(double* p, int n);
|
||||
|
||||
protected:
|
||||
int m_maxN = 0;
|
||||
double* m_w = nullptr;
|
||||
double* m_a = nullptr;
|
||||
double* m_p = nullptr;
|
||||
double* m_s = nullptr;
|
||||
double* m_v = nullptr;
|
||||
double* m_aa = nullptr;
|
||||
double* m_bb = nullptr;
|
||||
};
|
||||
|
||||
template <int maxN>
|
||||
class PolynomialFinder : public PolynomialFinderBase
|
||||
{
|
||||
public:
|
||||
PolynomialFinder()
|
||||
{
|
||||
m_maxN = maxN;
|
||||
m_w = m_ws;
|
||||
m_a = m_as;
|
||||
m_p = m_ps;
|
||||
m_s = m_ss;
|
||||
m_v = m_vs;
|
||||
m_aa = m_aas;
|
||||
m_bb = m_bbs;
|
||||
}
|
||||
|
||||
void solve(int n)
|
||||
{
|
||||
assert(n <= maxN);
|
||||
PolynomialFinderBase::solve(n);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_ws [2 * maxN + 1];
|
||||
double m_as [ maxN + 1];
|
||||
double m_ps [2 * maxN + 1];
|
||||
double m_ss [2 * maxN + 1];
|
||||
double m_vs [2 * maxN + 4];
|
||||
double m_aas [ maxN + 1];
|
||||
double m_bbs [ maxN + 1];
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// A Workspace is necessary to construct the polynomial and find its roots
|
||||
|
||||
struct WorkspaceBase
|
||||
{
|
||||
WorkspaceBase(PolynomialFinderBase* polyBase, RootFinderBase* rootsBase) : poly(*polyBase), roots(*rootsBase) { }
|
||||
|
||||
PolynomialFinderBase& poly;
|
||||
RootFinderBase& roots;
|
||||
|
||||
private:
|
||||
WorkspaceBase(WorkspaceBase&);
|
||||
WorkspaceBase& operator=(WorkspaceBase&);
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct Workspace : WorkspaceBase
|
||||
{
|
||||
Workspace() : WorkspaceBase(&m_poly, &m_roots) { }
|
||||
|
||||
private:
|
||||
PolynomialFinder<MaxOrder> m_poly;
|
||||
RootFinder<MaxOrder * 2> m_roots;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Half-band analog prototypes (s-plane)
|
||||
|
||||
class AnalogLowPass : public LayoutBase
|
||||
{
|
||||
public:
|
||||
AnalogLowPass();
|
||||
|
||||
void design(int numPoles, WorkspaceBase* w);
|
||||
|
||||
private:
|
||||
int m_numPoles = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
struct LowPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct HighPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct BandPassBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
struct BandStopBase : PoleFilterBase<AnalogLowPass>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency, WorkspaceBase* w);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : PoleFilter<LowPassBase, MaxOrder>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
LowPassBase::setup(order, sampleRate, cutoffFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : PoleFilter<HighPassBase, MaxOrder>
|
||||
{
|
||||
void setup(int order, double sampleRate, double cutoffFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
HighPassBase::setup(order, sampleRate, cutoffFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : PoleFilter<BandPassBase, MaxOrder, MaxOrder * 2>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
BandPassBase::setup(order, sampleRate, centerFrequency, widthFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : PoleFilter<BandStopBase, MaxOrder, MaxOrder * 2>
|
||||
{
|
||||
void setup(int order, double sampleRate, double centerFrequency, double widthFrequency)
|
||||
{
|
||||
Workspace<MaxOrder> w;
|
||||
BandStopBase::setup(order, sampleRate, centerFrequency, widthFrequency, &w);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 3
|
||||
};
|
||||
|
||||
static int getNumParams() { return 3; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthHzParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(int(params[1]), params[0], params[2], params[3]); }
|
||||
};
|
||||
|
||||
// Factored kind and name
|
||||
|
||||
struct LowPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "Legendre Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Legendre High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPassDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Legendre Band Pass"; }
|
||||
};
|
||||
|
||||
struct BandStopDescription
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "Legendre Band Stop"; }
|
||||
};
|
||||
|
||||
// This glues on the Order parameter
|
||||
template <int MaxOrder, template <class> class TypeClass, template <int> class FilterClass>
|
||||
struct OrderBase : TypeClass<FilterClass<MaxOrder>>
|
||||
{
|
||||
ParamInfo getParamInfo_1() const
|
||||
{
|
||||
return ParamInfo(idOrder, "Order", "Order", 1, MaxOrder, 2, &ParamInfo::Int_toControlValue, &ParamInfo::Int_toNativeValue,
|
||||
&ParamInfo::Int_toString);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Design filters
|
||||
//
|
||||
|
||||
template <int MaxOrder>
|
||||
struct LowPass : OrderBase<MaxOrder, TypeI, Legendre::LowPass>, LowPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct HighPass : OrderBase<MaxOrder, TypeI, Legendre::HighPass>, HighPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandPass : OrderBase<MaxOrder, TypeII, Legendre::BandPass>, BandPassDescription {};
|
||||
|
||||
template <int MaxOrder>
|
||||
struct BandStop : OrderBase<MaxOrder, TypeII, Legendre::BandStop>, BandStopDescription {};
|
||||
} // namespace Design
|
||||
} // namespace Legendre
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,118 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_MATHSUPPLEMENT_H
|
||||
#define DSPFILTERS_MATHSUPPLEMENT_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
const double doublePi = 3.1415926535897932384626433832795028841971;
|
||||
const double doublePi_2 = 1.5707963267948966192313216916397514420986;
|
||||
const double doubleLn2 = 0.69314718055994530941723212145818;//?????
|
||||
const double doubleLn10 = 2.3025850929940456840179914546844;//??????
|
||||
|
||||
typedef std::complex<double> complex_t;
|
||||
typedef std::pair<complex_t, complex_t> complex_pair_t;
|
||||
|
||||
template <typename Real>
|
||||
std::complex<Real> solve_quadratic_1(Real a, Real b, Real c) { return (-b + sqrt(std::complex<Real>(b * b - 4 * a * c, 0))) / (2. * a); }
|
||||
|
||||
template <typename Real>
|
||||
std::complex<Real> solve_quadratic_2(Real a, Real b, Real c) { return (-b - sqrt(std::complex<Real>(b * b - 4 * a * c, 0))) / (2. * a); }
|
||||
|
||||
inline complex_t infinity() { return complex_t(std::numeric_limits<double>::infinity()); }
|
||||
|
||||
inline complex_t adjust_imag(const complex_t& c)
|
||||
{
|
||||
if (fabs(c.imag()) < 1e-30) { return complex_t(c.real(), 0); }
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename Ty, typename To>
|
||||
std::complex<Ty> addmul(const std::complex<Ty>& c, Ty v, const std::complex<To>& c1)
|
||||
{
|
||||
return std::complex<Ty>(c.real() + v * c1.real(), c.imag() + v * c1.imag());
|
||||
}
|
||||
|
||||
template <typename Ty>
|
||||
std::complex<Ty> recip(const std::complex<Ty>& c)
|
||||
{
|
||||
Ty n = 1.0 / std::norm(c);
|
||||
|
||||
return std::complex<Ty>(n * c.real(), n * c.imag());
|
||||
}
|
||||
|
||||
template <typename Ty>
|
||||
Ty asinh(Ty x) { return log(x + std::sqrt(x * x + 1)); }
|
||||
|
||||
template <typename Ty>
|
||||
Ty acosh(Ty x) { return log(x + std::sqrt(x * x - 1)); }
|
||||
|
||||
template <typename Ty>
|
||||
bool is_nan(Ty v) { return !(v == v); }
|
||||
|
||||
template <>
|
||||
inline bool is_nan<complex_t>(complex_t v) { return is_nan(v.real()) || is_nan(v.imag()); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Hack to prevent denormals
|
||||
*
|
||||
*/
|
||||
|
||||
//const double anti_denormal_vsa = 1e-16; // doesn't prevent denormals
|
||||
//const double anti_denormal_vsa = 0;
|
||||
const double anti_denormal_vsa = 1e-8;
|
||||
|
||||
class DenormalPrevention
|
||||
{
|
||||
public:
|
||||
DenormalPrevention() : m_v(anti_denormal_vsa) { }
|
||||
|
||||
// small alternating current
|
||||
double ac() { return m_v = -m_v; }
|
||||
|
||||
// small direct current
|
||||
static double dc() { return anti_denormal_vsa; }
|
||||
|
||||
private:
|
||||
double m_v = 0;
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,217 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Design.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
ParamInfo::ParamInfo() { throw std::logic_error("invalid usage of ParamInfo"); }
|
||||
|
||||
double ParamInfo::clamp(double nativeValue) const
|
||||
{
|
||||
const double minValue = toNativeValue(0);
|
||||
const double maxValue = toNativeValue(1);
|
||||
if (nativeValue < minValue) { nativeValue = minValue; }
|
||||
else if (nativeValue > maxValue) { nativeValue = maxValue; }
|
||||
return nativeValue;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double ParamInfo::Int_toControlValue(double nativeValue) const { return (nativeValue - m_arg1) / (m_arg2 - m_arg1); }
|
||||
|
||||
double ParamInfo::Int_toNativeValue(double controlValue) const { return std::floor(m_arg1 + controlValue * (m_arg2 - m_arg1) + 0.5); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double ParamInfo::Real_toControlValue(double nativeValue) const { return (nativeValue - m_arg1) / (m_arg2 - m_arg1); }
|
||||
|
||||
double ParamInfo::Real_toNativeValue(double controlValue) const { return m_arg1 + controlValue * (m_arg2 - m_arg1); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double ParamInfo::Log_toControlValue(double nativeValue) const
|
||||
{
|
||||
const double base = 1.5;
|
||||
double l0 = log(m_arg1) / log(base);
|
||||
double l1 = log(m_arg2) / log(base);
|
||||
return (log(nativeValue) / log(base) - l0) / (l1 - l0);
|
||||
}
|
||||
|
||||
double ParamInfo::Log_toNativeValue(double controlValue) const
|
||||
{
|
||||
const double base = 1.5;
|
||||
double l0 = log(m_arg1) / log(base);
|
||||
double l1 = log(m_arg2) / log(base);
|
||||
return pow(base, l0 + controlValue * (l1 - l0));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double ParamInfo::Pow2_toControlValue(double nativeValue) const { return ((log(nativeValue) / log(2.)) - m_arg1) / (m_arg2 - m_arg1); }
|
||||
|
||||
double ParamInfo::Pow2_toNativeValue(double controlValue) const { return pow(2., (controlValue * (m_arg2 - m_arg1)) + m_arg1); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::string ParamInfo::Int_toString(double nativeValue) const { return std::to_string(int(nativeValue)); }
|
||||
|
||||
std::string ParamInfo::Hz_toString(double nativeValue) const { return (std::to_string(int(nativeValue)) + " Hz"); }
|
||||
|
||||
std::string ParamInfo::Real_toString(double nativeValue) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << std::fixed << std::setprecision(3) << nativeValue;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string ParamInfo::Db_toString(double nativeValue) const
|
||||
{
|
||||
const double af = fabs(nativeValue);
|
||||
int prec;
|
||||
if (af < 1) { prec = 3; }
|
||||
else if (af < 10) { prec = 2; }
|
||||
else { prec = 1; }
|
||||
std::ostringstream os;
|
||||
os << std::fixed << std::setprecision(prec) << nativeValue << " dB";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
ParamInfo ParamInfo::defaultSampleRateParam()
|
||||
{
|
||||
return ParamInfo(idSampleRate, "Fs", "Sample Rate", 11025, 192000, 44100, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Hz_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultCutoffFrequencyParam()
|
||||
{
|
||||
return ParamInfo(idFrequency, "Fc", "Cutoff Frequency", 10, 22040, 2000, &ParamInfo::Log_toControlValue, &ParamInfo::Log_toNativeValue,
|
||||
&ParamInfo::Hz_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultCenterFrequencyParam()
|
||||
{
|
||||
return ParamInfo(idFrequency, "Fc", "Center Frequency", 10, 22040, 2000, &ParamInfo::Log_toControlValue, &ParamInfo::Log_toNativeValue,
|
||||
&ParamInfo::Hz_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultQParam()
|
||||
{
|
||||
return ParamInfo(idQ, "Q", "Resonance", -4, 4, 1, &ParamInfo::Pow2_toControlValue, &ParamInfo::Pow2_toNativeValue, &ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultBandwidthParam()
|
||||
{
|
||||
return ParamInfo(idBandwidth, "BW", "Bandwidth (Octaves)", -4, 4, 1, &ParamInfo::Pow2_toControlValue, &ParamInfo::Pow2_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultBandwidthHzParam()
|
||||
{
|
||||
return ParamInfo(idBandwidthHz, "BW", "Bandwidth (Hz)", 10, 22040, 1720, &ParamInfo::Log_toControlValue, &ParamInfo::Log_toNativeValue,
|
||||
&ParamInfo::Hz_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultGainParam()
|
||||
{
|
||||
return ParamInfo(idGain, "Gain", "Gain", -24, 24, -6, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue, &ParamInfo::Db_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultSlopeParam()
|
||||
{
|
||||
return ParamInfo(idSlope, "Slope", "Slope", -2, 2, 1, &ParamInfo::Pow2_toControlValue, &ParamInfo::Pow2_toNativeValue, &ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultRippleDbParam()
|
||||
{
|
||||
return ParamInfo(idRippleDb, "Ripple", "Ripple dB", 0.001, 12, 0.01, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Db_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultStopDbParam()
|
||||
{
|
||||
return ParamInfo(idStopDb, "Stop", "Stopband dB", 3, 60, 48, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue, &ParamInfo::Db_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultRolloffParam()
|
||||
{
|
||||
return ParamInfo(idRolloff, "W", "Transition Width", -16, 4, 0, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultPoleRhoParam()
|
||||
{
|
||||
return ParamInfo(idPoleRho, "Pd", "Pole Distance", 0, 1, 0.5, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultPoleThetaParam()
|
||||
{
|
||||
return ParamInfo(idPoleTheta, "Pa", "Pole Angle", 0, doublePi, doublePi / 2, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultZeroRhoParam()
|
||||
{
|
||||
return ParamInfo(idZeroRho, "Pd", "Zero Distance", 0, 1, 0.5, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultZeroThetaParam()
|
||||
{
|
||||
return ParamInfo(idZeroTheta, "Pa", "Zero Angle", 0, doublePi, doublePi / 2, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultPoleRealParam()
|
||||
{
|
||||
return ParamInfo(idPoleReal, "A1", "Pole Real", -1, 1, 0.25, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
|
||||
ParamInfo ParamInfo::defaultZeroRealParam()
|
||||
{
|
||||
return ParamInfo(idZeroReal, "B1", "Zero Real", -1, 1, -0.25, &ParamInfo::Real_toControlValue, &ParamInfo::Real_toNativeValue,
|
||||
&ParamInfo::Real_toString);
|
||||
}
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,194 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_PARAMS_H
|
||||
#define DSPFILTERS_PARAMS_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Types.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* System for abstracting parameterizable filter specifications.
|
||||
*
|
||||
* This provides a "GUI-friendly" layer to the filters. Note that
|
||||
* it is not necessary to use this layer, it is possible to instantiate
|
||||
* the filters and their associated processing state directly,
|
||||
* and bypass the overhead for this API if it is not needed.
|
||||
*
|
||||
*/
|
||||
|
||||
// Unique IDs to help identify parameters
|
||||
enum ParamID
|
||||
{
|
||||
idSampleRate,
|
||||
idFrequency,
|
||||
idQ,
|
||||
idBandwidth,
|
||||
idBandwidthHz,
|
||||
idGain,
|
||||
idSlope,
|
||||
idOrder,
|
||||
idRippleDb,
|
||||
idStopDb,
|
||||
idRolloff,
|
||||
|
||||
idPoleRho,
|
||||
idPoleTheta,
|
||||
idZeroRho,
|
||||
idZeroTheta,
|
||||
|
||||
idPoleReal,
|
||||
idZeroReal
|
||||
};
|
||||
|
||||
enum { maxParameters = 8 };
|
||||
|
||||
struct Params
|
||||
{
|
||||
void clear() { for (int i = 0; i < maxParameters; ++i) { value[i] = 0; } }
|
||||
|
||||
double& operator[](int index) { return value[index]; }
|
||||
|
||||
const double& operator[](int index) const { return value[index]; }
|
||||
|
||||
double value[maxParameters];
|
||||
};
|
||||
|
||||
//
|
||||
// Provides meta-information about a filter parameter
|
||||
// to achieve run-time introspection.
|
||||
//
|
||||
class ParamInfo
|
||||
{
|
||||
public:
|
||||
typedef double (ParamInfo::*toControlValue_t)(double) const;
|
||||
typedef double (ParamInfo::*toNativeValue_t)(double) const;
|
||||
typedef std::string (ParamInfo::*toString_t)(double) const;
|
||||
|
||||
// dont use this one
|
||||
ParamInfo(); // throws std::logic_error
|
||||
|
||||
ParamInfo(ParamID id, const char* szLabel, const char* szName, const double arg1, const double arg2, const double defaultNativeValue,
|
||||
const toControlValue_t toControlValueProc, const toNativeValue_t toNativeValueProc, const toString_t toStringProc)
|
||||
: m_id(id), m_szLabel(szLabel), m_szName(szName), m_arg1(arg1), m_arg2(arg2), m_defaultNativeValue(defaultNativeValue),
|
||||
m_toControlValue(toControlValueProc), m_toNativeValue(toNativeValueProc), m_toString(toStringProc) { }
|
||||
|
||||
// Used to identify well-known parameters (like cutoff frequency)
|
||||
ParamID getId() const { return m_id; }
|
||||
|
||||
// Returns a short label suitable for placement on a control
|
||||
const char* getLabel() const { return m_szLabel; }
|
||||
|
||||
// Returns the full name
|
||||
const char* getName() const { return m_szName; }
|
||||
|
||||
double getDefaultValue() const { return m_defaultNativeValue; }
|
||||
|
||||
//
|
||||
// Control value is always in the range [0..1]
|
||||
//
|
||||
double toControlValue(double nativeValue) const { return (this->*m_toControlValue)(nativeValue); }
|
||||
|
||||
//
|
||||
// Native value is in filter-specific units. For example,
|
||||
// cutoff frequency would probably be in Hertz.
|
||||
//
|
||||
double toNativeValue(double controlValue) const { return (this->*m_toNativeValue)(controlValue); }
|
||||
|
||||
std::string toString(double nativeValue) const { return (this->*m_toString)(nativeValue); }
|
||||
|
||||
double clamp(double nativeValue) const;
|
||||
|
||||
//
|
||||
// These routines are used as function pointers when
|
||||
// constructing the various ParamInfo used by filters
|
||||
//
|
||||
|
||||
double Int_toControlValue(double nativeValue) const;
|
||||
double Int_toNativeValue(double controlValue) const;
|
||||
|
||||
double Real_toControlValue(double nativeValue) const;
|
||||
double Real_toNativeValue(double controlValue) const;
|
||||
|
||||
double Log_toControlValue(double nativeValue) const;
|
||||
double Log_toNativeValue(double controlValue) const;
|
||||
|
||||
double Pow2_toControlValue(double nativeValue) const;
|
||||
double Pow2_toNativeValue(double controlValue) const;
|
||||
|
||||
std::string Int_toString(double nativeValue) const;
|
||||
std::string Hz_toString(double nativeValue) const;
|
||||
std::string Real_toString(double nativeValue) const;
|
||||
std::string Db_toString(double nativeValue) const;
|
||||
|
||||
//
|
||||
// Creates the specified ParamInfo
|
||||
//
|
||||
|
||||
static ParamInfo defaultSampleRateParam();
|
||||
static ParamInfo defaultCutoffFrequencyParam();
|
||||
static ParamInfo defaultCenterFrequencyParam();
|
||||
static ParamInfo defaultQParam();
|
||||
static ParamInfo defaultBandwidthParam();
|
||||
static ParamInfo defaultBandwidthHzParam();
|
||||
static ParamInfo defaultGainParam();
|
||||
static ParamInfo defaultSlopeParam();
|
||||
static ParamInfo defaultRippleDbParam();
|
||||
static ParamInfo defaultStopDbParam();
|
||||
static ParamInfo defaultRolloffParam();
|
||||
static ParamInfo defaultPoleRhoParam();
|
||||
static ParamInfo defaultPoleThetaParam();
|
||||
static ParamInfo defaultZeroRhoParam();
|
||||
static ParamInfo defaultZeroThetaParam();
|
||||
static ParamInfo defaultPoleRealParam();
|
||||
static ParamInfo defaultZeroRealParam();
|
||||
|
||||
private:
|
||||
ParamID m_id;
|
||||
const char* m_szLabel = nullptr;
|
||||
const char* m_szName = nullptr;
|
||||
double m_arg1 = 0;
|
||||
double m_arg2 = 0;
|
||||
double m_defaultNativeValue = 0;
|
||||
toControlValue_t m_toControlValue;
|
||||
toNativeValue_t m_toNativeValue;
|
||||
toString_t m_toString;
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "PoleFilter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
complex_t LowPassTransform::transform(complex_t c) const
|
||||
{
|
||||
if (c == infinity()) { return complex_t(-1, 0); }
|
||||
|
||||
// frequency transform
|
||||
c = f * c;
|
||||
|
||||
// bilinear low pass transform
|
||||
return (1. + c) / (1. - c);
|
||||
}
|
||||
|
||||
LowPassTransform::LowPassTransform(double fc, LayoutBase& digital, LayoutBase const& analog)
|
||||
{
|
||||
digital.reset();
|
||||
|
||||
// prewarp
|
||||
f = tan(doublePi * fc);
|
||||
|
||||
const int numPoles = analog.getNumPoles();
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[i];
|
||||
digital.addPoleZeroConjugatePairs(transform(pair.poles.first), transform(pair.zeros.first));
|
||||
}
|
||||
|
||||
if (numPoles & 1)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[pairs];
|
||||
digital.add(transform(pair.poles.first), transform(pair.zeros.first));
|
||||
}
|
||||
|
||||
digital.setNormal(analog.getNormalW(), analog.getNormalGain());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
complex_t HighPassTransform::transform(complex_t c) const
|
||||
{
|
||||
if (c == infinity()) { return complex_t(1, 0); }
|
||||
|
||||
// frequency transform
|
||||
c = f * c;
|
||||
|
||||
// bilinear high pass transform
|
||||
return - (1. + c) / (1. - c);
|
||||
}
|
||||
|
||||
HighPassTransform::HighPassTransform(double fc, LayoutBase& digital, LayoutBase const& analog)
|
||||
{
|
||||
digital.reset();
|
||||
|
||||
// prewarp
|
||||
f = 1. / tan(doublePi * fc);
|
||||
|
||||
const int numPoles = analog.getNumPoles();
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[i];
|
||||
digital.addPoleZeroConjugatePairs(transform(pair.poles.first), transform(pair.zeros.first));
|
||||
}
|
||||
|
||||
if (numPoles & 1)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[pairs];
|
||||
digital.add(transform(pair.poles.first), transform(pair.zeros.first));
|
||||
}
|
||||
|
||||
digital.setNormal(doublePi - analog.getNormalW(), analog.getNormalGain());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BandPassTransform::BandPassTransform(double fc, double fw, LayoutBase& digital, LayoutBase const& analog)
|
||||
{
|
||||
// handle degenerate cases efficiently
|
||||
// THIS DOESNT WORK because the cascade states won't match
|
||||
#if 0
|
||||
const double fw_2 = fw / 2;
|
||||
if (fc - fw_2 < 0) { LowPassTransform::transform (fc + fw_2, digital, analog); }
|
||||
else if (fc + fw_2 >= 0.5) { HighPassTransform::transform (fc - fw_2, digital, analog); }
|
||||
else
|
||||
#endif
|
||||
digital.reset();
|
||||
|
||||
const double ww = 2 * doublePi * fw;
|
||||
|
||||
// pre-calcs
|
||||
wc2 = 2 * doublePi * fc - (ww / 2);
|
||||
wc = wc2 + ww;
|
||||
|
||||
// what is this crap?
|
||||
if (wc2 < 1e-8) { wc2 = 1e-8; }
|
||||
if (wc > doublePi - 1e-8) { wc = doublePi - 1e-8; }
|
||||
|
||||
a = cos((wc + wc2) * 0.5) /
|
||||
cos((wc - wc2) * 0.5);
|
||||
b = 1 / tan((wc - wc2) * 0.5);
|
||||
a2 = a * a;
|
||||
b2 = b * b;
|
||||
ab = a * b;
|
||||
ab_2 = 2 * ab;
|
||||
|
||||
const int numPoles = analog.getNumPoles();
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[i];
|
||||
ComplexPair p1 = transform(pair.poles.first);
|
||||
const ComplexPair z1 = transform(pair.zeros.first);
|
||||
|
||||
//
|
||||
// Optimize out the calculations for conjugates for Release builds
|
||||
//
|
||||
#ifndef NDEBUG
|
||||
ComplexPair p2 = transform(pair.poles.second);
|
||||
assert(p2.first == std::conj (p1.first));
|
||||
assert(p2.second == std::conj (p1.second));
|
||||
#endif
|
||||
|
||||
digital.addPoleZeroConjugatePairs(p1.first, z1.first);
|
||||
digital.addPoleZeroConjugatePairs(p1.second, z1.second);
|
||||
}
|
||||
|
||||
if (numPoles & 1)
|
||||
{
|
||||
const ComplexPair poles = transform(analog[pairs].poles.first);
|
||||
const ComplexPair zeros = transform(analog[pairs].zeros.first);
|
||||
|
||||
digital.add(poles, zeros);
|
||||
}
|
||||
|
||||
const double wn = analog.getNormalW();
|
||||
digital.setNormal(2 * atan(sqrt(tan((wc + wn) * 0.5) * tan((wc2 + wn) * 0.5))), analog.getNormalGain());
|
||||
}
|
||||
|
||||
ComplexPair BandPassTransform::transform(complex_t c) const
|
||||
{
|
||||
if (c == infinity()) { return ComplexPair(-1, 1); }
|
||||
|
||||
c = (1. + c) / (1. - c); // bilinear
|
||||
|
||||
complex_t v = 0;
|
||||
v = addmul(v, 4 * (b2 * (a2 - 1) + 1), c);
|
||||
v += 8 * (b2 * (a2 - 1) - 1);
|
||||
v *= c;
|
||||
v += 4 * (b2 * (a2 - 1) + 1);
|
||||
v = std::sqrt(v);
|
||||
|
||||
complex_t u = -v;
|
||||
u = addmul(u, ab_2, c);
|
||||
u += ab_2;
|
||||
|
||||
v = addmul(v, ab_2, c);
|
||||
v += ab_2;
|
||||
|
||||
complex_t d = 0;
|
||||
d = addmul(d, 2 * (b - 1), c) + 2 * (1 + b);
|
||||
|
||||
return ComplexPair(u / d, v / d);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
BandStopTransform::BandStopTransform(double fc, double fw, LayoutBase& digital, LayoutBase const& analog)
|
||||
{
|
||||
digital.reset();
|
||||
|
||||
const double ww = 2 * doublePi * fw;
|
||||
|
||||
wc2 = 2 * doublePi * fc - (ww / 2);
|
||||
wc = wc2 + ww;
|
||||
|
||||
// this is crap
|
||||
if (wc2 < 1e-8) { wc2 = 1e-8; }
|
||||
if (wc > doublePi - 1e-8) { wc = doublePi - 1e-8; }
|
||||
|
||||
a = cos((wc + wc2) * .5) /
|
||||
cos((wc - wc2) * .5);
|
||||
b = tan((wc - wc2) * .5);
|
||||
a2 = a * a;
|
||||
b2 = b * b;
|
||||
|
||||
const int numPoles = analog.getNumPoles();
|
||||
const int pairs = numPoles / 2;
|
||||
for (int i = 0; i < pairs; ++i)
|
||||
{
|
||||
const PoleZeroPair& pair = analog[i];
|
||||
const ComplexPair p = transform(pair.poles.first);
|
||||
ComplexPair z = transform(pair.zeros.first);
|
||||
|
||||
//
|
||||
// Optimize out the calculations for conjugates for Release builds
|
||||
//
|
||||
// trick to get the conjugate
|
||||
if (z.second == z.first) { z.second = std::conj(z.first); }
|
||||
|
||||
digital.addPoleZeroConjugatePairs(p.first, z.first);
|
||||
digital.addPoleZeroConjugatePairs(p.second, z.second);
|
||||
}
|
||||
|
||||
if (numPoles & 1)
|
||||
{
|
||||
const ComplexPair poles = transform(analog[pairs].poles.first);
|
||||
const ComplexPair zeros = transform(analog[pairs].zeros.first);
|
||||
|
||||
digital.add(poles, zeros);
|
||||
}
|
||||
|
||||
if (fc < 0.25) { digital.setNormal(doublePi, analog.getNormalGain()); }
|
||||
else { digital.setNormal(0, analog.getNormalGain()); }
|
||||
}
|
||||
|
||||
ComplexPair BandStopTransform::transform(complex_t c) const
|
||||
{
|
||||
if (c == infinity()) { c = -1; }
|
||||
else { c = (1. + c) / (1. - c); }// bilinear
|
||||
|
||||
complex_t u(0);
|
||||
u = addmul(u, 4 * (b2 + a2 - 1), c);
|
||||
u += 8 * (b2 - a2 + 1);
|
||||
u *= c;
|
||||
u += 4 * (a2 + b2 - 1);
|
||||
u = std::sqrt(u);
|
||||
|
||||
complex_t v = u * -.5;
|
||||
v += a;
|
||||
v = addmul(v, -a, c);
|
||||
|
||||
u *= .5;
|
||||
u += a;
|
||||
u = addmul(u, -a, c);
|
||||
|
||||
complex_t d(b + 1);
|
||||
d = addmul(d, b - 1, c);
|
||||
|
||||
return ComplexPair(u / d, v / d);
|
||||
}
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,200 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
#include "Cascade.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Base for filters designed via algorithmic placement of poles and zeros.
|
||||
*
|
||||
* Typically, the filter is first designed as a half-band low pass or
|
||||
* low shelf analog filter (s-plane). Then, using a transformation such
|
||||
* as the ones from Constantinides, the poles and zeros of the analog filter
|
||||
* are calculated in the z-plane.
|
||||
*
|
||||
*/
|
||||
|
||||
// Factored implementations to reduce template instantiations
|
||||
|
||||
class PoleFilterBase2 : public Cascade
|
||||
{
|
||||
public:
|
||||
// This gets the poles/zeros directly from the digital
|
||||
// prototype. It is used to double check the correctness
|
||||
// of the recovery of pole/zeros from biquad coefficients.
|
||||
//
|
||||
// It can also be used to accelerate the interpolation
|
||||
// of pole/zeros for parameter modulation, since a pole
|
||||
// filter already has them calculated
|
||||
|
||||
#if 1
|
||||
// Commenting this out will pass the call to the Cascade,
|
||||
// which tries to compute the poles and zeros from the biquad
|
||||
// coefficients.
|
||||
std::vector<PoleZeroPair> getPoleZeros() const
|
||||
{
|
||||
std::vector<PoleZeroPair> vpz;
|
||||
const int pairs = (m_digitalProto.getNumPoles() + 1) / 2;
|
||||
for (int i = 0; i < pairs; ++i) { vpz.push_back(m_digitalProto[i]); }
|
||||
return vpz;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
LayoutBase m_digitalProto;
|
||||
};
|
||||
|
||||
// Serves a container to hold the analog prototype
|
||||
// and the digital pole/zero layout.
|
||||
template <class AnalogPrototype>
|
||||
class PoleFilterBase : public PoleFilterBase2
|
||||
{
|
||||
protected:
|
||||
void setPrototypeStorage(const LayoutBase& analogStorage, const LayoutBase& digitalStorage)
|
||||
{
|
||||
m_analogProto.setStorage(analogStorage);
|
||||
m_digitalProto = digitalStorage;
|
||||
}
|
||||
|
||||
AnalogPrototype m_analogProto;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Storage for pole filters
|
||||
template <class BaseClass,
|
||||
int MaxAnalogPoles,
|
||||
int MaxDigitalPoles = MaxAnalogPoles>
|
||||
struct PoleFilter : BaseClass
|
||||
, CascadeStages<(MaxDigitalPoles + 1) / 2>
|
||||
{
|
||||
PoleFilter()
|
||||
{
|
||||
// This glues together the factored base classes
|
||||
// with the templatized storage classes.
|
||||
BaseClass::setCascadeStorage(this->getCascadeStorage());
|
||||
BaseClass::setPrototypeStorage(m_analogStorage, m_digitalStorage);
|
||||
}
|
||||
|
||||
private:
|
||||
Layout<MaxAnalogPoles> m_analogStorage;
|
||||
Layout<MaxDigitalPoles> m_digitalStorage;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* s-plane to z-plane transforms
|
||||
*
|
||||
* For pole filters, an analog prototype is created via placement of
|
||||
* poles and zeros in the s-plane. The analog prototype is either
|
||||
* a halfband low pass or a halfband low shelf. The poles, zeros,
|
||||
* and normalization parameters are transformed into the z-plane
|
||||
* using variants of the bilinear transformation.
|
||||
*
|
||||
*/
|
||||
|
||||
// low pass to low pass
|
||||
class LowPassTransform
|
||||
{
|
||||
public:
|
||||
LowPassTransform(double fc, LayoutBase& digital, LayoutBase const& analog);
|
||||
|
||||
private:
|
||||
complex_t transform(complex_t c) const;
|
||||
|
||||
double f = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// low pass to high pass
|
||||
class HighPassTransform
|
||||
{
|
||||
public:
|
||||
HighPassTransform(double fc, LayoutBase& digital, LayoutBase const& analog);
|
||||
|
||||
private:
|
||||
complex_t transform(complex_t c) const;
|
||||
|
||||
double f = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// low pass to band pass transform
|
||||
class BandPassTransform
|
||||
{
|
||||
public:
|
||||
BandPassTransform(double fc, double fw, LayoutBase& digital, LayoutBase const& analog);
|
||||
|
||||
private:
|
||||
ComplexPair transform(complex_t c) const;
|
||||
|
||||
double wc = 0;
|
||||
double wc2 = 0;
|
||||
double a = 0;
|
||||
double b = 0;
|
||||
double a2 = 0;
|
||||
double b2 = 0;
|
||||
double ab = 0;
|
||||
double ab_2 = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// low pass to band stop transform
|
||||
class BandStopTransform
|
||||
{
|
||||
public:
|
||||
BandStopTransform(double fc, double fw, LayoutBase& digital, LayoutBase const& analog);
|
||||
|
||||
private:
|
||||
ComplexPair transform(complex_t c) const;
|
||||
|
||||
double wc = 0;
|
||||
double wc2 = 0;
|
||||
double a = 0;
|
||||
double b = 0;
|
||||
double a2 = 0;
|
||||
double b2 = 0;
|
||||
};
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,205 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "RBJ.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
namespace RBJ
|
||||
{
|
||||
void LowPass::setup(double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double q)
|
||||
{
|
||||
double w0 = 2 * doublePi * cutoffFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * q);
|
||||
double b0 = (1 - cs) / 2;
|
||||
double b1 = 1 - cs;
|
||||
double b2 = (1 - cs) / 2;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void HighPass::setup(double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double q)
|
||||
{
|
||||
double w0 = 2 * doublePi * cutoffFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * q);
|
||||
double b0 = (1 + cs) / 2;
|
||||
double b1 = -(1 + cs);
|
||||
double b2 = (1 + cs) / 2;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BandPass1::setup(double sampleRate,
|
||||
double centerFrequency,
|
||||
double bandWidth)
|
||||
{
|
||||
double w0 = 2 * doublePi * centerFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * bandWidth);
|
||||
double b0 = bandWidth * AL;// sn / 2;
|
||||
double b1 = 0;
|
||||
double b2 = -bandWidth * AL;//-sn / 2;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BandPass2::setup(double sampleRate,
|
||||
double centerFrequency,
|
||||
double bandWidth)
|
||||
{
|
||||
double w0 = 2 * doublePi * centerFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * bandWidth);
|
||||
double b0 = AL;
|
||||
double b1 = 0;
|
||||
double b2 = -AL;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BandStop::setup(double sampleRate,
|
||||
double centerFrequency,
|
||||
double bandWidth)
|
||||
{
|
||||
double w0 = 2 * doublePi * centerFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * bandWidth);
|
||||
double b0 = 1;
|
||||
double b1 = -2 * cs;
|
||||
double b2 = 1;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void LowShelf::setup(double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double gainDb,
|
||||
double shelfSlope)
|
||||
{
|
||||
double A = pow(10, gainDb / 40);
|
||||
double w0 = 2 * doublePi * cutoffFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / 2 * std::sqrt((A + 1 / A) * (1 / shelfSlope - 1) + 2);
|
||||
double sq = 2 * sqrt(A) * AL;
|
||||
double b0 = A * ((A + 1) - (A - 1) * cs + sq);
|
||||
double b1 = 2 * A * ((A - 1) - (A + 1) * cs);
|
||||
double b2 = A * ((A + 1) - (A - 1) * cs - sq);
|
||||
double a0 = (A + 1) + (A - 1) * cs + sq;
|
||||
double a1 = -2 * ((A - 1) + (A + 1) * cs);
|
||||
double a2 = (A + 1) + (A - 1) * cs - sq;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void HighShelf::setup(double sampleRate,
|
||||
double cutoffFrequency,
|
||||
double gainDb,
|
||||
double shelfSlope)
|
||||
{
|
||||
double A = pow(10, gainDb / 40);
|
||||
double w0 = 2 * doublePi * cutoffFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / 2 * std::sqrt((A + 1 / A) * (1 / shelfSlope - 1) + 2);
|
||||
double sq = 2 * sqrt(A) * AL;
|
||||
double b0 = A * ((A + 1) + (A - 1) * cs + sq);
|
||||
double b1 = -2 * A * ((A - 1) + (A + 1) * cs);
|
||||
double b2 = A * ((A + 1) + (A - 1) * cs - sq);
|
||||
double a0 = (A + 1) - (A - 1) * cs + sq;
|
||||
double a1 = 2 * ((A - 1) - (A + 1) * cs);
|
||||
double a2 = (A + 1) - (A - 1) * cs - sq;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void BandShelf::setup(double sampleRate,
|
||||
double centerFrequency,
|
||||
double gainDb,
|
||||
double bandWidth)
|
||||
{
|
||||
double A = pow(10, gainDb / 40);
|
||||
double w0 = 2 * doublePi * centerFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn * sinh(doubleLn2 / 2 * bandWidth * w0 / sn);
|
||||
assert(!Dsp::is_nan (AL));
|
||||
double b0 = 1 + AL * A;
|
||||
double b1 = -2 * cs;
|
||||
double b2 = 1 - AL * A;
|
||||
double a0 = 1 + AL / A;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL / A;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
|
||||
void AllPass::setup(double sampleRate,
|
||||
double phaseFrequency,
|
||||
double q)
|
||||
{
|
||||
double w0 = 2 * doublePi * phaseFrequency / sampleRate;
|
||||
double cs = cos(w0);
|
||||
double sn = sin(w0);
|
||||
double AL = sn / (2 * q);
|
||||
double b0 = 1 - AL;
|
||||
double b1 = -2 * cs;
|
||||
double b2 = 1 + AL;
|
||||
double a0 = 1 + AL;
|
||||
double a1 = -2 * cs;
|
||||
double a2 = 1 - AL;
|
||||
setCoefficients(a0, a1, a2, b0, b1, b2);
|
||||
}
|
||||
} // namespace RBJ
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,249 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_RBJ_H
|
||||
#define DSPFILTERS_RBJ_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Biquad.h"
|
||||
#include "Design.h"
|
||||
#include "Filter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Filter realizations based on Robert Bristol-Johnson formulae:
|
||||
*
|
||||
* http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
*
|
||||
*/
|
||||
|
||||
namespace RBJ
|
||||
{
|
||||
|
||||
//
|
||||
// Raw filters
|
||||
//
|
||||
|
||||
struct LowPass : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double cutoffFrequency, double q);
|
||||
};
|
||||
|
||||
struct HighPass : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double cutoffFrequency, double q);
|
||||
};
|
||||
|
||||
struct BandPass1 : BiquadBase
|
||||
{
|
||||
// (constant skirt gain, peak gain = Q)
|
||||
void setup(double sampleRate, double centerFrequency, double bandWidth);
|
||||
};
|
||||
|
||||
struct BandPass2 : BiquadBase
|
||||
{
|
||||
// (constant 0 dB peak gain)
|
||||
void setup(double sampleRate, double centerFrequency, double bandWidth);
|
||||
};
|
||||
|
||||
struct BandStop : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double centerFrequency, double bandWidth);
|
||||
};
|
||||
|
||||
struct LowShelf : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double cutoffFrequency, double gainDb, double shelfSlope);
|
||||
};
|
||||
|
||||
struct HighShelf : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double cutoffFrequency, double gainDb, double shelfSlope);
|
||||
};
|
||||
|
||||
struct BandShelf : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double centerFrequency, double gainDb, double bandWidth);
|
||||
};
|
||||
|
||||
struct AllPass : BiquadBase
|
||||
{
|
||||
void setup(double sampleRate, double phaseFrequency, double q);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Gui-friendly Design layer
|
||||
//
|
||||
|
||||
namespace Design
|
||||
{
|
||||
struct TypeIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 3
|
||||
};
|
||||
|
||||
static int getNumParams() { return 3; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultQParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeI : TypeIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(params[0], params[1], params[2]); }
|
||||
};
|
||||
|
||||
struct TypeIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 3
|
||||
};
|
||||
|
||||
static int getNumParams() { return 3; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultBandwidthParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeII : TypeIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(params[0], params[1], params[2]); }
|
||||
};
|
||||
|
||||
struct TypeIIIBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultCutoffFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultSlopeParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIII : TypeIIIBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(params[0], params[1], params[2], params[3]); }
|
||||
};
|
||||
|
||||
struct TypeIVBase : DesignBase
|
||||
{
|
||||
enum
|
||||
{
|
||||
NumParams = 4
|
||||
};
|
||||
|
||||
static int getNumParams() { return 4; }
|
||||
static ParamInfo getParamInfo_1() { return ParamInfo::defaultCenterFrequencyParam(); }
|
||||
static ParamInfo getParamInfo_2() { return ParamInfo::defaultGainParam(); }
|
||||
static ParamInfo getParamInfo_3() { return ParamInfo::defaultBandwidthParam(); }
|
||||
};
|
||||
|
||||
template <class FilterClass>
|
||||
struct TypeIV : TypeIVBase, FilterClass
|
||||
{
|
||||
void setParams(const Params& params) { FilterClass::setup(params[0], params[1], params[2], params[3]); }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct LowPass : TypeI<RBJ::LowPass>
|
||||
{
|
||||
static Kind getKind() { return kindLowPass; }
|
||||
static const char* getName() { return "RBJ Low Pass"; }
|
||||
};
|
||||
|
||||
struct HighPass : TypeI<RBJ::HighPass>
|
||||
{
|
||||
static Kind getKind() { return kindHighPass; }
|
||||
static const char* getName() { return "RBJ High Pass"; }
|
||||
};
|
||||
|
||||
struct BandPass1 : TypeII<RBJ::BandPass1>
|
||||
{
|
||||
static Kind getKind() { return kindBandPass; }
|
||||
static const char* getName() { return "RBJ Band Pass 1"; }
|
||||
};
|
||||
|
||||
struct BandPass2 : TypeII<RBJ::BandPass2>
|
||||
{
|
||||
static Kind getKind() { return kindBandPass; }
|
||||
static const char* getName() { return "RBJ Band Pass 2"; }
|
||||
};
|
||||
|
||||
struct BandStop : TypeII<RBJ::BandStop>
|
||||
{
|
||||
static Kind getKind() { return kindBandStop; }
|
||||
static const char* getName() { return "RBJ Band Stop"; }
|
||||
};
|
||||
|
||||
struct LowShelf : TypeIII<RBJ::LowShelf>
|
||||
{
|
||||
static Kind getKind() { return kindLowShelf; }
|
||||
static const char* getName() { return "RBJ Low Shelf"; }
|
||||
};
|
||||
|
||||
struct HighShelf : TypeIII<RBJ::HighShelf>
|
||||
{
|
||||
static Kind getKind() { return kindHighShelf; }
|
||||
static const char* getName() { return "RBJ High Shelf"; }
|
||||
};
|
||||
|
||||
struct BandShelf : TypeIV<RBJ::BandShelf>
|
||||
{
|
||||
static Kind getKind() { return kindBandShelf; }
|
||||
static const char* getName() { return "RBJ Band Shelf"; }
|
||||
};
|
||||
|
||||
struct AllPass : TypeI<RBJ::AllPass>
|
||||
{
|
||||
static Kind getKind() { return kindOther; }
|
||||
static const char* getName() { return "RBJ All Pass"; }
|
||||
};
|
||||
} // namespace Design
|
||||
} // namespace RBJ
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "RootFinder.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
void RootFinderBase::solve(int degree,
|
||||
bool polish,
|
||||
bool doSort)
|
||||
{
|
||||
assert(degree <= m_maxdegree);
|
||||
|
||||
const double EPS = 1.0e-30;
|
||||
|
||||
int its;
|
||||
|
||||
int m = degree;
|
||||
|
||||
// copy coefficients
|
||||
for (int j = 0; j <= m; ++j) { m_ad[j] = m_a[j]; }
|
||||
|
||||
// for each root
|
||||
for (int j = m - 1; j >= 0; --j)
|
||||
{
|
||||
// initial guess at 0
|
||||
complex_t x = 0.0;
|
||||
laguerre(j + 1, m_ad, x, its);
|
||||
|
||||
if (fabs(std::imag(x)) <= 2.0 * EPS * fabs(std::real(x))) { x = complex_t(std::real(x), 0.0); }
|
||||
|
||||
m_root[j] = x;
|
||||
|
||||
// deflate
|
||||
complex_t b = m_ad[j + 1];
|
||||
for (int jj = j; jj >= 0; --jj)
|
||||
{
|
||||
complex_t c = m_ad[jj];
|
||||
m_ad[jj] = b;
|
||||
b = x * b + c;
|
||||
}
|
||||
}
|
||||
|
||||
if (polish) { for (int j = 0; j < m; ++j) { laguerre(degree, m_a, m_root[j], its); } }
|
||||
|
||||
if (doSort) { sort(degree); }
|
||||
}
|
||||
|
||||
void RootFinderBase::sort(int degree)
|
||||
{
|
||||
for (int j = 1; j < degree; ++j)
|
||||
{
|
||||
complex_t x = m_root[j];
|
||||
|
||||
int i;
|
||||
for (i = j - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_root[i].imag() >= x.imag()) { break; }
|
||||
|
||||
m_root[i + 1] = m_root[i];
|
||||
}
|
||||
|
||||
m_root[i + 1] = x;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void RootFinderBase::laguerre(int degree,
|
||||
complex_t a[],
|
||||
complex_t& x,
|
||||
int& its)
|
||||
{
|
||||
const int MR = 8, MT = 10, MAXIT = MT * MR;
|
||||
const double EPS = std::numeric_limits<double>::epsilon();
|
||||
|
||||
static const double frac[MR + 1] =
|
||||
{ 0.0, 0.5, 0.25, 0.75, 0.13, 0.38, 0.62, 0.88, 1.0 };
|
||||
|
||||
complex_t f;
|
||||
|
||||
int m = degree;
|
||||
for (int iter = 1; iter <= MAXIT; ++iter)
|
||||
{
|
||||
its = iter;
|
||||
complex_t b = a[m];
|
||||
double err = std::abs(b);
|
||||
complex_t d = f = 0.0;
|
||||
double abx = std::abs(x);
|
||||
for (int j = m - 1; j >= 0; --j)
|
||||
{
|
||||
f = x * f + d;
|
||||
d = x * d + b;
|
||||
b = x * b + a[j];
|
||||
err = std::abs(b) + abx * err;
|
||||
}
|
||||
err *= EPS;
|
||||
if (std::abs(b) <= err) { return; }
|
||||
complex_t g = d / b;
|
||||
complex_t g2 = g * g;
|
||||
complex_t h = g2 - 2.0 * f / b;
|
||||
|
||||
complex_t sq = sqrt(double(m - 1) * (double(m) * h - g2));
|
||||
complex_t gp = g + sq;
|
||||
complex_t gm = g - sq;
|
||||
|
||||
double abp = std::abs(gp);
|
||||
double abm = std::abs(gm);
|
||||
if (abp < abm) { gp = gm; }
|
||||
complex_t dx = std::max(abp, abm) > 0.0 ? double(m) / gp : std::polar(1 + abx, double(iter));
|
||||
complex_t x1 = x - dx;
|
||||
if (x == x1) { return; }
|
||||
if (iter % MT != 0) { x = x1; }
|
||||
else { x -= frac[iter / MT] * dx; }
|
||||
}
|
||||
|
||||
throw std::logic_error("laguerre failed");
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
complex_t RootFinderBase::eval(int degree,
|
||||
const complex_t& x)
|
||||
{
|
||||
complex_t y;
|
||||
|
||||
if (x != 0.) { for (int i = 0; i <= degree; ++i) { y += m_a[i] * pow(x, double(i)); } }
|
||||
else { y = m_a[0]; }
|
||||
|
||||
return y;
|
||||
}
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,108 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "Types.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
//
|
||||
// Finds the complex roots of the given polynomial with
|
||||
// complex-valued coefficients using a numerical method.
|
||||
//
|
||||
|
||||
class RootFinderBase
|
||||
{
|
||||
public:
|
||||
struct Array
|
||||
{
|
||||
Array(int /*max*/, complex_t* /*values*/) /* : m_max (max), m_values (values)*/ { }
|
||||
|
||||
//complex_t& operator[] (int index) { };
|
||||
};
|
||||
|
||||
//
|
||||
// Find roots of polynomial f(x)=a[0]+a[1]*x+a[2]*x^2...+a[degree]*x^degree
|
||||
// The input coefficients are set using coef()[].
|
||||
// The solutions are placed in roots.
|
||||
//
|
||||
void solve(int degree, bool polish = true, bool doSort = true);
|
||||
|
||||
// Evaluates the polynomial at x
|
||||
complex_t eval(int degree, const complex_t& x);
|
||||
|
||||
// Direct access to the input coefficient array of size degree+1.
|
||||
complex_t* coef() { return m_a; }
|
||||
|
||||
// Direct access to the resulting roots array of size degree
|
||||
complex_t* root() { return m_root; }
|
||||
|
||||
// sort the roots by descending imaginary part
|
||||
void sort(int degree);
|
||||
|
||||
private:
|
||||
// Improves x as a root using Laguerre's method.
|
||||
// The input coefficient array has degree+1 elements.
|
||||
void laguerre(int degree, complex_t a[], complex_t& x, int& its);
|
||||
|
||||
protected:
|
||||
int m_maxdegree = 0;
|
||||
complex_t* m_a = nullptr; // input coefficients (m_maxdegree+1 elements)
|
||||
complex_t* m_ad = nullptr; // copy of deflating coefficients
|
||||
complex_t* m_root = nullptr; // array of roots (maxdegree elements)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <int maxdegree>
|
||||
struct RootFinder : RootFinderBase
|
||||
{
|
||||
RootFinder()
|
||||
{
|
||||
m_maxdegree = maxdegree;
|
||||
m_a = m_a0;
|
||||
m_ad = m_ad0;
|
||||
m_root = m_r;
|
||||
}
|
||||
|
||||
private:
|
||||
complex_t m_a0 [maxdegree + 1];
|
||||
complex_t m_ad0[maxdegree + 1];
|
||||
complex_t m_r [maxdegree];
|
||||
};
|
||||
} // namespace Dsp
|
||||
@@ -0,0 +1,133 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_SMOOTHEDFILTER_H
|
||||
#define DSPFILTERS_SMOOTHEDFILTER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Filter.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Implements smooth modulation of time-varying filter parameters
|
||||
*
|
||||
*/
|
||||
template <class DesignClass, int Channels, class StateType = DirectFormII>
|
||||
class SmoothedFilterDesign final : public FilterDesign<DesignClass, Channels, StateType>
|
||||
{
|
||||
public:
|
||||
typedef FilterDesign<DesignClass, Channels, StateType> filter_type_t;
|
||||
|
||||
SmoothedFilterDesign(int transitionSamples) : m_transitionSamples(transitionSamples) { }
|
||||
|
||||
// Process a block of samples.
|
||||
template <typename Sample>
|
||||
void processBlock(int nSamples,
|
||||
Sample* const* destChannelArray)
|
||||
{
|
||||
const int numChannels = this->getNumChannels();
|
||||
|
||||
// If this goes off it means setup() was never called
|
||||
assert(m_remainingSamples >= 0);
|
||||
|
||||
// first handle any transition samples
|
||||
int remainingSamples = std::min(m_remainingSamples, nSamples);
|
||||
|
||||
if (remainingSamples > 0)
|
||||
{
|
||||
// interpolate parameters for each sample
|
||||
const double t = 1. / m_remainingSamples;
|
||||
double dp[maxParameters];
|
||||
for (int i = 0; i < DesignClass::NumParams; ++i) { dp[i] = (this->getParams()[i] - m_transitionParams[i]) * t; }
|
||||
|
||||
for (int n = 0; n < remainingSamples; ++n)
|
||||
{
|
||||
for (int i = DesignClass::NumParams; --i >= 0;) { m_transitionParams[i] += dp[i]; }
|
||||
|
||||
m_transitionFilter.setParams(m_transitionParams);
|
||||
|
||||
for (int i = numChannels; --i >= 0;)
|
||||
{
|
||||
Sample* dest = destChannelArray[i] + n;
|
||||
*dest = this->m_state[i].process(*dest, m_transitionFilter);
|
||||
}
|
||||
}
|
||||
|
||||
m_remainingSamples -= remainingSamples;
|
||||
|
||||
if (m_remainingSamples == 0) { m_transitionParams = this->getParams(); }
|
||||
}
|
||||
|
||||
// do what's left
|
||||
if (nSamples - remainingSamples > 0)
|
||||
{
|
||||
// no transition
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
{
|
||||
this->m_design.process(nSamples - remainingSamples, destChannelArray[i] + remainingSamples, this->m_state[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process(int nSamples, float* const* arrayOfChannels) override { processBlock(nSamples, arrayOfChannels); }
|
||||
void process(int nSamples, double* const* arrayOfChannels) override { processBlock(nSamples, arrayOfChannels); }
|
||||
|
||||
#include "SmoothedFilterSynthesisH.inl"
|
||||
|
||||
protected:
|
||||
void doSetParams(const Params& parameters) override
|
||||
{
|
||||
if (m_remainingSamples >= 0) { m_remainingSamples = m_transitionSamples; }
|
||||
else
|
||||
{
|
||||
// first time
|
||||
m_remainingSamples = 0;
|
||||
m_transitionParams = parameters;
|
||||
}
|
||||
|
||||
filter_type_t::doSetParams(parameters);
|
||||
}
|
||||
|
||||
Params m_transitionParams;
|
||||
DesignClass m_transitionFilter;
|
||||
int m_transitionSamples = 0;
|
||||
|
||||
int m_remainingSamples = -1; // remaining transition samples
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
|
||||
// Process a block of samples.
|
||||
template <typename Sample>
|
||||
void processBlockSynthesis (int nSamples,
|
||||
Sample* const* destChannelArray)
|
||||
{
|
||||
const int numChannels = this->getNumChannels();
|
||||
|
||||
// If this goes off it means setup() was never called
|
||||
assert (m_remainingSamples >= 0);
|
||||
|
||||
// first handle any transition samples
|
||||
int remainingSamples = std::min (m_remainingSamples, nSamples);
|
||||
|
||||
if (remainingSamples > 0) // A PRIORI, never used in our case!
|
||||
{
|
||||
// interpolate parameters for each sample
|
||||
const double t = 1. / m_remainingSamples;
|
||||
double dp[maxParameters];
|
||||
for (int i = 0; i < DesignClass::NumParams; ++i)
|
||||
dp[i] = (this->getParams()[i] - m_transitionParams[i]) * t;
|
||||
|
||||
for (int n = 0; n < remainingSamples; ++n)
|
||||
{
|
||||
for (int i = DesignClass::NumParams; --i >=0;)
|
||||
m_transitionParams[i] += dp[i];
|
||||
|
||||
m_transitionFilter.setParams (m_transitionParams);
|
||||
|
||||
for (int i = numChannels; --i >= 0;)
|
||||
{
|
||||
Sample* dest = destChannelArray[i]+n;
|
||||
*dest = this->m_state[i].processSynthesis (*dest, m_transitionFilter); //not sure to be defined !!!
|
||||
}
|
||||
}
|
||||
|
||||
m_remainingSamples -= remainingSamples;
|
||||
|
||||
if (m_remainingSamples == 0)
|
||||
m_transitionParams = this->getParams();
|
||||
}
|
||||
|
||||
// do what's left
|
||||
if (nSamples - remainingSamples > 0)
|
||||
{
|
||||
// no transition
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
this->m_design.processSynthesis (nSamples - remainingSamples,
|
||||
destChannelArray[i] + remainingSamples,
|
||||
this->m_state[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void processSynthesis (int nSamples, double* const* arrayOfChannels)
|
||||
{
|
||||
processBlockSynthesis (nSamples, arrayOfChannels);
|
||||
}
|
||||
|
||||
void doSetParamsSynthesis (const Params& parameters)
|
||||
{
|
||||
if (m_remainingSamples >= 0)
|
||||
{
|
||||
m_remainingSamples = m_transitionSamples;
|
||||
}
|
||||
else
|
||||
{
|
||||
// first time
|
||||
m_remainingSamples = 0;
|
||||
m_transitionParams = parameters;
|
||||
}
|
||||
|
||||
filter_type_t::doSetParamsSynthesis (parameters);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Common.h"
|
||||
#include "State.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_STATE_H
|
||||
#define DSPFILTERS_STATE_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "Biquad.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Various forms of state information required to
|
||||
* process channels of actual sample data.
|
||||
*
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* State for applying a second order section to a sample using Direct Form I
|
||||
*
|
||||
* Difference equation:
|
||||
*
|
||||
* y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
|
||||
* - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
|
||||
*/
|
||||
class DirectFormI
|
||||
{
|
||||
public:
|
||||
DirectFormI() { reset(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_x1 = 0;
|
||||
m_x2 = 0;
|
||||
m_y1 = 0;
|
||||
m_y2 = 0;
|
||||
}
|
||||
|
||||
template <typename Sample>
|
||||
Sample process1(const Sample in, const BiquadBase& s, const double vsa) // very small amount
|
||||
{
|
||||
double out = s.m_b0 * in + s.m_b1 * m_x1 + s.m_b2 * m_x2 - s.m_a1 * m_y1 - s.m_a2 * m_y2 + vsa;
|
||||
m_x2 = m_x1;
|
||||
m_y2 = m_y1;
|
||||
m_x1 = in;
|
||||
m_y1 = out;
|
||||
|
||||
return Sample(out);
|
||||
}
|
||||
|
||||
protected:
|
||||
double m_x2 = 0; // x[n-2]
|
||||
double m_y2 = 0; // y[n-2]
|
||||
double m_x1 = 0; // x[n-1]
|
||||
double m_y1 = 0; // y[n-1]
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* State for applying a second order section to a sample using Direct Form II
|
||||
*
|
||||
* Difference equation:
|
||||
*
|
||||
* v[n] = x[n] - (a1/a0)*v[n-1] - (a2/a0)*v[n-2]
|
||||
* y(n) = (b0/a0)*v[n] + (b1/a0)*v[n-1] + (b2/a0)*v[n-2]
|
||||
*
|
||||
*/
|
||||
class DirectFormII
|
||||
{
|
||||
public:
|
||||
DirectFormII() { reset(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_v1 = 0;
|
||||
m_v2 = 0;
|
||||
}
|
||||
|
||||
template <typename Sample>
|
||||
Sample process1(const Sample in, const BiquadBase& s, const double vsa)
|
||||
{
|
||||
double w = in - s.m_a1 * m_v1 - s.m_a2 * m_v2 + vsa;
|
||||
double out = s.m_b0 * w + s.m_b1 * m_v1 + s.m_b2 * m_v2;
|
||||
|
||||
m_v2 = m_v1;
|
||||
m_v1 = w;
|
||||
|
||||
return Sample(out);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_v1 = 0; // v[-1]
|
||||
double m_v2 = 0; // v[-2]
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Transposed Direct Form I and II
|
||||
* by lubomir i. ivanov (neolit123 [at] gmail)
|
||||
*
|
||||
* Reference:
|
||||
* http://www.kvraudio.com/forum/viewtopic.php?p=4430351
|
||||
*
|
||||
*/
|
||||
|
||||
// I think this one is broken
|
||||
class TransposedDirectFormI
|
||||
{
|
||||
public:
|
||||
TransposedDirectFormI() { reset(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_v = 0;
|
||||
m_s1 = 0;
|
||||
m_s1_1 = 0;
|
||||
m_s2 = 0;
|
||||
m_s2_1 = 0;
|
||||
m_s3 = 0;
|
||||
m_s3_1 = 0;
|
||||
m_s4 = 0;
|
||||
m_s4_1 = 0;
|
||||
}
|
||||
|
||||
template <typename Sample>
|
||||
Sample process1(const Sample in, const BiquadBase& s, const double /*vsa*/)
|
||||
{
|
||||
|
||||
// can be: in += m_s1_1;
|
||||
m_v = in + m_s1_1;
|
||||
double out = s.m_b0 * m_v + m_s3_1;
|
||||
m_s1 = m_s2_1 - s.m_a1 * m_v;
|
||||
m_s2 = -s.m_a2 * m_v;
|
||||
m_s3 = s.m_b1 * m_v + m_s4_1;
|
||||
m_s4 = s.m_b2 * m_v;
|
||||
|
||||
m_s4_1 = m_s4;
|
||||
m_s3_1 = m_s3;
|
||||
m_s2_1 = m_s2;
|
||||
m_s1_1 = m_s1;
|
||||
|
||||
return Sample(out);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_v = 0;
|
||||
double m_s1 = 0;
|
||||
double m_s1_1 = 0;
|
||||
double m_s2 = 0;
|
||||
double m_s2_1 = 0;
|
||||
double m_s3 = 0;
|
||||
double m_s3_1 = 0;
|
||||
double m_s4 = 0;
|
||||
double m_s4_1 = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class TransposedDirectFormII
|
||||
{
|
||||
public:
|
||||
TransposedDirectFormII() { reset(); }
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_s1 = 0;
|
||||
m_s1_1 = 0;
|
||||
m_s2 = 0;
|
||||
m_s2_1 = 0;
|
||||
}
|
||||
|
||||
template <typename Sample>
|
||||
Sample process1(const Sample in, const BiquadBase& s, const double vsa)
|
||||
{
|
||||
double out = m_s1_1 + s.m_b0 * in + vsa;
|
||||
m_s1 = m_s2_1 + s.m_b1 * in - s.m_a1 * out;
|
||||
m_s2 = s.m_b2 * in - s.m_a2 * out;
|
||||
m_s1_1 = m_s1;
|
||||
m_s2_1 = m_s2;
|
||||
|
||||
return Sample(out);
|
||||
}
|
||||
|
||||
private:
|
||||
double m_s1 = 0;
|
||||
double m_s1_1 = 0;
|
||||
double m_s2 = 0;
|
||||
double m_s2_1 = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Holds an array of states suitable for multi-channel processing
|
||||
template <int Channels, class StateType>
|
||||
class ChannelsState
|
||||
{
|
||||
public:
|
||||
ChannelsState() { }
|
||||
|
||||
int getNumChannels() const { return Channels; }
|
||||
|
||||
void reset() { for (int i = 0; i < Channels; ++i) { m_state[i].reset(); } }
|
||||
|
||||
StateType& operator[](int index)
|
||||
{
|
||||
assert(index >= 0 && index < Channels);
|
||||
return m_state[index];
|
||||
}
|
||||
|
||||
template <class Filter, typename Sample>
|
||||
void process(int nSamples, Sample* const* arrayOfChannels, Filter& filter)
|
||||
{
|
||||
for (int i = 0; i < Channels; ++i) { filter.process(nSamples, arrayOfChannels[i], m_state[i]); }
|
||||
}
|
||||
|
||||
private:
|
||||
StateType m_state[Channels];
|
||||
};
|
||||
|
||||
// Empty state, can't process anything
|
||||
template <class StateType>
|
||||
class ChannelsState<0, StateType>
|
||||
{
|
||||
public:
|
||||
int getNumChannels() const { return 0; }
|
||||
|
||||
void reset() { throw std::logic_error("attempt to reset empty ChannelState"); }
|
||||
|
||||
template <class FilterDesign, typename Sample>
|
||||
void process(int /*nSamples*/, Sample* const* /*arrayOfChannels*/, FilterDesign& /*filter*/)
|
||||
{
|
||||
throw std::logic_error("attempt to process empty ChannelState");
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
|
||||
template <typename Sample>
|
||||
inline Sample processSynthesis (const Sample in, const Cascade& c)
|
||||
{
|
||||
double out = in;
|
||||
StateType* state = m_stateArray;
|
||||
Biquad const* stage = c.m_stageArray;
|
||||
// const double vsa = ac(); // => vsa = -vsa
|
||||
int i = c.m_numStages - 1;
|
||||
out = (state++)->process1 (out, *stage++, 0); // => 0 instead of vsa
|
||||
for (; --i >= 0;)
|
||||
out = (state++)->process1 (out, *stage++, 0);
|
||||
//for (int i = c.m_numStages; --i >= 0; ++state, ++stage)
|
||||
// out = state->process1 (out, *stage, vsa);
|
||||
return Sample (out);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_TYPES_H
|
||||
#define DSPFILTERS_TYPES_H
|
||||
|
||||
#include "Common.h"
|
||||
#include "MathSupplement.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
// A conjugate or real pair
|
||||
struct ComplexPair : complex_pair_t
|
||||
{
|
||||
ComplexPair() { }
|
||||
|
||||
explicit ComplexPair(const complex_t& c1)
|
||||
: complex_pair_t(c1, 0.)
|
||||
{
|
||||
assert(isReal());
|
||||
}
|
||||
|
||||
ComplexPair(const complex_t& c1,
|
||||
const complex_t& c2)
|
||||
: complex_pair_t(c1, c2) { }
|
||||
|
||||
bool isConjugate() const { return second == std::conj(first); }
|
||||
|
||||
bool isReal() const { return first.imag() == 0 && second.imag() == 0; }
|
||||
|
||||
// Returns true if this is either a conjugate pair,
|
||||
// or a pair of reals where neither is zero.
|
||||
bool isMatchedPair() const
|
||||
{
|
||||
if (first.imag() != 0) { return second == std::conj(first); }
|
||||
return second.imag() == 0 &&
|
||||
second.real() != 0 &&
|
||||
first.real() != 0;
|
||||
}
|
||||
|
||||
bool is_nan() const { return Dsp::is_nan(first) || Dsp::is_nan(second); }
|
||||
};
|
||||
|
||||
// A pair of pole/zeros. This fits in a biquad (but is missing the gain)
|
||||
struct PoleZeroPair
|
||||
{
|
||||
ComplexPair poles;
|
||||
ComplexPair zeros;
|
||||
|
||||
PoleZeroPair() { }
|
||||
|
||||
// single pole/zero
|
||||
PoleZeroPair(const complex_t& p, const complex_t& z)
|
||||
: poles(p), zeros(z) { }
|
||||
|
||||
// pole/zero pair
|
||||
PoleZeroPair(const complex_t& p1, const complex_t& z1,
|
||||
const complex_t& p2, const complex_t& z2)
|
||||
: poles(p1, p2)
|
||||
, zeros(z1, z2) { }
|
||||
|
||||
bool isSinglePole() const { return poles.second == 0. && zeros.second == 0.; }
|
||||
|
||||
bool is_nan() const { return poles.is_nan() || zeros.is_nan(); }
|
||||
};
|
||||
|
||||
// Identifies the general class of filter
|
||||
enum Kind
|
||||
{
|
||||
kindLowPass,
|
||||
kindHighPass,
|
||||
kindBandPass,
|
||||
kindBandStop,
|
||||
kindLowShelf,
|
||||
kindHighShelf,
|
||||
kindBandShelf,
|
||||
kindOther
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,723 @@
|
||||
/*******************************************************************************
|
||||
|
||||
"A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
By Vinnie Falco
|
||||
|
||||
Official project location:
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
See Documentation.cpp for contact information, notes, and bibliography.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
Copyright (c) 2009 by Vinnie Falco
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef DSPFILTERS_UTILITIES_H
|
||||
#define DSPFILTERS_UTILITIES_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
namespace Dsp
|
||||
{
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*
|
||||
* These routines are handy for manipulating buffers of samples.
|
||||
*
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Add src samples to dest, without clip or overflow checking.
|
||||
template <class Td,
|
||||
class Ts>
|
||||
void add(int samples,
|
||||
Td* dest,
|
||||
Ts const* src,
|
||||
int destSkip = 0,
|
||||
int srcSkip = 0)
|
||||
{
|
||||
if (srcSkip != 0 || destSkip != 0)
|
||||
{
|
||||
++srcSkip;
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Td(*src);
|
||||
dest += destSkip;
|
||||
src += srcSkip;
|
||||
}
|
||||
}
|
||||
else { while (--samples >= 0) *dest++ += Td(*src++); }
|
||||
}
|
||||
|
||||
// Multichannel add
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void add(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ts const* const* src) { for (int i = channels; --i >= 0;) add(samples, dest[i], src[i]); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Copy samples from src to dest, which may not overlap. Performs an implicit
|
||||
// type conversion if Ts and Td are different (for example, float to double).
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void copy(int samples,
|
||||
Td* dest,
|
||||
Ts const* src,
|
||||
int destSkip = 0,
|
||||
int srcSkip = 0)
|
||||
{
|
||||
if (srcSkip != 0)
|
||||
{
|
||||
if (destSkip != 0)
|
||||
{
|
||||
++srcSkip;
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest++ = *src++;
|
||||
dest += destSkip;
|
||||
src += srcSkip;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++srcSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest++ = *src++;
|
||||
src += srcSkip;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (destSkip != 0)
|
||||
{
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = *src++;
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
else { while (--samples >= 0) *dest++ = *src++; }
|
||||
}
|
||||
|
||||
// Wrapper that uses memcpy if there is no skip and the types are the same
|
||||
template <typename Ty>
|
||||
void copy(int samples,
|
||||
Ty* dest,
|
||||
Ty const* src,
|
||||
int destSkip = 0,
|
||||
int srcSkip = 0)
|
||||
{
|
||||
if (destSkip != 0 || srcSkip != 0) copy<Ty, Ty>(samples, dest, src, destSkip, srcSkip);
|
||||
else ::memcpy(dest, src, samples * sizeof(src[0]));
|
||||
}
|
||||
|
||||
// Copy a set of channels from src to dest, with implicit type conversion.
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void copy(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ts const* const* src,
|
||||
int destSkip = 0,
|
||||
int srcSkip = 0) { for (int i = channels; --i >= 0;) copy(samples, dest[i], src[i], destSkip, srcSkip); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Deinterleave channels. Performs implicit type conversion.
|
||||
template <typename Td, typename Ts>
|
||||
void deinterleave(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ts const* src)
|
||||
{
|
||||
assert(channels > 1);
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
Td* l = dest[0];
|
||||
Td* r = dest[1];
|
||||
int n = (samples + 7) / 8;
|
||||
switch (samples % 8)
|
||||
{
|
||||
case 0: do
|
||||
{
|
||||
*l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 7: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 6: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 5: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 4: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 3: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 2: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
case 1: *l++ = *src++;
|
||||
*r++ = *src++;
|
||||
} while (--n > 0);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
for (int i = channels; --i >= 0;) copy(samples, dest[i], src + i, 0, channels - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience for a stereo pair of channels
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void deinterleave(int samples,
|
||||
Td* left,
|
||||
Td* right,
|
||||
Ts const* src)
|
||||
{
|
||||
Td* dest[2];
|
||||
dest[0] = left;
|
||||
dest[1] = right;
|
||||
deinterleave(2, samples, dest, src);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Fade dest
|
||||
template <typename Td,
|
||||
typename Ty>
|
||||
void fade(int samples,
|
||||
Td* dest,
|
||||
Ty start = 0,
|
||||
Ty end = 1)
|
||||
{
|
||||
Ty t = start;
|
||||
Ty dt = (end - start) / samples;
|
||||
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest++ *= t;
|
||||
t += dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Fade dest cannels
|
||||
template <typename Td,
|
||||
typename Ty>
|
||||
void fade(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ty start = 0,
|
||||
Ty end = 1) { for (int i = channels; --i >= 0;) fade(samples, dest[i], start, end); }
|
||||
|
||||
// Fade src into dest
|
||||
template <typename Td,
|
||||
typename Ts,
|
||||
typename Ty>
|
||||
void fade(int samples,
|
||||
Td* dest,
|
||||
Ts const* src,
|
||||
Ty start = 0,
|
||||
Ty end = 1)
|
||||
{
|
||||
Ty t = start;
|
||||
Ty dt = (end - start) / samples;
|
||||
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Td(*dest + t * (*src++ - *dest));
|
||||
++dest;
|
||||
t += dt;
|
||||
}
|
||||
}
|
||||
|
||||
// Fade src channels into dest channels
|
||||
template <typename Td,
|
||||
typename Ts,
|
||||
typename Ty>
|
||||
void fade(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ts const* const* src,
|
||||
Ty start = 0,
|
||||
Ty end = 1) { for (int i = channels; --i >= 0;) fade(samples, dest[i], src[i], start, end); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Interleave separate channels from source pointers to destination
|
||||
// (Destination requires channels*frames samples of storage). Performs
|
||||
// implicit type conversion.
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void interleave(int channels,
|
||||
size_t samples,
|
||||
Td* dest,
|
||||
Ts const* const* src)
|
||||
{
|
||||
assert(channels>1);
|
||||
|
||||
if (samples == 0) return;
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
const Ts* l = src[0];
|
||||
const Ts* r = src[1];
|
||||
|
||||
// note that Duff's Device only works when samples>0
|
||||
int n = (samples + 7) / 8;
|
||||
switch (samples % 8)
|
||||
{
|
||||
case 0: do
|
||||
{
|
||||
*dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 7: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 6: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 5: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 4: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 3: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 2: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
case 1: *dest++ = *l++;
|
||||
*dest++ = *r++;
|
||||
} while (--n > 0);
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: { for (int i = channels; --i >= 0;) copy(samples, dest + i, src[i], channels - 1, 0); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Convenience for a stereo channel pair
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void interleave(int samples,
|
||||
Td* dest,
|
||||
Ts const* left,
|
||||
Ts const* right)
|
||||
{
|
||||
const Ts* src[2];
|
||||
src[0] = left;
|
||||
src[1] = right;
|
||||
interleave(2, samples, dest, src);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Multiply samples by a constant, without clip or overflow checking.
|
||||
template <typename Td,
|
||||
typename Ty>
|
||||
void multiply(int samples,
|
||||
Td* dest,
|
||||
Ty factor,
|
||||
int destSkip = 0)
|
||||
{
|
||||
if (destSkip != 0)
|
||||
{
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Td(*dest * factor);
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Td(*dest * factor);
|
||||
++dest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Multiply a set of channels by a constant.
|
||||
template <typename Td,
|
||||
typename Ty>
|
||||
void multiply(int channels,
|
||||
int samples,
|
||||
Td* const* dest,
|
||||
Ty factor,
|
||||
int destSkip = 0) { for (int i = channels; --i >= 0;) multiply(samples, dest[i], factor, destSkip); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Copy samples from src to dest in reversed order. Performs implicit
|
||||
// type conversion. src and dest may not overlap.
|
||||
template <typename Td,
|
||||
typename Ts>
|
||||
void reverse(int samples,
|
||||
Td* dest,
|
||||
Ts const* src,
|
||||
int destSkip = 0,
|
||||
int srcSkip = 0)
|
||||
{
|
||||
src += (srcSkip + 1) * samples;
|
||||
|
||||
if (srcSkip != 0 || destSkip == 0)
|
||||
{
|
||||
++srcSkip;
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
src -= srcSkip;
|
||||
*dest = *src;
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
else { while (--samples >= 0) *dest++ = *--src; }
|
||||
}
|
||||
|
||||
template <typename Td, typename Ts>
|
||||
void reverse(int channels, size_t frames, Td* const* dest, const Ts* const* src) { for (int i = channels; --i >= 0;) reverse(frames, dest[i], src[i]); }
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename Tn>
|
||||
void to_mono(int samples, Tn* dest, Tn const* left, Tn const* right)
|
||||
{
|
||||
#if 1
|
||||
while (samples-- > 0) *dest++ = (*left++ + *right++) * Tn(0.70710678118654752440084436210485);
|
||||
#else
|
||||
while (samples-- > 0)
|
||||
*dest++ = (*left++ + *right++) * Tn(0.5);
|
||||
#endif
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
void validate(int numChannels, int nSamples, T const* const* src)
|
||||
{
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
{
|
||||
T const* p = src[i];
|
||||
for (int j = nSamples; j > 0; --j)
|
||||
{
|
||||
T v = *p++;
|
||||
assert(v < 2 && v > -2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* this stuff all depends on is_pod which is not always available
|
||||
*
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
template <typename Ty,
|
||||
bool isPod>
|
||||
struct zero
|
||||
{
|
||||
static void process (int samples,
|
||||
Ty* dest,
|
||||
int destSkip)
|
||||
{
|
||||
if (destSkip != 0)
|
||||
{
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Ty();
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fill (dest, dest + samples, Ty());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Ty>
|
||||
struct zero<Ty, true>
|
||||
{
|
||||
static void process (int samples,
|
||||
Ty* dest,
|
||||
int destSkip)
|
||||
{
|
||||
if (destSkip != 0)
|
||||
zero<Ty,false>::process (samples, dest, destSkip);
|
||||
else
|
||||
::memset (dest, 0, samples * sizeof(dest[0]));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Fill a channel with zeros. This works even if Ty is not a basic type.
|
||||
template <typename Ty>
|
||||
void zero (int samples,
|
||||
Ty* dest,
|
||||
int destSkip = 0)
|
||||
{
|
||||
detail::zero<Ty, tr1::is_pod<Ty>::value>::process (samples, dest, destSkip );
|
||||
}
|
||||
|
||||
#else
|
||||
// Fill a channel with zeros. This works even if Ty is not a basic type.
|
||||
template <typename Ty>
|
||||
void zero(int samples,
|
||||
Ty* dest,
|
||||
int destSkip = 0)
|
||||
{
|
||||
if (destSkip != 0)
|
||||
{
|
||||
++destSkip;
|
||||
while (--samples >= 0)
|
||||
{
|
||||
*dest = Ty();
|
||||
dest += destSkip;
|
||||
}
|
||||
}
|
||||
else { std::fill(dest, dest + samples, Ty()); }
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Fill a set of channels with zero.
|
||||
template <typename Ty>
|
||||
void zero(int channels,
|
||||
int samples,
|
||||
Ty* const* dest,
|
||||
int destSkip = 0) { for (int i = channels; --i >= 0;) zero(samples, dest[i], destSkip); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Implementation of Brent's Method provided by
|
||||
// John D. Cook (http://www.johndcook.com/)
|
||||
// The return value of Minimize is the minimum of the function f.
|
||||
// The location where f takes its minimum is returned in the variable minLoc.
|
||||
// Notation and implementation based on Chapter 5 of Richard Brent's book
|
||||
// "Algorithms for Minimization Without Derivatives".
|
||||
//
|
||||
// Reference:
|
||||
// http://www.codeproject.com/KB/recipes/one_variable_optimize.aspx?msg=2779038
|
||||
|
||||
template <class TFunction>
|
||||
double BrentMinimize(TFunction& f, // [in] objective function to minimize
|
||||
double leftEnd, // [in] smaller value of bracketing interval
|
||||
double rightEnd, // [in] larger value of bracketing interval
|
||||
double epsilon, // [in] stopping tolerance
|
||||
double& minLoc) // [out] location of minimum
|
||||
{
|
||||
double e, q, r, u, w, fw, fx;
|
||||
static const double c = 0.5 * (3.0 - std::sqrt(5.0));
|
||||
static const double SQRT_DBL_EPSILON = std::sqrt(DBL_EPSILON);
|
||||
|
||||
double& a = leftEnd;
|
||||
double& b = rightEnd;
|
||||
double& x = minLoc;
|
||||
|
||||
double v = w = x = a + c * (b - a);
|
||||
double d = e = 0.0;
|
||||
double fv = fw = fx = f(x);
|
||||
int counter = 0;
|
||||
loop:
|
||||
counter++;
|
||||
double m = 0.5 * (a + b);
|
||||
double tol = SQRT_DBL_EPSILON * fabs(x) + epsilon;
|
||||
double t2 = 2.0 * tol;
|
||||
// Check stopping criteria
|
||||
if (fabs(x - m) > t2 - 0.5 * (b - a))
|
||||
{
|
||||
double p = q = r = 0.0;
|
||||
if (fabs(e) > tol)
|
||||
{
|
||||
// fit parabola
|
||||
r = (x - w) * (fx - fv);
|
||||
q = (x - v) * (fx - fw);
|
||||
p = (x - v) * q - (x - w) * r;
|
||||
q = 2.0 * (q - r);
|
||||
(q > 0.0) ? p = -p : q = -q;
|
||||
r = e;
|
||||
e = d;
|
||||
}
|
||||
if (fabs(p) < fabs(0.5 * q * r) && p < q * (a - x) && p < q * (b - x))
|
||||
{
|
||||
// A parabolic interpolation step
|
||||
d = p / q;
|
||||
u = x + d;
|
||||
// f must not be evaluated too close to a or b
|
||||
if (u - a < t2 || b - u < t2) d = (x < m) ? tol : -tol;
|
||||
}
|
||||
else
|
||||
{
|
||||
// A golden section step
|
||||
e = (x < m) ? b : a;
|
||||
e -= x;
|
||||
d = c * e;
|
||||
}
|
||||
// f must not be evaluated too close to x
|
||||
if (fabs(d) >= tol) u = x + d;
|
||||
else if (d > 0.0) u = x + tol;
|
||||
else u = x - tol;
|
||||
double fu = f(u);
|
||||
// Update a, b, v, w, and x
|
||||
if (fu <= fx)
|
||||
{
|
||||
(u < x) ? b = x : a = x;
|
||||
v = w;
|
||||
fv = fw;
|
||||
w = x;
|
||||
fw = fx;
|
||||
x = u;
|
||||
fx = fu;
|
||||
}
|
||||
else
|
||||
{
|
||||
(u < x) ? a = u : b = u;
|
||||
if (fu <= fw || w == x)
|
||||
{
|
||||
v = w;
|
||||
fv = fw;
|
||||
w = u;
|
||||
fw = fu;
|
||||
}
|
||||
else if (fu <= fv || v == x || v == w)
|
||||
{
|
||||
v = u;
|
||||
fv = fu;
|
||||
}
|
||||
}
|
||||
goto loop; // Yes, the dreaded goto statement. But the code
|
||||
// here is faithful to Brent's orginal pseudocode.
|
||||
}
|
||||
return fx;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Tracks the peaks in the signal stream using the attack and release parameters
|
||||
template <int Channels = 2, typename Value=float>
|
||||
class EnvelopeFollower
|
||||
{
|
||||
public:
|
||||
EnvelopeFollower() { for (int i = 0; i < Channels; ++i) m_env[i] = 0; }
|
||||
|
||||
Value operator[](int channel) const { return m_env[channel]; }
|
||||
|
||||
void Setup(int sampleRate, double attackMs, double releaseMs)
|
||||
{
|
||||
m_a = pow(0.01, 1.0 / (attackMs * sampleRate * 0.001));
|
||||
m_r = pow(0.01, 1.0 / (releaseMs * sampleRate * 0.001));
|
||||
}
|
||||
|
||||
void Process(size_t samples, const Value** src)
|
||||
{
|
||||
for (int i = 0; i < Channels; ++i)
|
||||
{
|
||||
const Value* cur = src[i];
|
||||
|
||||
double e = m_env[i];
|
||||
for (int n = samples; n; n--)
|
||||
{
|
||||
double v = std::abs(*cur++);
|
||||
if (v > e) e = m_a * (e - v) + v;
|
||||
else e = m_r * (e - v) + v;
|
||||
}
|
||||
m_env[i] = e;
|
||||
}
|
||||
}
|
||||
|
||||
double m_env[Channels];
|
||||
|
||||
protected:
|
||||
double m_a = 0;
|
||||
double m_r = 0;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Helpful for discovering discontinuities in buffers
|
||||
template <int Channels = 2, typename Value=float>
|
||||
class SlopeDetector
|
||||
{
|
||||
public:
|
||||
SlopeDetector() : m_firstTime(true) { for (int i = 0; i < Channels; ++i) m_slope[i] = 0; }
|
||||
|
||||
Value getSlope(int channel) const { return m_slope[channel]; }
|
||||
|
||||
void process(size_t nSamples, const Value** input)
|
||||
{
|
||||
for (int i = 0; i < Channels; ++i)
|
||||
{
|
||||
const Value* src = input[i];
|
||||
int n = nSamples;
|
||||
|
||||
if (m_firstTime)
|
||||
{
|
||||
m_prev[i] = *src++;
|
||||
--n;
|
||||
}
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
n--;
|
||||
Value cur = *src++;
|
||||
Value diff = std::abs(cur - m_prev[i]);
|
||||
m_slope[i] = std::max(diff, m_slope[i]);
|
||||
m_prev[i] = cur;
|
||||
}
|
||||
}
|
||||
|
||||
m_firstTime = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_firstTime = false;
|
||||
Value m_slope [Channels];
|
||||
Value m_prev [Channels];
|
||||
};
|
||||
} // namespace Dsp
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2013 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "CompiledExpression.h"
|
||||
#include "Operation.h"
|
||||
#include "ParsedExpression.h"
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
CompiledExpression::CompiledExpression(const ParsedExpression& expression)
|
||||
{
|
||||
ParsedExpression expr = expression.optimize(); // Just in case it wasn't already optimized.
|
||||
std::vector<std::pair<ExpressionTreeNode, int>> temps;
|
||||
compileExpression(expr.getRootNode(), temps);
|
||||
}
|
||||
|
||||
CompiledExpression& CompiledExpression::operator=(const CompiledExpression& expression)
|
||||
{
|
||||
arguments = expression.arguments;
|
||||
target = expression.target;
|
||||
variableIndices = expression.variableIndices;
|
||||
variableNames = expression.variableNames;
|
||||
workspace.resize(expression.workspace.size());
|
||||
argValues.resize(expression.argValues.size());
|
||||
operation.resize(expression.operation.size());
|
||||
for (size_t i = 0; i < operation.size(); ++i) { operation[i] = expression.operation[i]->clone(); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CompiledExpression::compileExpression(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps)
|
||||
{
|
||||
if (findTempIndex(node, temps) != -1) { return; } // We have already processed a node identical to this one.
|
||||
|
||||
// Process the child nodes.
|
||||
|
||||
std::vector<int> args;
|
||||
for (size_t i = 0; i < node.getChildren().size(); ++i)
|
||||
{
|
||||
compileExpression(node.getChildren()[i], temps);
|
||||
args.push_back(findTempIndex(node.getChildren()[i], temps));
|
||||
}
|
||||
|
||||
// Process this node.
|
||||
|
||||
if (node.getOperation().getId() == Operation::VARIABLE)
|
||||
{
|
||||
variableIndices[node.getOperation().getName()] = int(workspace.size());
|
||||
variableNames.insert(node.getOperation().getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
int stepIndex = int(arguments.size());
|
||||
arguments.push_back(std::vector<int>());
|
||||
target.push_back(int(workspace.size()));
|
||||
operation.push_back(node.getOperation().clone());
|
||||
if (args.size() == 0) arguments[stepIndex].push_back(0); // The value won't actually be used. We just need something there.
|
||||
else
|
||||
{
|
||||
// If the arguments are sequential, we can just pass a pointer to the first one.
|
||||
|
||||
bool sequential = true;
|
||||
for (size_t i = 1; i < args.size(); ++i) if (args[i] != args[i - 1] + 1) sequential = false;
|
||||
if (sequential) arguments[stepIndex].push_back(args[0]);
|
||||
else
|
||||
{
|
||||
arguments[stepIndex] = args;
|
||||
if (args.size() > argValues.size()) argValues.resize(args.size(), 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
temps.push_back(std::make_pair(node, workspace.size()));
|
||||
workspace.push_back(0.0);
|
||||
}
|
||||
|
||||
int CompiledExpression::findTempIndex(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps)
|
||||
{
|
||||
for (size_t i = 0; i < temps.size(); ++i) { if (temps[i].first == node) { return int(i); } }
|
||||
return -1;
|
||||
}
|
||||
|
||||
double& CompiledExpression::getVariableReference(const std::string& name)
|
||||
{
|
||||
auto index = variableIndices.find(name);
|
||||
if (index == variableIndices.end()) throw Exception("getVariableReference: Unknown variable '" + name + "'");
|
||||
return workspace[index->second];
|
||||
}
|
||||
|
||||
double CompiledExpression::evaluate() const
|
||||
{
|
||||
// Loop over the operations and evaluate each one.
|
||||
|
||||
for (size_t step = 0; step < operation.size(); ++step)
|
||||
{
|
||||
const std::vector<int>& args = arguments[step];
|
||||
if (args.size() == 1) workspace[target[step]] = operation[step]->evaluate(&workspace[args[0]], dummyVariables);
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < args.size(); ++i) argValues[i] = workspace[args[i]];
|
||||
workspace[target[step]] = operation[step]->evaluate(&argValues[0], dummyVariables);
|
||||
}
|
||||
}
|
||||
return workspace[workspace.size() - 1];
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2013 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "windowsIncludes.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
class Operation;
|
||||
class ParsedExpression;
|
||||
|
||||
/**
|
||||
* A CompiledExpression is a highly optimized representation of an expression for cases when you want to evaluate
|
||||
* it many times as quickly as possible. You should treat it as an opaque object; none of the internal representation
|
||||
* is visible.
|
||||
*
|
||||
* A CompiledExpression is created by calling createCompiledExpression() on a ParsedExpression.
|
||||
*
|
||||
* WARNING: CompiledExpression is NOT thread safe. You should never access a CompiledExpression from two threads at
|
||||
* the same time.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT CompiledExpression
|
||||
{
|
||||
public:
|
||||
CompiledExpression() {}
|
||||
CompiledExpression(const CompiledExpression& expression) { *this = expression; }
|
||||
~CompiledExpression() { for (size_t i = 0; i < operation.size(); ++i) if (operation[i] != nullptr) delete operation[i]; }
|
||||
CompiledExpression& operator=(const CompiledExpression& expression);
|
||||
/**
|
||||
* Get the names of all variables used by this expression.
|
||||
*/
|
||||
const std::set<std::string>& getVariables() const { return variableNames; }
|
||||
/**
|
||||
* Get a reference to the memory location where the value of a particular variable is stored. This can be used
|
||||
* to set the value of the variable before calling evaluate().
|
||||
*/
|
||||
double& getVariableReference(const std::string& name);
|
||||
/**
|
||||
* Evaluate the expression. The values of all variables should have been set before calling this.
|
||||
*/
|
||||
double evaluate() const;
|
||||
private:
|
||||
friend class ParsedExpression;
|
||||
CompiledExpression(const ParsedExpression& expression);
|
||||
void compileExpression(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps);
|
||||
int findTempIndex(const ExpressionTreeNode& node, std::vector<std::pair<ExpressionTreeNode, int>>& temps);
|
||||
std::vector<std::vector<int>> arguments;
|
||||
std::vector<int> target;
|
||||
std::vector<Operation*> operation;
|
||||
std::map<std::string, int> variableIndices;
|
||||
std::set<std::string> variableNames;
|
||||
mutable std::vector<double> workspace;
|
||||
mutable std::vector<double> argValues;
|
||||
std::map<std::string, double> dummyVariables;
|
||||
};
|
||||
} // namespace Lepton
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "windowsIncludes.h"
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
|
||||
/**
|
||||
* This class is the interface for defining your own function that may be included in expressions.
|
||||
* To use it, create a concrete subclass that implements all of the virtual methods for each new function
|
||||
* you want to define. Then when you call Parser::parse() to parse an expression, pass a map of
|
||||
* function names to CustomFunction objects.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT CustomFunction
|
||||
{
|
||||
public:
|
||||
virtual ~CustomFunction() { }
|
||||
/**
|
||||
* Get the number of arguments this function exprects.
|
||||
*/
|
||||
virtual int getNumArguments() const = 0;
|
||||
/**
|
||||
* Evaluate the function.
|
||||
*
|
||||
* @param arguments the array of argument values
|
||||
*/
|
||||
virtual double evaluate(const double* arguments) const = 0;
|
||||
/**
|
||||
* Evaluate a derivative of the function.
|
||||
*
|
||||
* @param arguments the array of argument values
|
||||
* @param derivOrder an array specifying the number of times the function has been differentiated
|
||||
* with respect to each of its arguments. For example, the array {0, 2} indicates
|
||||
* a second derivative with respect to the second argument.
|
||||
*/
|
||||
virtual double evaluateDerivative(const double* arguments, const int* derivOrder) const = 0;
|
||||
/**
|
||||
* Create a new duplicate of this object on the heap using the "new" operator.
|
||||
*/
|
||||
virtual CustomFunction* clone() const = 0;
|
||||
};
|
||||
} // namespace Lepton
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef LEPTON_EXCEPTION_H_
|
||||
#define LEPTON_EXCEPTION_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
|
||||
/**
|
||||
* This class is used for all exceptions thrown by Lepton.
|
||||
*/
|
||||
|
||||
class Exception final : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const std::string& message) : message(message) { }
|
||||
~Exception() throw() override { }
|
||||
|
||||
const char* what() const throw() override { return message.c_str(); }
|
||||
|
||||
private:
|
||||
std::string message;
|
||||
};
|
||||
} // namespace Lepton
|
||||
|
||||
#endif /*LEPTON_EXCEPTION_H_*/
|
||||
@@ -0,0 +1,92 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009-2013 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ExpressionProgram.h"
|
||||
#include "Operation.h"
|
||||
#include "ParsedExpression.h"
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
ExpressionProgram::ExpressionProgram() : maxArgs(0), stackSize(0) {}
|
||||
|
||||
ExpressionProgram::ExpressionProgram(const ParsedExpression& expression) : maxArgs(0), stackSize(0)
|
||||
{
|
||||
buildProgram(expression.getRootNode());
|
||||
int currentStackSize = 0;
|
||||
for (size_t i = 0; i < operations.size(); ++i)
|
||||
{
|
||||
int args = operations[i]->getNumArguments();
|
||||
if (args > maxArgs) { maxArgs = args; }
|
||||
currentStackSize += 1 - args;
|
||||
if (currentStackSize > stackSize) { stackSize = currentStackSize; }
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionProgram::~ExpressionProgram() { for (size_t i = 0; i < operations.size(); ++i) { delete operations[i]; } }
|
||||
|
||||
ExpressionProgram::ExpressionProgram(const ExpressionProgram& program) { *this = program; }
|
||||
|
||||
ExpressionProgram& ExpressionProgram::operator=(const ExpressionProgram& program)
|
||||
{
|
||||
maxArgs = program.maxArgs;
|
||||
stackSize = program.stackSize;
|
||||
operations.resize(program.operations.size());
|
||||
for (size_t i = 0; i < operations.size(); ++i) { operations[i] = program.operations[i]->clone(); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ExpressionProgram::buildProgram(const ExpressionTreeNode& node)
|
||||
{
|
||||
for (size_t i = node.getChildren().size() - 1; i >= 0; i--) { buildProgram(node.getChildren()[i]); }
|
||||
operations.push_back(node.getOperation().clone());
|
||||
}
|
||||
|
||||
int ExpressionProgram::getNumOperations() const { return int(operations.size()); }
|
||||
|
||||
const Operation& ExpressionProgram::getOperation(int index) const { return *operations[index]; }
|
||||
|
||||
int ExpressionProgram::getStackSize() const { return stackSize; }
|
||||
|
||||
double ExpressionProgram::evaluate() const { return evaluate(std::map<std::string, double>()); }
|
||||
|
||||
double ExpressionProgram::evaluate(const std::map<std::string, double>& variables) const
|
||||
{
|
||||
std::vector<double> stack(stackSize + 1);
|
||||
int stackPointer = stackSize;
|
||||
for (size_t i = 0; i < operations.size(); ++i)
|
||||
{
|
||||
int numArgs = operations[i]->getNumArguments();
|
||||
double result = operations[i]->evaluate(&stack[stackPointer], variables);
|
||||
stackPointer += numArgs - 1;
|
||||
stack[stackPointer] = result;
|
||||
}
|
||||
return stack[stackSize - 1];
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#ifndef LEPTON_EXPRESSION_PROGRAM_H_
|
||||
#define LEPTON_EXPRESSION_PROGRAM_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "windowsIncludes.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
class ParsedExpression;
|
||||
|
||||
/**
|
||||
* An ExpressionProgram is a linear sequence of Operations for evaluating an expression. The evaluation
|
||||
* is done with a stack. The arguments to each Operation are first taken off the stack in order, then it is
|
||||
* evaluated and the result is pushed back onto the stack. At the end, the stack contains a single value,
|
||||
* which is the value of the expression.
|
||||
*
|
||||
* An ExpressionProgram is created by calling createProgram() on a ParsedExpression.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT ExpressionProgram
|
||||
{
|
||||
public:
|
||||
ExpressionProgram();
|
||||
ExpressionProgram(const ExpressionProgram& program);
|
||||
~ExpressionProgram();
|
||||
ExpressionProgram& operator=(const ExpressionProgram& program);
|
||||
/**
|
||||
* Get the number of Operations that make up this program.
|
||||
*/
|
||||
int getNumOperations() const;
|
||||
/**
|
||||
* Get an Operation in this program.
|
||||
*/
|
||||
const Operation& getOperation(int index) const;
|
||||
/**
|
||||
* Get the size of the stack needed to execute this program. This is the largest number of elements present
|
||||
* on the stack at any point during evaluation.
|
||||
*/
|
||||
int getStackSize() const;
|
||||
/**
|
||||
* Evaluate the expression. If the expression involves any variables, this method will throw an exception.
|
||||
*/
|
||||
double evaluate() const;
|
||||
/**
|
||||
* Evaluate the expression.
|
||||
*
|
||||
* @param variables a map specifying the values of all variables that appear in the expression. If any
|
||||
* variable appears in the expression but is not included in this map, an exception
|
||||
* will be thrown.
|
||||
*/
|
||||
double evaluate(const std::map<std::string, double>& variables) const;
|
||||
private:
|
||||
friend class ParsedExpression;
|
||||
ExpressionProgram(const ParsedExpression& expression);
|
||||
void buildProgram(const ExpressionTreeNode& node);
|
||||
std::vector<Operation*> operations;
|
||||
int maxArgs, stackSize;
|
||||
};
|
||||
} // namespace Lepton
|
||||
|
||||
#endif /*LEPTON_EXPRESSION_PROGRAM_H_*/
|
||||
@@ -0,0 +1,103 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "Exception.h"
|
||||
#include "Operation.h"
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const std::vector<ExpressionTreeNode>& children) : operation(operation), children(children)
|
||||
{
|
||||
if (size_t(operation->getNumArguments()) != children.size())
|
||||
{
|
||||
throw Exception("Parse error: wrong number of arguments to function: " + operation->getName());
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child1, const ExpressionTreeNode& child2) : operation(operation)
|
||||
{
|
||||
children.push_back(child1);
|
||||
children.push_back(child2);
|
||||
if (size_t(operation->getNumArguments()) != children.size())
|
||||
{
|
||||
throw Exception("Parse error: wrong number of arguments to function: " + operation->getName());
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child) : operation(operation)
|
||||
{
|
||||
children.push_back(child);
|
||||
if (size_t(operation->getNumArguments()) != children.size())
|
||||
{
|
||||
throw Exception("Parse error: wrong number of arguments to function: " + operation->getName());
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode(Operation* operation) : operation(operation)
|
||||
{
|
||||
if (size_t(operation->getNumArguments()) != children.size())
|
||||
{
|
||||
throw Exception("Parse error: wrong number of arguments to function: " + operation->getName());
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode(const ExpressionTreeNode& node) : operation(node.getOperation().clone()), children(node.getChildren()) {}
|
||||
|
||||
ExpressionTreeNode::ExpressionTreeNode() {}
|
||||
|
||||
ExpressionTreeNode::~ExpressionTreeNode() { delete operation; }
|
||||
|
||||
bool ExpressionTreeNode::operator!=(const ExpressionTreeNode& node) const
|
||||
{
|
||||
if (node.getOperation() != getOperation()) { return true; }
|
||||
if (getOperation().isSymmetric() && getChildren().size() == 2)
|
||||
{
|
||||
if (getChildren()[0] == node.getChildren()[0] && getChildren()[1] == node.getChildren()[1]) { return false; }
|
||||
if (getChildren()[0] == node.getChildren()[1] && getChildren()[1] == node.getChildren()[0]) { return false; }
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < getChildren().size(); ++i) { if (getChildren()[i] != node.getChildren()[i]) { return true; } }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExpressionTreeNode::operator==(const ExpressionTreeNode& node) const { return !(*this != node); }
|
||||
|
||||
ExpressionTreeNode& ExpressionTreeNode::operator=(const ExpressionTreeNode& node)
|
||||
{
|
||||
delete operation;
|
||||
operation = node.getOperation().clone();
|
||||
children = node.getChildren();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Operation& ExpressionTreeNode::getOperation() const { return *operation; }
|
||||
const std::vector<ExpressionTreeNode>& ExpressionTreeNode::getChildren() const { return children; }
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef LEPTON_EXPRESSION_TREE_NODE_H_
|
||||
#define LEPTON_EXPRESSION_TREE_NODE_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "windowsIncludes.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
class Operation;
|
||||
|
||||
/**
|
||||
* This class represents a node in the abstract syntax tree representation of an expression.
|
||||
* Each node is defined by an Operation and a set of children. When the expression is
|
||||
* evaluated, each child is first evaluated in order, then the resulting values are passed
|
||||
* as the arguments to the Operation's evaluate() method.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT ExpressionTreeNode
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new ExpressionTreeNode.
|
||||
*
|
||||
* @param operation the operation for this node. The ExpressionTreeNode takes over ownership
|
||||
* of this object, and deletes it when the node is itself deleted.
|
||||
* @param children the children of this node
|
||||
*/
|
||||
ExpressionTreeNode(Operation* operation, const std::vector<ExpressionTreeNode>& children);
|
||||
/**
|
||||
* Create a new ExpressionTreeNode with two children.
|
||||
*
|
||||
* @param operation the operation for this node. The ExpressionTreeNode takes over ownership
|
||||
* of this object, and deletes it when the node is itself deleted.
|
||||
* @param child1 the first child of this node
|
||||
* @param child2 the second child of this node
|
||||
*/
|
||||
ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child1, const ExpressionTreeNode& child2);
|
||||
/**
|
||||
* Create a new ExpressionTreeNode with one child.
|
||||
*
|
||||
* @param operation the operation for this node. The ExpressionTreeNode takes over ownership
|
||||
* of this object, and deletes it when the node is itself deleted.
|
||||
* @param child the child of this node
|
||||
*/
|
||||
ExpressionTreeNode(Operation* operation, const ExpressionTreeNode& child);
|
||||
/**
|
||||
* Create a new ExpressionTreeNode with no children.
|
||||
*
|
||||
* @param operation the operation for this node. The ExpressionTreeNode takes over ownership
|
||||
* of this object, and deletes it when the node is itself deleted.
|
||||
*/
|
||||
ExpressionTreeNode(Operation* operation);
|
||||
ExpressionTreeNode(const ExpressionTreeNode& node);
|
||||
ExpressionTreeNode();
|
||||
~ExpressionTreeNode();
|
||||
bool operator==(const ExpressionTreeNode& node) const;
|
||||
bool operator!=(const ExpressionTreeNode& node) const;
|
||||
ExpressionTreeNode& operator=(const ExpressionTreeNode& node);
|
||||
/**
|
||||
* Get the Operation performed by this node.
|
||||
*/
|
||||
const Operation& getOperation() const;
|
||||
/**
|
||||
* Get this node's child nodes.
|
||||
*/
|
||||
const std::vector<ExpressionTreeNode>& getChildren() const;
|
||||
private:
|
||||
Operation* operation = nullptr;
|
||||
std::vector<ExpressionTreeNode> children;
|
||||
};
|
||||
} // namespace Lepton
|
||||
|
||||
#endif /*LEPTON_EXPRESSION_TREE_NODE_H_*/
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef LEPTON_H_
|
||||
#define LEPTON_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "CompiledExpression.h"
|
||||
#include "CustomFunction.h"
|
||||
#include "ExpressionProgram.h"
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "Operation.h"
|
||||
#include "ParsedExpression.h"
|
||||
#include "Parser.h"
|
||||
|
||||
#endif /*LEPTON_H_*/
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
@@ -0,0 +1,87 @@
|
||||
#ifndef LEPTON_MSVC_ERFC_H_
|
||||
#define LEPTON_MSVC_ERFC_H_
|
||||
|
||||
/*
|
||||
* Up to version 11 (VC++ 2012), Microsoft does not support the
|
||||
* standard C99 erf() and erfc() functions so we have to fake them here.
|
||||
* These were added in version 12 (VC++ 2013), which sets _MSC_VER=1800
|
||||
* (VC11 has _MSC_VER=1700).
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define M_PI 3.14159265358979323846264338327950288
|
||||
|
||||
#if _MSC_VER <= 1700 // 1700 is VC11, 1800 is VC12
|
||||
/***************************
|
||||
* erf.cpp
|
||||
* author: Steve Strand
|
||||
* written: 29-Jan-04
|
||||
***************************/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
static const double rel_error= 1E-12; //calculate 12 significant figures
|
||||
//you can adjust rel_error to trade off between accuracy and speed
|
||||
//but don't ask for > 15 figures (assuming usual 52 bit mantissa in a double)
|
||||
|
||||
static double erfc(double x);
|
||||
|
||||
static double erf(double x)
|
||||
//erf(x) = 2/sqrt(pi)*integral(exp(-t^2),t,0,x)
|
||||
// = 2/sqrt(pi)*[x - x^3/3 + x^5/5*2! - x^7/7*3! + ...]
|
||||
// = 1-erfc(x)
|
||||
{
|
||||
static const double two_sqrtpi= 1.128379167095512574; // 2/sqrt(pi)
|
||||
if (fabs(x) > 2.2) {
|
||||
return 1.0 - erfc(x); //use continued fraction when fabs(x) > 2.2
|
||||
}
|
||||
double sum= x, term= x, xsqr= x*x;
|
||||
int j= 1;
|
||||
do {
|
||||
term*= xsqr/j;
|
||||
sum-= term/(2*j+1);
|
||||
++j;
|
||||
term*= xsqr/j;
|
||||
sum+= term/(2*j+1);
|
||||
++j;
|
||||
} while (fabs(term)/sum > rel_error);
|
||||
return two_sqrtpi*sum;
|
||||
}
|
||||
|
||||
|
||||
static double erfc(double x)
|
||||
//erfc(x) = 2/sqrt(pi)*integral(exp(-t^2),t,x,inf)
|
||||
// = exp(-x^2)/sqrt(pi) * [1/x+ (1/2)/x+ (2/2)/x+ (3/2)/x+ (4/2)/x+ ...]
|
||||
// = 1-erf(x)
|
||||
//expression inside [] is a continued fraction so '+' means add to denominator only
|
||||
{
|
||||
static const double one_sqrtpi= 0.564189583547756287; // 1/sqrt(pi)
|
||||
if (fabs(x) < 2.2) {
|
||||
return 1.0 - erf(x); //use series when fabs(x) < 2.2
|
||||
}
|
||||
// Don't look for x==0 here!
|
||||
if (x < 0) { //continued fraction only valid for x>0
|
||||
return 2.0 - erfc(-x);
|
||||
}
|
||||
double a=1, b=x; //last two convergent numerators
|
||||
double c=x, d=x*x+0.5; //last two convergent denominators
|
||||
double q1, q2= b/d; //last two convergents (a/c and b/d)
|
||||
double n= 1.0, t;
|
||||
do {
|
||||
t= a*n+b*x;
|
||||
a= b;
|
||||
b= t;
|
||||
t= c*n+d*x;
|
||||
c= d;
|
||||
d= t;
|
||||
n+= 0.5;
|
||||
q1= q2;
|
||||
q2= b/d;
|
||||
} while (fabs(q1-q2)/q2 > rel_error);
|
||||
return one_sqrtpi*exp(-x*x)*q2;
|
||||
}
|
||||
|
||||
#endif // _MSC_VER <= 1700
|
||||
#endif // _MSC_VER
|
||||
|
||||
#endif // LEPTON_MSVC_ERFC_H_
|
||||
@@ -0,0 +1,355 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "Operation.h"
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "MSVC_erfc.h"
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
double Operation::Erf::evaluate(double* args, const std::map<std::string, double>& /*variable*/) const { return erf(args[0]); }
|
||||
|
||||
double Operation::Erfc::evaluate(double* args, const std::map<std::string, double>& /*variable*/) const { return erfc(args[0]); }
|
||||
|
||||
ExpressionTreeNode Operation::Constant::differentiate(const std::vector<ExpressionTreeNode>& /*children*/,
|
||||
const std::vector<ExpressionTreeNode>& /*childDerivs*/, const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Constant(0.0));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Variable::differentiate(const std::vector<ExpressionTreeNode>& /*children*/,
|
||||
const std::vector<ExpressionTreeNode>& /*childDerivs*/, const std::string& variable) const
|
||||
{
|
||||
if (variable == name) { return ExpressionTreeNode(new Constant(1.0)); }
|
||||
return ExpressionTreeNode(new Constant(0.0));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Custom::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
if (function->getNumArguments() == 0) { return ExpressionTreeNode(new Constant(0.0)); }
|
||||
ExpressionTreeNode result = ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Custom(*this, 0), children), childDerivs[0]);
|
||||
for (int i = 1; i < getNumArguments(); ++i)
|
||||
{
|
||||
result = ExpressionTreeNode(new Add(),
|
||||
result,
|
||||
ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Custom(*this, i), children), childDerivs[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Add::differentiate(const std::vector<ExpressionTreeNode>& /*children*/, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Add(), childDerivs[0], childDerivs[1]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Subtract::differentiate(const std::vector<ExpressionTreeNode>& /*children*/, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Subtract(), childDerivs[0], childDerivs[1]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Multiply::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Add(),
|
||||
ExpressionTreeNode(new Multiply(), children[0], childDerivs[1]),
|
||||
ExpressionTreeNode(new Multiply(), children[1], childDerivs[0]));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Divide::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Divide(),
|
||||
ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Multiply(), children[1], childDerivs[0]),
|
||||
ExpressionTreeNode(new Multiply(), children[0], childDerivs[1])),
|
||||
ExpressionTreeNode(new Square(), children[1]));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Power::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Add(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
children[1],
|
||||
ExpressionTreeNode(new Power(),
|
||||
children[0], ExpressionTreeNode(new AddConstant(-1.0), children[1]))),
|
||||
childDerivs[0]),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Log(), children[0]),
|
||||
ExpressionTreeNode(new Power(), children[0], children[1])),
|
||||
childDerivs[1]));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Negate::differentiate(const std::vector<ExpressionTreeNode>& /*children*/, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const { return ExpressionTreeNode(new Negate(), childDerivs[0]); }
|
||||
|
||||
ExpressionTreeNode Operation::Sqrt::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new MultiplyConstant(0.5),
|
||||
ExpressionTreeNode(new Reciprocal(),
|
||||
ExpressionTreeNode(new Sqrt(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Exp::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Exp(), children[0]), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Log::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Reciprocal(), children[0]), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Sin::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Cos(), children[0]), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Cos::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Sin(), children[0])),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Sec::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Sec(), children[0]),
|
||||
ExpressionTreeNode(new Tan(), children[0])),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Csc::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Csc(), children[0]),
|
||||
ExpressionTreeNode(new Cot(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Tan::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Square(),
|
||||
ExpressionTreeNode(new Sec(), children[0])),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Cot::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Square(),
|
||||
ExpressionTreeNode(new Csc(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Asin::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Reciprocal(),
|
||||
ExpressionTreeNode(new Sqrt(),
|
||||
ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Constant(1.0)),
|
||||
ExpressionTreeNode(new Square(), children[0])))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Acos::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Reciprocal(),
|
||||
ExpressionTreeNode(new Sqrt(),
|
||||
ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Constant(1.0)),
|
||||
ExpressionTreeNode(new Square(), children[0]))))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Atan::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Reciprocal(),
|
||||
ExpressionTreeNode(new AddConstant(1.0),
|
||||
ExpressionTreeNode(new Square(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Sinh::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Cosh(), children[0]), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Cosh::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new Sinh(), children[0]), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Tanh::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Constant(1.0)),
|
||||
ExpressionTreeNode(new Square(),
|
||||
ExpressionTreeNode(new Tanh(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Erf::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Constant(2.0 / sqrt(M_PI))),
|
||||
ExpressionTreeNode(new Exp(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Square(), children[0])))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Erfc::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Constant(-2.0 / sqrt(M_PI))),
|
||||
ExpressionTreeNode(new Exp(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Square(), children[0])))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Step::differentiate(const std::vector<ExpressionTreeNode>& /*children*/, const std::vector<ExpressionTreeNode>& /*childDerivs*/,
|
||||
const std::string& /*variable*/) const { return ExpressionTreeNode(new Constant(0.0)); }
|
||||
|
||||
ExpressionTreeNode Operation::Delta::differentiate(const std::vector<ExpressionTreeNode>& /*children*/, const std::vector<ExpressionTreeNode>& /*childDerivs*/,
|
||||
const std::string& /*variable*/) const { return ExpressionTreeNode(new Constant(0.0)); }
|
||||
|
||||
ExpressionTreeNode Operation::Square::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new MultiplyConstant(2.0),
|
||||
children[0]),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Cube::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new MultiplyConstant(3.0),
|
||||
ExpressionTreeNode(new Square(), children[0])),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Reciprocal::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(),
|
||||
ExpressionTreeNode(new Negate(),
|
||||
ExpressionTreeNode(new Reciprocal(),
|
||||
ExpressionTreeNode(new Square(), children[0]))),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::AddConstant::differentiate(const std::vector<ExpressionTreeNode>& /*children*/,
|
||||
const std::vector<ExpressionTreeNode>& childDerivs, const std::string& /*variable*/) const
|
||||
{
|
||||
return childDerivs[0];
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::MultiplyConstant::differentiate(const std::vector<ExpressionTreeNode>& /*children*/,
|
||||
const std::vector<ExpressionTreeNode>& childDerivs, const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new MultiplyConstant(value), childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::PowerConstant::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
return ExpressionTreeNode(new Multiply(), ExpressionTreeNode(new MultiplyConstant(value), ExpressionTreeNode(new PowerConstant(value - 1), children[0])),
|
||||
childDerivs[0]);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Min::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
const ExpressionTreeNode step(new Step(), ExpressionTreeNode(new Subtract(), children[0], children[1]));
|
||||
return ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Multiply(), childDerivs[1], step),
|
||||
ExpressionTreeNode(new Multiply(), childDerivs[0],
|
||||
ExpressionTreeNode(new AddConstant(-1), step)));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Max::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
const ExpressionTreeNode step(new Step(), ExpressionTreeNode(new Subtract(), children[0], children[1]));
|
||||
return ExpressionTreeNode(new Subtract(),
|
||||
ExpressionTreeNode(new Multiply(), childDerivs[0], step),
|
||||
ExpressionTreeNode(new Multiply(), childDerivs[1],
|
||||
ExpressionTreeNode(new AddConstant(-1), step)));
|
||||
}
|
||||
|
||||
ExpressionTreeNode Operation::Abs::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs,
|
||||
const std::string& /*variable*/) const
|
||||
{
|
||||
const ExpressionTreeNode step(new Step(), children[0]);
|
||||
return ExpressionTreeNode(new Multiply(), childDerivs[0], ExpressionTreeNode(new AddConstant(-1), ExpressionTreeNode(new MultiplyConstant(2), step)));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,370 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ParsedExpression.h"
|
||||
#include "CompiledExpression.h"
|
||||
#include "ExpressionProgram.h"
|
||||
#include "Operation.h"
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
ParsedExpression::ParsedExpression() : rootNode(ExpressionTreeNode()) {}
|
||||
|
||||
ParsedExpression::ParsedExpression(const ExpressionTreeNode& rootNode) : rootNode(rootNode) {}
|
||||
|
||||
const ExpressionTreeNode& ParsedExpression::getRootNode() const { return rootNode; }
|
||||
|
||||
double ParsedExpression::evaluate() const { return evaluate(getRootNode(), std::map<std::string, double>()); }
|
||||
|
||||
double ParsedExpression::evaluate(const std::map<std::string, double>& variables) const { return evaluate(getRootNode(), variables); }
|
||||
|
||||
double ParsedExpression::evaluate(const ExpressionTreeNode& node, const std::map<std::string, double>& variables)
|
||||
{
|
||||
size_t numArgs = node.getChildren().size();
|
||||
std::vector<double> args(std::max(numArgs, size_t(1)));
|
||||
for (size_t i = 0; i < numArgs; ++i) { args[i] = evaluate(node.getChildren()[i], variables); }
|
||||
return node.getOperation().evaluate(&args[0], variables);
|
||||
}
|
||||
|
||||
ParsedExpression ParsedExpression::optimize() const
|
||||
{
|
||||
ExpressionTreeNode result = precalculateConstantSubexpressions(getRootNode());
|
||||
while (true)
|
||||
{
|
||||
ExpressionTreeNode simplified = substituteSimplerExpression(result);
|
||||
if (simplified == result) { break; }
|
||||
result = simplified;
|
||||
}
|
||||
return ParsedExpression(result);
|
||||
}
|
||||
|
||||
ParsedExpression ParsedExpression::optimize(const std::map<std::string, double>& variables) const
|
||||
{
|
||||
ExpressionTreeNode result = preevaluateVariables(getRootNode(), variables);
|
||||
result = precalculateConstantSubexpressions(result);
|
||||
while (true)
|
||||
{
|
||||
ExpressionTreeNode simplified = substituteSimplerExpression(result);
|
||||
if (simplified == result) { break; }
|
||||
result = simplified;
|
||||
}
|
||||
return ParsedExpression(result);
|
||||
}
|
||||
|
||||
ExpressionTreeNode ParsedExpression::preevaluateVariables(const ExpressionTreeNode& node, const std::map<std::string, double>& variables)
|
||||
{
|
||||
if (node.getOperation().getId() == Operation::VARIABLE)
|
||||
{
|
||||
const Operation::Variable& var = dynamic_cast<const Operation::Variable&>(node.getOperation());
|
||||
auto iter = variables.find(var.getName());
|
||||
if (iter == variables.end()) { return node; }
|
||||
return ExpressionTreeNode(new Operation::Constant(iter->second));
|
||||
}
|
||||
std::vector<ExpressionTreeNode> children(node.getChildren().size());
|
||||
for (size_t i = 0; i < children.size(); ++i) { children[i] = preevaluateVariables(node.getChildren()[i], variables); }
|
||||
return ExpressionTreeNode(node.getOperation().clone(), children);
|
||||
}
|
||||
|
||||
ExpressionTreeNode ParsedExpression::precalculateConstantSubexpressions(const ExpressionTreeNode& node)
|
||||
{
|
||||
std::vector<ExpressionTreeNode> children(node.getChildren().size());
|
||||
for (size_t i = 0; i < children.size(); ++i) { children[i] = precalculateConstantSubexpressions(node.getChildren()[i]); }
|
||||
ExpressionTreeNode result = ExpressionTreeNode(node.getOperation().clone(), children);
|
||||
if (node.getOperation().getId() == Operation::VARIABLE) { return result; }
|
||||
for (size_t i = 0; i < children.size(); ++i) { if (children[i].getOperation().getId() != Operation::CONSTANT) { return result; } }
|
||||
return ExpressionTreeNode(new Operation::Constant(evaluate(result, std::map<std::string, double>())));
|
||||
}
|
||||
|
||||
ExpressionTreeNode ParsedExpression::substituteSimplerExpression(const ExpressionTreeNode& node)
|
||||
{
|
||||
std::vector<ExpressionTreeNode> childs(node.getChildren().size());
|
||||
for (size_t i = 0; i < childs.size(); ++i) { childs[i] = substituteSimplerExpression(node.getChildren()[i]); }
|
||||
Operation::Id op1 = childs[0].getOperation().getId();
|
||||
Operation::Id op2 = childs[1].getOperation().getId();
|
||||
switch (node.getOperation().getId())
|
||||
{
|
||||
case Operation::ADD:
|
||||
{
|
||||
const double first = getConstantValue(childs[0]);
|
||||
const double second = getConstantValue(childs[1]);
|
||||
if (first == 0.0) { return childs[1]; } // Add 0
|
||||
if (second == 0.0) { return childs[0]; } // Add 0
|
||||
if (first == first) { return ExpressionTreeNode(new Operation::AddConstant(first), childs[1]); } // Add a constant
|
||||
if (second == second) { return ExpressionTreeNode(new Operation::AddConstant(second), childs[0]); } // Add a constant
|
||||
if (op2 == Operation::NEGATE) { return ExpressionTreeNode(new Operation::Subtract(), childs[0], childs[1].getChildren()[0]); } // a+(-b) = a-b
|
||||
if (op1 == Operation::NEGATE) { return ExpressionTreeNode(new Operation::Subtract(), childs[1], childs[0].getChildren()[0]); } // (-a)+b = b-a
|
||||
break;
|
||||
}
|
||||
case Operation::SUBTRACT:
|
||||
{
|
||||
if (childs[0] == childs[1]) { return ExpressionTreeNode(new Operation::Constant(0.0)); } // Subtracting anything from itself is 0
|
||||
const double first = getConstantValue(childs[0]);
|
||||
if (first == 0.0) { return ExpressionTreeNode(new Operation::Negate(), childs[1]); } // Subtract from 0
|
||||
const double second = getConstantValue(childs[1]);
|
||||
if (second == 0.0) { return childs[0]; } // Subtract 0
|
||||
if (second == second) { return ExpressionTreeNode(new Operation::AddConstant(-second), childs[0]); } // Subtract a constant
|
||||
if (op2 == Operation::NEGATE) { return ExpressionTreeNode(new Operation::Add(), childs[0], childs[1].getChildren()[0]); } // a-(-b) = a+b
|
||||
break;
|
||||
}
|
||||
case Operation::MULTIPLY:
|
||||
{
|
||||
double first = getConstantValue(childs[0]);
|
||||
double second = getConstantValue(childs[1]);
|
||||
if (first == 0.0 || second == 0.0) { return ExpressionTreeNode(new Operation::Constant(0.0)); } // Multiply by 0
|
||||
if (first == 1.0) { return childs[1]; } // Multiply by 1
|
||||
if (second == 1.0) { return childs[0]; } // Multiply by 1
|
||||
if (op1 == Operation::CONSTANT)
|
||||
{ // Multiply by a constant
|
||||
if (op2 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Combine two multiplies into a single one
|
||||
return ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(first * dynamic_cast<const Operation::MultiplyConstant*>(&childs[1].getOperation())->getValue()),
|
||||
childs[1].getChildren()[0]);
|
||||
}
|
||||
return ExpressionTreeNode(new Operation::MultiplyConstant(first), childs[1]);
|
||||
}
|
||||
if (op2 == Operation::CONSTANT)
|
||||
{ // Multiply by a constant
|
||||
if (op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Combine two multiplies into a single one
|
||||
return ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(second * dynamic_cast<const Operation::MultiplyConstant*>(&childs[0].getOperation())->getValue()),
|
||||
childs[0].getChildren()[0]);
|
||||
}
|
||||
return ExpressionTreeNode(new Operation::MultiplyConstant(second), childs[0]);
|
||||
}
|
||||
if (op1 == Operation::NEGATE && op2 == Operation::NEGATE)
|
||||
{ // The two negations cancel
|
||||
return ExpressionTreeNode(new Operation::Multiply(), childs[0].getChildren()[0], childs[1].getChildren()[0]);
|
||||
}
|
||||
if (op1 == Operation::NEGATE && op2 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Negate the constant
|
||||
return ExpressionTreeNode(new Operation::Multiply(), childs[0].getChildren()[0],
|
||||
ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(
|
||||
-dynamic_cast<const Operation::MultiplyConstant*>(&childs[1].getOperation())->getValue()),
|
||||
childs[1].getChildren()[0]));
|
||||
}
|
||||
if (op2 == Operation::NEGATE && op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Negate the constant
|
||||
return ExpressionTreeNode(new Operation::Multiply(),
|
||||
ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(
|
||||
-dynamic_cast<const Operation::MultiplyConstant*>(&childs[0].getOperation())->getValue()),
|
||||
childs[0].getChildren()[0]), childs[1].getChildren()[0]);
|
||||
}
|
||||
if (op1 == Operation::NEGATE)
|
||||
{ // Pull the negation out so it can possibly be optimized further
|
||||
return ExpressionTreeNode(new Operation::Negate(), ExpressionTreeNode(new Operation::Multiply(), childs[0].getChildren()[0], childs[1]));
|
||||
}
|
||||
if (op2 == Operation::NEGATE)
|
||||
{ // Pull the negation out so it can possibly be optimized further
|
||||
return ExpressionTreeNode(new Operation::Negate(), ExpressionTreeNode(new Operation::Multiply(), childs[0], childs[1].getChildren()[0]));
|
||||
}
|
||||
if (op2 == Operation::RECIPROCAL) { return ExpressionTreeNode(new Operation::Divide(), childs[0], childs[1].getChildren()[0]); } // a*(1/b) = a/b
|
||||
if (op1 == Operation::RECIPROCAL) { return ExpressionTreeNode(new Operation::Divide(), childs[1], childs[0].getChildren()[0]); } // (1/a)*b = b/a
|
||||
if (childs[0] == childs[1]) { return ExpressionTreeNode(new Operation::Square(), childs[0]); } // x*x = square(x)
|
||||
if (op1 == Operation::SQUARE && childs[0].getChildren()[0] == childs[1]) { return ExpressionTreeNode(new Operation::Cube(), childs[1]); } // x^3
|
||||
if (op2 == Operation::SQUARE && childs[1].getChildren()[0] == childs[0]) { return ExpressionTreeNode(new Operation::Cube(), childs[0]); } // x^3
|
||||
break;
|
||||
}
|
||||
case Operation::DIVIDE:
|
||||
{
|
||||
if (childs[0] == childs[1]) { return ExpressionTreeNode(new Operation::Constant(1.0)); } // Dividing anything from itself is 0
|
||||
const double numerator = getConstantValue(childs[0]);
|
||||
if (numerator == 0.0) { return ExpressionTreeNode(new Operation::Constant(0.0)); } // 0 divided by something
|
||||
if (numerator == 1.0) { return ExpressionTreeNode(new Operation::Reciprocal(), childs[1]); } // 1 divided by something
|
||||
const double denominator = getConstantValue(childs[1]);
|
||||
if (denominator == 1.0) { return childs[0]; } // Divide by 1
|
||||
if (op2 == Operation::CONSTANT)
|
||||
{
|
||||
if (op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Combine a multiply and a divide into one multiply
|
||||
return ExpressionTreeNode(new Operation::MultiplyConstant
|
||||
(dynamic_cast<const Operation::MultiplyConstant*>(&childs[0].getOperation())->getValue() / denominator),
|
||||
childs[0].getChildren()[0]);
|
||||
}
|
||||
return ExpressionTreeNode(new Operation::MultiplyConstant(1.0 / denominator), childs[0]); // Replace a divide with a multiply
|
||||
}
|
||||
if (op1 == Operation::NEGATE && op2 == Operation::NEGATE)
|
||||
{ // The two negations cancel
|
||||
return ExpressionTreeNode(new Operation::Divide(), childs[0].getChildren()[0], childs[1].getChildren()[0]);
|
||||
}
|
||||
if (op2 == Operation::NEGATE && op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Negate the constant
|
||||
return ExpressionTreeNode(new Operation::Divide(), ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(
|
||||
-dynamic_cast<const Operation::MultiplyConstant*>(&childs[0].getOperation())->getValue()),
|
||||
childs[0].getChildren()[0]), childs[1].getChildren()[0]);
|
||||
}
|
||||
if (op1 == Operation::NEGATE)
|
||||
{ // Pull the negation out so it can possibly be optimized further
|
||||
return ExpressionTreeNode(new Operation::Negate(), ExpressionTreeNode(new Operation::Divide(), childs[0].getChildren()[0], childs[1]));
|
||||
}
|
||||
if (op2 == Operation::NEGATE)
|
||||
{ // Pull the negation out so it can possibly be optimized further
|
||||
return ExpressionTreeNode(new Operation::Negate(), ExpressionTreeNode(new Operation::Divide(), childs[0], childs[1].getChildren()[0]));
|
||||
}
|
||||
if (childs[1].getOperation().getId() == Operation::RECIPROCAL)
|
||||
{ // a/(1/b) = a*b
|
||||
return ExpressionTreeNode(new Operation::Multiply(), childs[0], childs[1].getChildren()[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Operation::POWER:
|
||||
{
|
||||
double base = getConstantValue(childs[0]);
|
||||
if (base == 0.0) { return ExpressionTreeNode(new Operation::Constant(0.0)); } // 0 to any power is 0
|
||||
if (base == 1.0) { return ExpressionTreeNode(new Operation::Constant(1.0)); } // 1 to any power is 1
|
||||
double exponent = getConstantValue(childs[1]);
|
||||
if (exponent == 0.0) { return ExpressionTreeNode(new Operation::Constant(1.0)); } // x^0 = 1
|
||||
if (exponent == 1.0) { return childs[0]; } // x^1 = x
|
||||
if (exponent == -1.0) { return ExpressionTreeNode(new Operation::Reciprocal(), childs[0]); } // x^-1 = recip(x)
|
||||
if (exponent == 2.0) { return ExpressionTreeNode(new Operation::Square(), childs[0]); } // x^2 = square(x)
|
||||
if (exponent == 3.0) { return ExpressionTreeNode(new Operation::Cube(), childs[0]); } // x^3 = cube(x)
|
||||
if (exponent == 0.5) { return ExpressionTreeNode(new Operation::Sqrt(), childs[0]); } // x^0.5 = sqrt(x)
|
||||
if (exponent == exponent) { return ExpressionTreeNode(new Operation::PowerConstant(exponent), childs[0]); } // Constant power
|
||||
break;
|
||||
}
|
||||
case Operation::NEGATE:
|
||||
{
|
||||
if (op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Combine a multiply and a negate into a single multiply
|
||||
return ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(-dynamic_cast<const Operation::MultiplyConstant*>(&childs[0].getOperation())->getValue()),
|
||||
childs[0].getChildren()[0]);
|
||||
}
|
||||
if (op1 == Operation::CONSTANT) { return ExpressionTreeNode(new Operation::Constant(-getConstantValue(childs[0]))); } // Negate a constant
|
||||
if (op1 == Operation::NEGATE) { return childs[0].getChildren()[0]; } // The two negations cancel
|
||||
break;
|
||||
}
|
||||
case Operation::MULTIPLY_CONSTANT:
|
||||
{
|
||||
if (op1 == Operation::MULTIPLY_CONSTANT)
|
||||
{ // Combine two multiplies into a single one
|
||||
return ExpressionTreeNode(
|
||||
new Operation::MultiplyConstant(
|
||||
dynamic_cast<const Operation::MultiplyConstant*>(&node.getOperation())->getValue() * dynamic_cast<const Operation::MultiplyConstant*>(&
|
||||
childs[0].getOperation())->getValue()), childs[0].getChildren()[0]);
|
||||
}
|
||||
if (op1 == Operation::CONSTANT)
|
||||
{ // Multiply two constants
|
||||
return ExpressionTreeNode(
|
||||
new Operation::Constant(
|
||||
dynamic_cast<const Operation::MultiplyConstant*>(&node.getOperation())->getValue() * getConstantValue(childs[0])));
|
||||
}
|
||||
if (op1 == Operation::NEGATE)
|
||||
{ // Combine a multiply and a negate into a single multiply
|
||||
return ExpressionTreeNode(new Operation::MultiplyConstant(-dynamic_cast<const Operation::MultiplyConstant*>(&node.getOperation())->getValue()),
|
||||
childs[0].getChildren()[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// If operation ID is not one of the above,
|
||||
// we don't substitute a simpler expression.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ExpressionTreeNode(node.getOperation().clone(), childs);
|
||||
}
|
||||
|
||||
ParsedExpression ParsedExpression::differentiate(const std::string& variable) const { return differentiate(getRootNode(), variable); }
|
||||
|
||||
ExpressionTreeNode ParsedExpression::differentiate(const ExpressionTreeNode& node, const std::string& variable)
|
||||
{
|
||||
std::vector<ExpressionTreeNode> childDerivs(node.getChildren().size());
|
||||
for (size_t i = 0; i < childDerivs.size(); ++i) { childDerivs[i] = differentiate(node.getChildren()[i], variable); }
|
||||
return node.getOperation().differentiate(node.getChildren(), childDerivs, variable);
|
||||
}
|
||||
|
||||
double ParsedExpression::getConstantValue(const ExpressionTreeNode& node)
|
||||
{
|
||||
if (node.getOperation().getId() == Operation::CONSTANT) { return dynamic_cast<const Operation::Constant&>(node.getOperation()).getValue(); }
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
ExpressionProgram ParsedExpression::createProgram() const { return ExpressionProgram(*this); }
|
||||
|
||||
CompiledExpression ParsedExpression::createCompiledExpression() const { return CompiledExpression(*this); }
|
||||
|
||||
ParsedExpression ParsedExpression::renameVariables(const std::map<std::string, std::string>& replacements) const
|
||||
{
|
||||
return ParsedExpression(renameNodeVariables(getRootNode(), replacements));
|
||||
}
|
||||
|
||||
ExpressionTreeNode ParsedExpression::renameNodeVariables(const ExpressionTreeNode& node, const std::map<std::string, std::string>& replacements)
|
||||
{
|
||||
if (node.getOperation().getId() == Operation::VARIABLE)
|
||||
{
|
||||
auto replace = replacements.find(node.getOperation().getName());
|
||||
if (replace != replacements.end()) { return ExpressionTreeNode(new Operation::Variable(replace->second)); }
|
||||
}
|
||||
std::vector<ExpressionTreeNode> children;
|
||||
for (size_t i = 0; i < node.getChildren().size(); ++i) { children.push_back(renameNodeVariables(node.getChildren()[i], replacements)); }
|
||||
return ExpressionTreeNode(node.getOperation().clone(), children);
|
||||
}
|
||||
|
||||
std::ostream& Lepton::operator<<(std::ostream& out, const ExpressionTreeNode& node)
|
||||
{
|
||||
if (node.getOperation().isInfixOperator() && node.getChildren().size() == 2)
|
||||
{
|
||||
out << "(" << node.getChildren()[0] << ")" << node.getOperation().getName() << "(" << node.getChildren()[1] << ")";
|
||||
}
|
||||
else if (node.getOperation().isInfixOperator() && node.getChildren().size() == 1)
|
||||
{
|
||||
out << "(" << node.getChildren()[0] << ")" << node.getOperation().getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
out << node.getOperation().getName();
|
||||
if (!node.getChildren().empty())
|
||||
{
|
||||
out << "(";
|
||||
for (size_t i = 0; i < node.getChildren().size(); ++i)
|
||||
{
|
||||
if (i > 0) { out << ", "; }
|
||||
out << node.getChildren()[i];
|
||||
}
|
||||
out << ")";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& Lepton::operator<<(std::ostream& out, const ParsedExpression& exp)
|
||||
{
|
||||
out << exp.getRootNode();
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef LEPTON_PARSED_EXPRESSION_H_
|
||||
#define LEPTON_PARSED_EXPRESSION_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009=2013 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "windowsIncludes.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
class CompiledExpression;
|
||||
class ExpressionProgram;
|
||||
|
||||
/**
|
||||
* This class represents the result of parsing an expression. It provides methods for working with the
|
||||
* expression in various ways, such as evaluating it, getting the tree representation of the expresson, etc.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT ParsedExpression
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create an uninitialized ParsedExpression. This exists so that ParsedExpressions can be put in STL containers.
|
||||
* Doing anything with it will produce an exception.
|
||||
*/
|
||||
ParsedExpression();
|
||||
/**
|
||||
* Create a ParsedExpression. Normally you will not call this directly. Instead, use the Parser class
|
||||
* to parse expression.
|
||||
*/
|
||||
ParsedExpression(const ExpressionTreeNode& rootNode);
|
||||
/**
|
||||
* Get the root node of the expression's abstract syntax tree.
|
||||
*/
|
||||
const ExpressionTreeNode& getRootNode() const;
|
||||
/**
|
||||
* Evaluate the expression. If the expression involves any variables, this method will throw an exception.
|
||||
*/
|
||||
double evaluate() const;
|
||||
/**
|
||||
* Evaluate the expression.
|
||||
*
|
||||
* @param variables a map specifying the values of all variables that appear in the expression. If any
|
||||
* variable appears in the expression but is not included in this map, an exception
|
||||
* will be thrown.
|
||||
*/
|
||||
double evaluate(const std::map<std::string, double>& variables) const;
|
||||
/**
|
||||
* Create a new ParsedExpression which produces the same result as this one, but is faster to evaluate.
|
||||
*/
|
||||
ParsedExpression optimize() const;
|
||||
/**
|
||||
* Create a new ParsedExpression which produces the same result as this one, but is faster to evaluate.
|
||||
*
|
||||
* @param variables a map specifying values for a subset of variables that appear in the expression.
|
||||
* All occurrences of these variables in the expression are replaced with the values
|
||||
* specified.
|
||||
*/
|
||||
ParsedExpression optimize(const std::map<std::string, double>& variables) const;
|
||||
/**
|
||||
* Create a new ParsedExpression which is the analytic derivative of this expression with respect to a
|
||||
* particular variable.
|
||||
*
|
||||
* @param variable the variable with respect to which the derivate should be taken
|
||||
*/
|
||||
ParsedExpression differentiate(const std::string& variable) const;
|
||||
/**
|
||||
* Create an ExpressionProgram that represents the same calculation as this expression.
|
||||
*/
|
||||
ExpressionProgram createProgram() const;
|
||||
/**
|
||||
* Create a CompiledExpression that represents the same calculation as this expression.
|
||||
*/
|
||||
CompiledExpression createCompiledExpression() const;
|
||||
/**
|
||||
* Create a new ParsedExpression which is identical to this one, except that the names of some
|
||||
* variables have been changed.
|
||||
*
|
||||
* @param replacements a map whose keys are the names of variables, and whose values are the
|
||||
* new names to replace them with
|
||||
*/
|
||||
ParsedExpression renameVariables(const std::map<std::string, std::string>& replacements) const;
|
||||
private:
|
||||
static double evaluate(const ExpressionTreeNode& node, const std::map<std::string, double>& variables);
|
||||
static ExpressionTreeNode preevaluateVariables(const ExpressionTreeNode& node, const std::map<std::string, double>& variables);
|
||||
static ExpressionTreeNode precalculateConstantSubexpressions(const ExpressionTreeNode& node);
|
||||
static ExpressionTreeNode substituteSimplerExpression(const ExpressionTreeNode& node);
|
||||
static ExpressionTreeNode differentiate(const ExpressionTreeNode& node, const std::string& variable);
|
||||
static double getConstantValue(const ExpressionTreeNode& node);
|
||||
static ExpressionTreeNode renameNodeVariables(const ExpressionTreeNode& node, const std::map<std::string, std::string>& replacements);
|
||||
ExpressionTreeNode rootNode;
|
||||
};
|
||||
|
||||
LEPTON_EXPORT std::ostream& operator<<(std::ostream& out, const ExpressionTreeNode& node);
|
||||
|
||||
LEPTON_EXPORT std::ostream& operator<<(std::ostream& out, const ParsedExpression& exp);
|
||||
} // namespace Lepton
|
||||
|
||||
#endif /*LEPTON_PARSED_EXPRESSION_H_*/
|
||||
@@ -0,0 +1,388 @@
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009-2013 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "Parser.h"
|
||||
#include "CustomFunction.h"
|
||||
#include "ExpressionTreeNode.h"
|
||||
#include "Operation.h"
|
||||
#include "ParsedExpression.h"
|
||||
#include <exception>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Lepton;
|
||||
|
||||
static const std::string Digits = "0123456789";
|
||||
static const std::string Operators = "+-*/^";
|
||||
static const bool LeftAssociative[] = { true, true, true, true, false };
|
||||
static const int Precedence[] = { 0, 0, 1, 1, 3 };
|
||||
static const Operation::Id OperationId[] = { Operation::ADD, Operation::SUBTRACT, Operation::MULTIPLY, Operation::DIVIDE, Operation::POWER };
|
||||
|
||||
class Lepton::ParseToken
|
||||
{
|
||||
public:
|
||||
enum Type { Number, Operator, Variable, Function, LeftParen, RightParen, Comma, Whitespace };
|
||||
|
||||
ParseToken(std::string text, Type type) : text(text), type(type) { }
|
||||
|
||||
const std::string& getText() const { return text; }
|
||||
|
||||
Type getType() const { return type; }
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
Type type;
|
||||
};
|
||||
|
||||
std::string Parser::trim(const std::string& expression)
|
||||
{
|
||||
// Remove leading and trailing spaces.
|
||||
|
||||
size_t start, end;
|
||||
for (start = 0; start < expression.size() && isspace(expression[start]); ++start) { }
|
||||
for (end = expression.size() - 1; end > start && isspace(expression[end]); end--) { }
|
||||
if (start == end && isspace(expression[end])) { return ""; }
|
||||
return expression.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
ParseToken Parser::getNextToken(const std::string& expression, size_t start)
|
||||
{
|
||||
char c = expression[start];
|
||||
if (c == '(') { return ParseToken("(", ParseToken::LeftParen); }
|
||||
if (c == ')') { return ParseToken(")", ParseToken::RightParen); }
|
||||
if (c == ',') { return ParseToken(",", ParseToken::Comma); }
|
||||
if (Operators.find(c) != std::string::npos) { return ParseToken(std::string(1, c), ParseToken::Operator); }
|
||||
if (isspace(c))
|
||||
{
|
||||
// White space
|
||||
|
||||
for (size_t pos = start + 1; pos < expression.size(); ++pos)
|
||||
{
|
||||
if (!isspace(expression[pos])) { return ParseToken(expression.substr(start, pos - start), ParseToken::Whitespace); }
|
||||
}
|
||||
return ParseToken(expression.substr(start, std::string::npos), ParseToken::Whitespace);
|
||||
}
|
||||
if (c == '.' || Digits.find(c) != std::string::npos)
|
||||
{
|
||||
// A number
|
||||
|
||||
bool foundDecimal = (c == '.');
|
||||
bool foundExp = false;
|
||||
size_t pos = start + 1;
|
||||
for (; pos < expression.size(); ++pos)
|
||||
{
|
||||
c = expression[pos];
|
||||
if (Digits.find(c) != std::string::npos) { continue; }
|
||||
if (c == '.' && !foundDecimal)
|
||||
{
|
||||
foundDecimal = true;
|
||||
continue;
|
||||
}
|
||||
if ((c == 'e' || c == 'E') && !foundExp)
|
||||
{
|
||||
foundExp = true;
|
||||
if (pos < expression.size() - 1 && (expression[pos + 1] == '-' || expression[pos + 1] == '+')) { pos++; }
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ParseToken(expression.substr(start, pos - start), ParseToken::Number);
|
||||
}
|
||||
|
||||
// A variable, function, or left parenthesis
|
||||
|
||||
for (size_t pos = start; pos < expression.size(); ++pos)
|
||||
{
|
||||
c = expression[pos];
|
||||
if (c == '(') { return ParseToken(expression.substr(start, pos - start + 1), ParseToken::Function); }
|
||||
if (Operators.find(c) != std::string::npos || c == ',' || c == ')' || isspace(c))
|
||||
{
|
||||
return ParseToken(expression.substr(start, pos - start), ParseToken::Variable);
|
||||
}
|
||||
}
|
||||
return ParseToken(expression.substr(start, std::string::npos), ParseToken::Variable);
|
||||
}
|
||||
|
||||
std::vector<ParseToken> Parser::tokenize(const std::string& expression)
|
||||
{
|
||||
std::vector<ParseToken> tokens;
|
||||
size_t pos = 0;
|
||||
while (pos < expression.size())
|
||||
{
|
||||
ParseToken token = getNextToken(expression, pos);
|
||||
if (token.getType() != ParseToken::Whitespace) { tokens.push_back(token); }
|
||||
pos += token.getText().size();
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
ParsedExpression Parser::parse(const std::string& expression) { return parse(expression, std::map<std::string, CustomFunction*>()); }
|
||||
|
||||
ParsedExpression Parser::parse(const std::string& expression, const std::map<std::string, CustomFunction*>& customFunctions)
|
||||
{
|
||||
// First split the expression into subexpressions.
|
||||
|
||||
std::string primaryExpression = expression;
|
||||
std::vector<std::string> subexpressions;
|
||||
while (true)
|
||||
{
|
||||
std::string::size_type pos = primaryExpression.find_last_of(';');
|
||||
if (pos == std::string::npos) { break; }
|
||||
std::string sub = trim(primaryExpression.substr(pos + 1));
|
||||
if (!sub.empty()) { subexpressions.push_back(sub); }
|
||||
primaryExpression = primaryExpression.substr(0, pos);
|
||||
}
|
||||
|
||||
// Parse the subexpressions.
|
||||
|
||||
std::map<std::string, ExpressionTreeNode> subexpDefs;
|
||||
for (size_t i = 0; i < subexpressions.size(); ++i)
|
||||
{
|
||||
size_t equalsPos = subexpressions[i].find('=');
|
||||
if (equalsPos == std::string::npos) { throw Exception("Parse error: subexpression does not specify a name"); }
|
||||
std::string name = trim(subexpressions[i].substr(0, equalsPos));
|
||||
if (name.empty()) { throw Exception("Parse error: subexpression does not specify a name"); }
|
||||
std::vector<ParseToken> tokens = tokenize(subexpressions[i].substr(equalsPos + 1));
|
||||
size_t pos = 0;
|
||||
subexpDefs[name] = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0);
|
||||
if (pos != tokens.size()) { throw Exception("Parse error: unexpected text at end of subexpression: " + tokens[pos].getText()); }
|
||||
}
|
||||
|
||||
// Now parse the primary expression.
|
||||
|
||||
std::vector<ParseToken> tokens = tokenize(primaryExpression);
|
||||
size_t pos = 0;
|
||||
ExpressionTreeNode result = parsePrecedence(tokens, pos, customFunctions, subexpDefs, 0);
|
||||
if (pos != tokens.size()) { throw Exception("Parse error: unexpected text at end of expression: " + tokens[pos].getText()); }
|
||||
return ParsedExpression(result);
|
||||
}
|
||||
|
||||
ExpressionTreeNode Parser::parsePrecedence(const std::vector<ParseToken>& tokens, size_t& pos, const std::map<std::string, CustomFunction*>& customFunctions,
|
||||
const std::map<std::string, ExpressionTreeNode>& subexpressionDefs, int precedence)
|
||||
{
|
||||
if (pos == tokens.size()) { throw Exception("Parse error: unexpected end of expression"); }
|
||||
|
||||
// Parse the next value (number, variable, function, parenthesized expression)
|
||||
|
||||
ParseToken token = tokens[pos];
|
||||
ExpressionTreeNode result;
|
||||
if (token.getType() == ParseToken::Number)
|
||||
{
|
||||
double value;
|
||||
std::stringstream(token.getText()) >> value;
|
||||
result = ExpressionTreeNode(new Operation::Constant(value));
|
||||
pos++;
|
||||
}
|
||||
else if (token.getType() == ParseToken::Variable)
|
||||
{
|
||||
const auto subexp = subexpressionDefs.find(token.getText());
|
||||
if (subexp == subexpressionDefs.end())
|
||||
{
|
||||
Operation* op = new Operation::Variable(token.getText());
|
||||
result = ExpressionTreeNode(op);
|
||||
}
|
||||
else { result = subexp->second; }
|
||||
pos++;
|
||||
}
|
||||
else if (token.getType() == ParseToken::LeftParen)
|
||||
{
|
||||
pos++;
|
||||
result = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0);
|
||||
if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen) { throw Exception("Parse error: unbalanced parentheses"); }
|
||||
pos++;
|
||||
}
|
||||
else if (token.getType() == ParseToken::Function)
|
||||
{
|
||||
pos++;
|
||||
std::vector<ExpressionTreeNode> args;
|
||||
bool moreArgs;
|
||||
do
|
||||
{
|
||||
args.push_back(parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 0));
|
||||
moreArgs = (pos < tokens.size() && tokens[pos].getType() == ParseToken::Comma);
|
||||
if (moreArgs) { pos++; }
|
||||
} while (moreArgs);
|
||||
if (pos == tokens.size() || tokens[pos].getType() != ParseToken::RightParen) { throw Exception("Parse error: unbalanced parentheses"); }
|
||||
pos++;
|
||||
Operation* op = getFunctionOperation(token.getText(), customFunctions);
|
||||
try { result = ExpressionTreeNode(op, args); }
|
||||
catch (...)
|
||||
{
|
||||
delete op;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (token.getType() == ParseToken::Operator && token.getText() == "-")
|
||||
{
|
||||
pos++;
|
||||
const ExpressionTreeNode toNegate = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, 2);
|
||||
result = ExpressionTreeNode(new Operation::Negate(), toNegate);
|
||||
}
|
||||
else { throw Exception("Parse error: unexpected token: " + token.getText()); }
|
||||
|
||||
// Now deal with the next binary operator.
|
||||
|
||||
while (pos < tokens.size() && tokens[pos].getType() == ParseToken::Operator)
|
||||
{
|
||||
token = tokens[pos];
|
||||
int opIndex = int(Operators.find(token.getText()));
|
||||
int opPrecedence = Precedence[opIndex];
|
||||
if (opPrecedence < precedence) { return result; }
|
||||
pos++;
|
||||
ExpressionTreeNode arg = parsePrecedence(tokens, pos, customFunctions, subexpressionDefs, LeftAssociative[opIndex] ? opPrecedence + 1 : opPrecedence);
|
||||
Operation* op = getOperatorOperation(token.getText());
|
||||
try { result = ExpressionTreeNode(op, result, arg); }
|
||||
catch (...)
|
||||
{
|
||||
delete op;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Operation* Parser::getOperatorOperation(const std::string& name)
|
||||
{
|
||||
switch (OperationId[Operators.find(name)])
|
||||
{
|
||||
case Operation::ADD:
|
||||
return new Operation::Add();
|
||||
case Operation::SUBTRACT:
|
||||
return new Operation::Subtract();
|
||||
case Operation::MULTIPLY:
|
||||
return new Operation::Multiply();
|
||||
case Operation::DIVIDE:
|
||||
return new Operation::Divide();
|
||||
case Operation::POWER:
|
||||
return new Operation::Power();
|
||||
default:
|
||||
throw Exception("Parse error: unknown operator");
|
||||
}
|
||||
}
|
||||
|
||||
Operation* Parser::getFunctionOperation(const std::string& name, const std::map<std::string, CustomFunction*>& customFunctions)
|
||||
{
|
||||
static std::map<std::string, Operation::Id> opMap;
|
||||
if (opMap.empty())
|
||||
{
|
||||
opMap["sqrt"] = Operation::SQRT;
|
||||
opMap["exp"] = Operation::EXP;
|
||||
opMap["log"] = Operation::LOG;
|
||||
opMap["sin"] = Operation::SIN;
|
||||
opMap["cos"] = Operation::COS;
|
||||
opMap["sec"] = Operation::SEC;
|
||||
opMap["csc"] = Operation::CSC;
|
||||
opMap["tan"] = Operation::TAN;
|
||||
opMap["cot"] = Operation::COT;
|
||||
opMap["asin"] = Operation::ASIN;
|
||||
opMap["acos"] = Operation::ACOS;
|
||||
opMap["atan"] = Operation::ATAN;
|
||||
opMap["sinh"] = Operation::SINH;
|
||||
opMap["cosh"] = Operation::COSH;
|
||||
opMap["tanh"] = Operation::TANH;
|
||||
opMap["erf"] = Operation::ERF;
|
||||
opMap["erfc"] = Operation::ERFC;
|
||||
opMap["step"] = Operation::STEP;
|
||||
opMap["delta"] = Operation::DELTA;
|
||||
opMap["square"] = Operation::SQUARE;
|
||||
opMap["cube"] = Operation::CUBE;
|
||||
opMap["recip"] = Operation::RECIPROCAL;
|
||||
opMap["min"] = Operation::MIN;
|
||||
opMap["max"] = Operation::MAX;
|
||||
opMap["abs"] = Operation::ABS;
|
||||
}
|
||||
const std::string trimmed = name.substr(0, name.size() - 1);
|
||||
|
||||
// First check custom functions.
|
||||
|
||||
const auto custom = customFunctions.find(trimmed);
|
||||
if (custom != customFunctions.end()) { return new Operation::Custom(trimmed, custom->second->clone()); }
|
||||
|
||||
// Now try standard functions.
|
||||
|
||||
const auto iter = opMap.find(trimmed);
|
||||
if (iter == opMap.end()) { throw Exception("Parse error: unknown function: " + trimmed); }
|
||||
switch (iter->second)
|
||||
{
|
||||
case Operation::SQRT:
|
||||
return new Operation::Sqrt();
|
||||
case Operation::EXP:
|
||||
return new Operation::Exp();
|
||||
case Operation::LOG:
|
||||
return new Operation::Log();
|
||||
case Operation::SIN:
|
||||
return new Operation::Sin();
|
||||
case Operation::COS:
|
||||
return new Operation::Cos();
|
||||
case Operation::SEC:
|
||||
return new Operation::Sec();
|
||||
case Operation::CSC:
|
||||
return new Operation::Csc();
|
||||
case Operation::TAN:
|
||||
return new Operation::Tan();
|
||||
case Operation::COT:
|
||||
return new Operation::Cot();
|
||||
case Operation::ASIN:
|
||||
return new Operation::Asin();
|
||||
case Operation::ACOS:
|
||||
return new Operation::Acos();
|
||||
case Operation::ATAN:
|
||||
return new Operation::Atan();
|
||||
case Operation::SINH:
|
||||
return new Operation::Sinh();
|
||||
case Operation::COSH:
|
||||
return new Operation::Cosh();
|
||||
case Operation::TANH:
|
||||
return new Operation::Tanh();
|
||||
case Operation::ERF:
|
||||
return new Operation::Erf();
|
||||
case Operation::ERFC:
|
||||
return new Operation::Erfc();
|
||||
case Operation::STEP:
|
||||
return new Operation::Step();
|
||||
case Operation::DELTA:
|
||||
return new Operation::Delta();
|
||||
case Operation::SQUARE:
|
||||
return new Operation::Square();
|
||||
case Operation::CUBE:
|
||||
return new Operation::Cube();
|
||||
case Operation::RECIPROCAL:
|
||||
return new Operation::Reciprocal();
|
||||
case Operation::MIN:
|
||||
return new Operation::Min();
|
||||
case Operation::MAX:
|
||||
return new Operation::Max();
|
||||
case Operation::ABS:
|
||||
return new Operation::Abs();
|
||||
default:
|
||||
throw Exception("Parse error: unknown function");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef LEPTON_PARSER_H_
|
||||
#define LEPTON_PARSER_H_
|
||||
|
||||
/* -------------------------------------------------------------------------- *
|
||||
* Lepton *
|
||||
* -------------------------------------------------------------------------- *
|
||||
* This is part of the Lepton expression parser originating from *
|
||||
* Simbios, the NIH National Center for Physics-Based Simulation of *
|
||||
* Biological Structures at Stanford, funded under the NIH Roadmap for *
|
||||
* Medical Research, grant U54 GM072970. See https://simtk.org. *
|
||||
* *
|
||||
* Portions copyright (c) 2009 Stanford University and the Authors. *
|
||||
* Authors: Peter Eastman *
|
||||
* Contributors: *
|
||||
* *
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a *
|
||||
* copy of this software and associated documentation files (the "Software"), *
|
||||
* to deal in the Software without restriction, including without limitation *
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
|
||||
* and/or sell copies of the Software, and to permit persons to whom the *
|
||||
* Software is furnished to do so, subject to the following conditions: *
|
||||
* *
|
||||
* The above copyright notice and this permission notice shall be included in *
|
||||
* all copies or substantial portions of the Software. *
|
||||
* *
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
|
||||
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
|
||||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
|
||||
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "windowsIncludes.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Lepton
|
||||
{
|
||||
class CustomFunction;
|
||||
class ExpressionTreeNode;
|
||||
class Operation;
|
||||
class ParsedExpression;
|
||||
class ParseToken;
|
||||
|
||||
/**
|
||||
* This class provides the main interface for parsing expressions.
|
||||
*/
|
||||
|
||||
class LEPTON_EXPORT Parser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Parse a mathematical expression and return a representation of it as an abstract syntax tree.
|
||||
*/
|
||||
static ParsedExpression parse(const std::string& expression);
|
||||
/**
|
||||
* Parse a mathematical expression and return a representation of it as an abstract syntax tree.
|
||||
*
|
||||
* @param expression
|
||||
* @param customFunctions a map specifying user defined functions that may appear in the expression.
|
||||
* The key are function names, and the values are corresponding CustomFunction objects.
|
||||
*/
|
||||
static ParsedExpression parse(const std::string& expression, const std::map<std::string, CustomFunction*>& customFunctions);
|
||||
private:
|
||||
static std::string trim(const std::string& expression);
|
||||
static std::vector<ParseToken> tokenize(const std::string& expression);
|
||||
static ParseToken getNextToken(const std::string& expression, size_t start);
|
||||
static ExpressionTreeNode parsePrecedence(const std::vector<ParseToken>& tokens, size_t& pos,
|
||||
const std::map<std::string, CustomFunction*>& customFunctions,
|
||||
const std::map<std::string, ExpressionTreeNode>& subexpressionDefs, int precedence);
|
||||
static Operation* getOperatorOperation(const std::string& name);
|
||||
static Operation* getFunctionOperation(const std::string& name, const std::map<std::string, CustomFunction*>& customFunctions);
|
||||
};
|
||||
} // namespace Lepton
|
||||
|
||||
#endif /*LEPTON_PARSER_H_*/
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef LEPTON_WINDOW_INCLUDE_H_
|
||||
#define LEPTON_WINDOW_INCLUDE_H_
|
||||
|
||||
/*
|
||||
* Shared libraries are messy in Visual Studio. We have to distinguish three
|
||||
* cases:
|
||||
* (1) this header is being used to build the Lepton shared library
|
||||
* (dllexport)
|
||||
* (2) this header is being used by a *client* of the Lepton shared
|
||||
* library (dllimport)
|
||||
* (3) we are building the Lepton static library, or the client is
|
||||
* being compiled with the expectation of linking with the
|
||||
* Lepton static library (nothing special needed)
|
||||
* In the CMake script for building this library, we define one of the symbols
|
||||
* Lepton_BUILDING_{SHARED|STATIC}_LIBRARY
|
||||
* Client code normally has no special symbol defined, in which case we'll
|
||||
* assume it wants to use the shared library. However, if the client defines
|
||||
* the symbol LEPTON_USE_STATIC_LIBRARIES we'll suppress the dllimport so
|
||||
* that the client code can be linked with static libraries. Note that
|
||||
* the client symbol is not library dependent, while the library symbols
|
||||
* affect only the Lepton library, meaning that other libraries can
|
||||
* be clients of this one. However, we are assuming all-static or all-shared.
|
||||
*/
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// We don't want to hear about how sprintf is "unsafe".
|
||||
#pragma warning(disable:4996)
|
||||
#if defined(LEPTON_BUILDING_SHARED_LIBRARY)
|
||||
#define LEPTON_EXPORT __declspec(dllexport)
|
||||
// Keep MS VC++ quiet about lack of dll export of private members.
|
||||
#pragma warning(disable:4251)
|
||||
#elif defined(LEPTON_BUILDING_STATIC_LIBRARY) || defined(LEPTON_USE_STATIC_LIBRARIES)
|
||||
#define LEPTON_EXPORT __declspec(dllimport) // i.e., a client of a shared library
|
||||
#else
|
||||
#define LEPTON_EXPORT
|
||||
#endif
|
||||
#else
|
||||
#define LEPTON_EXPORT // Linux, Mac
|
||||
#endif
|
||||
|
||||
#endif // LEPTON_WINDOW_INCLUDE_H_
|
||||
@@ -0,0 +1,600 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPBlockConvolver.h
|
||||
*
|
||||
* @brief Single-block overlap-save convolution processor class.
|
||||
*
|
||||
* This file includes single-block overlap-save convolution processor class.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPBLOCKCONVOLVER_INCLUDED
|
||||
#define R8B_CDSPBLOCKCONVOLVER_INCLUDED
|
||||
|
||||
#include "CDSPFIRFilter.h"
|
||||
#include "CDSPProcessor.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Single-block overlap-save convolution processing class.
|
||||
*
|
||||
* Class that implements single-block overlap-save convolution processing. The
|
||||
* length of a single FFT block used depends on the length of the filter
|
||||
* kernel.
|
||||
*
|
||||
* The rationale behind "single-block" processing is that increasing the FFT
|
||||
* block length by 2 is more efficient than performing convolution at the same
|
||||
* FFT block length but using two blocks.
|
||||
*
|
||||
* This class also implements a built-in resampling by any whole-number
|
||||
* factor, which simplifies the overall resampling objects topology.
|
||||
*/
|
||||
|
||||
class CDSPBlockConvolver final : public CDSPProcessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initializes internal variables and constants of *this
|
||||
* object.
|
||||
*
|
||||
* @param aFilter Pre-calculated filter data. Reference to this object is
|
||||
* inhertied by *this object, and the object will be released when *this
|
||||
* object is destroyed. If upsampling is used, filter's gain should be
|
||||
* equal to the upsampling factor.
|
||||
* @param aUpFactor The upsampling factor, positive value. E.g. value of 2
|
||||
* means 2x upsampling should be performed over the input data.
|
||||
* @param aDownFactor The downsampling factor, positive value. E.g. value
|
||||
* of 2 means 2x downsampling should be performed over the output data.
|
||||
* @param PrevLatency Latency, in samples (any value >=0), which was left
|
||||
* in the output signal by a previous process. This value is usually
|
||||
* non-zero if the minimum-phase filters are in use. This value is always
|
||||
* zero if the linear-phase filters are in use.
|
||||
* @param aDoConsumeLatency "True" if the output latency should be
|
||||
* consumed. Does not apply to the fractional part of the latency (if such
|
||||
* part is available).
|
||||
*/
|
||||
|
||||
CDSPBlockConvolver(CDSPFIRFilter& aFilter, const int aUpFactor,
|
||||
const int aDownFactor, const double PrevLatency = 0.0,
|
||||
const bool aDoConsumeLatency = true)
|
||||
: Filter(&aFilter)
|
||||
, UpFactor(aUpFactor)
|
||||
, DownFactor(aDownFactor)
|
||||
, DoConsumeLatency(aDoConsumeLatency)
|
||||
, BlockLen2(2 << Filter->getBlockLenBits())
|
||||
{
|
||||
R8BASSERT(UpFactor > 0);
|
||||
R8BASSERT(DownFactor > 0);
|
||||
R8BASSERT(PrevLatency >= 0.0);
|
||||
|
||||
int fftinBits;
|
||||
UpShift = getBitOccupancy(UpFactor) - 1;
|
||||
|
||||
if ((1 << UpShift) == UpFactor)
|
||||
{
|
||||
fftinBits = Filter->getBlockLenBits() + 1 - UpShift;
|
||||
PrevInputLen = (Filter->getKernelLen() - 1) / UpFactor;
|
||||
InputLen = BlockLen2 - PrevInputLen * UpFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpShift = -1;
|
||||
fftinBits = Filter->getBlockLenBits() + 1;
|
||||
PrevInputLen = Filter->getKernelLen() - 1;
|
||||
InputLen = BlockLen2 - PrevInputLen;
|
||||
}
|
||||
|
||||
OutOffset = Filter->getLatency();
|
||||
LatencyFrac = Filter->getLatencyFrac() + PrevLatency * UpFactor;
|
||||
Latency = (int)LatencyFrac;
|
||||
LatencyFrac -= Latency;
|
||||
LatencyFrac /= DownFactor;
|
||||
|
||||
Latency += InputLen + OutOffset;
|
||||
|
||||
int fftoutBits;
|
||||
InputDelay = 0;
|
||||
UpSkipInit = 0;
|
||||
DownSkipInit = 0;
|
||||
DownShift = getBitOccupancy(DownFactor) - 1;
|
||||
|
||||
if ((1 << DownShift) == DownFactor)
|
||||
{
|
||||
fftoutBits = Filter->getBlockLenBits() + 1 - DownShift;
|
||||
|
||||
if (DownFactor > 1)
|
||||
{
|
||||
if (UpShift > 0)
|
||||
{
|
||||
// This case never happens in practice due to mutual
|
||||
// exclusion of "power of 2" DownFactor and UpFactor
|
||||
// values.
|
||||
|
||||
R8BASSERT(UpShift == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int Delay = Latency & (DownFactor - 1);
|
||||
|
||||
if (Delay > 0)
|
||||
{
|
||||
Delay = DownFactor - Delay;
|
||||
Latency += Delay;
|
||||
|
||||
if (Delay < UpFactor) { UpSkipInit = Delay; }
|
||||
else
|
||||
{
|
||||
UpSkipInit = UpFactor - 1;
|
||||
InputDelay = Delay - UpSkipInit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!DoConsumeLatency) { Latency /= DownFactor; }
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fftoutBits = Filter->getBlockLenBits() + 1;
|
||||
DownShift = -1;
|
||||
|
||||
if (!DoConsumeLatency && DownFactor > 1)
|
||||
{
|
||||
DownSkipInit = Latency % DownFactor;
|
||||
Latency /= DownFactor;
|
||||
}
|
||||
}
|
||||
|
||||
fftin = new CDSPRealFFTKeeper(fftinBits);
|
||||
|
||||
if (fftoutBits == fftinBits) { fftout = fftin; }
|
||||
else
|
||||
{
|
||||
ffto2 = new CDSPRealFFTKeeper(fftoutBits);
|
||||
fftout = ffto2;
|
||||
}
|
||||
|
||||
WorkBlocks.alloc(BlockLen2 * 2 + PrevInputLen);
|
||||
CurInput = &WorkBlocks[0];
|
||||
CurOutput = &WorkBlocks[BlockLen2];
|
||||
PrevInput = &WorkBlocks[BlockLen2 * 2];
|
||||
|
||||
clear();
|
||||
|
||||
R8BCONSOLE("CDSPBlockConvolver: flt_len=%i in_len=%i io=%i/%i "
|
||||
"fft=%i/%i latency=%i\n", Filter -> getKernelLen(), InputLen,
|
||||
UpFactor, DownFactor, (*fftin) -> getLen(), (*fftout) -> getLen(),
|
||||
getLatency());
|
||||
}
|
||||
|
||||
~CDSPBlockConvolver() override { Filter->unref(); }
|
||||
|
||||
int getLatency() const override { return (DoConsumeLatency ? 0 : Latency); }
|
||||
|
||||
double getLatencyFrac() const override { return (LatencyFrac); }
|
||||
|
||||
int getInLenBeforeOutStart(const int NextInLen) const override
|
||||
{
|
||||
return ((InputLen - InputDelay + NextInLen * DownFactor) /
|
||||
UpFactor);
|
||||
}
|
||||
|
||||
int getMaxOutLen(const int MaxInLen) const override
|
||||
{
|
||||
R8BASSERT(MaxInLen >= 0);
|
||||
|
||||
return ((MaxInLen * UpFactor + InputDelay + DownFactor - 1) /
|
||||
DownFactor);
|
||||
}
|
||||
|
||||
void clear() override
|
||||
{
|
||||
memset(&PrevInput[0], 0, PrevInputLen * sizeof(double));
|
||||
|
||||
if (DoConsumeLatency) { LatencyLeft = Latency; }
|
||||
else
|
||||
{
|
||||
LatencyLeft = 0;
|
||||
|
||||
if (DownShift > 0)
|
||||
{
|
||||
memset(&CurOutput[0], 0, (BlockLen2 >> DownShift) *
|
||||
sizeof(double));
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&CurOutput[BlockLen2 - OutOffset], 0, OutOffset *
|
||||
sizeof(double));
|
||||
|
||||
memset(&CurOutput[0], 0, (InputLen - OutOffset) *
|
||||
sizeof(double));
|
||||
}
|
||||
}
|
||||
|
||||
memset(CurInput, 0, InputDelay * sizeof(double));
|
||||
|
||||
InDataLeft = InputLen - InputDelay;
|
||||
UpSkip = UpSkipInit;
|
||||
DownSkip = DownSkipInit;
|
||||
}
|
||||
|
||||
int process(double* ip, int l0, double*& op0) override
|
||||
{
|
||||
R8BASSERT(l0 >= 0);
|
||||
R8BASSERT(UpFactor / DownFactor <= 1 || ip != op0 || l0 == 0);
|
||||
|
||||
double* op = op0;
|
||||
int l = l0 * UpFactor;
|
||||
l0 = 0;
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
const int Offs = InputLen - InDataLeft;
|
||||
|
||||
if (l < InDataLeft)
|
||||
{
|
||||
InDataLeft -= l;
|
||||
|
||||
if (UpShift >= 0)
|
||||
{
|
||||
memcpy(&CurInput[Offs >> UpShift], ip,
|
||||
(l >> UpShift) * sizeof(double));
|
||||
}
|
||||
else { copyUpsample(ip, &CurInput[Offs], l); }
|
||||
|
||||
copyToOutput(Offs - OutOffset, op, l, l0);
|
||||
break;
|
||||
}
|
||||
|
||||
const int b = InDataLeft;
|
||||
l -= b;
|
||||
InDataLeft = InputLen;
|
||||
int ilu;
|
||||
|
||||
if (UpShift >= 0)
|
||||
{
|
||||
const int bu = b >> UpShift;
|
||||
memcpy(&CurInput[Offs >> UpShift], ip,
|
||||
bu * sizeof(double));
|
||||
|
||||
ip += bu;
|
||||
ilu = InputLen >> UpShift;
|
||||
}
|
||||
else
|
||||
{
|
||||
copyUpsample(ip, &CurInput[Offs], b);
|
||||
ilu = InputLen;
|
||||
}
|
||||
|
||||
const int pil = int(PrevInputLen * sizeof(double));
|
||||
memcpy(&CurInput[ilu], PrevInput, pil);
|
||||
memcpy(PrevInput, &CurInput[ilu - PrevInputLen], pil);
|
||||
|
||||
(*fftin)->forward(CurInput);
|
||||
|
||||
if (UpShift > 0) { mirrorInputSpectrum(); }
|
||||
|
||||
if (Filter->isZeroPhase())
|
||||
{
|
||||
(*fftout)->multiplyBlocksZ(Filter->getKernelBlock(),
|
||||
CurInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
(*fftout)->multiplyBlocks(Filter->getKernelBlock(),
|
||||
CurInput);
|
||||
}
|
||||
|
||||
if (DownShift > 0)
|
||||
{
|
||||
const int z = BlockLen2 >> DownShift;
|
||||
CurInput[1] = Filter->getKernelBlock()[z] *
|
||||
CurInput[z];
|
||||
}
|
||||
|
||||
(*fftout)->inverse(CurInput);
|
||||
|
||||
copyToOutput(Offs - OutOffset, op, b, l0);
|
||||
|
||||
double* const tmp = CurInput;
|
||||
CurInput = CurOutput;
|
||||
CurOutput = tmp;
|
||||
}
|
||||
|
||||
return (l0);
|
||||
}
|
||||
|
||||
private:
|
||||
CDSPFIRFilter* Filter = nullptr; ///< Filter in use.
|
||||
///<
|
||||
CPtrKeeper<CDSPRealFFTKeeper*> fftin; ///< FFT object 1, used to produce
|
||||
///< the input spectrum (can embed the "power of 2" upsampling).
|
||||
///<
|
||||
CPtrKeeper<CDSPRealFFTKeeper*> ffto2; ///< FFT object 2 (can be NULL).
|
||||
///<
|
||||
CDSPRealFFTKeeper* fftout = nullptr; ///< FFT object used to produce the output
|
||||
///< signal (can embed the "power of 2" downsampling), may point to
|
||||
///< either "fftin" or "ffto2".
|
||||
///<
|
||||
int UpFactor = 0; ///< Upsampling factor.
|
||||
///<
|
||||
int DownFactor = 0; ///< Downsampling factor.
|
||||
///<
|
||||
bool DoConsumeLatency; ///< "True" if the output latency should be
|
||||
///< consumed. Does not apply to the fractional part of the latency
|
||||
///< (if such part is available).
|
||||
///<
|
||||
int BlockLen2 = 0; ///< Equals block length * 2.
|
||||
///<
|
||||
int OutOffset = 0; ///< Output offset, depends on filter's introduced latency.
|
||||
///<
|
||||
int PrevInputLen = 0; ///< The length of previous input data saved, used for
|
||||
///< overlap.
|
||||
///<
|
||||
int InputLen = 0; ///< The number of input samples that should be accumulated
|
||||
///< before the input block is processed.
|
||||
///<
|
||||
int Latency = 0; ///< Processing latency, in samples.
|
||||
///<
|
||||
double LatencyFrac = 0; ///< Fractional latency, in samples, that is left in
|
||||
///< the output signal.
|
||||
///<
|
||||
int UpShift = 0; ///< "Power of 2" upsampling shift. Equals -1 if UpFactor is
|
||||
///< not a "power of 2" value. Equals 0 if UpFactor equals 1.
|
||||
///<
|
||||
int DownShift = 0; ///< "Power of 2" downsampling shift. Equals -1 if
|
||||
///< DownFactor is not a "power of 2". Equals 0 if DownFactor equals
|
||||
///< 1.
|
||||
///<
|
||||
int InputDelay = 0; ///< Additional input delay, in samples. Used to make the
|
||||
///< output latency divisible by DownShift. Used only if UpShift <= 0
|
||||
///< and DownShift > 0.
|
||||
///<
|
||||
CFixedBuffer<double> WorkBlocks; ///< Previous input data, input and
|
||||
///< output data blocks, overall capacity = BlockLen2 * 2 +
|
||||
///< PrevInputLen. Used in the flip-flop manner.
|
||||
///<
|
||||
double* PrevInput = nullptr; ///< Previous input data buffer, capacity = BlockLen.
|
||||
///<
|
||||
double* CurInput = nullptr; ///< Input data buffer, capacity = BlockLen2.
|
||||
///<
|
||||
double* CurOutput = nullptr; ///< Output data buffer, capacity = BlockLen2.
|
||||
///<
|
||||
int InDataLeft = 0; ///< Samples left before processing input and output FFT
|
||||
///< blocks. Initialized to InputLen on clear.
|
||||
///<
|
||||
int LatencyLeft = 0; ///< Latency in samples left to skip.
|
||||
///<
|
||||
int UpSkip = 0; ///< The current upsampling sample skip (value in the range
|
||||
///< 0 to UpFactor - 1).
|
||||
///<
|
||||
int UpSkipInit = 0; ///< The initial UpSkip value after clear().
|
||||
///<
|
||||
int DownSkip = 0; ///< The current downsampling sample skip (value in the
|
||||
///< range 0 to DownFactor - 1). Not used if DownShift > 0.
|
||||
///<
|
||||
int DownSkipInit = 0; ///< The initial DownSkip value after clear().
|
||||
///<
|
||||
|
||||
/**
|
||||
* Function copies samples from the input buffer to the output buffer
|
||||
* while inserting zeros inbetween them to perform the whole-numbered
|
||||
* upsampling.
|
||||
*
|
||||
* @param[in,out] ip0 Input buffer. Will be advanced on function's return.
|
||||
* @param[out] op Output buffer.
|
||||
* @param l0 The number of samples to fill in the output buffer, including
|
||||
* both input samples and interpolation (zero) samples.
|
||||
*/
|
||||
|
||||
void copyUpsample(double*& ip0, double* op, int l0)
|
||||
{
|
||||
int b = min(UpSkip, l0);
|
||||
|
||||
if (b > 0)
|
||||
{
|
||||
l0 -= b;
|
||||
UpSkip -= b;
|
||||
*op = 0.0;
|
||||
op++;
|
||||
b--;
|
||||
|
||||
while (b > 0)
|
||||
{
|
||||
*op = 0.0;
|
||||
op++;
|
||||
b--;
|
||||
}
|
||||
}
|
||||
|
||||
double* ip = ip0;
|
||||
int l = l0 / UpFactor;
|
||||
int lz = l0 - l * UpFactor;
|
||||
|
||||
if (UpFactor == 3)
|
||||
{
|
||||
while (l > 0)
|
||||
{
|
||||
op[0] = *ip;
|
||||
op[1] = 0.0;
|
||||
op[2] = 0.0;
|
||||
ip++;
|
||||
op += UpFactor;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
else if (UpFactor == 5)
|
||||
{
|
||||
while (l > 0)
|
||||
{
|
||||
op[0] = *ip;
|
||||
op[1] = 0.0;
|
||||
op[2] = 0.0;
|
||||
op[3] = 0.0;
|
||||
op[4] = 0.0;
|
||||
ip++;
|
||||
op += UpFactor;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (l > 0)
|
||||
{
|
||||
op[0] = *ip;
|
||||
|
||||
for (int j = 1; j < UpFactor; ++j) { op[j] = 0.0; }
|
||||
|
||||
ip++;
|
||||
op += UpFactor;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
|
||||
if (lz > 0)
|
||||
{
|
||||
*op = *ip;
|
||||
op++;
|
||||
ip++;
|
||||
UpSkip = UpFactor - lz;
|
||||
|
||||
while (lz > 1)
|
||||
{
|
||||
*op = 0.0;
|
||||
op++;
|
||||
lz--;
|
||||
}
|
||||
}
|
||||
|
||||
ip0 = ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function copies sample data from the CurOutput buffer to the specified
|
||||
* output buffer and advances its position. If necessary, this function
|
||||
* "consumes" latency and performs downsampling.
|
||||
*
|
||||
* @param Offs CurOutput buffer offset, can be negative.
|
||||
* @param[out] op0 Output buffer pointer, will be advanced.
|
||||
* @param b The number of output samples available, including those which
|
||||
* are discarded during whole-number downsampling.
|
||||
* @param l0 The overall output sample count, will be increased.
|
||||
*/
|
||||
|
||||
void copyToOutput(int Offs, double*& op0, int b, int& l0)
|
||||
{
|
||||
if (Offs < 0)
|
||||
{
|
||||
if (Offs + b <= 0) { Offs += BlockLen2; }
|
||||
else
|
||||
{
|
||||
copyToOutput(Offs + BlockLen2, op0, -Offs, l0);
|
||||
b += Offs;
|
||||
Offs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (LatencyLeft > 0)
|
||||
{
|
||||
if (LatencyLeft >= b)
|
||||
{
|
||||
LatencyLeft -= b;
|
||||
return;
|
||||
}
|
||||
|
||||
Offs += LatencyLeft;
|
||||
b -= LatencyLeft;
|
||||
LatencyLeft = 0;
|
||||
}
|
||||
|
||||
const int df = DownFactor;
|
||||
|
||||
if (DownShift > 0)
|
||||
{
|
||||
int Skip = Offs & (df - 1);
|
||||
|
||||
if (Skip > 0)
|
||||
{
|
||||
Skip = df - Skip;
|
||||
b -= Skip;
|
||||
Offs += Skip;
|
||||
}
|
||||
|
||||
if (b > 0)
|
||||
{
|
||||
b = (b + df - 1) >> DownShift;
|
||||
memcpy(op0, &CurOutput[Offs >> DownShift],
|
||||
b * sizeof(double));
|
||||
|
||||
op0 += b;
|
||||
l0 += b;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (df > 1)
|
||||
{
|
||||
const double* ip = &CurOutput[Offs + DownSkip];
|
||||
int l = (b + df - 1 - DownSkip) / df;
|
||||
DownSkip += l * df - b;
|
||||
|
||||
double* op = op0;
|
||||
l0 += l;
|
||||
op0 += l;
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
*op = *ip;
|
||||
op++;
|
||||
ip += df;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(op0, &CurOutput[Offs], b * sizeof(double));
|
||||
op0 += b;
|
||||
l0 += b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function performs input spectrum mirroring which is used to perform a
|
||||
* fast "power of 2" upsampling. Such mirroring is equivalent to insertion
|
||||
* of zeros into the input signal.
|
||||
*/
|
||||
|
||||
void mirrorInputSpectrum()
|
||||
{
|
||||
const int bl1 = BlockLen2 >> UpShift;
|
||||
const int bl2 = bl1 + bl1;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = bl1 + 2; i < bl2; i += 2)
|
||||
{
|
||||
CurInput[i] = CurInput[bl2 - i];
|
||||
CurInput[i + 1] = -CurInput[bl2 - i + 1];
|
||||
}
|
||||
|
||||
CurInput[bl1] = CurInput[1];
|
||||
CurInput[bl1 + 1] = 0.0;
|
||||
CurInput[1] = CurInput[0];
|
||||
|
||||
for (i = 1; i < UpShift; ++i)
|
||||
{
|
||||
const int z = bl1 << i;
|
||||
memcpy(&CurInput[z], CurInput, z * sizeof(double));
|
||||
CurInput[z + 1] = 0.0;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8B_CDSPBLOCKCONVOLVER_INCLUDED
|
||||
@@ -0,0 +1,557 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPFIRFilter.h
|
||||
*
|
||||
* @brief FIR filter generator and filter cache classes.
|
||||
*
|
||||
* This file includes low-pass FIR filter generator and filter cache.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPFIRFILTER_INCLUDED
|
||||
#define R8B_CDSPFIRFILTER_INCLUDED
|
||||
|
||||
#include "CDSPSincFilterGen.h"
|
||||
#include "CDSPRealFFT.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* Enumeration of filter's phase responses.
|
||||
*/
|
||||
|
||||
enum EDSPFilterPhaseResponse
|
||||
{
|
||||
fprLinearPhase = 0 ///< Linear-phase response. Features a linear-phase
|
||||
///< high-latency response, with the latency expressed as integer
|
||||
///< value.
|
||||
// fprMinPhase ///< Minimum-phase response. Features a minimal latency
|
||||
///< response, but the response's phase is non-linear. The latency is
|
||||
///< usually expressed as non-integer value, and usually is small, but
|
||||
///< is never equal to zero. The minimum-phase filter is transformed
|
||||
///< from the linear-phase filter. The transformation has precision
|
||||
///< limits which may skew both the -3 dB point and attenuation of the
|
||||
///< filter being transformed: as it was measured, the skew happens
|
||||
///< purely at random, and in most cases it is within tolerable range.
|
||||
///< In a small (1%) random subset of cases the skew is bigger and
|
||||
///< cannot be predicted.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Calculation and storage class for FIR filters.
|
||||
*
|
||||
* Class that implements calculation and storing of a FIR filter (currently
|
||||
* contains low-pass filter calculation routine designed for sample rate
|
||||
* conversion). Objects of this class cannot be created directly, but can be
|
||||
* obtained via the CDSPFilterCache::getLPFilter() static function.
|
||||
*/
|
||||
|
||||
class CDSPFIRFilter : public R8B_BASECLASS
|
||||
{
|
||||
R8BNOCTOR(CDSPFIRFilter)
|
||||
|
||||
friend class CDSPFIRFilterCache;
|
||||
|
||||
public:
|
||||
~CDSPFIRFilter()
|
||||
{
|
||||
R8BASSERT(RefCount == 0);
|
||||
|
||||
delete Next;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The minimal allowed low-pass filter's transition band, in percent.
|
||||
*/
|
||||
static double getLPMinTransBand() { return (0.5); }
|
||||
|
||||
/**
|
||||
* @return The maximal allowed low-pass filter's transition band, in percent.
|
||||
*/
|
||||
static double getLPMaxTransBand() { return (45.0); }
|
||||
|
||||
/**
|
||||
* @return The minimal allowed low-pass filter's stop-band attenuation, in decibel.
|
||||
*/
|
||||
static double getLPMinAtten() { return (49.0); }
|
||||
|
||||
/**
|
||||
* @return The maximal allowed low-pass filter's stop-band attenuation, in decibel.
|
||||
*/
|
||||
static double getLPMaxAtten() { return (218.0); }
|
||||
|
||||
/**
|
||||
* @return "True" if kernel block of *this filter has zero-phase response.
|
||||
*/
|
||||
bool isZeroPhase() const { return (IsZeroPhase); }
|
||||
|
||||
/**
|
||||
* @return Filter's latency, in samples (integer part).
|
||||
*/
|
||||
int getLatency() const { return (Latency); }
|
||||
|
||||
/**
|
||||
* @return Filter's latency, in samples (fractional part). Always zero for linear-phase filters.
|
||||
*/
|
||||
double getLatencyFrac() const { return (LatencyFrac); }
|
||||
|
||||
/**
|
||||
* @return Filter kernel length, in samples. Not to be confused with the block length.
|
||||
*/
|
||||
int getKernelLen() const { return (KernelLen); }
|
||||
|
||||
/**
|
||||
* @return Filter's block length, espressed as Nth power of 2. The actual length is twice as large due to zero-padding.
|
||||
*/
|
||||
int getBlockLenBits() const { return (BlockLenBits); }
|
||||
|
||||
/**
|
||||
* @return Filter's kernel block, in complex-numbered form obtained via
|
||||
* the CDSPRealFFT::forward() function call, zero-padded, gain-adjusted
|
||||
* with the CDSPRealFFT::getInvMulConst() * ReqGain constant, immediately
|
||||
* suitable for convolution. Kernel block may have "zero-phase" response,
|
||||
* depending on the isZeroPhase() function's result.
|
||||
*/
|
||||
const double* getKernelBlock() const { return (KernelBlock); }
|
||||
|
||||
/**
|
||||
* This function should be called when the filter obtained via the
|
||||
* filter cache is no longer needed.
|
||||
*/
|
||||
|
||||
void unref();
|
||||
|
||||
private:
|
||||
double ReqNormFreq = 0; ///< Required normalized frequency, 0 to 1 inclusive.
|
||||
double ReqTransBand = 0; ///< Required transition band in percent, as passed by the user.
|
||||
double ReqAtten = 0; ///< Required stop-band attenuation in decibel, as passed by the user (positive value).
|
||||
EDSPFilterPhaseResponse ReqPhase = fprLinearPhase; ///< Required filter's phase response.
|
||||
double ReqGain = 0; ///< Required overall filter's gain.
|
||||
CDSPFIRFilter* Next = nullptr; ///< Next FIR filter in cache's list.
|
||||
int RefCount = 1; ///< The number of references made to *this FIR filter.
|
||||
bool IsZeroPhase = false; ///< "True" if kernel block of *this filter has zero-phase response.
|
||||
int Latency = 0; ///< Filter's latency in samples (integer part).
|
||||
double LatencyFrac = 0; ///< Filter's latency in samples (fractional part).
|
||||
int KernelLen = 0; ///< Filter kernel length, in samples.
|
||||
int BlockLenBits =
|
||||
0; ///< Block length used to store *this FIR filter, expressed as Nth power of 2. This value is used directly by the convolver.
|
||||
CFixedBuffer<double>
|
||||
KernelBlock; ///< FIR filter buffer, capacity equals to 1 << ( BlockLenBits + 1 ). Second part of the buffer contains zero-padding to allow alias-free convolution.
|
||||
|
||||
CDSPFIRFilter() { }
|
||||
|
||||
/**
|
||||
* Function builds filter kernel based on the "Req" parameters.
|
||||
*
|
||||
* @param ExtAttenCorrs External attentuation correction table, for
|
||||
* internal use.
|
||||
*/
|
||||
|
||||
void buildLPFilter(const double* const ExtAttenCorrs)
|
||||
{
|
||||
const double tb = ReqTransBand * 0.01;
|
||||
double fo1;
|
||||
double hl;
|
||||
double atten = -ReqAtten;
|
||||
|
||||
if (tb >= 0.25)
|
||||
{
|
||||
if (ReqAtten >= 117.0) { atten -= 1.60; }
|
||||
else if (ReqAtten >= 60.0) { atten -= 1.91; }
|
||||
else { atten -= 2.25; }
|
||||
}
|
||||
else if (tb >= 0.10)
|
||||
{
|
||||
if (ReqAtten >= 117.0) { atten -= 0.69; }
|
||||
else if (ReqAtten >= 60.0) { atten -= 0.73; }
|
||||
else { atten -= 1.13; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ReqAtten >= 117.0) { atten -= 0.21; }
|
||||
else if (ReqAtten >= 60.0) { atten -= 0.25; }
|
||||
else { atten -= 0.36; }
|
||||
}
|
||||
|
||||
static const int AttenCorrCount = 264;
|
||||
static const double AttenCorrMin = 49.0;
|
||||
static const double AttenCorrDiff = 176.25;
|
||||
int AttenCorr = (int)floor((-atten - AttenCorrMin) * AttenCorrCount / AttenCorrDiff + 0.5);
|
||||
|
||||
AttenCorr = min(AttenCorrCount, max(0, AttenCorr));
|
||||
|
||||
if (ExtAttenCorrs != nullptr) { atten -= ExtAttenCorrs[AttenCorr]; }
|
||||
else if (tb >= 0.25)
|
||||
{
|
||||
static const double AttenCorrScale = 101.0;
|
||||
static const signed char AttenCorrs[] = {
|
||||
-127, -127, -125, -125, -122, -119, -115, -110, -104, -97,
|
||||
-91, -82, -75, -24, -16, -6, 4, 14, 24, 29, 30, 32, 37, 44,
|
||||
51, 57, 63, 67, 65, 50, 53, 56, 58, 60, 63, 64, 66, 68, 74,
|
||||
77, 78, 78, 78, 79, 79, 60, 60, 60, 61, 59, 52, 47, 41, 36,
|
||||
30, 24, 17, 9, 0, -8, -10, -11, -14, -13, -18, -25, -31, -38,
|
||||
-44, -50, -57, -63, -68, -74, -81, -89, -96, -101, -104, -107,
|
||||
-109, -110, -86, -84, -85, -82, -80, -77, -73, -67, -62, -55,
|
||||
-48, -42, -35, -30, -20, -11, -2, 5, 6, 6, 7, 11, 16, 21, 26,
|
||||
34, 41, 46, 49, 52, 55, 56, 48, 49, 51, 51, 52, 52, 52, 52,
|
||||
52, 51, 51, 50, 47, 47, 50, 48, 46, 42, 38, 35, 31, 27, 24,
|
||||
20, 16, 12, 11, 12, 10, 8, 4, -1, -6, -11, -16, -19, -17, -21,
|
||||
-24, -27, -32, -34, -37, -38, -40, -41, -40, -40, -42, -41,
|
||||
-44, -45, -43, -41, -34, -31, -28, -24, -21, -18, -14, -10,
|
||||
-5, -1, 2, 5, 8, 7, 4, 3, 2, 2, 4, 6, 8, 9, 9, 10, 10, 10, 10,
|
||||
9, 8, 9, 11, 14, 13, 12, 11, 10, 8, 7, 6, 5, 3, 2, 2, -1, -1,
|
||||
-3, -3, -4, -4, -5, -4, -6, -7, -9, -5, -1, -1, 0, 1, 0, -2,
|
||||
-3, -4, -5, -5, -8, -13, -13, -13, -12, -13, -12, -11, -11,
|
||||
-9, -8, -7, -5, -3, -1, 2, 4, 6, 9, 10, 11, 14, 18, 21, 24,
|
||||
27, 30, 34, 37, 37, 39, 40
|
||||
};
|
||||
|
||||
atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
|
||||
}
|
||||
else if (tb >= 0.10)
|
||||
{
|
||||
static const double AttenCorrScale = 210.0;
|
||||
static const signed char AttenCorrs[] = {
|
||||
-113, -118, -122, -125, -126, -97, -95, -92, -92, -89, -82,
|
||||
-75, -69, -48, -42, -36, -30, -22, -14, -5, -2, 1, 6, 13, 22,
|
||||
28, 35, 41, 48, 55, 56, 56, 61, 65, 71, 77, 81, 83, 85, 85,
|
||||
74, 74, 73, 72, 71, 70, 68, 64, 59, 56, 49, 52, 46, 42, 36,
|
||||
32, 26, 20, 13, 7, -2, -6, -10, -15, -20, -27, -33, -38, -44,
|
||||
-43, -48, -53, -57, -63, -69, -73, -75, -79, -81, -74, -76,
|
||||
-77, -77, -78, -81, -80, -80, -78, -76, -65, -62, -59, -56,
|
||||
-51, -48, -44, -38, -33, -25, -19, -13, -5, -1, 2, 7, 13, 17,
|
||||
21, 25, 30, 35, 40, 45, 50, 53, 56, 57, 55, 58, 59, 62, 64,
|
||||
67, 67, 68, 68, 62, 61, 61, 59, 59, 57, 57, 55, 52, 48, 42,
|
||||
38, 35, 31, 26, 20, 15, 13, 10, 7, 3, -2, -8, -13, -17, -23,
|
||||
-28, -34, -37, -40, -41, -45, -48, -50, -53, -57, -59, -62,
|
||||
-63, -63, -57, -57, -56, -56, -54, -54, -53, -49, -48, -41,
|
||||
-38, -33, -31, -26, -23, -18, -12, -9, -7, -7, -3, 0, 5, 9,
|
||||
14, 16, 20, 22, 21, 23, 25, 27, 28, 29, 34, 33, 35, 33, 31,
|
||||
30, 29, 29, 26, 26, 25, 24, 20, 19, 15, 10, 8, 4, 1, -2, -6,
|
||||
-10, -16, -19, -23, -26, -27, -30, -34, -39, -43, -47, -51,
|
||||
-52, -54, -56, -58, -59, -62, -63, -66, -65, -65, -64, -59,
|
||||
-57, -54, -52, -48, -44, -42, -37, -32, -22, -17, -10, -3, 5,
|
||||
13, 22, 30, 40, 50, 60, 72
|
||||
};
|
||||
|
||||
atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
static const double AttenCorrScale = 196.0;
|
||||
static const signed char AttenCorrs[] = {
|
||||
-15, -17, -20, -20, -20, -21, -20, -16, -17, -18, -17, -13,
|
||||
-12, -11, -9, -7, -5, -4, -1, 1, 3, 4, 5, 6, 7, 9, 9, 10, 10,
|
||||
10, 11, 11, 11, 12, 12, 12, 10, 11, 10, 10, 8, 10, 11, 10, 11,
|
||||
11, 13, 14, 15, 19, 27, 26, 23, 18, 14, 8, 4, -2, -6, -12,
|
||||
-17, -23, -28, -33, -37, -42, -46, -49, -53, -57, -60, -61,
|
||||
-64, -65, -67, -66, -66, -66, -65, -64, -61, -59, -56, -52,
|
||||
-48, -42, -38, -31, -27, -19, -13, -7, -1, 8, 14, 22, 29, 37,
|
||||
45, 52, 59, 66, 73, 80, 86, 91, 96, 100, 104, 108, 111, 114,
|
||||
115, 117, 118, 120, 120, 118, 117, 114, 113, 111, 107, 103,
|
||||
99, 95, 89, 84, 78, 72, 66, 60, 52, 44, 37, 30, 21, 14, 6, -3,
|
||||
-11, -18, -26, -34, -43, -51, -58, -65, -73, -78, -85, -90,
|
||||
-97, -102, -107, -113, -115, -118, -121, -125, -125, -126,
|
||||
-126, -126, -125, -124, -121, -119, -115, -111, -109, -101,
|
||||
-102, -95, -88, -81, -73, -67, -63, -54, -47, -40, -33, -26,
|
||||
-18, -11, -5, 2, 8, 14, 19, 25, 31, 36, 37, 43, 47, 49, 51,
|
||||
52, 57, 57, 56, 57, 58, 58, 58, 57, 56, 52, 52, 50, 48, 44,
|
||||
41, 39, 37, 33, 31, 26, 24, 21, 18, 14, 11, 8, 4, 2, -2, -5,
|
||||
-7, -9, -11, -13, -15, -16, -18, -19, -20, -23, -24, -24, -25,
|
||||
-27, -26, -27, -29, -30, -31, -32, -35, -36, -39, -40, -44,
|
||||
-46, -51, -54, -59, -63, -69, -76, -83, -91, -98
|
||||
};
|
||||
|
||||
atten -= AttenCorrs[AttenCorr] / AttenCorrScale;
|
||||
}
|
||||
|
||||
double pwr = 7.43932822146293e-8 * sqr(atten) + 0.000102747434588003 * cos(0.00785021930010397 * atten) * cos(
|
||||
0.633854318781239 + 0.103208573657699 * atten)
|
||||
- 0.00798132247867036 - 0.000903555213543865 * atten - 0.0969365532127236 * exp(0.0779275237937911 * atten) - 1.37304948662012e-5 *
|
||||
atten * cos(0.00785021930010397 * atten);
|
||||
|
||||
if (pwr <= 0.067665322581)
|
||||
{
|
||||
if (tb >= 0.25)
|
||||
{
|
||||
hl = 2.6778150875894 / tb + 300.547590563091 * atan(atan(2.68959772209918 * pwr)) / (5.5099277187035 * tb - tb * tanh(cos(asinh(atten))));
|
||||
fo1 = 0.987205355829873 * tb + 1.00011788929851 * atan2(-0.321432067051302 - 6.19131357321578 * sqrt(pwr),
|
||||
hl + -1.14861472207245 / (hl - 14.1821147585957) + pow(0.9521145021664,
|
||||
pow(
|
||||
atan2(
|
||||
1.12018764830637,
|
||||
tb),
|
||||
2.10988901686912 * hl -
|
||||
20.9691278378345)));
|
||||
}
|
||||
else if (tb >= 0.10)
|
||||
{
|
||||
hl = (1.56688617018066 + 142.064321294568 * pwr + 0.00419441117131136 * cos(243.633511747297 * pwr) - 0.022953443903576 * atten -
|
||||
0.026629568860284 * cos(127.715550622571 * pwr)) / tb;
|
||||
fo1 = 0.982299356642411 * tb + 0.999441744774215 * asinh((-0.361783054039583 - 5.80540593623676 * sqrt(pwr)) / hl);
|
||||
}
|
||||
else
|
||||
{
|
||||
hl = (2.45739657014937 + 269.183679500541 * pwr * cos(
|
||||
5.73225668178813 + atan2(cosh(0.988861169868941 - 17.2201556280744 * pwr), 1.08340138240431 * pwr))) / tb;
|
||||
fo1 = 2.291956939 * tb + 0.01942450693 * sqr(tb) * hl - 4.67538973161837 * pwr * tb - 1.668433124 * tb * pow(pwr, pwr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tb >= 0.25)
|
||||
{
|
||||
hl = (1.50258368698213 + 158.556968859477 * asinh(pwr) * tanh(57.9466246871383 * tanh(pwr)) - 0.0105440479814834 * atten) / tb;
|
||||
fo1 = 0.994024401639321 * tb + (-0.236282717577215 - 6.8724924545387 * sqrt(sin(pwr))) / hl;
|
||||
}
|
||||
else if (tb >= 0.10)
|
||||
{
|
||||
hl = (1.50277377248945 + 158.222625721046 * asinh(pwr) * tanh(1.02875299001715 + 42.072277322604 * pwr) - 0.0108380943845632 * atten) / tb;
|
||||
fo1 = 0.992539376734551 * tb + (-0.251747813037178 - 6.74159892452584 * sqrt(tanh(tanh(tan(pwr))))) / hl;
|
||||
}
|
||||
else
|
||||
{
|
||||
hl = (1.15990238966306 * pwr - 5.02124037125213 * sqr(pwr) - 0.158676856669827 * atten * cos(
|
||||
1.1609073390614 * pwr - 6.33932586197475 * pwr * sqr(pwr))) / tb;
|
||||
fo1 = 0.867344453126885 * tb + 0.052693817907757 * tb * log(pwr) + 0.0895511178735932 * tb * atan(59.7538527741309 * pwr) -
|
||||
0.0745653568081453 * pwr * tb;
|
||||
}
|
||||
}
|
||||
|
||||
double WinParams[ 2 ];
|
||||
WinParams[0] = 125.0;
|
||||
WinParams[1] = pwr;
|
||||
|
||||
CDSPSincFilterGen sinc;
|
||||
sinc.Len2 = 0.25 * hl / ReqNormFreq;
|
||||
sinc.Freq1 = 0.0;
|
||||
sinc.Freq2 = M_PI * (1.0 - fo1) * ReqNormFreq;
|
||||
sinc.initBand(CDSPSincFilterGen::wftKaiser, WinParams, true);
|
||||
|
||||
KernelLen = sinc.KernelLen;
|
||||
BlockLenBits = getBitOccupancy(KernelLen - 1);
|
||||
const int BlockLen = 1 << BlockLenBits;
|
||||
|
||||
KernelBlock.alloc(BlockLen * 2);
|
||||
sinc.generateBand(&KernelBlock[0],
|
||||
&CDSPSincFilterGen::calcWindowKaiser);
|
||||
|
||||
/* if( ReqPhase == fprLinearPhase )
|
||||
{*/
|
||||
IsZeroPhase = true;
|
||||
Latency = sinc.fl2;
|
||||
LatencyFrac = 0.0;
|
||||
/* }
|
||||
else
|
||||
{
|
||||
IsZeroPhase = false;
|
||||
double DCGroupDelay;
|
||||
|
||||
calcMinPhaseTransform( &KernelBlock[ 0 ], KernelLen, 3, false, &DCGroupDelay );
|
||||
|
||||
Latency = (int) DCGroupDelay;
|
||||
LatencyFrac = DCGroupDelay - Latency;
|
||||
}*/
|
||||
|
||||
CDSPRealFFTKeeper ffto(BlockLenBits + 1);
|
||||
|
||||
if (IsZeroPhase)
|
||||
{
|
||||
// Calculate DC gain.
|
||||
|
||||
double s = 0.0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < KernelLen; ++i) { s += KernelBlock[i]; }
|
||||
|
||||
s = ffto->getInvMulConst() * ReqGain / s;
|
||||
|
||||
// Time-shift the filter so that zero-phase response is produced.
|
||||
// Simultaneously multiply by "s".
|
||||
|
||||
for (i = 0; i <= sinc.fl2; ++i) { KernelBlock[i] = KernelBlock[sinc.fl2 + i] * s; }
|
||||
for (i = 1; i <= sinc.fl2; ++i) { KernelBlock[BlockLen * 2 - i] = KernelBlock[i]; }
|
||||
|
||||
memset(&KernelBlock[sinc.fl2 + 1], 0, (BlockLen * 2 - KernelLen) * sizeof(double));
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizeFIRFilter(&KernelBlock[0], KernelLen, ffto->getInvMulConst() * ReqGain);
|
||||
memset(&KernelBlock[KernelLen], 0, (BlockLen * 2 - KernelLen) * sizeof(double));
|
||||
}
|
||||
|
||||
ffto->forward(KernelBlock);
|
||||
|
||||
R8BCONSOLE("CDSPFIRFilter: flt_len=%i latency=%i nfreq=%.4f tb=%.1f att=%.1f gain=%.3f\n", KernelLen, Latency, ReqNormFreq, ReqTransBand, ReqAtten,
|
||||
ReqGain);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief FIR filter cache class.
|
||||
*
|
||||
* Class that implements cache for calculated FIR filters. The required FIR
|
||||
* filter should be obtained via the getLPFilter() static function.
|
||||
*/
|
||||
|
||||
class CDSPFIRFilterCache : public R8B_BASECLASS
|
||||
{
|
||||
friend class CDSPFIRFilter;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return The number of filters present in the cache now. This value can
|
||||
* be monitored for debugging "forgotten" filters.
|
||||
*/
|
||||
|
||||
static int getObjCount()
|
||||
{
|
||||
R8BSYNC(StateSync);
|
||||
|
||||
return (ObjCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates or returns reference to a previously calculated
|
||||
* (cached) low-pass FIR filter. Note that the real transition band and
|
||||
* attenuation achieved by the filter varies with the magnitude of the
|
||||
* required attenuation, and are never 100% exact.
|
||||
*
|
||||
* @param ReqNormFreq Required normalized frequency, in the range 0 to 1,
|
||||
* inclusive. This is the point after which the stop-band spans.
|
||||
* @param ReqTransBand Required transition band, in percent of the
|
||||
* 0 to ReqNormFreq spectral bandwidth, in the range
|
||||
* CDSPFIRFilter::getLPMinTransBand() to
|
||||
* CDSPFIRFilter::getLPMaxTransBand(), inclusive. The transition band
|
||||
* specifies the part of the spectrum between the -3 dB and ReqNormFreq
|
||||
* points. The real resulting -3 dB point varies in the range from -3.00
|
||||
* to -3.05 dB, but is generally very close to -3 dB.
|
||||
* @param ReqAtten Required stop-band attenuation in decibel, in the range
|
||||
* CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(),
|
||||
* inclusive. Note that the actual stop-band attenuation of the resulting
|
||||
* filter may be 0.40-4.46 dB higher.
|
||||
* @param ReqPhase Required filter's phase response.
|
||||
* @param ReqGain Required overall filter's gain (1.0 for unity gain).
|
||||
* @param AttenCorrs Attentuation correction table, to pass to the filter
|
||||
* generation function. For internal use.
|
||||
* @return A reference to a new or a previously calculated low-pass FIR
|
||||
* filter object with the required characteristics. A reference count is
|
||||
* incremented in the returned filter object which should be released
|
||||
* after use via the CDSPFIRFilter::unref() function.
|
||||
*/
|
||||
|
||||
static CDSPFIRFilter& getLPFilter(const double ReqNormFreq, const double ReqTransBand, const double ReqAtten,
|
||||
const EDSPFilterPhaseResponse ReqPhase, const double ReqGain, const double* const AttenCorrs = nullptr)
|
||||
{
|
||||
R8BASSERT(ReqNormFreq > 0.0 && ReqNormFreq <= 1.0);
|
||||
R8BASSERT(ReqTransBand >= CDSPFIRFilter :: getLPMinTransBand());
|
||||
R8BASSERT(ReqTransBand <= CDSPFIRFilter :: getLPMaxTransBand());
|
||||
R8BASSERT(ReqAtten >= CDSPFIRFilter :: getLPMinAtten());
|
||||
R8BASSERT(ReqAtten <= CDSPFIRFilter :: getLPMaxAtten());
|
||||
R8BASSERT(ReqGain > 0.0);
|
||||
|
||||
R8BSYNC(StateSync);
|
||||
|
||||
CDSPFIRFilter* PrevObj = nullptr;
|
||||
CDSPFIRFilter* CurObj = Objects;
|
||||
|
||||
while (CurObj != nullptr)
|
||||
{
|
||||
if (CurObj->ReqNormFreq == ReqNormFreq &&
|
||||
CurObj->ReqTransBand == ReqTransBand &&
|
||||
CurObj->ReqAtten == ReqAtten &&
|
||||
CurObj->ReqPhase == ReqPhase &&
|
||||
CurObj->ReqGain == ReqGain) { break; }
|
||||
|
||||
if (CurObj->Next == nullptr && ObjCount >= R8B_FILTER_CACHE_MAX)
|
||||
{
|
||||
if (CurObj->RefCount == 0)
|
||||
{
|
||||
// Delete the last filter which is not used.
|
||||
|
||||
PrevObj->Next = nullptr;
|
||||
delete CurObj;
|
||||
ObjCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move the last filter to the top of the list since it
|
||||
// seems to be in use for a long time.
|
||||
|
||||
PrevObj->Next = nullptr;
|
||||
CurObj->Next = Objects.unkeep();
|
||||
Objects = CurObj;
|
||||
}
|
||||
|
||||
CurObj = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
PrevObj = CurObj;
|
||||
CurObj = CurObj->Next;
|
||||
}
|
||||
|
||||
if (CurObj != nullptr)
|
||||
{
|
||||
CurObj->RefCount++;
|
||||
|
||||
if (PrevObj == nullptr) { return (*CurObj); }
|
||||
|
||||
// Remove the filter from the list temporarily.
|
||||
|
||||
PrevObj->Next = CurObj->Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new filter object (with RefCount == 1) and build the
|
||||
// filter kernel.
|
||||
|
||||
CurObj = new CDSPFIRFilter();
|
||||
CurObj->ReqNormFreq = ReqNormFreq;
|
||||
CurObj->ReqTransBand = ReqTransBand;
|
||||
CurObj->ReqAtten = ReqAtten;
|
||||
CurObj->ReqPhase = ReqPhase;
|
||||
CurObj->ReqGain = ReqGain;
|
||||
ObjCount++;
|
||||
|
||||
CurObj->buildLPFilter(AttenCorrs);
|
||||
}
|
||||
|
||||
// Insert the filter at the start of the list.
|
||||
|
||||
CurObj->Next = Objects.unkeep();
|
||||
Objects = CurObj;
|
||||
|
||||
return (*CurObj);
|
||||
}
|
||||
|
||||
private:
|
||||
static CSyncObject StateSync; ///< Cache state synchronizer.
|
||||
///<
|
||||
static CPtrKeeper<CDSPFIRFilter*> Objects; ///< The chain of cached
|
||||
///< objects.
|
||||
///<
|
||||
static int ObjCount; ///< The number of objects currently preset in the
|
||||
///< cache.
|
||||
///<
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CDSPFIRFilter PUBLIC
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
inline void CDSPFIRFilter::unref()
|
||||
{
|
||||
R8BSYNC(CDSPFIRFilterCache :: StateSync);
|
||||
|
||||
RefCount--;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8B_CDSPFIRFILTER_INCLUDED
|
||||
@@ -0,0 +1,495 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPFracInterpolator.h
|
||||
*
|
||||
* @brief Fractional delay interpolator and filter bank classes.
|
||||
*
|
||||
* This file includes fractional delay interpolator class.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPFRACINTERPOLATOR_INCLUDED
|
||||
#define R8B_CDSPFRACINTERPOLATOR_INCLUDED
|
||||
|
||||
#include "CDSPSincFilterGen.h"
|
||||
#include "CDSPProcessor.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Sinc function-based fractional delay filter bank class.
|
||||
*
|
||||
* Class implements storage and initialization of a bank of sinc-based
|
||||
* fractional delay filters, expressed as 1st, 2nd or 3rd order polynomial
|
||||
* interpolation coefficients. The filters are windowed by either the "Vaneev"
|
||||
* or "Kaiser" power-raised window function. The FilterLen and FilterFracs
|
||||
* parameters can be varied freely without breaking the resampler.
|
||||
*
|
||||
* @param FilterLen Specifies the number of samples (taps) each fractional
|
||||
* delay filter should have. This must be an even value, the minimal value for
|
||||
* FilterLen is 6, the maximal value is 30. To achieve a higher resampling
|
||||
* precision, the oversampling should be used in the first place instead of
|
||||
* using a higher FilterLen value. The lower this value is the lower the
|
||||
* signal-to-noise performance of the interpolator will be. Each FilterLen
|
||||
* decrease by 2 decreases SNR by approximately 12 to 14 decibel.
|
||||
* @param FilterFracs The number of fractional delay positions to sample. For
|
||||
* a high signal-to-noise ratio this has to be a larger value. The larger the
|
||||
* FilterLen is the larger the FilterFracs should be. Approximate FilterLen to
|
||||
* FilterFracs correspondence (for 2nd order interpolation only): 6:11, 8:17,
|
||||
* 10:23, 12:41, 14:67, 16:97, 18:137, 20:211, 22:353, 24:673, 26:1051,
|
||||
* 28:1733, 30:2833. The FilterFracs can be considerably reduced with 3rd
|
||||
* order interpolation in use. In order to get consistent results when
|
||||
* resampling to/from different sample rates, it is suggested to set this
|
||||
* parameter to a suitable prime number.
|
||||
* @param ElementSize The size of each filter's tap, in "double" values. This
|
||||
* parameter corresponds to the complexity of interpolation. 4 should be set
|
||||
* for 3rd order, 3 for 2nd order, 2 for linear interpolation.
|
||||
* @param InterpPoints The number of points the interpolation is based on.
|
||||
* This value should not be confused with the ElementSize. Set to 2 for linear
|
||||
* interpolation.
|
||||
*/
|
||||
|
||||
template <int FilterLen, int FilterFracs, int ElementSize, int InterpPoints>
|
||||
class CDSPFracDelayFilterBank : public R8B_BASECLASS
|
||||
{
|
||||
public:
|
||||
CDSPFracDelayFilterBank()
|
||||
{
|
||||
R8BASSERT(FilterLen >= 6);
|
||||
R8BASSERT(FilterLen <= 30);
|
||||
R8BASSERT(( FilterLen & 1 ) == 0);
|
||||
R8BASSERT(FilterFracs > 0);
|
||||
R8BASSERT(ElementSize >= 2 && ElementSize <= 4);
|
||||
R8BASSERT(InterpPoints == 2 || InterpPoints == 8);
|
||||
|
||||
calculate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates the filter bank.
|
||||
*
|
||||
* @param Params Window function's parameters. If NULL then the built-in
|
||||
* table values for the current FilterLen will be used.
|
||||
*/
|
||||
|
||||
void calculate(const double* const Params = nullptr)
|
||||
{
|
||||
CDSPSincFilterGen sinc;
|
||||
sinc.Len2 = FilterLen / 2;
|
||||
|
||||
double* p = Table;
|
||||
const int pc2 = InterpPoints / 2;
|
||||
int i;
|
||||
|
||||
if (FilterLen <= 20)
|
||||
{
|
||||
for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
|
||||
{
|
||||
sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
|
||||
sinc.initFrac(CDSPSincFilterGen::wftVaneev, Params);
|
||||
sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowVaneev,
|
||||
ElementSize);
|
||||
|
||||
normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
|
||||
p += FilterSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = -pc2 + 1; i <= FilterFracs + pc2; ++i)
|
||||
{
|
||||
sinc.FracDelay = double(FilterFracs - i) / FilterFracs;
|
||||
sinc.initFrac(CDSPSincFilterGen::wftKaiser, Params, true);
|
||||
sinc.generateFrac(p, &CDSPSincFilterGen::calcWindowKaiser,
|
||||
ElementSize);
|
||||
|
||||
normalizeFIRFilter(p, FilterLen, 1.0, ElementSize);
|
||||
p += FilterSize;
|
||||
}
|
||||
}
|
||||
|
||||
const int TablePos2 = FilterSize;
|
||||
const int TablePos3 = FilterSize * 2;
|
||||
const int TablePos4 = FilterSize * 3;
|
||||
const int TablePos5 = FilterSize * 4;
|
||||
const int TablePos6 = FilterSize * 5;
|
||||
const int TablePos7 = FilterSize * 6;
|
||||
const int TablePos8 = FilterSize * 7;
|
||||
double* const TableEnd = Table + (FilterFracs + 1) * FilterSize;
|
||||
p = Table;
|
||||
|
||||
if (InterpPoints == 8)
|
||||
{
|
||||
if (ElementSize == 3)
|
||||
{
|
||||
// Calculate 2nd order spline (polynomial) interpolation
|
||||
// coefficients using 8 points.
|
||||
|
||||
while (p < TableEnd)
|
||||
{
|
||||
calcSpline2p8Coeffs(p, p[0], p[TablePos2],
|
||||
p[TablePos3], p[TablePos4], p[TablePos5],
|
||||
p[TablePos6], p[TablePos7], p[TablePos8]);
|
||||
|
||||
p += ElementSize;
|
||||
}
|
||||
}
|
||||
else if (ElementSize == 4)
|
||||
{
|
||||
// Calculate 3rd order spline (polynomial) interpolation
|
||||
// coefficients using 8 points.
|
||||
|
||||
while (p < TableEnd)
|
||||
{
|
||||
calcSpline3p8Coeffs(p, p[0], p[TablePos2],
|
||||
p[TablePos3], p[TablePos4], p[TablePos5],
|
||||
p[TablePos6], p[TablePos7], p[TablePos8]);
|
||||
|
||||
p += ElementSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate linear interpolation coefficients.
|
||||
|
||||
while (p < TableEnd)
|
||||
{
|
||||
p[1] = p[TablePos2] - p[0];
|
||||
p += ElementSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param i Filter index, in the range 0 to FilterFracs, inclusive.
|
||||
* @return Reference to the filter.
|
||||
*/
|
||||
|
||||
const double& operator [](const int i) const
|
||||
{
|
||||
R8BASSERT(i >= 0 && i <= FilterFracs);
|
||||
|
||||
return (Table[i * FilterSize]);
|
||||
}
|
||||
|
||||
private:
|
||||
static const int FilterSize = FilterLen * ElementSize; ///< This constant
|
||||
///< specifies the "size" of a single filter in "double" elements.
|
||||
///<
|
||||
double Table[ FilterSize * (FilterFracs + InterpPoints)]; ///< The
|
||||
///< table of fractional delay filters for all discrete fractional
|
||||
///< x = 0..1 sample positions, and interpolation coefficients.
|
||||
///<
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fractional delay filter bank-based interpolator class.
|
||||
*
|
||||
* Class implements the fractional delay interpolator. This implementation at
|
||||
* first puts the input signal into a ring buffer and then performs
|
||||
* interpolation. The interpolation is performed using sinc-based fractional
|
||||
* delay filters. These filters are contained in a bank, and for higher
|
||||
* precision they are interpolated between adjacent filters.
|
||||
*
|
||||
* To increase sample timing precision, this class uses "resettable counter"
|
||||
* approach. This gives less than "1 per 100 billion" sample timing error when
|
||||
* converting 44100 to 48000 sample rate.
|
||||
*
|
||||
* VERY IMPORTANT: the interpolation step should not exceed FilterLen / 2 + 1
|
||||
* samples or the algorithm in its current form will fail. However, this
|
||||
* condition can be easily met if the input signal is suitably downsampled
|
||||
* first before the interpolation is performed.
|
||||
*
|
||||
* @param FilterLen Specifies the number of samples (taps) each fractional
|
||||
* delay filter should have. See the r8b::CDSPFracDelayFilterBank class for
|
||||
* more details.
|
||||
* @param FilterFracs The number of fractional delay positions to sample. See
|
||||
* the r8b::CDSPFracDelayFilterBank class for more details.
|
||||
*/
|
||||
|
||||
template <int FilterLen, int FilterFracs>
|
||||
class CDSPFracInterpolator final : public CDSPProcessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initalizes the interpolator. It is important to call the
|
||||
* getMaxOutLen() function afterwards to obtain the optimal output buffer
|
||||
* length.
|
||||
*
|
||||
* @param aSrcSampleRate Source sample rate.
|
||||
* @param aDstSampleRate Destination sample rate.
|
||||
* @param aInitFracPos Initial fractional position, in samples, in the
|
||||
* range [0; 1). A non-zero value can be specified to remove the
|
||||
* fractional delay introduced by a minimum-phase filter. This value is
|
||||
* usually equal to the CDSPBlockConvolver.getLatencyFrac() value.
|
||||
*/
|
||||
|
||||
CDSPFracInterpolator(const double aSrcSampleRate,
|
||||
const double aDstSampleRate, const double aInitFracPos)
|
||||
: SrcSampleRate(aSrcSampleRate)
|
||||
, DstSampleRate(aDstSampleRate)
|
||||
, InitFracPos(aInitFracPos)
|
||||
{
|
||||
R8BASSERT(SrcSampleRate > 0.0);
|
||||
R8BASSERT(DstSampleRate > 0.0);
|
||||
R8BASSERT(InitFracPos >= 0.0 && InitFracPos < 1.0);
|
||||
R8BASSERT(BufLenBits >= 5);
|
||||
R8BASSERT(( 1 << BufLenBits ) >= FilterLen * 3);
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
int getLatency() const override { return (0); }
|
||||
|
||||
double getLatencyFrac() const override { return (0.0); }
|
||||
|
||||
int getInLenBeforeOutStart(const int NextInLen) const override { return (FilterLenD2 + NextInLen); }
|
||||
|
||||
int getMaxOutLen(const int MaxInLen) const override
|
||||
{
|
||||
R8BASSERT(MaxInLen >= 0);
|
||||
|
||||
return ((int)ceil(MaxInLen * DstSampleRate / SrcSampleRate) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function changes the destination sample rate "on the fly". Note that
|
||||
* the getMaxOutLen() function may needed to be called after calling this
|
||||
* function as the maximal number of output samples produced by the
|
||||
* interpolator depends on the destination sample rate.
|
||||
*
|
||||
* It can be a useful approach to construct *this object passing the
|
||||
* maximal possible destination sample rate to the constructor, obtaining
|
||||
* the getMaxOutLen() value and then setting the destination sample rate
|
||||
* to whatever lower value is needed.
|
||||
*
|
||||
* It is advisable to change the sample rate in small increments, and as
|
||||
* rarely as possible: e.g. every several samples.
|
||||
*
|
||||
* @param NewDstSampleRate New destination sample rate.
|
||||
*/
|
||||
|
||||
void setDstSampleRate(const double NewDstSampleRate)
|
||||
{
|
||||
R8BASSERT(DstSampleRate > 0.0);
|
||||
|
||||
DstSampleRate = NewDstSampleRate;
|
||||
InCounter = 0;
|
||||
InPosInt = 0;
|
||||
InPosShift = InPosFrac;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function clears (resets) the state of *this object and returns it to
|
||||
* the state after construction. All input data accumulated in the
|
||||
* internal buffer so far will be discarded.
|
||||
*
|
||||
* Note that the destination sample rate will remain unchanged, even if it
|
||||
* was changed since the time of *this object's construction.
|
||||
*/
|
||||
void clear() override
|
||||
{
|
||||
BufLeft = 0;
|
||||
WritePos = 0;
|
||||
ReadPos = BufLen - FilterLenD2Minus1; // Set "read" position to
|
||||
// account for filter's latency at zero fractional delay which
|
||||
// equals to FilterLenD2Minus1.
|
||||
|
||||
memset(&Buf[ReadPos], 0, FilterLenD2Minus1 * sizeof(double));
|
||||
|
||||
InCounter = 0;
|
||||
InPosInt = 0;
|
||||
InPosFrac = InitFracPos;
|
||||
InPosShift = InitFracPos;
|
||||
}
|
||||
|
||||
int process(double* ip, int l, double*& op0) override
|
||||
{
|
||||
R8BASSERT(l >= 0);
|
||||
R8BASSERT(ip != op0 || l == 0 || SrcSampleRate > DstSampleRate);
|
||||
|
||||
double* op = op0;
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
// Add new input samples to both halves of the ring buffer.
|
||||
|
||||
const int b = min(min(l, BufLen - WritePos),
|
||||
BufLeftMax - BufLeft);
|
||||
|
||||
double* const wp1 = Buf + WritePos;
|
||||
double* const wp2 = wp1 + BufLen;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < b; ++i)
|
||||
{
|
||||
wp1[i] = ip[i];
|
||||
wp2[i] = ip[i];
|
||||
}
|
||||
|
||||
ip += b;
|
||||
WritePos = (WritePos + b) & BufLenMask;
|
||||
l -= b;
|
||||
BufLeft += b;
|
||||
|
||||
// Produce as many output samples as possible.
|
||||
|
||||
while (BufLeft >= FilterLenD2Plus1)
|
||||
{
|
||||
double x = InPosFrac * FilterFracs;
|
||||
const int fti = (int)x; // Function table index.
|
||||
x -= fti; // Coefficient for interpolation between adjacent
|
||||
// fractional delay filters.
|
||||
const double x2 = x * x;
|
||||
const double* const ftp = &FilterBank[fti];
|
||||
const double* const rp = Buf + ReadPos;
|
||||
double s = 0.0;
|
||||
int ii = 0;
|
||||
|
||||
#if R8B_FLTTEST
|
||||
const double x3 = x2 * x;
|
||||
#endif // R8B_FLTTEST
|
||||
|
||||
for (i = 0; i < FilterLen; ++i)
|
||||
{
|
||||
#if !R8B_FLTTEST
|
||||
s += (ftp[ii] + ftp[ii + 1] * x +
|
||||
ftp[ii + 2] * x2) * rp[i];
|
||||
#else // !R8B_FLTTEST
|
||||
s += ( ftp[ ii ] + ftp[ ii + 1 ] * x +
|
||||
ftp[ ii + 2 ] * x2 + ftp[ ii + 3 ] * x3 ) * rp[ i ];
|
||||
#endif // !R8B_FLTTEST
|
||||
|
||||
ii += FilterElementSize;
|
||||
}
|
||||
|
||||
*op = s;
|
||||
op++;
|
||||
|
||||
InCounter++;
|
||||
const double NextInPos =
|
||||
InCounter * SrcSampleRate / DstSampleRate + InPosShift;
|
||||
|
||||
const int NextInPosInt = (int)NextInPos;
|
||||
const int PosIncr = NextInPosInt - InPosInt;
|
||||
InPosInt = NextInPosInt;
|
||||
InPosFrac = NextInPos - NextInPosInt;
|
||||
|
||||
ReadPos = (ReadPos + PosIncr) & BufLenMask;
|
||||
BufLeft -= PosIncr;
|
||||
}
|
||||
}
|
||||
|
||||
if (InCounter > 1000)
|
||||
{
|
||||
// Reset the interpolation position counter to achieve a higher
|
||||
// sample timing precision.
|
||||
|
||||
InCounter = 0;
|
||||
InPosInt = 0;
|
||||
InPosShift = InPosFrac;
|
||||
}
|
||||
|
||||
return (int(op - op0));
|
||||
}
|
||||
|
||||
private:
|
||||
#if !R8B_FLTTEST
|
||||
static const int FilterElementSize = 3; ///< The number of "doubles" a
|
||||
///< single filter tap consists of (includes interpolation
|
||||
///< coefficients).
|
||||
///<
|
||||
#else // !R8B_FLTTEST
|
||||
static const int FilterElementSize = 4; ///< The number of "doubles" a
|
||||
///< single filter tap consists of (includes interpolation
|
||||
///< coefficients). During filter testing a higher precision
|
||||
///< interpolation is used.
|
||||
///<
|
||||
#endif // !R8B_FLTTEST
|
||||
|
||||
static const int FilterLenD2 = FilterLen >> 1; ///< = FilterLen / 2.
|
||||
///<
|
||||
static const int FilterLenD2Minus1 = FilterLenD2 - 1; ///< =
|
||||
///< FilterLen / 2 - 1. This value also equals to filter's latency in
|
||||
///< samples (taps).
|
||||
///<
|
||||
static const int FilterLenD2Plus1 = FilterLenD2 + 1; ///< =
|
||||
///< FilterLen / 2 + 1.
|
||||
///<
|
||||
static const int BufLenBits = 8; ///< The length of the ring buffer,
|
||||
///< expressed as Nth power of 2. This value can be reduced if it is
|
||||
///< known that only short input buffers will be passed to the
|
||||
///< interpolator. The minimum value of this parameter is 5, and
|
||||
///< 1 << BufLenBits should be at least 3 times larger than the
|
||||
///< FilterLen.
|
||||
///<
|
||||
static const int BufLen = 1 << BufLenBits; ///< The length of the ring
|
||||
///< buffer. The actual length is twice as long to allow "beyond max
|
||||
///< position" positioning.
|
||||
///<
|
||||
static const int BufLenMask = BufLen - 1; ///< Mask used for quick buffer
|
||||
///< position wrapping.
|
||||
///<
|
||||
static const int BufLeftMax = BufLen - FilterLenD2Minus1; ///< The number
|
||||
///< of new samples that the ring buffer can hold at most. The
|
||||
///< remaining FilterLenD2Minus1 samples hold "previous" input samples
|
||||
///< for the filter.
|
||||
///<
|
||||
double Buf[ BufLen * 2 ]; ///< The ring buffer.
|
||||
///<
|
||||
double SrcSampleRate = 0; ///< Source sample rate.
|
||||
///<
|
||||
double DstSampleRate = 0; ///< Destination sample rate.
|
||||
///<
|
||||
double InitFracPos = 0; ///< Initial fractional position, in samples, in the
|
||||
///< range [0; 1).
|
||||
///<
|
||||
int BufLeft = 0; ///< The number of samples left in the buffer to process.
|
||||
///< When this value is below FilterLenD2Plus1, the interpolation
|
||||
///< cycle ends.
|
||||
///<
|
||||
int WritePos = 0; ///< The current buffer write position. Incremented together
|
||||
///< with the BufLeft variable.
|
||||
///<
|
||||
int ReadPos = 0; ///< The current buffer read position.
|
||||
///<
|
||||
int InCounter = 0; ///< Interpolation step counter.
|
||||
///<
|
||||
int InPosInt = 0; ///< Interpolation position (integer part).
|
||||
///<
|
||||
double InPosFrac = 0; ///< Interpolation position (fractional part).
|
||||
///<
|
||||
double InPosShift = 0; ///< Interpolation position fractional shift.
|
||||
///<
|
||||
|
||||
#if !R8B_FLTTEST
|
||||
static const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3,
|
||||
8> FilterBank; ///< Filter bank object, defined statically if no
|
||||
///< filter test takes place.
|
||||
///<
|
||||
#else // !R8B_FLTTEST
|
||||
public:
|
||||
CDSPFracDelayFilterBank< FilterLen, FilterFracs, 4, 8 > FilterBank; ///<
|
||||
///< Filter bank object, defined as a member variable to allow for
|
||||
///< recalculation.
|
||||
///<
|
||||
#endif // !R8B_FLTTEST
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#if !R8B_FLTTEST
|
||||
template <int FilterLen, int FilterFracs>
|
||||
const CDSPFracDelayFilterBank<FilterLen, FilterFracs, 3, 8>
|
||||
CDSPFracInterpolator<FilterLen, FilterFracs>::FilterBank;
|
||||
#endif // !R8B_FLTTEST
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8B_CDSPFRACINTERPOLATOR_INCLUDED
|
||||
@@ -0,0 +1,105 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPProcessor.h
|
||||
*
|
||||
* @brief The base virtual class for DSP processing algorithms.
|
||||
*
|
||||
* This file includes the base virtual class for DSP processing algorithm
|
||||
* classes like FIR filtering and interpolation.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "r8bbase.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The base virtual class for DSP processing algorithms.
|
||||
*
|
||||
* This class can be used as a base class for various DSP processing
|
||||
* algorithms (processors). DSP processors that are derived from this class
|
||||
* can be seamlessly integrated into various DSP processing graphs.
|
||||
*/
|
||||
|
||||
class CDSPProcessor : public R8B_BASECLASS
|
||||
{
|
||||
R8BNOCTOR(CDSPProcessor)
|
||||
|
||||
public:
|
||||
CDSPProcessor() { }
|
||||
|
||||
virtual ~CDSPProcessor() { }
|
||||
|
||||
/**
|
||||
* @return The latency, in samples, which is present in the output signal.
|
||||
* This value is usually zero if the DSP processor "consumes" the latency
|
||||
* automatically.
|
||||
*/
|
||||
|
||||
virtual int getLatency() const = 0;
|
||||
|
||||
/**
|
||||
* @return Fractional latency, in samples, which is present in the output
|
||||
* signal. This value is usually zero if a linear-phase filtering is used.
|
||||
* With minimum-phase filters in use, this value can be non-zero even if
|
||||
* the getLatency() function returns zero.
|
||||
*/
|
||||
|
||||
virtual double getLatencyFrac() const = 0;
|
||||
|
||||
/**
|
||||
* @param NextInLen The number of input samples required before the output
|
||||
* starts on the next resampling step.
|
||||
* @return The cumulative number of samples that should be passed to *this
|
||||
* object before the actual output starts. This value includes latencies
|
||||
* induced by all processors which run after *this processor in chain.
|
||||
*/
|
||||
|
||||
virtual int getInLenBeforeOutStart(const int NextInLen) const = 0;
|
||||
|
||||
/**
|
||||
* @param MaxInLen The number of samples planned to process at once, at
|
||||
* most.
|
||||
* @return The maximal length of the output buffer required when
|
||||
* processing the "MaxInLen" number of input samples.
|
||||
*/
|
||||
|
||||
virtual int getMaxOutLen(const int MaxInLen) const = 0;
|
||||
|
||||
/**
|
||||
* Function clears (resets) the state of *this object and returns it to
|
||||
* the state after construction. All input data accumulated in the
|
||||
* internal buffer so far will be discarded.
|
||||
*/
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
/**
|
||||
* Function performs DSP processing.
|
||||
*
|
||||
* @param ip Input data pointer.
|
||||
* @param l0 How many samples to process.
|
||||
* @param[out] op0 Output data pointer. The capacity of this buffer should
|
||||
* be equal to the value returned by the getMaxOutLen() function for the
|
||||
* given "l0". This buffer can be equal to "ip" only if the
|
||||
* getMaxOutLen( l0 ) function returned a value lesser than "l0". This
|
||||
* pointer can be incremented on function's return if latency compensation
|
||||
* was performed by the processor. Note that on function's return, this
|
||||
* pointer may point to some internal buffers, including the "ip" buffer,
|
||||
* ignoring the originally passed value.
|
||||
* @return The number of output samples written to the "op0" buffer and
|
||||
* available after processing. This value can be smaller or larger in
|
||||
* comparison to the original "l0" value due to processing and filter's
|
||||
* latency compensation that took place, and due to resampling if it was
|
||||
* performed.
|
||||
*/
|
||||
|
||||
virtual int process(double* ip, int l0, double*& op0) = 0;
|
||||
};
|
||||
} // namespace r8b
|
||||
@@ -0,0 +1,592 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPRealFFT.h
|
||||
*
|
||||
* @brief Real-valued FFT transform class.
|
||||
*
|
||||
* This file includes FFT object implementation. All created FFT objects are
|
||||
* kept in a global list after use for future reusal. Such approach minimizes
|
||||
* time necessary to initialize the FFT object of the required length.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPREALFFT_INCLUDED
|
||||
#define R8B_CDSPREALFFT_INCLUDED
|
||||
|
||||
#include "r8bbase.h"
|
||||
|
||||
#if !R8B_IPP
|
||||
#include "fft4g.h"
|
||||
#endif // !R8B_IPP
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Real-valued FFT transform class.
|
||||
*
|
||||
* Class implements a wrapper for real-valued discrete fast Fourier transform
|
||||
* functions. The object of this class can only be obtained via the
|
||||
* CDSPRealFFTKeeper class.
|
||||
*
|
||||
* Uses functions from the FFT package by: Copyright(C) 1996-2001 Takuya OOURA
|
||||
* http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
|
||||
*
|
||||
* Also uses Intel IPP library functions if available (the R8B_IPP=1 macro was
|
||||
* defined). Note that IPP library's FFT functions are 2-3 times more
|
||||
* efficient on the modern Intel Core i7-3770K processor than Ooura's
|
||||
* functions. It may be worthwhile investing in IPP. Note, that FFT functions
|
||||
* take less than 20% of the overall sample rate conversion time. However,
|
||||
* when the "power of 2" resampling is used the performance of FFT functions
|
||||
* becomes "everything".
|
||||
*/
|
||||
|
||||
class CDSPRealFFT : public R8B_BASECLASS
|
||||
{
|
||||
R8BNOCTOR(CDSPRealFFT)
|
||||
|
||||
friend class CDSPRealFFTKeeper;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return A multiplication constant that should be used after inverse
|
||||
* transform to obtain a correct value scale.
|
||||
*/
|
||||
|
||||
double getInvMulConst() const { return (InvMulConst); }
|
||||
|
||||
/**
|
||||
* @return The length (the number of real values in a transform) of *this
|
||||
* FFT object, expressed as Nth power of 2.
|
||||
*/
|
||||
|
||||
int getLenBits() const { return (LenBits); }
|
||||
|
||||
/**
|
||||
* @return The length (the number of real values in a transform) of *this
|
||||
* FFT object.
|
||||
*/
|
||||
|
||||
int getLen() const { return (Len); }
|
||||
|
||||
/**
|
||||
* Function performs in-place forward FFT.
|
||||
*
|
||||
* @param[in,out] p Pointer to data block to transform, length should be
|
||||
* equal to *this object's getLen().
|
||||
*/
|
||||
|
||||
void forward(double* const p) const
|
||||
{
|
||||
#if R8B_IPP
|
||||
|
||||
ippsFFTFwd_RToPerm_64f( p, p, SPtr, WorkBuffer );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
ooura_fft::rdft(Len, 1, p, wi.getPtr(), wd.getPtr());
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
/**
|
||||
* Function performs in-place inverse FFT.
|
||||
*
|
||||
* @param[in,out] p Pointer to data block to transform, length should be
|
||||
* equal to *this object's getLen().
|
||||
*/
|
||||
|
||||
void inverse(double* const p) const
|
||||
{
|
||||
#if R8B_IPP
|
||||
|
||||
ippsFFTInv_PermToR_64f( p, p, SPtr, WorkBuffer );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
ooura_fft::rdft(Len, -1, p, wi.getPtr(), wd.getPtr());
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
/**
|
||||
* Function multiplies two complex-valued data blocks and places result in
|
||||
* a new data block. Length of all data blocks should be equal to *this
|
||||
* object's block length. Input blocks should have been produced with the
|
||||
* forward() function of *this object.
|
||||
*
|
||||
* @param ip1 Input data block 1.
|
||||
* @param ip2 Input data block 2.
|
||||
* @param[out] op Output data block, should not be equal to ip1 nor ip2.
|
||||
*/
|
||||
|
||||
void multiplyBlocks(const double* const ip1, const double* const ip2, double* const op) const
|
||||
{
|
||||
#if R8B_IPP
|
||||
|
||||
ippsMulPerm_64f( (Ipp64f*) ip1, (Ipp64f*) ip2, (Ipp64f*) op, Len );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
op[0] = ip1[0] * ip2[0];
|
||||
op[1] = ip1[1] * ip2[1];
|
||||
|
||||
int i = 2;
|
||||
|
||||
while (i < Len)
|
||||
{
|
||||
op[i] = ip1[i] * ip2[i] - ip1[i + 1] * ip2[i + 1];
|
||||
op[i + 1] = ip1[i] * ip2[i + 1] + ip1[i + 1] * ip2[i];
|
||||
i += 2;
|
||||
}
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
/**
|
||||
* Function is similar to the multiplyBlocks() function, but instead of
|
||||
* replacing data in the output buffer, the data is summed with the output
|
||||
* buffer.
|
||||
*
|
||||
* @param ip1 Input data block 1.
|
||||
* @param ip2 Input data block 2.
|
||||
* @param[out] op Output data block, should not be equal to ip1 nor ip2.
|
||||
*/
|
||||
|
||||
void multiplyBlocksAdd(const double* const ip1, const double* const ip2, double* const op) const
|
||||
{
|
||||
op[0] += ip1[0] * ip2[0];
|
||||
op[1] += ip1[1] * ip2[1];
|
||||
|
||||
#if R8B_IPP
|
||||
|
||||
ippsAddProduct_64fc( (const Ipp64fc*) ( ip1 + 2 ), (const Ipp64fc*) ( ip2 + 2 ), (Ipp64fc*) ( op + 2 ), ( Len >> 1 ) - 1 );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
int i = 2;
|
||||
|
||||
while (i < Len)
|
||||
{
|
||||
op[i] += ip1[i] * ip2[i] - ip1[i + 1] * ip2[i + 1];
|
||||
op[i + 1] += ip1[i] * ip2[i + 1] + ip1[i + 1] * ip2[i];
|
||||
i += 2;
|
||||
}
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
/**
|
||||
* Function multiplies two complex-valued data blocks in-place. Length of
|
||||
* both data blocks should be equal to *this object's block length. Blocks
|
||||
* should have been produced with the forward() function of *this object.
|
||||
*
|
||||
* @param ip Input data block 1.
|
||||
* @param[in,out] op Output/input data block 2.
|
||||
*/
|
||||
|
||||
void multiplyBlocks(const double* const ip, double* const op) const
|
||||
{
|
||||
#if R8B_IPP
|
||||
|
||||
ippsMulPerm_64f( (Ipp64f*) op, (Ipp64f*) ip, (Ipp64f*) op, Len );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
op[0] *= ip[0];
|
||||
op[1] *= ip[1];
|
||||
|
||||
int i = 2;
|
||||
|
||||
while (i < Len)
|
||||
{
|
||||
const double t = op[i] * ip[i] - op[i + 1] * ip[i + 1];
|
||||
op[i + 1] = op[i] * ip[i + 1] + op[i + 1] * ip[i];
|
||||
op[i] = t;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
/**
|
||||
* Function multiplies two complex-valued data blocks in-place,
|
||||
* considering that the "ip" block contains "zero-phase" response. Length
|
||||
* of both data blocks should be equal to *this object's block length.
|
||||
* Blocks should have been produced with the forward() function of *this
|
||||
* object.
|
||||
*
|
||||
* @param ip Input data block 1, "zero-phase" response.
|
||||
* @param[in,out] op Output/input data block 2.
|
||||
*/
|
||||
|
||||
void multiplyBlocksZ(const double* const ip, double* const op) const
|
||||
{
|
||||
op[0] *= ip[0];
|
||||
op[1] *= ip[1];
|
||||
|
||||
int i = 2;
|
||||
|
||||
while (i < Len)
|
||||
{
|
||||
op[i] *= ip[i];
|
||||
op[i + 1] *= ip[i];
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function performs in-place spectrum squaring. May cause aliasing
|
||||
* if the filter was not zero-padded before the forward() function call.
|
||||
*
|
||||
* @param[in,out] p Pointer to data block to square, length should be
|
||||
* equal to *this object's getLen(). This data block should contain
|
||||
* complex spectrum data, previously obtained via the forward() function.
|
||||
*/
|
||||
|
||||
void sqr(double* const p) const
|
||||
{
|
||||
p[0] *= p[0];
|
||||
p[1] *= p[1];
|
||||
|
||||
#if R8B_IPP
|
||||
|
||||
ippsSqr_64fc( (Ipp64fc*) ( p + 2 ), (Ipp64fc*) ( p + 2 ),
|
||||
( Len >> 1 ) - 1 );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
int i = 2;
|
||||
|
||||
while (i < Len)
|
||||
{
|
||||
const double r = p[i] * p[i] - p[i + 1] * p[i + 1];
|
||||
p[i + 1] = p[i] * (p[i + 1] + p[i + 1]);
|
||||
p[i] = r;
|
||||
i += 2;
|
||||
}
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
private:
|
||||
int LenBits = 0; ///< Length of FFT block (expressed as Nth power of 2).
|
||||
///<
|
||||
int Len = 0; ///< Length of FFT block (number of real values).
|
||||
///<
|
||||
double InvMulConst = 0; ///< Inverse FFT multiply constant.
|
||||
///<
|
||||
CDSPRealFFT* Next = nullptr; ///< Next object in a singly-linked list.
|
||||
///<
|
||||
|
||||
#if R8B_IPP
|
||||
IppsFFTSpec_R_64f* SPtr = nullptr; ///< Pointer to initialized data buffer
|
||||
///< to be passed to IPP's FFT functions.
|
||||
///<
|
||||
CFixedBuffer< unsigned char > SpecBuffer; ///< Working buffer.
|
||||
///<
|
||||
CFixedBuffer< unsigned char > WorkBuffer; ///< Working buffer.
|
||||
///<
|
||||
#else // R8B_IPP
|
||||
CFixedBuffer<int> wi; ///< Working buffer (ints).
|
||||
///<
|
||||
CFixedBuffer<double> wd; ///< Working buffer (doubles).
|
||||
///<
|
||||
#endif // R8B_IPP
|
||||
|
||||
/**
|
||||
* A simple class that keeps the pointer to the object and deletes it
|
||||
* automatically.
|
||||
*/
|
||||
|
||||
class CObjKeeper
|
||||
{
|
||||
R8BNOCTOR(CObjKeeper)
|
||||
|
||||
public:
|
||||
CObjKeeper() { }
|
||||
|
||||
~CObjKeeper() { delete Object; }
|
||||
|
||||
CObjKeeper& operator =(CDSPRealFFT* const aObject)
|
||||
{
|
||||
Object = aObject;
|
||||
return (*this);
|
||||
}
|
||||
|
||||
operator CDSPRealFFT*() const { return (Object); }
|
||||
|
||||
private:
|
||||
CDSPRealFFT* Object = nullptr; ///< FFT object being kept.
|
||||
///<
|
||||
};
|
||||
|
||||
CDSPRealFFT() { }
|
||||
|
||||
/**
|
||||
* Constructor initializes FFT object.
|
||||
*
|
||||
* @param aLenBits The length of FFT block (Nth power of 2), specifies the
|
||||
* number of real values in a block. Values from 1 to 30 inclusive are
|
||||
* supported.
|
||||
*/
|
||||
|
||||
CDSPRealFFT(const int aLenBits)
|
||||
: LenBits(aLenBits), Len(1 << aLenBits)
|
||||
#if R8B_IPP
|
||||
, InvMulConst( 1.0 / Len )
|
||||
#else // R8B_IPP
|
||||
, InvMulConst(2.0 / Len)
|
||||
#endif // R8B_IPP
|
||||
{
|
||||
#if R8B_IPP
|
||||
|
||||
int SpecSize = 0;
|
||||
int SpecBufferSize = 0;
|
||||
int BufferSize = 0;
|
||||
|
||||
ippsFFTGetSize_R_64f( LenBits, IPP_FFT_NODIV_BY_ANY,
|
||||
ippAlgHintFast, &SpecSize, &SpecBufferSize, &BufferSize );
|
||||
|
||||
CFixedBuffer< unsigned char > InitBuffer( SpecBufferSize );
|
||||
SpecBuffer.alloc( SpecSize );
|
||||
WorkBuffer.alloc( BufferSize );
|
||||
|
||||
ippsFFTInit_R_64f( &SPtr, LenBits, IPP_FFT_NODIV_BY_ANY,
|
||||
ippAlgHintFast, SpecBuffer, InitBuffer );
|
||||
|
||||
#else // R8B_IPP
|
||||
|
||||
wi.alloc((int)ceil(2.0 + sqrt(double(Len >> 1))));
|
||||
wi[0] = 0;
|
||||
wd.alloc(Len >> 1);
|
||||
|
||||
#endif // R8B_IPP
|
||||
}
|
||||
|
||||
~CDSPRealFFT() { delete Next; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A "keeper" class for real-valued FFT transform objects.
|
||||
*
|
||||
* Class implements "keeper" functionality for handling CDSPRealFFT objects.
|
||||
* The allocated FFT objects are placed on the global static list of objects
|
||||
* for future reuse instead of deallocation.
|
||||
*/
|
||||
|
||||
class CDSPRealFFTKeeper : public R8B_BASECLASS
|
||||
{
|
||||
R8BNOCTOR(CDSPRealFFTKeeper)
|
||||
|
||||
public:
|
||||
CDSPRealFFTKeeper() { }
|
||||
|
||||
/**
|
||||
* Function acquires FFT object with the specified block length.
|
||||
*
|
||||
* @param LenBits The length of FFT block (Nth power of 2), in the range
|
||||
* [1; 30] inclusive, specifies the number of real values in a FFT block.
|
||||
*/
|
||||
|
||||
CDSPRealFFTKeeper(const int LenBits) { Object = acquire(LenBits); }
|
||||
|
||||
~CDSPRealFFTKeeper() { if (Object != nullptr) { release(Object); } }
|
||||
|
||||
/**
|
||||
* @return Pointer to the acquired FFT object.
|
||||
*/
|
||||
|
||||
const CDSPRealFFT* operator ->() const
|
||||
{
|
||||
R8BASSERT(Object != nullptr);
|
||||
|
||||
return (Object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function acquires FFT object with the specified block length. This
|
||||
* function can be called any number of times.
|
||||
*
|
||||
* @param LenBits The length of FFT block (Nth power of 2), in the range
|
||||
* [1; 30] inclusive, specifies the number of real values in a FFT block.
|
||||
*/
|
||||
|
||||
void init(const int LenBits)
|
||||
{
|
||||
if (Object != nullptr)
|
||||
{
|
||||
if (Object->LenBits == LenBits) { return; }
|
||||
|
||||
release(Object);
|
||||
}
|
||||
|
||||
Object = acquire(LenBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function releases a previously acquired FFT object.
|
||||
*/
|
||||
|
||||
void reset()
|
||||
{
|
||||
if (Object != nullptr)
|
||||
{
|
||||
release(Object);
|
||||
Object = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CDSPRealFFT* Object = nullptr; ///< FFT object.
|
||||
///<
|
||||
|
||||
static CSyncObject StateSync; ///< FFTObjects synchronizer.
|
||||
///<
|
||||
static CDSPRealFFT::CObjKeeper FFTObjects[]; ///< Pool of FFT objects of
|
||||
///< various lengths.
|
||||
///<
|
||||
|
||||
/**
|
||||
* Function acquires FFT object from the global pool.
|
||||
*
|
||||
* @param LenBits FFT block length (expressed as Nth power of 2).
|
||||
*/
|
||||
|
||||
CDSPRealFFT* acquire(const int LenBits)
|
||||
{
|
||||
R8BASSERT(LenBits > 0 && LenBits <= 30);
|
||||
|
||||
R8BSYNC(StateSync);
|
||||
|
||||
if (FFTObjects[LenBits] == nullptr) { return (new CDSPRealFFT(LenBits)); }
|
||||
|
||||
CDSPRealFFT* ffto = FFTObjects[LenBits];
|
||||
FFTObjects[LenBits] = ffto->Next;
|
||||
|
||||
return (ffto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function releases a previously acquired FFT object.
|
||||
*
|
||||
* @param ffto FFT object to release.
|
||||
*/
|
||||
|
||||
void release(CDSPRealFFT* const ffto)
|
||||
{
|
||||
R8BSYNC(StateSync);
|
||||
|
||||
ffto->Next = FFTObjects[ffto->LenBits];
|
||||
FFTObjects[ffto->LenBits] = ffto;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Function calculates the minimum-phase transform of the filter kernel, using
|
||||
* a discrete Hilbert transform in cepstrum domain.
|
||||
*
|
||||
* For more details, see part III.B of
|
||||
* http://www.hpl.hp.com/personal/Niranjan_Damera-Venkata/files/ComplexMinPhase.pdf
|
||||
*
|
||||
* @param[in,out] Kernel Filter kernel buffer.
|
||||
* @param KernelLen Filter kernel's length, in samples.
|
||||
* @param LenMult Kernel length multiplier. Used as a coefficient of the
|
||||
* "oversampling" in the frequency domain. Such oversampling is needed to
|
||||
* improve the precision of the minimum-phase transform. If the filter's
|
||||
* attenuation is high, this multiplier should be increased or otherwise the
|
||||
* required attenuation will not be reached due to "smoothing" effect of this
|
||||
* transform.
|
||||
* @param DoFinalMul "True" if the final multiplication after transform should
|
||||
* be performed or not. Such multiplication returns the gain of the signal to
|
||||
* its original value. This parameter can be set to "false" if normalization
|
||||
* of the resulting filter kernel is planned to be used.
|
||||
* @param[out] DCGroupDelay If not NULL, this variable receives group delay
|
||||
* at DC offset, in samples (can be a non-integer value).
|
||||
*/
|
||||
|
||||
inline void calcMinPhaseTransform(double* const Kernel, const int KernelLen,
|
||||
const int LenMult = 2, const bool DoFinalMul = true,
|
||||
double* const DCGroupDelay = nullptr)
|
||||
{
|
||||
R8BASSERT(KernelLen > 0);
|
||||
R8BASSERT(LenMult >= 2);
|
||||
|
||||
const int LenBits = getBitOccupancy((KernelLen * LenMult) - 1);
|
||||
const int Len = 1 << LenBits;
|
||||
const int Len2 = Len >> 1;
|
||||
int i;
|
||||
|
||||
CFixedBuffer<double> ip(Len);
|
||||
CFixedBuffer<double> ip2(Len2 + 1);
|
||||
|
||||
memcpy(&ip[0], Kernel, KernelLen * sizeof(double));
|
||||
memset(&ip[KernelLen], 0, (Len - KernelLen) * sizeof(double));
|
||||
|
||||
CDSPRealFFTKeeper ffto(LenBits);
|
||||
ffto->forward(ip);
|
||||
|
||||
// Create the "log |c|" spectrum while saving the original power spectrum
|
||||
// in the "ip2" buffer.
|
||||
|
||||
ip2[0] = ip[0];
|
||||
ip[0] = log(fabs(ip[0]) + 1e-50);
|
||||
ip2[Len2] = ip[1];
|
||||
ip[1] = log(fabs(ip[1]) + 1e-50);
|
||||
|
||||
for (i = 1; i < Len2; ++i)
|
||||
{
|
||||
ip2[i] = sqrt(ip[i * 2] * ip[i * 2] +
|
||||
ip[i * 2 + 1] * ip[i * 2 + 1]);
|
||||
|
||||
ip[i * 2] = log(ip2[i] + 1e-50);
|
||||
ip[i * 2 + 1] = 0.0;
|
||||
}
|
||||
|
||||
// Convert to cepstrum and apply discrete Hilbert transform.
|
||||
|
||||
ffto->inverse(ip);
|
||||
|
||||
ip[0] = 0.0;
|
||||
|
||||
for (i = 1; i < Len2; ++i) { ip[i] *= ffto->getInvMulConst(); }
|
||||
|
||||
ip[Len2] = 0.0;
|
||||
|
||||
for (i = Len2 + 1; i < Len; ++i) { ip[i] *= -ffto->getInvMulConst(); }
|
||||
|
||||
// Convert Hilbert-transformed cepstrum back to the "log |c|" spectrum and
|
||||
// perform its exponentiation, multiplied by the power spectrum previously
|
||||
// saved in the "ip2" buffer.
|
||||
|
||||
ffto->forward(ip);
|
||||
|
||||
ip[0] = ip2[0];
|
||||
ip[1] = ip2[Len2];
|
||||
|
||||
for (i = 1; i < Len2; ++i)
|
||||
{
|
||||
const double p = ip2[i];
|
||||
ip[i * 2 + 0] = cos(ip[i * 2 + 1]) * p;
|
||||
ip[i * 2 + 1] = sin(ip[i * 2 + 1]) * p;
|
||||
}
|
||||
|
||||
ffto->inverse(ip);
|
||||
|
||||
if (DoFinalMul) { for (i = 0; i < KernelLen; ++i) { Kernel[i] = ip[i] * ffto->getInvMulConst(); } }
|
||||
else { memcpy(&Kernel[0], &ip[0], KernelLen * sizeof(double)); }
|
||||
|
||||
if (DCGroupDelay != nullptr)
|
||||
{
|
||||
double tmp;
|
||||
|
||||
calcFIRFilterResponseAndGroupDelay(Kernel, KernelLen, 0.0,
|
||||
tmp, tmp, *DCGroupDelay);
|
||||
}
|
||||
}
|
||||
} // namespace r8b
|
||||
|
||||
#endif // VOX_CDSPREALFFT_INCLUDED
|
||||
@@ -0,0 +1,521 @@
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPResampler.h
|
||||
*
|
||||
* @brief The master sample rate converter (resampler) class.
|
||||
*
|
||||
* This file includes the master sample rate converter (resampler) class that
|
||||
* combines all elements of this library into a single front-end class.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPRESAMPLER_INCLUDED
|
||||
#define R8B_CDSPRESAMPLER_INCLUDED
|
||||
|
||||
#include "CDSPBlockConvolver.h"
|
||||
#include "CDSPFracInterpolator.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The master sample rate converter (resampler) class.
|
||||
*
|
||||
* This class can be considered the "master" sample rate converter (resampler)
|
||||
* class since it combines all functionality of this library into a single
|
||||
* front-end class to perform sample rate conversion to/from any sample rate,
|
||||
* including non-integer sample rates.
|
||||
*
|
||||
* Note that objects of this class can be constructed on the stack as it has a
|
||||
* small member data size. The default template parameters of this class are
|
||||
* suited for 27-bit fixed point resampling.
|
||||
*
|
||||
* Use the CDSPResampler16 class for 16-bit resampling.
|
||||
*
|
||||
* Use the CDSPResampler16IR class for 16-bit impulse response resampling.
|
||||
*
|
||||
* Use the CDSPResampler24 class for 24-bit resampling (including 32-bit
|
||||
* floating point resampling).
|
||||
*
|
||||
* @param CInterpClass Interpolator class that should be used by the
|
||||
* resampler. The desired interpolation quality can be defined via the
|
||||
* template parameters of the interpolator class. See
|
||||
* r8b::CDSPFracInterpolator and r8b::CDSPFracDelayFilterBank for description
|
||||
* of the template parameters.
|
||||
*/
|
||||
|
||||
template <class CInterpClass =
|
||||
CDSPFracInterpolator<R8B_FLTLEN, R8B_FLTFRACS>>
|
||||
class CDSPResampler : public CDSPProcessor
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initalizes the resampler object.
|
||||
*
|
||||
* Note that increasing the transition band and decreasing attenuation
|
||||
* reduces the filter length, this in turn reduces the "input before
|
||||
* output" delay. However, the filter length has only a minor influence on
|
||||
* the overall resampling speed.
|
||||
*
|
||||
* It should be noted that the ReqAtten specifies the minimal difference
|
||||
* between the loudest input signal component and the produced aliasing
|
||||
* artifacts during resampling. For example, if ReqAtten=100 was specified
|
||||
* when performing 2x upsampling, the analysis of the resulting signal may
|
||||
* display high-frequency components which are quieter than the loudest
|
||||
* part of the input signal by only 100 decibel meaning the high-frequency
|
||||
* part did not become "magically" completely silent after resampling. You
|
||||
* have to specify a higher ReqAtten value if you need a totally clean
|
||||
* high-frequency content. On the other hand, it may not be reasonable to
|
||||
* have a high-frequency content cleaner than the input signal itself: if
|
||||
* the input signal is 16-bit, setting ReqAtten to 150 will make its
|
||||
* high-frequency content 24-bit, but the original part of the signal will
|
||||
* remain 16-bit.
|
||||
*
|
||||
* @param SrcSampleRate Source signal sample rate. Both sample rates can
|
||||
* be specified as a ratio, e.g. SrcSampleRate = 1.0, DstSampleRate = 2.0.
|
||||
* @param DstSampleRate Destination signal sample rate. The "power of 2"
|
||||
* ratios between the source and destination sample rates force resampler
|
||||
* to use several fast "power of 2" resampling steps, without using
|
||||
* fractional interpolation at all. Note that the "power of 2" upsampling
|
||||
* (but not downsampling) requires a lot of buffer memory: e.g. upsampling
|
||||
* by a factor of 16 requires an intermediate buffer MaxInLen*(16+8)
|
||||
* samples long. So, when doing the "power of 2" upsampling it is highly
|
||||
* recommended to do it in small steps, e.g. no more than 256 samples at
|
||||
* once (also set the MaxInLen to 256).
|
||||
* @param MaxInLen The maximal planned length of the input buffer (in
|
||||
* samples) that will be passed to the resampler. The resampler relies on
|
||||
* this value as it allocates intermediate buffers. Input buffers longer
|
||||
* than this value should never be supplied to the resampler. Note that
|
||||
* the resampler may use the input buffer itself for intermediate sample
|
||||
* data storage.
|
||||
* @param ReqTransBand Required transition band, in percent of the
|
||||
* spectral space of the input signal (or the output signal if
|
||||
* downsampling is performed) between filter's -3 dB point and the Nyquist
|
||||
* frequency. The range is from CDSPFIRFilter::getLPMinTransBand() to
|
||||
* CDSPFIRFilter::getLPMaxTransBand(), inclusive. When upsampling 88200 or
|
||||
* 96000 audio to a higher sample rates the ReqTransBand can be
|
||||
* considerably increased, up to 30. The selection of ReqTransBand depends
|
||||
* on the level of desire to preserve the high-frequency content. While
|
||||
* values 0.5 to 2 are extremely "greedy" settings, not necessary in most
|
||||
* cases, values 2 to 3 can be used in most cases. Values 3 to 4 are
|
||||
* relaxed settings, but they still offer a flat frequency response up to
|
||||
* 21kHz with 44.1k source or destination sample rate.
|
||||
* @param ReqAtten Required stop-band attenuation in decibel, in the range
|
||||
* CDSPFIRFilter::getLPMinAtten() to CDSPFIRFilter::getLPMaxAtten(),
|
||||
* inclusive. The actual attenuation may be 0.40-4.46 dB higher. The
|
||||
* general formula for selecting the ReqAtten is 6.02 * Bits + 40, where
|
||||
* "Bits" is the bit resolution (e.g. 16, 24), "40" is an added resolution
|
||||
* for stationary signals, this value can be decreased to 20 to 10 if the
|
||||
* signal being resampled is mostly non-stationary (e.g. impulse
|
||||
* response).
|
||||
* @param ReqPhase Required filter's phase response. Note that this
|
||||
* setting does not affect interpolator's phase response which is always
|
||||
* linear-phase. Also note that if the "power of 2" resampling was engaged
|
||||
* by the resampler together with the minimum-phase response, the audio
|
||||
* stream may become fractionally delayed by up to 1 sample, depending on
|
||||
* the minimum-phase filter's actual fractional delay. If the output
|
||||
* stream should always start at "time zero" offset with minimum-phase
|
||||
* filters the UsePower2 should be set to "false". Linear-phase filters
|
||||
* do not have fractional delay.
|
||||
* @param UsePower2 "True" if the "power of 2" resampling optimization
|
||||
* should be used when possible. This value should be set to "false" if
|
||||
* the access to interpolator is needed in any case (also the source and
|
||||
* destination sample rates should not be equal).
|
||||
* @see CDSPFIRFilterCache::getLPFilter()
|
||||
*/
|
||||
|
||||
CDSPResampler(const double SrcSampleRate, const double DstSampleRate,
|
||||
const int MaxInLen, const double ReqTransBand = 2.0,
|
||||
const double ReqAtten = 206.91,
|
||||
const EDSPFilterPhaseResponse ReqPhase = fprLinearPhase,
|
||||
const bool UsePower2 = true)
|
||||
{
|
||||
R8BASSERT(SrcSampleRate > 0.0);
|
||||
R8BASSERT(DstSampleRate > 0.0);
|
||||
R8BASSERT(MaxInLen > 0);
|
||||
|
||||
if (SrcSampleRate == DstSampleRate)
|
||||
{
|
||||
ConvCount = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
int SrcSRMult;
|
||||
int SrcSRDiv = 1;
|
||||
int MaxOutLen = MaxInLen;
|
||||
int ConvBufCapacities[ 2 ];
|
||||
double PrevLatencyFrac = 0.0;
|
||||
|
||||
if (DstSampleRate * 2 > SrcSampleRate)
|
||||
{
|
||||
// Only a single convolver with 2X upsampling is required.
|
||||
|
||||
SrcSRMult = 2;
|
||||
const double NormFreq = (DstSampleRate > SrcSampleRate ? 0.5 : 0.5 * DstSampleRate / SrcSampleRate);
|
||||
|
||||
Convs[0] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(NormFreq, ReqTransBand, ReqAtten, ReqPhase, 2.0), 2, 1, 0.0);
|
||||
|
||||
ConvCount = 1;
|
||||
MaxOutLen = Convs[0]->getMaxOutLen(MaxOutLen);
|
||||
ConvBufCapacities[0] = MaxOutLen;
|
||||
PrevLatencyFrac = Convs[0]->getLatencyFrac();
|
||||
|
||||
// Find if the destination to source sample rate ratio is
|
||||
// a "power of 2" value.
|
||||
|
||||
int UseConvCount = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const double TestSR = SrcSampleRate * (1 << UseConvCount);
|
||||
|
||||
if (TestSR > DstSampleRate)
|
||||
{
|
||||
UseConvCount = 0; // Power of 2 not found.
|
||||
break;
|
||||
}
|
||||
|
||||
if (TestSR == DstSampleRate)
|
||||
{
|
||||
break; // Power of 2 found.
|
||||
}
|
||||
|
||||
UseConvCount++;
|
||||
}
|
||||
|
||||
if (UsePower2 && UseConvCount > 0)
|
||||
{
|
||||
R8BASSERT(UseConvCount <= ConvCountMax);
|
||||
|
||||
ConvBufCapacities[1] = 0;
|
||||
ConvCount = UseConvCount;
|
||||
|
||||
for (int i = 1; i < UseConvCount; ++i)
|
||||
{
|
||||
const double tb = (i >= 2 ? 45.0 : 34.0);
|
||||
|
||||
Convs[i] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(0.5, tb, ReqAtten, ReqPhase, 2.0), 2, 1, PrevLatencyFrac);
|
||||
|
||||
MaxOutLen = Convs[i]->getMaxOutLen(MaxOutLen);
|
||||
ConvBufCapacities[i & 1] = MaxOutLen;
|
||||
PrevLatencyFrac = Convs[i]->getLatencyFrac();
|
||||
}
|
||||
|
||||
ConvBufs[0].alloc(ConvBufCapacities[0]);
|
||||
|
||||
if (ConvBufCapacities[1] > 0) { ConvBufs[1].alloc(ConvBufCapacities[1]); }
|
||||
|
||||
return; // No interpolator is needed.
|
||||
}
|
||||
|
||||
ConvBufs[0].alloc(ConvBufCapacities[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SrcSRMult = 1;
|
||||
ConvBufCapacities[0] = 0;
|
||||
ConvCount = 0;
|
||||
const double CheckSR = DstSampleRate * 4;
|
||||
|
||||
while (CheckSR * SrcSRDiv <= SrcSampleRate)
|
||||
{
|
||||
SrcSRDiv *= 2;
|
||||
|
||||
// If downsampling is even deeper, use a less steep filter at
|
||||
// this step.
|
||||
|
||||
const double tb =
|
||||
(CheckSR * SrcSRDiv <= SrcSampleRate ? 45.0 : 34.0);
|
||||
|
||||
Convs[ConvCount] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(0.5, tb, ReqAtten, ReqPhase, 1.0), 1, 2, PrevLatencyFrac);
|
||||
|
||||
MaxOutLen = Convs[ConvCount]->getMaxOutLen(MaxOutLen);
|
||||
PrevLatencyFrac = Convs[ConvCount]->getLatencyFrac();
|
||||
ConvCount++;
|
||||
|
||||
R8BASSERT(ConvCount < ConvCountMax);
|
||||
}
|
||||
|
||||
const double NormFreq = DstSampleRate * SrcSRDiv / SrcSampleRate;
|
||||
const int downf = (UsePower2 && NormFreq == 0.5 ? 2 : 1);
|
||||
|
||||
Convs[ConvCount] = new CDSPBlockConvolver(CDSPFIRFilterCache::getLPFilter(NormFreq, ReqTransBand, ReqAtten, ReqPhase, 1.0), 1, downf,
|
||||
PrevLatencyFrac);
|
||||
|
||||
MaxOutLen = Convs[ConvCount]->getMaxOutLen(MaxOutLen);
|
||||
PrevLatencyFrac = Convs[ConvCount]->getLatencyFrac();
|
||||
ConvCount++;
|
||||
|
||||
if (downf > 1)
|
||||
{
|
||||
return; // No interpolator is needed.
|
||||
}
|
||||
}
|
||||
|
||||
Interp = new CInterpClass(SrcSampleRate * SrcSRMult / SrcSRDiv, DstSampleRate, PrevLatencyFrac);
|
||||
|
||||
MaxOutLen = Interp->getMaxOutLen(MaxOutLen);
|
||||
|
||||
if (MaxOutLen <= ConvBufCapacities[0]) { InterpBuf = ConvBufs[0]; }
|
||||
else if (MaxOutLen <= MaxInLen) { InterpBuf = nullptr; }
|
||||
else
|
||||
{
|
||||
TmpBuf.alloc(MaxOutLen);
|
||||
InterpBuf = TmpBuf;
|
||||
}
|
||||
}
|
||||
|
||||
int getLatency() const override { return (0); }
|
||||
|
||||
double getLatencyFrac() const override { return (0.0); }
|
||||
|
||||
int getInLenBeforeOutStart(const int NextInLen) const override
|
||||
{
|
||||
int l = (Interp == nullptr ? 0 : Interp->getInLenBeforeOutStart(NextInLen));
|
||||
|
||||
for (int i = ConvCount - 1; i >= 0; i--) { l = Convs[i]->getInLenBeforeOutStart(l); }
|
||||
|
||||
return (l);
|
||||
}
|
||||
|
||||
int getMaxOutLen(const int/* MaxInLen */) const override { return (0); }
|
||||
|
||||
/**
|
||||
* Function clears (resets) the state of *this object and returns it to
|
||||
* the state after construction. All input data accumulated in the
|
||||
* internal buffer so far will be discarded.
|
||||
*
|
||||
* This function makes it possible to use *this object for converting
|
||||
* separate streams from the same source sample rate to the same
|
||||
* destination sample rate without reconstructing the object. It is more
|
||||
* efficient to clear the state of the resampler object than to destroy it
|
||||
* and create a new object.
|
||||
*/
|
||||
void clear() override
|
||||
{
|
||||
for (int i = 0; i < ConvCount; ++i) { Convs[i]->clear(); }
|
||||
|
||||
if (Interp != nullptr) { Interp->clear(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Function performs sample rate conversion.
|
||||
*
|
||||
* If the source and destination sample rates are equal, the resampler
|
||||
* will do nothing and will simply return the input buffer unchanged.
|
||||
*
|
||||
* You do not need to allocate an intermediate output buffer for use with
|
||||
* this function. If required, the resampler will allocate a suitable
|
||||
* intermediate output buffer itself.
|
||||
*
|
||||
* @param ip0 Input buffer. This buffer may be used as output buffer by
|
||||
* this function.
|
||||
* @param l The number of samples available in the input buffer. Should
|
||||
* not exceed the MaxInLen supplied to the constructor.
|
||||
* @param[out] op0 This variable receives the pointer to the resampled
|
||||
* data. On function's return, this pointer may point to the address
|
||||
* within the "ip0" input buffer, or to *this object's internal buffer. In
|
||||
* real-time applications it is suggested to pass this pointer to the next
|
||||
* output audio block and consume any data left from the previous output
|
||||
* audio block first before calling the process() function again. The
|
||||
* buffer pointed to by the "op0" on return may be owned by the resampler,
|
||||
* so it should not be freed by the caller.
|
||||
* @return The number of samples available in the "op0" output buffer. If
|
||||
* the data from the output buffer "op0" is going to be written to a
|
||||
* bigger output buffer, it is suggested to check the returned number of
|
||||
* samples so that no overflow of the bigger output buffer happens.
|
||||
*/
|
||||
int process(double* ip0, int l, double*& op0) override
|
||||
{
|
||||
R8BASSERT(l >= 0);
|
||||
|
||||
if (ConvCount == 0)
|
||||
{
|
||||
op0 = ip0;
|
||||
return (l);
|
||||
}
|
||||
|
||||
double* ip = ip0;
|
||||
double* op = nullptr;
|
||||
|
||||
for (int i = 0; i < ConvCount; ++i)
|
||||
{
|
||||
op = (ConvBufs[i & 1] == nullptr ? ip0 : ConvBufs[i & 1]);
|
||||
l = Convs[i]->process(ip, l, op);
|
||||
ip = op;
|
||||
}
|
||||
|
||||
if (Interp == nullptr)
|
||||
{
|
||||
op0 = op;
|
||||
return (l);
|
||||
}
|
||||
|
||||
op = (InterpBuf == nullptr ? ip0 : InterpBuf);
|
||||
op0 = op;
|
||||
|
||||
return (Interp->process(ip, l, op));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function performs resampling of an input sample buffer of the specified
|
||||
* length in the "one-shot" mode. This function can be useful when impulse
|
||||
* response resampling is required.
|
||||
*
|
||||
* @param MaxInLen The max input length value which was previously passed
|
||||
* to the constructor.
|
||||
* @param ip Input buffer pointer.
|
||||
* @param iplen Length of the input buffer in samples.
|
||||
* @param op Output buffer pointer.
|
||||
* @param oplen Length of the output buffer in samples.
|
||||
*/
|
||||
|
||||
void oneshot(const int MaxInLen, const double* ip, int iplen,
|
||||
double* op, int oplen)
|
||||
{
|
||||
const CFixedBuffer<double> ZeroBuf(MaxInLen);
|
||||
memset(&ZeroBuf[0], 0, MaxInLen * sizeof(double));
|
||||
|
||||
while (oplen > 0)
|
||||
{
|
||||
int rc;
|
||||
double* p;
|
||||
|
||||
if (iplen == 0)
|
||||
{
|
||||
rc = MaxInLen;
|
||||
p = static_cast<double*>(&ZeroBuf[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = min(iplen, MaxInLen);
|
||||
p = const_cast<double*>(ip);
|
||||
ip += rc;
|
||||
iplen -= rc;
|
||||
}
|
||||
|
||||
double* op0;
|
||||
int wc = process(p, rc, op0);
|
||||
|
||||
wc = min(oplen, wc);
|
||||
memcpy(op, op0, wc * sizeof(double));
|
||||
op += wc;
|
||||
oplen -= wc;
|
||||
}
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
private:
|
||||
static const int ConvCountMax = 8; ///< 8 convolvers with the
|
||||
///< built-in 2x up- or downsampling is enough for 256x up- or
|
||||
///< downsampling.
|
||||
///<
|
||||
CPtrKeeper<CDSPBlockConvolver*> Convs[ ConvCountMax ]; ///< Convolvers.
|
||||
///<
|
||||
int ConvCount = 0; ///< The number of objects defined in the Convs[] array.
|
||||
///< Equals to 0 if sample rate conversion is not needed.
|
||||
///<
|
||||
CPtrKeeper<CInterpClass*> Interp; ///< Fractional interpolator object.
|
||||
///< Equals NULL if no fractional interpolation is required meaning
|
||||
///< the "power of 2" resampling is performed or no resampling is
|
||||
///< performed at all.
|
||||
///<
|
||||
CFixedBuffer<double> ConvBufs[ 2 ]; ///< Intermediate convolution
|
||||
///< buffers to use, used only when at least 2x upsampling is
|
||||
///< performed. These buffers are used in flip-flop manner. If NULL
|
||||
///< then the input buffer will be used instead.
|
||||
///<
|
||||
CFixedBuffer<double> TmpBuf; ///< Additional output buffer, can be
|
||||
///< addressed by the InterpBuf pointer.
|
||||
///<
|
||||
double* InterpBuf = nullptr; ///< Final output interpolation buffer to use. If NULL
|
||||
///< then the input buffer will be used instead. Otherwise this
|
||||
///< pointer points to either ConvBufs or TmpBuf.
|
||||
///<
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The resampler class for 16-bit resampling.
|
||||
*
|
||||
* This class defines resampling parameters suitable for 16-bit resampling,
|
||||
* using linear-phase low-pass filter. See the r8b::CDSPResampler class for
|
||||
* details.
|
||||
*/
|
||||
|
||||
class CDSPResampler16 final : public CDSPResampler<CDSPFracInterpolator<18, 137>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initializes the 16-bit resampler. See the
|
||||
* r8b::CDSPResampler class for details.
|
||||
*
|
||||
* @param SrcSampleRate Source signal sample rate.
|
||||
* @param DstSampleRate Destination signal sample rate.
|
||||
* @param MaxInLen The maximal planned length of the input buffer (in
|
||||
* samples) that will be passed to the resampler.
|
||||
* @param ReqTransBand Required transition band, in percent.
|
||||
*/
|
||||
|
||||
CDSPResampler16(const double SrcSampleRate, const double DstSampleRate, const int MaxInLen, const double ReqTransBand = 2.0)
|
||||
: CDSPResampler<CDSPFracInterpolator<18, 137>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 136.45, fprLinearPhase, true) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The resampler class for 16-bit impulse response resampling.
|
||||
*
|
||||
* This class defines resampling parameters suitable for 16-bit impulse
|
||||
* response resampling, using linear-phase low-pass filter. Impulse responses
|
||||
* usually do not feature stationary signal components and thus need resampler
|
||||
* with a less SNR. See the r8b::CDSPResampler class for details.
|
||||
*/
|
||||
|
||||
class CDSPResampler16IR final : public CDSPResampler<CDSPFracInterpolator<14, 67>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initializes the 16-bit impulse response resampler. See the
|
||||
* r8b::CDSPResampler class for details.
|
||||
*
|
||||
* @param SrcSampleRate Source signal sample rate.
|
||||
* @param DstSampleRate Destination signal sample rate.
|
||||
* @param MaxInLen The maximal planned length of the input buffer (in
|
||||
* samples) that will be passed to the resampler.
|
||||
* @param ReqTransBand Required transition band, in percent.
|
||||
*/
|
||||
|
||||
CDSPResampler16IR(const double SrcSampleRate, const double DstSampleRate, const int MaxInLen, const double ReqTransBand = 2.0)
|
||||
: CDSPResampler<CDSPFracInterpolator<14, 67>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 109.56, fprLinearPhase, true) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The resampler class for 24-bit resampling.
|
||||
*
|
||||
* This class defines resampling parameters suitable for 24-bit resampling
|
||||
* (including 32-bit floating point resampling), using linear-phase low-pass
|
||||
* filter. See the r8b::CDSPResampler class for details.
|
||||
*/
|
||||
|
||||
class CDSPResampler24 final : public CDSPResampler<CDSPFracInterpolator<24, 673>>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor initializes the 24-bit resampler (including 32-bit floating
|
||||
* point). See the r8b::CDSPResampler class for details.
|
||||
*
|
||||
* @param SrcSampleRate Source signal sample rate.
|
||||
* @param DstSampleRate Destination signal sample rate.
|
||||
* @param MaxInLen The maximal planned length of the input buffer (in
|
||||
* samples) that will be passed to the resampler.
|
||||
* @param ReqTransBand Required transition band, in percent.
|
||||
*/
|
||||
|
||||
CDSPResampler24(const double SrcSampleRate, const double DstSampleRate, const int MaxInLen, const double ReqTransBand = 2.0)
|
||||
: CDSPResampler<CDSPFracInterpolator<24, 673>>(SrcSampleRate, DstSampleRate, MaxInLen, ReqTransBand, 180.15, fprLinearPhase, true) { }
|
||||
};
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8B_CDSPRESAMPLER_INCLUDED
|
||||
@@ -0,0 +1,783 @@
|
||||
//$ nobt
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file CDSPSincFilterGen.h
|
||||
*
|
||||
* @brief Sinc function-based FIR filter generator class.
|
||||
*
|
||||
* This file includes the CDSPSincFilterGen class implementation that
|
||||
* generates FIR filters.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8B_CDSPSINCFILTERGEN_INCLUDED
|
||||
#define R8B_CDSPSINCFILTERGEN_INCLUDED
|
||||
|
||||
#include "r8bbase.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Sinc function-based FIR filter generator class.
|
||||
*
|
||||
* Structure that holds state used to perform generation of sinc functions of
|
||||
* various types, windowed by the Blackman window by default (but the window
|
||||
* function can be changed if necessary).
|
||||
*/
|
||||
|
||||
class CDSPSincFilterGen
|
||||
{
|
||||
public:
|
||||
double Len2 = 0; ///< Required half filter kernel's length in samples (can be
|
||||
///< a fractional value). Final physical kernel length will be
|
||||
///< provided in the KernelLen variable. Len2 should be >= 2.
|
||||
///<
|
||||
int KernelLen = 0; ///< Resulting length of the filter kernel, this variable
|
||||
///< is set after the call to one of the "init" functions.
|
||||
///<
|
||||
int fl2 = 0; ///< Internal "half kernel length" value. This value can be used
|
||||
///< as filter's latency in samples (taps), this variable is set after
|
||||
///< the call to one of the "init" functions.
|
||||
///<
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
double Freq1; ///< Required corner circular frequency 1 [0; pi].
|
||||
///< Used only in the generateBand() function.
|
||||
///<
|
||||
double Freq2; ///< Required corner circular frequency 2 [0; pi].
|
||||
///< Used only in the generateBand() function. The range
|
||||
///< [Freq1; Freq2] defines a pass band for the generateBand()
|
||||
///< function.
|
||||
///<
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
double FracDelay; ///< Fractional delay in the range [0; 1], used
|
||||
///< only in the generateFrac() function. Note that the
|
||||
///< FracDelay parameter is actually inversed. At 0.0 value it
|
||||
///< produces 1 sample delay (with the latency equal to fl2),
|
||||
///< at 1.0 value it produces 0 sample delay (with the latency
|
||||
///< equal to fl2 - 1).
|
||||
///<
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Window function type.
|
||||
*/
|
||||
|
||||
enum EWindowFunctionType
|
||||
{
|
||||
wftCosine, ///< Generalized cosine window function. No parameters
|
||||
///< required. The "Power" parameter is optional.
|
||||
///<
|
||||
wftKaiser, ///< Kaiser window function. Requires the "Beta" parameter.
|
||||
///< The "Power" parameter is optional.
|
||||
///<
|
||||
wftGaussian, ///< Gaussian window function. Requires the "Sigma"
|
||||
///< parameter. The "Power" parameter is optional.
|
||||
///<
|
||||
wftVaneev ///< Vaneev window function, mainly used for short
|
||||
///< fractional delay filters, requires 4 cosine width parameters,
|
||||
///< plus the "Power" parameter which is mandatory.
|
||||
///<
|
||||
};
|
||||
|
||||
typedef double ( CDSPSincFilterGen::*CWindowFunc )(); ///< Window
|
||||
///< calculation function pointer type.
|
||||
///<
|
||||
|
||||
/**
|
||||
* Function initializes *this structure for generation of a window
|
||||
* function, odd-sized.
|
||||
*
|
||||
* @param WinType Window function type.
|
||||
* @param Params Window function's parameters. If NULL, the table values
|
||||
* may be used.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function. If "true", the power factor should be specified as the
|
||||
* last value in the Params array. If Params is NULL, the table or default
|
||||
* value of -1.0 (off) will be used.
|
||||
*/
|
||||
|
||||
void initWindow(const EWindowFunctionType WinType = wftCosine,
|
||||
const double* const Params = nullptr, const bool UsePower = false)
|
||||
{
|
||||
R8BASSERT(Len2 >= 2.0);
|
||||
|
||||
fl2 = (int)floor(Len2);
|
||||
KernelLen = fl2 + fl2 + 1;
|
||||
|
||||
setWindow(WinType, Params, UsePower, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes *this structure for generation of band-limited
|
||||
* sinc filter kernel. The generateBand() or generateBandPow() functions
|
||||
* should be used to calculate the filter.
|
||||
*
|
||||
* @param WinType Window function type.
|
||||
* @param Params Window function's parameters. If NULL, the table values
|
||||
* may be used.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function. If "true", the power factor should be specified as the
|
||||
* last value in the Params array. If Params is NULL, the table or default
|
||||
* value of -1.0 (off) will be used.
|
||||
*/
|
||||
|
||||
void initBand(const EWindowFunctionType WinType = wftCosine,
|
||||
const double* const Params = nullptr, const bool UsePower = false)
|
||||
{
|
||||
R8BASSERT(Len2 >= 2.0);
|
||||
|
||||
fl2 = (int)floor(Len2);
|
||||
KernelLen = fl2 + fl2 + 1;
|
||||
f1.init(Freq1, 0.0);
|
||||
f2.init(Freq2, 0.0);
|
||||
|
||||
setWindow(WinType, Params, UsePower, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes *this structure for Hilbert transformation filter
|
||||
* calculation. Freq1 and Freq2 variables are not used.
|
||||
* The generateHilbert() function should be used to calculate the filter.
|
||||
*
|
||||
* @param WinType Window function type.
|
||||
* @param Params Window function's parameters. If NULL, the table values
|
||||
* may be used.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function. If "true", the power factor should be specified as the
|
||||
* last value in the Params array. If Params is NULL, the table or default
|
||||
* value of -1.0 (off) will be used.
|
||||
*/
|
||||
|
||||
void initHilbert(const EWindowFunctionType WinType = wftCosine,
|
||||
const double* const Params = nullptr, const bool UsePower = false)
|
||||
{
|
||||
R8BASSERT(Len2 >= 2.0);
|
||||
|
||||
fl2 = (int)floor(Len2);
|
||||
KernelLen = fl2 + fl2 + 1;
|
||||
|
||||
setWindow(WinType, Params, UsePower, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes *this structure for generation of full-bandwidth
|
||||
* fractional delay sinc filter kernel. Freq1 and Freq2 variables are not
|
||||
* used. The generateFrac() function should be used to calculate the
|
||||
* filter.
|
||||
*
|
||||
* @param WinType Window function type.
|
||||
* @param Params Window function's parameters. If NULL, the table values
|
||||
* may be used.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function. If "true", the power factor should be specified as the
|
||||
* last value in the Params array. If Params is NULL, the table or default
|
||||
* value of -1.0 (off) will be used.
|
||||
*/
|
||||
|
||||
void initFrac(const EWindowFunctionType WinType = wftCosine,
|
||||
const double* const Params = nullptr, const bool UsePower = false)
|
||||
{
|
||||
R8BASSERT(Len2 >= 2.0);
|
||||
|
||||
fl2 = (int)ceil(Len2);
|
||||
KernelLen = fl2 + fl2;
|
||||
|
||||
setWindow(WinType, Params, UsePower, false, FracDelay);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The next "Hann" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowHann() { return (0.5 + 0.5 * w1.generate()); }
|
||||
|
||||
/**
|
||||
* @return The next "Hamming" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowHamming() { return (0.54 + 0.46 * w1.generate()); }
|
||||
|
||||
/**
|
||||
* @return The next "Blackman" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowBlackman() { return (0.42 + 0.5 * w1.generate() + 0.08 * w2.generate()); }
|
||||
|
||||
/**
|
||||
* @return The next "Nuttall" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowNuttall()
|
||||
{
|
||||
return (0.355768 + 0.487396 * w1.generate() +
|
||||
0.144232 * w2.generate() + 0.012604 * w3.generate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The next "Blackman-Nuttall" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowBlackmanNuttall()
|
||||
{
|
||||
return (0.3635819 + 0.4891775 * w1.generate() +
|
||||
0.1365995 * w2.generate() + 0.0106411 * w3.generate());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The next "Kaiser" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowKaiser()
|
||||
{
|
||||
const double n = 1.0 - sqr(wn / Len2 + KaiserLen2Frac);
|
||||
wn++;
|
||||
|
||||
if (n < 0.0) { return (0.0); }
|
||||
|
||||
return (besselI0(KaiserBeta * sqrt(n)) / KaiserDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The next "Gaussian" window function coefficient.
|
||||
*/
|
||||
|
||||
double calcWindowGaussian()
|
||||
{
|
||||
const double f = exp(-0.5 * sqr(wn / GaussianSigma +
|
||||
GaussianSigmaFrac));
|
||||
|
||||
wn++;
|
||||
|
||||
return (f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The next "Vaneev" windowing function coefficient, for use with
|
||||
* the fractional delay filters.
|
||||
*/
|
||||
|
||||
double calcWindowVaneev()
|
||||
{
|
||||
const double v1 = 0.5 + 0.5 * w1.generate();
|
||||
const double v2 = 0.5 + 0.5 * w2.generate();
|
||||
const double v3 = 0.5 + 0.5 * w3.generate();
|
||||
const double v4 = 0.5 + 0.5 * w4.generate();
|
||||
|
||||
return (v1 * sqr(v2) * sqr(sqr(v3)) * sqr(sqr(sqr(v4))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates window function only.
|
||||
*
|
||||
* @param[out] op Output buffer, length = KernelLen.
|
||||
* @param wfunc Window calculation function to use.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
void generateWindow(T* op,
|
||||
CWindowFunc wfunc = &CDSPSincFilterGen::calcWindowBlackman)
|
||||
{
|
||||
op += fl2;
|
||||
T* op2 = op;
|
||||
|
||||
int l = fl2;
|
||||
|
||||
if (Power < 0.0)
|
||||
{
|
||||
*op = (*this.*wfunc)();
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
const double v = (*this.*wfunc)();
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = v;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*op = pows((*this.*wfunc)(), Power);
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
const double v = pows((*this.*wfunc)(), Power);
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = v;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates band-limited windowed sinc function-based filter
|
||||
* kernel.
|
||||
*
|
||||
* @param[out] op Output buffer, length = KernelLen.
|
||||
* @param wfunc Window calculation function to use.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
void generateBand(T* op,
|
||||
CWindowFunc wfunc = &CDSPSincFilterGen::calcWindowBlackman)
|
||||
{
|
||||
op += fl2;
|
||||
T* op2 = op;
|
||||
f1.generate();
|
||||
f2.generate();
|
||||
int t = 1;
|
||||
|
||||
if (Power < 0.0)
|
||||
{
|
||||
*op = (Freq2 - Freq1) * (*this.*wfunc)() / M_PI;
|
||||
|
||||
while (t <= fl2)
|
||||
{
|
||||
const double v = (f2.generate() - f1.generate()) *
|
||||
(*this.*wfunc)() / t / M_PI;
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = v;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*op = (Freq2 - Freq1) * pows((*this.*wfunc)(), Power) / M_PI;
|
||||
|
||||
while (t <= fl2)
|
||||
{
|
||||
const double v = (f2.generate() - f1.generate()) *
|
||||
pows((*this.*wfunc)(), Power) / t / M_PI;
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = v;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates windowed Hilbert transformer filter kernel.
|
||||
*
|
||||
* @param[out] op Output buffer, length = KernelLen.
|
||||
* @param wfunc Window calculation function to use.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
void generateHilbert(T* op,
|
||||
CWindowFunc wfunc = &CDSPSincFilterGen::calcWindowBlackman)
|
||||
{
|
||||
static const double fvalues[ 2 ] = { 0.0, 2.0 };
|
||||
op += fl2;
|
||||
T* op2 = op;
|
||||
|
||||
(*this.*wfunc)();
|
||||
*op = 0.0;
|
||||
|
||||
int t = 1;
|
||||
|
||||
if (Power < 0.0)
|
||||
{
|
||||
while (t <= fl2)
|
||||
{
|
||||
const double v = fvalues[t & 1] *
|
||||
(*this.*wfunc)() / t / M_PI;
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = -v;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (t <= fl2)
|
||||
{
|
||||
const double v = fvalues[t & 1] *
|
||||
pows((*this.*wfunc)(), Power) / t / M_PI;
|
||||
|
||||
++op;
|
||||
--op2;
|
||||
*op = v;
|
||||
*op2 = -v;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function calculates windowed fractional delay filter kernel.
|
||||
*
|
||||
* @param[out] op Output buffer, length = KernelLen.
|
||||
* @param wfunc Window calculation function to use.
|
||||
* @param opinc Output buffer increment, in "op" elements.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
void generateFrac(T* op,
|
||||
CWindowFunc wfunc = &CDSPSincFilterGen::calcWindowBlackman,
|
||||
const int opinc = 1)
|
||||
{
|
||||
R8BASSERT(opinc != 0);
|
||||
|
||||
double f[ 2 ];
|
||||
f[0] = sin(FracDelay * M_PI);
|
||||
f[1] = -f[0];
|
||||
|
||||
int t = -fl2;
|
||||
|
||||
if (t + FracDelay < -Len2)
|
||||
{
|
||||
(*this.*wfunc)();
|
||||
*op = 0.0;
|
||||
op += opinc;
|
||||
t++;
|
||||
}
|
||||
|
||||
int mt = (FracDelay >= 1.0 - 1e-13 && FracDelay <= 1.0 + 1e-13 ? -1 : 0);
|
||||
|
||||
if (Power < 0.0)
|
||||
{
|
||||
while (t < mt)
|
||||
{
|
||||
*op = f[t & 1] * (*this.*wfunc)() / (t + FracDelay) /
|
||||
M_PI;
|
||||
|
||||
op += opinc;
|
||||
t++;
|
||||
}
|
||||
|
||||
double ut = t + FracDelay;
|
||||
*op = (fabs(ut) <= 1e-13 ? (*this.*wfunc)() : f[t & 1] * (*this.*wfunc)() / ut / M_PI);
|
||||
|
||||
mt = fl2 - 2;
|
||||
|
||||
while (t < mt)
|
||||
{
|
||||
op += opinc;
|
||||
t++;
|
||||
*op = f[t & 1] * (*this.*wfunc)() / (t + FracDelay) /
|
||||
M_PI;
|
||||
}
|
||||
|
||||
op += opinc;
|
||||
t++;
|
||||
ut = t + FracDelay;
|
||||
*op = (ut > Len2 ? 0.0 : f[t & 1] * (*this.*wfunc)() / ut / M_PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (t < mt)
|
||||
{
|
||||
*op = f[t & 1] * pows((*this.*wfunc)(), Power) /
|
||||
(t + FracDelay) / M_PI;
|
||||
|
||||
op += opinc;
|
||||
t++;
|
||||
}
|
||||
|
||||
double ut = t + FracDelay;
|
||||
*op = (fabs(ut) <= 1e-13 ? pows((*this.*wfunc)(), Power) : f[t & 1] * pows((*this.*wfunc)(), Power) / ut / M_PI);
|
||||
|
||||
mt = fl2 - 2;
|
||||
|
||||
while (t < mt)
|
||||
{
|
||||
op += opinc;
|
||||
t++;
|
||||
*op = f[t & 1] * pows((*this.*wfunc)(), Power) /
|
||||
(t + FracDelay) / M_PI;
|
||||
}
|
||||
|
||||
op += opinc;
|
||||
t++;
|
||||
ut = t + FracDelay;
|
||||
*op = (ut > Len2 ? 0.0 : f[t & 1] * pows((*this.*wfunc)(), Power) / ut / M_PI);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
double Power = 0; ///< The power factor used to raise the window function.
|
||||
///< Equals a negative value if the power factor should not be used.
|
||||
///<
|
||||
CSineGen f1; ///< Sine function 1. Used in the generateBand() function.
|
||||
///<
|
||||
CSineGen f2; ///< Sine function 2. Used in the generateBand() function.
|
||||
///<
|
||||
int wn = 0; ///< Window function integer position. 0 - center of the window
|
||||
///< function. This variable may not be used by some window functions.
|
||||
///<
|
||||
CSineGen w1; ///< Cosine wave 1 for window function.
|
||||
///<
|
||||
CSineGen w2; ///< Cosine wave 2 for window function.
|
||||
///<
|
||||
CSineGen w3; ///< Cosine wave 3 for window function.
|
||||
///<
|
||||
CSineGen w4; ///< Cosine wave 4 for window function.
|
||||
///<
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
double KaiserBeta; // Kaiser window function's "Beta" coefficient.
|
||||
double KaiserDiv; // Kaiser window function's divisor.
|
||||
double KaiserLen2Frac; // Equals FracDelay / Len2.
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
double GaussianSigma; // Gaussian window function's "Sigma" coefficient.
|
||||
double GaussianSigmaFrac; // Equals FracDelay / GaussianSigma.
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param FilterLen2 Half filter length in samples (taps).
|
||||
* @return The Kaiser power-raised window function parameters for the
|
||||
* specified filter length.
|
||||
*/
|
||||
|
||||
static const double* getKaiserParams(const int FilterLen2)
|
||||
{
|
||||
R8BASSERT(FilterLen2 >= 3 && FilterLen2 <= 15);
|
||||
|
||||
static const double Coeffs[][ 2 ] = {
|
||||
{ 3.41547411, 1.41275111 }, // 6 @ 51.38
|
||||
{ 3.72300147, 1.75212634 }, // 8 @ 67.60
|
||||
{ 4.34839223, 1.85801372 }, // 10 @ 79.86
|
||||
{ 4.90860405, 1.97194591 }, // 12 @ 93.29
|
||||
{ 5.17430411, 2.20609617 }, // 14 @ 106.74
|
||||
{ 21.08445389, 0.59684098 }, // 16 @ 119.71
|
||||
{ 9.14552738, 1.57619894 }, // 18 @ 134.53
|
||||
{ 22.02344341, 0.71669064 }, // 20 @ 148.44
|
||||
{ 16.41763757, 1.05884118 }, // 22 @ 164.61
|
||||
{ 12.55262798, 1.51553897 }, // 24 @ 180.15
|
||||
{ 9.84861210, 2.09912671 }, // 26 @ 194.15
|
||||
{ 9.73150659, 2.29079494 }, // 28 @ 206.91
|
||||
{ 10.42657217, 2.29183875 }, // 30 @ 218.20
|
||||
};
|
||||
|
||||
return (Coeffs[FilterLen2 - 3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes Kaiser window function calculation. The FracDelay
|
||||
* variable should be initialized when using this window function.
|
||||
*
|
||||
* @param Params Function parameters. If NULL, the table values will be
|
||||
* used. If not NULL, the first parameter should specify the "Beta" value.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function.
|
||||
* @param IsCentered "True" if centered window should be used. This
|
||||
* parameter usually equals to "false" for fractional delay filters only.
|
||||
*/
|
||||
|
||||
void setWindowKaiser(const double* Params, const bool UsePower,
|
||||
const bool IsCentered)
|
||||
{
|
||||
wn = (IsCentered ? 0 : -fl2);
|
||||
|
||||
if (Params == nullptr)
|
||||
{
|
||||
Params = getKaiserParams(fl2);
|
||||
KaiserBeta = Params[0];
|
||||
Power = (UsePower ? Params[1] : -1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
KaiserBeta = clampr(Params[0], 1.0, 350.0);
|
||||
Power = (UsePower ? fabs(Params[1]) : -1.0);
|
||||
}
|
||||
|
||||
KaiserDiv = besselI0(KaiserBeta);
|
||||
KaiserLen2Frac = FracDelay / Len2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes Gaussian window function calculation. The FracDelay
|
||||
* variable should be initialized when using this window function.
|
||||
*
|
||||
* @param Params Function parameters. If NULL, the table values will be
|
||||
* used. If not NULL, the first parameter should specify the "Sigma"
|
||||
* value.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function.
|
||||
* @param IsCentered "True" if centered window should be used. This
|
||||
* parameter usually equals to "false" for fractional delay filters only.
|
||||
*/
|
||||
|
||||
void setWindowGaussian(const double* Params, const bool UsePower,
|
||||
const bool IsCentered)
|
||||
{
|
||||
wn = (IsCentered ? 0 : -fl2);
|
||||
|
||||
if (Params == nullptr)
|
||||
{
|
||||
GaussianSigma = 1.0;
|
||||
Power = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
GaussianSigma = clampr(fabs(Params[0]), 1e-1, 100.0);
|
||||
Power = (UsePower ? fabs(Params[1]) : -1.0);
|
||||
}
|
||||
|
||||
GaussianSigma *= Len2;
|
||||
GaussianSigmaFrac = FracDelay / GaussianSigma;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes "Vaneev" window function calculation.
|
||||
*
|
||||
* @param Params Function parameters. If NULL, the table values will be
|
||||
* used. If not NULL, the first 4 parameters should specify the cosine
|
||||
* multipliers while the fifth parameter should specify the "Power" value.
|
||||
* @param IsCentered "True" if centered window should be used. This
|
||||
* parameter usually equals to "false" for fractional delay filters only.
|
||||
*/
|
||||
|
||||
void setWindowVaneev(const double* Params, const bool IsCentered)
|
||||
{
|
||||
R8BASSERT(fl2 >= 3 && fl2 <= 15);
|
||||
|
||||
// This set of parameters was obtained via probabilistic optimization.
|
||||
// The number after @ shows the approximate (+/- 1 dB) signal-to-noise
|
||||
// ratio for the given filter. SNR can be also decreased by using a
|
||||
// filter bank with suboptimal number of sampled fractional delay
|
||||
// filters: thus the FilterFracs should be selected with care.
|
||||
|
||||
static const double Coeffs[][ 5 ] = {
|
||||
{ 0.35926104, 0.66154037, 0.79264845, 0.31897879, 0.18844972 }, // 6 @ 51.91
|
||||
{ 0.81690764, 0.39409966, 0.01546567, 0.02067949, 1.15143000 }, // 8 @ 67.87
|
||||
{ 0.26545140, 0.84346586, 0.12114879, 0.23640230, 0.72659219 }, // 10 @ 81.82
|
||||
{ 0.56254211, 0.32615646, 0.88375690, 0.46944169, 0.32862728 }, // 12 @ 95.36
|
||||
{ 0.51926261, 0.41265523, 0.89552919, 0.47699008, 0.37308306 }, // 14 @ 109.60
|
||||
{ 0.55650321, 0.92583533, 0.58934379, 0.16399064, 0.67129777 }, // 16 @ 122.89
|
||||
{ 0.27930548, 0.94898807, 0.70335882, 0.32080180, 0.59102482 }, // 18 @ 136.45
|
||||
{ 0.12620836, 0.94993219, 0.70209891, 0.34747431, 0.64429174 }, // 20 @ 150.52
|
||||
{ 0.83595860, 0.95040751, 0.64127591, 0.30856013, 0.69692727 }, // 22 @ 163.41
|
||||
{ 0.41252871, 0.96236749, 0.74895429, 0.41669175, 0.65996102 }, // 24 @ 174.32
|
||||
{ 0.98567539, 0.88907131, 0.65652775, 0.34585902, 0.77265757 }, // 26 @ 191.26
|
||||
{ 0.64526843, 0.67729329, 0.91813705, 0.43972488, 0.68332682 }, // 28 @ 195.77
|
||||
{ 0.65310281, 0.66723395, 0.91751074, 0.43956737, 0.73651421 }, // 30 @ 207.04
|
||||
};
|
||||
|
||||
double p[ 4 ];
|
||||
|
||||
if (Params == nullptr)
|
||||
{
|
||||
Params = Coeffs[fl2 - 3];
|
||||
Power = Params[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
p[0] = clampr(Params[0], -4.0, 4.0);
|
||||
p[1] = clampr(Params[1], -4.0, 4.0);
|
||||
p[2] = clampr(Params[2], -4.0, 4.0);
|
||||
p[3] = clampr(Params[3], -4.0, 4.0);
|
||||
Power = fabs(Params[4]);
|
||||
Params = p;
|
||||
}
|
||||
|
||||
if (IsCentered)
|
||||
{
|
||||
w1.init(Params[0] * M_PI / Len2, M_PI * 0.5);
|
||||
w2.init(Params[1] * M_PI / Len2, M_PI * 0.5);
|
||||
w3.init(Params[2] * M_PI / Len2, M_PI * 0.5);
|
||||
w4.init(Params[3] * M_PI / Len2, M_PI * 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double step1 = Params[0] * M_PI / Len2;
|
||||
w1.init(step1, M_PI * 0.5 - step1 * fl2 + step1 * FracDelay);
|
||||
|
||||
const double step2 = Params[1] * M_PI / Len2;
|
||||
w2.init(step2, M_PI * 0.5 - step2 * fl2 + step2 * FracDelay);
|
||||
|
||||
const double step3 = Params[2] * M_PI / Len2;
|
||||
w3.init(step3, M_PI * 0.5 - step3 * fl2 + step3 * FracDelay);
|
||||
|
||||
const double step4 = Params[3] * M_PI / Len2;
|
||||
w4.init(step4, M_PI * 0.5 - step4 * fl2 + step4 * FracDelay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function initializes calculation of window function of the specified
|
||||
* type.
|
||||
*
|
||||
* @param WinType Window function type.
|
||||
* @param Params Window function's parameters. If NULL, the table values
|
||||
* may be used.
|
||||
* @param UsePower "True" if the power factor should be used to raise the
|
||||
* window function. If "true", the power factor should be specified as the
|
||||
* last value in the Params array. If Params is NULL, the table or default
|
||||
* value of -1.0 (off) will be used.
|
||||
* @param IsCentered "True" if centered window should be used. This
|
||||
* parameter usually equals to "false" for fractional delay filters only.
|
||||
* @param UseFracDelay Fractional delay to use.
|
||||
*/
|
||||
|
||||
void setWindow(const EWindowFunctionType WinType,
|
||||
const double* const Params, const bool UsePower,
|
||||
const bool IsCentered, const double UseFracDelay = 0.0)
|
||||
{
|
||||
FracDelay = UseFracDelay;
|
||||
|
||||
if (WinType == wftCosine)
|
||||
{
|
||||
if (IsCentered)
|
||||
{
|
||||
w1.init(M_PI / Len2, M_PI * 0.5);
|
||||
w2.init(M_2PI / Len2, M_PI * 0.5);
|
||||
w3.init(M_3PI / Len2, M_PI * 0.5);
|
||||
}
|
||||
else
|
||||
{
|
||||
const double step1 = M_PI / Len2;
|
||||
w1.init(step1, M_PI * 0.5 - step1 * fl2 +
|
||||
step1 * FracDelay);
|
||||
|
||||
const double step2 = M_2PI / Len2;
|
||||
w2.init(step2, M_PI * 0.5 - step2 * fl2 +
|
||||
step2 * FracDelay);
|
||||
|
||||
const double step3 = M_3PI / Len2;
|
||||
w3.init(step3, M_PI * 0.5 - step3 * fl2 +
|
||||
step3 * FracDelay);
|
||||
}
|
||||
|
||||
Power = (UsePower && Params != nullptr ? Params[0] : -1.0);
|
||||
}
|
||||
else if (WinType == wftKaiser) { setWindowKaiser(Params, UsePower, IsCentered); }
|
||||
else if (WinType == wftGaussian) { setWindowGaussian(Params, UsePower, IsCentered); }
|
||||
else { setWindowVaneev(Params, IsCentered); }
|
||||
}
|
||||
};
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8B_CDSPSINCFILTERGEN_INCLUDED
|
||||
@@ -0,0 +1,55 @@
|
||||
# r8brain-free-src #
|
||||
## Introduction ##
|
||||
Open source (under the MIT license) high-quality professional audio sample rate converter (SRC) (resampling) library. Features routines for SRC, both up- and downsampling, to/from any sample rate, including non-integer sample rates: it can be also used for conversion to/from SACD sample rate and even go beyond that. SRC routines were implemented in multi-platform C++ code, and have a high level of optimality.
|
||||
|
||||
The structure of this library's objects is such that they can be frequently created and destroyed in large applications with a minimal performance impact due to a high level of reusability of its most "initialization-expensive" objects: the fast Fourier transform and FIR filter objects.
|
||||
|
||||
The SRC algorithm at first produces 2X oversampled (relative to the source sample rate, or the destination sample rate if the downsampling is performed) signal and then performs interpolation using a bank of short (14 to 28 taps, depending on the required precision) polynomial-interpolated sinc function-based fractional delay filters. This puts the algorithm into the league of the fastest among the most precise SRC algorithms. The more precise alternative being only the whole number-factored SRC, which can be slower.
|
||||
|
||||
## Requirements ##
|
||||
C++ compiler and system with the "double" floating point type (53-bit mantissa) support. No explicit code for the "float" type is present in this library, because as practice has shown the "float"-based code performs considerably slower on a modern processor, at least in this library. However, if the "double" type really represents the "float" type (24-bit mantissa) in a given compiler, on a given system, the library won't become broken, only the conversion quality may become degraded. This library always uses the "sizeof( double )" operator to obtain "double" floating point type's size in bytes. This library does not have dependencies beside the standard C library, the "windows.h" on Windows and the "pthread.h" on Mac OS X and Linux.
|
||||
|
||||
## Links ##
|
||||
* [Documentation](https://c16f948c1577658f1b05f6c1d146730273eb6285.googledrive.com/host/0BwakvlMNBQdwUXhLMDFJLWdBSlU/Documentation/)
|
||||
* [Discussion](http://www.kvraudio.com/forum/viewtopic.php?t=389711)
|
||||
* [r8brain-free-src-1.6-dll.zip](https://drive.google.com/open?id=0BwakvlMNBQdwR1JlZ3pKcVBpaWc&authuser=0)
|
||||
|
||||
## Usage Information ##
|
||||
The sample rate converter (resampler) is represented by the **r8b::CDSPResampler<>** class, which is a single front-end class for the whole library. You do not basically need to use nor understand any other classes beside this class. Several derived classes that have varying levels of precision are also available.
|
||||
|
||||
The code of the library resides in the "r8b" C++ namespace, effectively isolating it from all other code. The code is thread-safe. A separate resampler object should be created for each audio channel or stream being processed.
|
||||
|
||||
Note that you will need to compile the "r8bbase.cpp" source file and include the resulting object file into your application build. This source file includes definitions of several global static objects used by the library. You may also need to include to your project: the "Kernel32" library (on Windows) and the "pthread" library on Mac OS X and Linux.
|
||||
|
||||
The library is able to process signal of any scale and loudness: it is not limited to just a "usual" -1.0 to 1.0 range.
|
||||
|
||||
The code of this library was commented in the [Doxygen](http://www.doxygen.org/) style. To generate the documentation locally you may run the "doxygen ./other/r8bdoxy.txt" command from the library's directory.
|
||||
|
||||
Preliminary tests show that the r8b::CDSPResampler24 resampler class achieves 15.6\*n\_cores Mflops when converting 1 channel of audio from 44100 to 96000 sample rate, on a typical Intel Core i7-4770K processor-based system without overclocking. This approximately translates to a real-time resampling of 160\*n\_cores audio streams, at 100% CPU load. When comparing performance of this resampler library to another library make sure that the competing library is also tuned to produce a fully linear-phase response.
|
||||
|
||||
## Dynamic Link Library ##
|
||||
The functions of this SRC library are also accessible in simplified form via the DLL file on Windows, requiring a processor with SSE2 support. Delphi Pascal interface unit file for the DLL file is available. DLL and C LIB files are distributed in a separate ZIP file on the project's home page. On non-Windows systems it is preferrable to use the C++ library directly.
|
||||
|
||||
## Real-time Applications ##
|
||||
The resampler class of this library was designed as asynchronous processor: it may produce any number of output samples, depending on the input sample data length and the resampling parameters. The resampler must be fed with the input sample data until enough output sample data was produced, with any excess output samples used before feeding the resampler with more input data. A "relief" factor here is that the resampler removes the initial processing latency automatically, and that after initial moments of processing the output becomes steady, with only minor output sample data length fluctuations.
|
||||
|
||||
Note that the r8b::CDSPResampler::getInLenBeforeOutStart() function can be used to estimate the number of input samples that should be provided to the resampler before the actual output starts.
|
||||
|
||||
## Notes ##
|
||||
When using the r8b::CDSPResampler<> class directly, you may select the transition band/steepness of the low-pass (reconstruction) filter, expressed as a percentage of the full spectral bandwidth of the input signal (or the output signal if the downsampling is performed), and the desired stop-band attenuation in decibel.
|
||||
|
||||
The transition band is specified as the normalized spectral space of the input signal (or the output signal if the downsampling is performed) between the low-pass filter's -3 dB point and the Nyquist frequency, and ranges from 0.5% to 45%. Stop-band attenuation can be specified in the range 49 to 218 decibel.
|
||||
|
||||
This SRC library also implements a faster "power of 2" resampling (e.g. 2X, 4X, 8X, 16X, etc. upsampling and downsampling).
|
||||
|
||||
This library was tested for compatibility with [GNU C++](http://gcc.gnu.org/), [Microsoft Visual C++](http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products) and [Intel C++](http://software.intel.com/en-us/c-compilers) compilers, on 32- and 64-bit Windows, Mac OS X and CentOS Linux.
|
||||
|
||||
All code is fully "inline", without the need to compile many source files. The memory footprint is quite modest.
|
||||
|
||||
## Users ##
|
||||
This library is used by:
|
||||
|
||||
* [Combo Model V VSTi instrument](http://www.martinic.com/combov/)
|
||||
* [WDM Asio Link Driver](http://midithru.net/Home/AsioLink)
|
||||
* [Boogex Guitar Amp audio plugin](http://www.voxengo.com/product/boogex/)
|
||||
* [OpenMPT](http://openmpt.org/)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Please credit the creator of this library in your documentation in the
|
||||
following way: "Sample rate converter designed by Aleksey Vaneev of Voxengo"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
@@ -0,0 +1,17 @@
|
||||
PROJECT_NAME = "r8brain-free-src"
|
||||
PROJECT_BRIEF = "High-quality pro audio sample rate converter library"
|
||||
PROJECT_LOGO = ./other/icon.png
|
||||
OUTPUT_DIRECTORY = ./
|
||||
BRIEF_MEMBER_DESC = NO
|
||||
SHORT_NAMES = YES
|
||||
TAB_SIZE = 4
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
INLINE_INFO = NO
|
||||
SORT_BRIEF_DOCS = YES
|
||||
SORT_MEMBERS_CTORS_1ST = YES
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_NAMESPACES = NO
|
||||
WARN_NO_PARAMDOC = YES
|
||||
INPUT = ./
|
||||
HTML_OUTPUT = Documentation
|
||||
GENERATE_LATEX = NO
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @file r8bbase.cpp
|
||||
*
|
||||
* @brief C++ file that should be compiled and included into your application.
|
||||
*
|
||||
* This is a single library file that should be compiled and included into the
|
||||
* project that uses the "r8brain-free-src" sample rate converter. This file
|
||||
* defines several global static objects used by the library.
|
||||
*
|
||||
* You may also need to include to your project: the "Kernel32" library
|
||||
* (on Windows) and the "pthread" library on Mac OS X and Linux.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#include "CDSPFIRFilter.h"
|
||||
#include "CDSPFracInterpolator.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
CSyncObject CDSPRealFFTKeeper::StateSync;
|
||||
CDSPRealFFT::CObjKeeper CDSPRealFFTKeeper::FFTObjects[ 31 ];
|
||||
CSyncObject CDSPFIRFilterCache::StateSync;
|
||||
CPtrKeeper<CDSPFIRFilter*> CDSPFIRFilterCache::Objects;
|
||||
int CDSPFIRFilterCache::ObjCount = 0;
|
||||
} // namespace r8b
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,131 @@
|
||||
//$ nobt
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file r8bconf.h
|
||||
*
|
||||
* @brief The "configuration" inclusion file you can modify.
|
||||
*
|
||||
* This is the "configuration" inclusion file for the "r8brain-free-src"
|
||||
* sample rate converter. You may redefine the macros here as you see fit.
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8BCONF_INCLUDED
|
||||
#define R8BCONF_INCLUDED
|
||||
|
||||
#if defined( _WIN32 ) || defined( _WIN64 )
|
||||
#define R8B_WIN 1
|
||||
#elif defined( __APPLE__ )
|
||||
#define R8B_MAC 1
|
||||
#else // defined( __APPLE__ )
|
||||
#define R8B_LNX 1 // Assume Linux (Unix) platform by default.
|
||||
#endif // defined( __APPLE__ )
|
||||
|
||||
#if !defined( R8B_FLTLEN )
|
||||
/**
|
||||
* This macro defines the default fractional delay filter length. Macro is
|
||||
* used by the r8b::CDSPResampler class.
|
||||
*/
|
||||
|
||||
#define R8B_FLTLEN 28
|
||||
#endif // !defined( R8B_FLTLEN )
|
||||
|
||||
#if !defined( R8B_FLTFRACS )
|
||||
/**
|
||||
* This macro defines the default number of fractional delay filters that
|
||||
* are sampled by the filter bank. Macro is used by the r8b::CDSPResampler
|
||||
* class. In order to get consistent results when resampling to/from
|
||||
* different sample rates, it is suggested to set this macro to a suitable
|
||||
* prime number.
|
||||
*/
|
||||
|
||||
#define R8B_FLTFRACS 1733
|
||||
#endif // !defined( R8B_FLTFRACS )
|
||||
|
||||
#if !defined( R8B_IPP )
|
||||
/**
|
||||
* Set the R8B_IPP macro definition to 1 to enable the use of Intel IPP's
|
||||
* fast Fourier transform functions. Also uncomment and correct the IPP
|
||||
* header inclusion macros.
|
||||
*
|
||||
* Do not forget to call the ippInit() function at the start of the
|
||||
* application, before using this library's functions.
|
||||
*/
|
||||
|
||||
#define R8B_IPP 0
|
||||
|
||||
// #include <ippcore.h>
|
||||
// #include <ipps.h>
|
||||
#endif // !defined( R8B_IPP )
|
||||
|
||||
#if !defined( R8BASSERT )
|
||||
/**
|
||||
* Assertion macro used to check for certain run-time conditions. By
|
||||
* default no action is taken if assertion fails.
|
||||
*
|
||||
* @param e Expression to check.
|
||||
*/
|
||||
|
||||
#define R8BASSERT( e )
|
||||
#endif // !defined( R8BASSERT )
|
||||
|
||||
#if !defined( R8BCONSOLE )
|
||||
/**
|
||||
* Console output macro, used to output various resampler status strings,
|
||||
* including filter design parameters, convolver parameters.
|
||||
*
|
||||
* @param e Expression to send to the console, usually consists of a
|
||||
* standard "printf" format string followed by several parameters
|
||||
* (__VA_ARGS__).
|
||||
*/
|
||||
|
||||
#define R8BCONSOLE( ... )
|
||||
#endif // !defined( R8BCONSOLE )
|
||||
|
||||
#if !defined( R8B_BASECLASS )
|
||||
/**
|
||||
* Macro defines the name of the class from which all classes that are
|
||||
* designed to be created on heap are derived. The default
|
||||
* r8b::CStdClassAllocator class uses "stdlib" memory allocation
|
||||
* functions.
|
||||
*
|
||||
* The classes that are best placed on stack or as class members are not
|
||||
* derived from any class.
|
||||
*/
|
||||
|
||||
#define R8B_BASECLASS :: r8b :: CStdClassAllocator
|
||||
#endif // !defined( R8B_BASECLASS )
|
||||
|
||||
#if !defined( R8B_MEMALLOCCLASS )
|
||||
/**
|
||||
* Macro defines the name of the class that implements raw memory
|
||||
* allocation functions, see the r8b::CStdMemAllocator class for details.
|
||||
*/
|
||||
|
||||
#define R8B_MEMALLOCCLASS :: r8b :: CStdMemAllocator
|
||||
#endif // !defined( R8B_MEMALLOCCLASS )
|
||||
|
||||
#if !defined( R8B_FILTER_CACHE_MAX )
|
||||
/**
|
||||
* This macro specifies the number of filters kept in the cache at most.
|
||||
* The actual number can be higher if many different filters are in use at
|
||||
* the same time.
|
||||
*/
|
||||
|
||||
#define R8B_FILTER_CACHE_MAX 96
|
||||
#endif // !defined( R8B_FILTER_CACHE_MAX )
|
||||
|
||||
#if !defined( R8B_FLTTEST )
|
||||
/**
|
||||
* This macro, when equal to 1, enables fractional delay filter bank
|
||||
* testing: in this mode the filter bank becomes dynamic member of the
|
||||
* CDSPFracInterpolator object instead of being a global static object.
|
||||
*/
|
||||
|
||||
#define R8B_FLTTEST 0
|
||||
#endif // !defined( R8B_FLTTEST )
|
||||
|
||||
#endif // R8BCONF_INCLUDED
|
||||
@@ -0,0 +1,284 @@
|
||||
//$ nobt
|
||||
//$ nocpp
|
||||
|
||||
/**
|
||||
* @file r8butil.h
|
||||
*
|
||||
* @brief The inclusion file with several utility functions.
|
||||
*
|
||||
* This file includes several utility functions used by various utility
|
||||
* programs like "calcErrorTable.cpp".
|
||||
*
|
||||
* r8brain-free-src Copyright (c) 2013-2014 Aleksey Vaneev
|
||||
* See the "License.txt" file for license.
|
||||
*/
|
||||
|
||||
#ifndef R8BUTIL_INCLUDED
|
||||
#define R8BUTIL_INCLUDED
|
||||
|
||||
#include "r8bbase.h"
|
||||
|
||||
namespace r8b
|
||||
{
|
||||
|
||||
/**
|
||||
* @param re Real part of the frequency response.
|
||||
* @param im Imaginary part of the frequency response.
|
||||
* @return A magnitude response value converted from the linear scale to the
|
||||
* logarithmic scale.
|
||||
*/
|
||||
|
||||
inline double convertResponseToLog(const double re, const double im) { return (4.34294481903251828 * log(re * re + im * im + 1e-100)); }
|
||||
|
||||
/**
|
||||
* An utility function that performs frequency response scanning step update
|
||||
* based on the current magnitude response's slope.
|
||||
*
|
||||
* @param[in,out] step The current scanning step. Will be updated on
|
||||
* function's return. Must be a positive value.
|
||||
* @param curg Squared magnitude response at the current frequency point.
|
||||
* @param[in,out] prevg_log Previous magnitude response, log scale. Will be
|
||||
* updated on function's return.
|
||||
* @param prec Precision multiplier, affects the size of the step.
|
||||
* @param maxstep The maximal allowed step.
|
||||
* @param minstep The minimal allowed step.
|
||||
*/
|
||||
|
||||
inline void updateScanStep(double& step, const double curg, double& prevg_log, const double prec, const double maxstep, const double minstep = 1e-11)
|
||||
{
|
||||
double curg_log = 4.34294481903251828 * log(curg + 1e-100);
|
||||
curg_log += (prevg_log - curg_log) * 0.7;
|
||||
|
||||
const double slope = fabs(curg_log - prevg_log);
|
||||
prevg_log = curg_log;
|
||||
|
||||
if (slope > 0.0)
|
||||
{
|
||||
step /= prec * slope;
|
||||
step = max(min(step, maxstep), minstep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function locates normalized frequency at which the minimum filter gain
|
||||
* is reached. The scanning is performed from lower (left) to higher
|
||||
* (right) frequencies, the whole range is scanned.
|
||||
*
|
||||
* Function expects that the magnitude response is always reducing from lower
|
||||
* to high frequencies, starting at "minth".
|
||||
*
|
||||
* @param flt Filter response.
|
||||
* @param fltlen Filter response's length in samples (taps).
|
||||
* @param[out] ming The current minimal gain (squared). On function's return
|
||||
* will contain the minimal gain value found (squared).
|
||||
* @param[out] minth The normalized frequency where the minimal gain is
|
||||
* currently at. On function's return will point to the normalized frequency
|
||||
* where the new minimum was found.
|
||||
* @param thend The ending frequency, inclusive.
|
||||
*/
|
||||
|
||||
inline void findFIRFilterResponseMinLtoR(const double* const flt,
|
||||
const int fltlen, double& ming, double& minth, const double thend)
|
||||
{
|
||||
const double maxstep = minth * 2e-3;
|
||||
double curth = minth;
|
||||
double re;
|
||||
double im;
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im);
|
||||
double prevg_log = convertResponseToLog(re, im);
|
||||
double step = 1e-11;
|
||||
|
||||
while (true)
|
||||
{
|
||||
curth += step;
|
||||
|
||||
if (curth > thend) { break; }
|
||||
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im);
|
||||
const double curg = re * re + im * im;
|
||||
|
||||
if (curg > ming)
|
||||
{
|
||||
ming = curg;
|
||||
minth = curth;
|
||||
break;
|
||||
}
|
||||
|
||||
ming = curg;
|
||||
minth = curth;
|
||||
|
||||
updateScanStep(step, curg, prevg_log, 0.31, maxstep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function locates normalized frequency at which the maximal filter gain
|
||||
* is reached. The scanning is performed from lower (left) to higher
|
||||
* (right) frequencies, the whole range is scanned.
|
||||
*
|
||||
* Note: this function may "stall" in very rare cases if the magnitude
|
||||
* response happens to be "saw-tooth" like, requiring a very small stepping to
|
||||
* be used. If this happens, it may take dozens of seconds to complete.
|
||||
*
|
||||
* @param flt Filter response.
|
||||
* @param fltlen Filter response's length in samples (taps).
|
||||
* @param[out] maxg The current maximal gain (squared). On function's return
|
||||
* will contain the maximal gain value (squared).
|
||||
* @param[out] maxth The normalized frequency where the maximal gain is
|
||||
* currently at. On function's return will point to the normalized frequency
|
||||
* where the maximum was reached.
|
||||
* @param thend The ending frequency, inclusive.
|
||||
*/
|
||||
|
||||
inline void findFIRFilterResponseMaxLtoR(const double* const flt,
|
||||
const int fltlen, double& maxg, double& maxth, const double thend)
|
||||
{
|
||||
const double maxstep = maxth * 1e-4;
|
||||
double premaxth = maxth;
|
||||
double premaxg = maxg;
|
||||
double postmaxth = maxth;
|
||||
double postmaxg = maxg;
|
||||
|
||||
double prevth = maxth;
|
||||
double prevg = maxg;
|
||||
double curth = maxth;
|
||||
double re;
|
||||
double im;
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im);
|
||||
double prevg_log = convertResponseToLog(re, im);
|
||||
double step = 1e-11;
|
||||
|
||||
bool WasPeak = false;
|
||||
int AfterPeakCount = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
curth += step;
|
||||
|
||||
if (curth > thend) { break; }
|
||||
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * curth, re, im);
|
||||
const double curg = re * re + im * im;
|
||||
|
||||
if (curg > maxg)
|
||||
{
|
||||
premaxth = prevth;
|
||||
premaxg = prevg;
|
||||
maxg = curg;
|
||||
maxth = curth;
|
||||
WasPeak = true;
|
||||
AfterPeakCount = 0;
|
||||
}
|
||||
else if (WasPeak)
|
||||
{
|
||||
if (AfterPeakCount == 0)
|
||||
{
|
||||
postmaxth = curth;
|
||||
postmaxg = curg;
|
||||
}
|
||||
|
||||
if (AfterPeakCount == 5)
|
||||
{
|
||||
// Perform 2 approximate binary searches.
|
||||
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
double l = (k == 0 ? premaxth : maxth);
|
||||
double curgl = (k == 0 ? premaxg : maxg);
|
||||
double r = (k == 0 ? maxth : postmaxth);
|
||||
double curgr = (k == 0 ? maxg : postmaxg);
|
||||
|
||||
while (true)
|
||||
{
|
||||
const double c = (l + r) * 0.5;
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * c, re, im);
|
||||
|
||||
const double curgTmp = re * re + im * im;
|
||||
|
||||
if (curgl > curgr)
|
||||
{
|
||||
r = c;
|
||||
curgr = curgTmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
l = c;
|
||||
curgl = curgTmp;
|
||||
}
|
||||
|
||||
if (r - l < 1e-11)
|
||||
{
|
||||
if (curgl > curgr)
|
||||
{
|
||||
maxth = l;
|
||||
maxg = curgl;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxth = r;
|
||||
maxg = curgr;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
AfterPeakCount++;
|
||||
}
|
||||
|
||||
prevth = curth;
|
||||
prevg = curg;
|
||||
|
||||
updateScanStep(step, curg, prevg_log, 1.0, maxstep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function locates normalized frequency at which the specified maximum
|
||||
* filter gain is reached. The scanning is performed from higher (right)
|
||||
* to lower (left) frequencies, scanning stops when the required gain
|
||||
* value was crossed. Function uses an extremely efficient binary search and
|
||||
* thus expects that the magnitude response has the "main lobe" form produced
|
||||
* by windowing, with a minimal pass-band ripple.
|
||||
*
|
||||
* @param flt Filter response.
|
||||
* @param fltlen Filter response's length in samples (taps).
|
||||
* @param maxg Maximal gain (squared).
|
||||
* @param[out] th The current normalized frequency. On function's return will
|
||||
* point to the normalized frequency where "maxg" is reached.
|
||||
* @param thend The leftmost frequency to scan, inclusive.
|
||||
*/
|
||||
|
||||
inline void findFIRFilterResponseLevelRtoL(const double* const flt, const int fltlen, const double maxg, double& th, const double thend)
|
||||
{
|
||||
// Perform exact binary search.
|
||||
|
||||
double l = thend;
|
||||
double r = th;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const double c = (l + r) * 0.5;
|
||||
|
||||
if (r - l < 1e-14)
|
||||
{
|
||||
th = c;
|
||||
break;
|
||||
}
|
||||
|
||||
double re;
|
||||
double im;
|
||||
calcFIRFilterResponse(flt, fltlen, M_PI * c, re, im);
|
||||
const double curg = re * re + im * im;
|
||||
|
||||
if (curg > maxg) { l = c; }
|
||||
else { r = c; }
|
||||
}
|
||||
}
|
||||
} // namespace r8b
|
||||
|
||||
#endif // R8BUTIL_INCLUDED
|
||||
@@ -0,0 +1,28 @@
|
||||
wavelib
|
||||
=======
|
||||
|
||||
C Implementation of Discrete Wavelet Transform (DWT,SWT and MODWT), Continuous Wavelet transform (CWT) and Discrete Packet Transform ( Full Tree Decomposition and Best Basis DWPT).
|
||||
|
||||
Discrete Wavelet Transform Methods Implemented
|
||||
|
||||
DWT/IDWT A decimated Discrete Wavelet Transform implementation using implicit signal extension and up/downsampling so it is a fast implementation. A FFT based implementation is optional but will not be usually needed. Both periodic and symmetric options are available.
|
||||
|
||||
SWT/ISWT Stationary Wavelet Transform. It works only for signal lengths that are multiples of 2^J where J is the number of decomposition levels. For signals of other lengths see MODWT implementation.
|
||||
|
||||
MODWT/IMODWT Maximal Overlap Discrete Wavelet Transform is another undecimated transform. It is implemented for signals of any length but only orthogonal wavelets (Daubechies, Symlets and Coiflets) can be deployed. This implementation is based on the method laid out in "Wavelet Methods For Wavelet Analysis" by Donald Percival and Andrew Walden.
|
||||
|
||||
Discrete Wavelet Packet Transform Methods Implemented
|
||||
|
||||
WTREE A Fully Decimated Wavelet Tree Decomposition. This is a highly redundant transform and retains all coefficients at each node. This is not recommended for compression and denoising applications.
|
||||
|
||||
DWPT/IDWPT Is a derivative of WTREE method which retains coefficients based on entropy methods. This is a non-redundant transform and output length is of the same order as the input.
|
||||
|
||||
CWT/ICWT C translation ( with some modifications) of Continuous Wavelet Transform Software provided by C. Torrence and G. Compo, and is available at URL: http://atoc.colorado.edu/research/wavelets/'. A generalized Inverse Transform with approximate reconstruction is also added.
|
||||
|
||||
Documentation Available at - https://github.com/rafat/wavelib/wiki
|
||||
|
||||
Live Demo (Emscripten) - http://rafat.github.io/wavelib/
|
||||
|
||||
License - BSD 3-Clause
|
||||
|
||||
Contace - rafat.hsn@gmail.com
|
||||
@@ -0,0 +1,240 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable : 4200)
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
#ifndef fft_type
|
||||
#define fft_type double
|
||||
#endif
|
||||
|
||||
#ifndef cplx_type
|
||||
#define cplx_type double
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct cplx_t
|
||||
{
|
||||
cplx_type re;
|
||||
cplx_type im;
|
||||
} cplx_data;
|
||||
|
||||
typedef struct wave_set* wave_object;
|
||||
|
||||
wave_object wave_init(char* wname);
|
||||
|
||||
struct wave_set
|
||||
{
|
||||
char wname[50];
|
||||
int filtlength;// When all filters are of the same length. [Matlab uses zero-padding to make all filters of the same length]
|
||||
int lpd_len;// Default filtlength = lpd_len = lpr_len = hpd_len = hpr_len
|
||||
int hpd_len;
|
||||
int lpr_len;
|
||||
int hpr_len;
|
||||
double* lpd;
|
||||
double* hpd;
|
||||
double* lpr;
|
||||
double* hpr;
|
||||
double params[0];
|
||||
};
|
||||
|
||||
typedef struct fft_t
|
||||
{
|
||||
fft_type re;
|
||||
fft_type im;
|
||||
} fft_data;
|
||||
|
||||
typedef struct fft_set* fft_object;
|
||||
|
||||
fft_object fft_init(int N, int sgn);
|
||||
|
||||
struct fft_set
|
||||
{
|
||||
int N;
|
||||
int sgn;
|
||||
int factors[64];
|
||||
int lf;
|
||||
int lt;
|
||||
fft_data twiddle[1];
|
||||
};
|
||||
|
||||
typedef struct fft_real_set* fft_real_object;
|
||||
|
||||
fft_real_object fft_real_init(int N, int sgn);
|
||||
|
||||
struct fft_real_set
|
||||
{
|
||||
fft_object cobj;
|
||||
fft_data twiddle2[1];
|
||||
};
|
||||
|
||||
typedef struct conv_set* conv_object;
|
||||
|
||||
conv_object conv_init(int N, int L);
|
||||
|
||||
struct conv_set
|
||||
{
|
||||
fft_real_object fobj;
|
||||
fft_real_object iobj;
|
||||
int ilen1;
|
||||
int ilen2;
|
||||
int clen;
|
||||
};
|
||||
|
||||
typedef struct wt_set* wt_object;
|
||||
|
||||
wt_object wt_init(wave_object wave, char* method, int siglength, int J);
|
||||
|
||||
struct wt_set
|
||||
{
|
||||
wave_object wave;
|
||||
conv_object cobj;
|
||||
char method[10];
|
||||
int siglength;// Length of the original signal.
|
||||
int outlength;// Length of the output DWT vector
|
||||
int lenlength;// Length of the Output Dimension Vector "length"
|
||||
int J; // Number of decomposition Levels
|
||||
int MaxIter;// Maximum Iterations J <= MaxIter
|
||||
int even;// even = 1 if signal is of even length. even = 0 otherwise
|
||||
char ext[10];// Type of Extension used - "per" or "sym"
|
||||
char cmethod[10]; // Convolution Method - "direct" or "FFT"
|
||||
|
||||
int N; //
|
||||
int cfftset;
|
||||
int zpad;
|
||||
int length[102];
|
||||
double* output;
|
||||
double params[0];
|
||||
};
|
||||
|
||||
typedef struct wtree_set* wtree_object;
|
||||
|
||||
wtree_object wtree_init(wave_object wave, int siglength, int J);
|
||||
|
||||
struct wtree_set
|
||||
{
|
||||
wave_object wave;
|
||||
conv_object cobj;
|
||||
char method[10];
|
||||
int siglength;// Length of the original signal.
|
||||
int outlength;// Length of the output DWT vector
|
||||
int lenlength;// Length of the Output Dimension Vector "length"
|
||||
int J; // Number of decomposition Levels
|
||||
int MaxIter;// Maximum Iterations J <= MaxIter
|
||||
int even;// even = 1 if signal is of even length. even = 0 otherwise
|
||||
char ext[10];// Type of Extension used - "per" or "sym"
|
||||
|
||||
int N; //
|
||||
int nodes;
|
||||
int cfftset;
|
||||
int zpad;
|
||||
int length[102];
|
||||
double* output;
|
||||
int* nodelength;
|
||||
int* coeflength;
|
||||
double params[0];
|
||||
};
|
||||
|
||||
typedef struct wpt_set* wpt_object;
|
||||
|
||||
wpt_object wpt_init(wave_object wave, int siglength, int J);
|
||||
|
||||
struct wpt_set
|
||||
{
|
||||
wave_object wave;
|
||||
conv_object cobj;
|
||||
int siglength;// Length of the original signal.
|
||||
int outlength;// Length of the output DWT vector
|
||||
int lenlength;// Length of the Output Dimension Vector "length"
|
||||
int J; // Number of decomposition Levels
|
||||
int MaxIter;// Maximum Iterations J <= MaxIter
|
||||
int even;// even = 1 if signal is of even length. even = 0 otherwise
|
||||
char ext[10];// Type of Extension used - "per" or "sym"
|
||||
char entropy[20];
|
||||
double eparam;
|
||||
|
||||
int N; //
|
||||
int nodes;
|
||||
int length[102];
|
||||
double* output;
|
||||
double* costvalues;
|
||||
double* basisvector;
|
||||
int* nodeindex;
|
||||
int* numnodeslevel;
|
||||
int* coeflength;
|
||||
double params[0];
|
||||
};
|
||||
|
||||
|
||||
typedef struct cwt_set* cwt_object;
|
||||
|
||||
cwt_object cwt_init(char* wave, double param, int siglength, double dt, int J);
|
||||
|
||||
struct cwt_set
|
||||
{
|
||||
char wave[10];// Wavelet - morl/morlet,paul,dog/dgauss
|
||||
int siglength;// Length of Input Data
|
||||
int J;// Total Number of Scales
|
||||
double s0;// Smallest scale. It depends on the sampling rate. s0 <= 2 * dt for most wavelets
|
||||
double dt;// Sampling Rate
|
||||
double dj;// Separation between scales. eg., scale = s0 * 2 ^ ( [0:N-1] *dj ) or scale = s0 *[0:N-1] * dj
|
||||
char type[10];// Scale Type - Power or Linear
|
||||
int pow;// Base of Power in case type = pow. Typical value is pow = 2
|
||||
int sflag;
|
||||
int pflag;
|
||||
int npad;
|
||||
int mother;
|
||||
double m;// Wavelet parameter param
|
||||
double smean;// Input Signal mean
|
||||
|
||||
cplx_data* output;
|
||||
double* scale;
|
||||
double* period;
|
||||
double* coi;
|
||||
double params[0];
|
||||
};
|
||||
|
||||
void dwt(wt_object wt, double* inp);
|
||||
void idwt(wt_object wt, double* dwtop);
|
||||
void wtree(wtree_object wt, double* inp);
|
||||
void dwpt(wpt_object wt, double* inp);
|
||||
void idwpt(wpt_object wt, double* dwtop);
|
||||
void swt(wt_object wt, double* inp);
|
||||
void iswt(wt_object wt, double* swtop);
|
||||
void modwt(wt_object wt, double* inp);
|
||||
void imodwt(wt_object wt, double* dwtop);
|
||||
void setDWTExtension(wt_object wt, char* extension);
|
||||
void setWTREEExtension(wtree_object wt, char* extension);
|
||||
void setDWPTExtension(wpt_object wt, char* extension);
|
||||
void setDWPTEntropy(wpt_object wt, char* entropy, double eparam);
|
||||
void setWTConv(wt_object wt, char* cmethod);
|
||||
int getWTREENodelength(wtree_object wt, int X);
|
||||
void getWTREECoeffs(wtree_object wt, int X, int Y, double* coeffs, int N);
|
||||
int getDWPTNodelength(wpt_object wt, int X);
|
||||
void getDWPTCoeffs(wpt_object wt, int X, int Y, double* coeffs, int N);
|
||||
int setCWTScales(cwt_object wt, double s0, double dj, char* type, int power);
|
||||
void setCWTScaleVector(cwt_object wt, double* scale, int J, double s0, double dj);
|
||||
void setCWTPadding(cwt_object wt, int pad);
|
||||
int cwt(cwt_object wt, double* inp);
|
||||
void icwt(cwt_object wt, double* cwtop);
|
||||
int getCWTScaleLength(int N);
|
||||
void wave_summary(wave_object obj);
|
||||
void wt_summary(wt_object wt);
|
||||
void wtree_summary(wtree_object wt);
|
||||
void wpt_summary(wpt_object wt);
|
||||
void cwt_summary(cwt_object wt);
|
||||
void wave_free(wave_object object);
|
||||
void wt_free(wt_object object);
|
||||
void wtree_free(wtree_object object);
|
||||
void wpt_free(wpt_object object);
|
||||
void cwt_free(cwt_object object);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* conv.c
|
||||
*
|
||||
* Created on: May 1, 2013
|
||||
* Author: Rafat Hussain
|
||||
*/
|
||||
|
||||
#include "conv.h"
|
||||
|
||||
int factorf(int M)
|
||||
{
|
||||
int N = M;
|
||||
while (N % 7 == 0) { N = N / 7; }
|
||||
while (N % 3 == 0) { N = N / 3; }
|
||||
while (N % 5 == 0) { N = N / 5; }
|
||||
while (N % 2 == 0) { N = N / 2; }
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
|
||||
int findnext(int M)
|
||||
{
|
||||
int N = M;
|
||||
|
||||
while (factorf(N) != 1) { ++N; }
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
int findnexte(int M)
|
||||
{
|
||||
int N = M;
|
||||
|
||||
while (factorf(N) != 1 || N % 2 != 0) { ++N; }
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
|
||||
conv_object conv_init(int N, int L)
|
||||
{
|
||||
const int conv_len = N + L - 1;
|
||||
const conv_object obj = (conv_object)malloc(sizeof(struct conv_set));
|
||||
|
||||
//obj->clen = npow2(conv_len);
|
||||
//obj->clen = conv_len;
|
||||
obj->clen = findnexte(conv_len);
|
||||
obj->ilen1 = N;
|
||||
obj->ilen2 = L;
|
||||
|
||||
obj->fobj = fft_real_init(obj->clen, 1);
|
||||
obj->iobj = fft_real_init(obj->clen, -1);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
void conv_directx(fft_type* inp1, int N, fft_type* inp2, int L,fft_type* oup)
|
||||
{
|
||||
const int M = N + L - 1;
|
||||
|
||||
for (int k = 0; k < M; ++k)
|
||||
{
|
||||
oup[k] = 0.0;
|
||||
for (int n = 0; n < N; ++n) { if ((k - n) >= 0 && (k - n) < L) { oup[k] += inp1[n] * inp2[k - n]; } }
|
||||
}
|
||||
}
|
||||
|
||||
void conv_direct(fft_type* inp1, int N, fft_type* inp2, int L,fft_type* oup)
|
||||
{
|
||||
int k, m;
|
||||
fft_type t1, tmin;
|
||||
|
||||
const int M = N + L - 1;
|
||||
int i = 0;
|
||||
|
||||
if (N >= L)
|
||||
{
|
||||
for (k = 0; k < L; ++k)
|
||||
{
|
||||
oup[k] = 0.0;
|
||||
for (m = 0; m <= k; ++m) { oup[k] += inp1[m] * inp2[k - m]; }
|
||||
}
|
||||
|
||||
for (k = L; k < M; ++k)
|
||||
{
|
||||
oup[k] = 0.0;
|
||||
i++;
|
||||
t1 = L + i;
|
||||
tmin = MIN(t1, N);
|
||||
for (m = i; m < tmin; ++m) { oup[k] += inp1[m] * inp2[k - m]; }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (k = 0; k < N; ++k)
|
||||
{
|
||||
oup[k] = 0.0;
|
||||
for (m = 0; m <= k; ++m) { oup[k] += inp2[m] * inp1[k - m]; }
|
||||
}
|
||||
|
||||
for (k = N; k < M; ++k)
|
||||
{
|
||||
oup[k] = 0.0;
|
||||
i++;
|
||||
t1 = N + i;
|
||||
tmin = MIN(t1, L);
|
||||
for (m = i; m < tmin; ++m) { oup[k] += inp2[m] * inp1[k - m]; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void conv_fft(const conv_object obj,fft_type* inp1,fft_type* inp2,fft_type* oup)
|
||||
{
|
||||
int i;
|
||||
|
||||
const int N = obj->clen;
|
||||
const int L1 = obj->ilen1;
|
||||
const int L2 = obj->ilen2;
|
||||
const int ls = L1 + L2 - 1;
|
||||
|
||||
fft_type* a = (fft_type*)malloc(sizeof(fft_data) * N);
|
||||
fft_type* b = (fft_type*)malloc(sizeof(fft_data) * N);
|
||||
fft_data* c = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
fft_data* ao = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
fft_data* bo = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
fft_type* co = (fft_type*)malloc(sizeof(fft_data) * N);
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
if (i < L1) { a[i] = inp1[i]; }
|
||||
else { a[i] = 0.0; }
|
||||
|
||||
if (i < L2) { b[i] = inp2[i]; }
|
||||
else { b[i] = 0.0; }
|
||||
}
|
||||
|
||||
fft_r2c_exec(obj->fobj, a, ao);
|
||||
fft_r2c_exec(obj->fobj, b, bo);
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
c[i].re = ao[i].re * bo[i].re - ao[i].im * bo[i].im;
|
||||
c[i].im = ao[i].im * bo[i].re + ao[i].re * bo[i].im;
|
||||
}
|
||||
|
||||
fft_c2r_exec(obj->iobj, c, co);
|
||||
|
||||
for (i = 0; i < ls; ++i) { oup[i] = co[i] / N; }
|
||||
|
||||
free(a);
|
||||
free(b);
|
||||
free(c);
|
||||
free(ao);
|
||||
free(bo);
|
||||
free(co);
|
||||
}
|
||||
|
||||
|
||||
void free_conv(conv_object object)
|
||||
{
|
||||
free_real_fft(object->fobj);
|
||||
free_real_fft(object->iobj);
|
||||
free(object);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* conv.h
|
||||
*
|
||||
* Created on: May 1, 2013
|
||||
* Author: Rafat Hussain
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "real.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
|
||||
typedef struct conv_set* conv_object;
|
||||
|
||||
conv_object conv_init(int N, int L);
|
||||
|
||||
struct conv_set
|
||||
{
|
||||
fft_real_object fobj;
|
||||
fft_real_object iobj;
|
||||
int ilen1;
|
||||
int ilen2;
|
||||
int clen;
|
||||
};
|
||||
|
||||
int factorf(int M);
|
||||
int findnext(int M);
|
||||
int findnexte(int M);
|
||||
void conv_direct(fft_type* inp1, int N, fft_type* inp2, int L, fft_type* oup);
|
||||
void conv_directx(fft_type* inp1, int N, fft_type* inp2, int L, fft_type* oup);
|
||||
//void conv_fft(const conv_object obj,fft_type *inp1,fft_type *inp2,fft_type *oup);
|
||||
//void conv_fft(const conv_object obj,fft_type *inp1,fft_type *inp2,fft_type *oup);
|
||||
void conv_fft(const conv_object obj, fft_type* inp1, fft_type* inp2, fft_type* oup);
|
||||
//void free_conv(conv_object object);
|
||||
void free_conv(conv_object object);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
Copyright (c) 2015, Rafat Hussain
|
||||
*/
|
||||
/*
|
||||
This code is a C translation ( with some modifications) of Wavelet Software provided by
|
||||
C. Torrence and G. Compo, and is available at URL: http://atoc.colorado.edu/research/wavelets/''.
|
||||
*/
|
||||
|
||||
#include "cwt.h"
|
||||
|
||||
/*
|
||||
static double factorial3(int N)
|
||||
{
|
||||
double factorial = 1;
|
||||
for (int i = 1; i <= N; ++i) { factorial *= i; }
|
||||
return factorial;
|
||||
}
|
||||
*/
|
||||
double factorial(int N)
|
||||
{
|
||||
if (N > 40)
|
||||
{
|
||||
printf("This program is only valid for N <= 40 \n");
|
||||
return -1.0;
|
||||
}
|
||||
double fact[41] = {
|
||||
1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000,
|
||||
20922789888000, 355687428096000, 6402373705728000, 121645100408832000.0, 2432902008176640000.0, 51090942171709440000.0, 1124000727777607680000.0,
|
||||
25852016738884976640000.0, 620448401733239439360000.0, 15511210043330985984000000.0, 403291461126605635584000000.0, 10888869450418352160768000000.0,
|
||||
304888344611713860501504000000.0, 8841761993739701954543616000000.0, 265252859812191058636308480000000.0, 8222838654177922817725562880000000.0,
|
||||
263130836933693530167218012160000000.0, 8683317618811886495518194401280000000.0, 295232799039604140847618609643520000000.0,
|
||||
10333147966386144929666651337523200000000.0,
|
||||
371993326789901217467999448150835200000000.0, 13763753091226345046315979581580902400000000.0, 523022617466601111760007224100074291200000000.0,
|
||||
20397882081197443358640281739902897356800000000.0, 815915283247897734345611269596115894272000000000.0
|
||||
};
|
||||
|
||||
return fact[N];
|
||||
}
|
||||
|
||||
static void wave_function(int nk, double dt, int mother, double param, double scale1, double* kwave, double pi, double* period1, double* coi1,
|
||||
fft_data* daughter)
|
||||
{
|
||||
double norm, expnt, fourier_factor;
|
||||
int k, m;
|
||||
double temp;
|
||||
int sign, re;
|
||||
|
||||
|
||||
if (mother == 0)
|
||||
{
|
||||
//MORLET
|
||||
if (param < 0.0) { param = 6.0; }
|
||||
norm = sqrt(2.0 * pi * scale1 / dt) * pow(pi, -0.25);
|
||||
|
||||
for (k = 1; k <= nk / 2 + 1; ++k)
|
||||
{
|
||||
temp = (scale1 * kwave[k - 1] - param);
|
||||
expnt = -0.5 * temp * temp;
|
||||
daughter[k - 1].re = norm * exp(expnt);
|
||||
daughter[k - 1].im = 0.0;
|
||||
}
|
||||
for (k = nk / 2 + 2; k <= nk; ++k) { daughter[k - 1].re = daughter[k - 1].im = 0.0; }
|
||||
fourier_factor = (4.0 * pi) / (param + sqrt(2.0 + param * param));
|
||||
*period1 = scale1 * fourier_factor;
|
||||
*coi1 = fourier_factor / sqrt(2.0);
|
||||
}
|
||||
else if (mother == 1)
|
||||
{
|
||||
// PAUL
|
||||
if (param < 0.0) { param = 4.0; }
|
||||
m = (int)param;
|
||||
norm = sqrt(2.0 * pi * scale1 / dt) * (pow(2.0, (double)m) / sqrt(m * factorial(2 * m - 1)));
|
||||
for (k = 1; k <= nk / 2 + 1; ++k)
|
||||
{
|
||||
temp = scale1 * kwave[k - 1];
|
||||
expnt = - temp;
|
||||
daughter[k - 1].re = norm * pow(temp, (double)m) * exp(expnt);
|
||||
daughter[k - 1].im = 0.0;
|
||||
}
|
||||
for (k = nk / 2 + 2; k <= nk; ++k) { daughter[k - 1].re = daughter[k - 1].im = 0.0; }
|
||||
fourier_factor = (4.0 * pi) / (2.0 * m + 1.0);
|
||||
*period1 = scale1 * fourier_factor;
|
||||
*coi1 = fourier_factor * sqrt(2.0);
|
||||
}
|
||||
else if (mother == 2)
|
||||
{
|
||||
if (param < 0.0) { param = 2.0; }
|
||||
m = (int)param;
|
||||
|
||||
if (m % 2 == 0) { re = 1; }
|
||||
else { re = 0; }
|
||||
|
||||
if (m % 4 == 0 || m % 4 == 1) { sign = -1; }
|
||||
else { sign = 1; }
|
||||
|
||||
|
||||
norm = sqrt(2.0 * pi * scale1 / dt) * sqrt(1.0 / gamma(m + 0.50));
|
||||
norm *= sign;
|
||||
|
||||
if (re == 1)
|
||||
{
|
||||
for (k = 1; k <= nk; ++k)
|
||||
{
|
||||
temp = scale1 * kwave[k - 1];
|
||||
daughter[k - 1].re = norm * pow(temp, (double)m) * exp(-0.50 * pow(temp, 2.0));
|
||||
daughter[k - 1].im = 0.0;
|
||||
}
|
||||
}
|
||||
else if (re == 0)
|
||||
{
|
||||
for (k = 1; k <= nk; ++k)
|
||||
{
|
||||
temp = scale1 * kwave[k - 1];
|
||||
daughter[k - 1].re = 0.0;
|
||||
daughter[k - 1].im = norm * pow(temp, (double)m) * exp(-0.50 * pow(temp, 2.0));
|
||||
}
|
||||
}
|
||||
fourier_factor = (2.0 * pi) * sqrt(2.0 / (2.0 * m + 1.0));
|
||||
*period1 = scale1 * fourier_factor;
|
||||
*coi1 = fourier_factor / sqrt(2.0);
|
||||
}
|
||||
}
|
||||
|
||||
int cwavelet(double* y, int N, double dt, int mother, double param, double s0, double dj, int jtot, int npad, double* wave, double* scale, double* period,
|
||||
double* coi)
|
||||
{
|
||||
double period1, coi1;
|
||||
|
||||
const double pi = 4.0 * atan(1.0);
|
||||
|
||||
if (npad < N)
|
||||
{
|
||||
printf("npad must be >= N \n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const fft_object obj = fft_init(npad, 1);
|
||||
const fft_object iobj = fft_init(npad, -1);
|
||||
|
||||
fft_data* ypad = (fft_data*)malloc(sizeof(fft_data) * npad);
|
||||
fft_data* yfft = (fft_data*)malloc(sizeof(fft_data) * npad);
|
||||
fft_data* daughter = (fft_data*)malloc(sizeof(fft_data) * npad);
|
||||
double* kwave = (double*)malloc(sizeof(double) * npad);
|
||||
|
||||
double ymean = 0.0;
|
||||
|
||||
for (int i = 0; i < N; ++i) { ymean += y[i]; }
|
||||
|
||||
ymean /= N;
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
ypad[i].re = y[i] - ymean;
|
||||
ypad[i].im = 0.0;
|
||||
}
|
||||
|
||||
for (int i = N; i < npad; ++i) { ypad[i].re = ypad[i].im = 0.0; }
|
||||
|
||||
|
||||
// Find FFT of the input y (ypad)
|
||||
|
||||
fft_exec(obj, ypad, yfft);
|
||||
|
||||
for (int i = 0; i < npad; ++i)
|
||||
{
|
||||
yfft[i].re /= (double)npad;
|
||||
yfft[i].im /= (double)npad;
|
||||
}
|
||||
|
||||
|
||||
//Construct the wavenumber array
|
||||
|
||||
const double freq1 = 2.0 * pi / ((double)npad * dt);
|
||||
kwave[0] = 0.0;
|
||||
|
||||
for (int i = 1; i < npad / 2 + 1; ++i) { kwave[i] = i * freq1; }
|
||||
|
||||
for (int i = npad / 2 + 1; i < npad; ++i) { kwave[i] = -kwave[npad - i]; }
|
||||
|
||||
// Main loop
|
||||
|
||||
for (int j = 1; j <= jtot; ++j)
|
||||
{
|
||||
const double scale1 = scale[j - 1];// = s0*pow(2.0, (double)(j - 1)*dj);
|
||||
wave_function(npad, dt, mother, param, scale1, kwave, pi, &period1, &coi1, daughter);
|
||||
period[j - 1] = period1;
|
||||
for (int k = 0; k < npad; ++k)
|
||||
{
|
||||
const double tmp1 = daughter[k].re * yfft[k].re - daughter[k].im * yfft[k].im;
|
||||
const double tmp2 = daughter[k].re * yfft[k].im + daughter[k].im * yfft[k].re;
|
||||
daughter[k].re = tmp1;
|
||||
daughter[k].im = tmp2;
|
||||
}
|
||||
fft_exec(iobj, daughter, ypad);
|
||||
const int iter = 2 * (j - 1) * N;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
wave[iter + 2 * i] = ypad[i].re;
|
||||
wave[iter + 2 * i + 1] = ypad[i].im;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 1; i <= (N + 1) / 2; ++i)
|
||||
{
|
||||
coi[i - 1] = coi1 * dt * ((double)i - 1.0);
|
||||
coi[N - i] = coi[i - 1];
|
||||
}
|
||||
|
||||
|
||||
free(kwave);
|
||||
free(ypad);
|
||||
free(yfft);
|
||||
free(daughter);
|
||||
|
||||
free_fft(obj);
|
||||
free_fft(iobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psi0(int mother, double param, double* val, int* real)
|
||||
{
|
||||
int sign;
|
||||
|
||||
const int m = (int)param;
|
||||
const double pi = 4.0 * atan(1.0);
|
||||
|
||||
if (mother == 0)
|
||||
{
|
||||
// Morlet
|
||||
*val = 1.0 / sqrt(sqrt(pi));
|
||||
*real = 1;
|
||||
}
|
||||
else if (mother == 1)
|
||||
{
|
||||
//Paul
|
||||
if (m % 2 == 0) { *real = 1; }
|
||||
else { *real = 0; }
|
||||
|
||||
if (m % 4 == 0 || m % 4 == 1) { sign = 1; }
|
||||
else { sign = -1; }
|
||||
*val = sign * pow(2.0, (double)m) * factorial(m) / (sqrt(pi * factorial(2 * m)));
|
||||
}
|
||||
else if (mother == 2)
|
||||
{
|
||||
// D.O.G
|
||||
*real = 1;
|
||||
|
||||
if (m % 2 == 0)
|
||||
{
|
||||
if (m % 4 == 0) { sign = -1; }
|
||||
else { sign = 1; }
|
||||
const double coeff = sign * pow(2.0, (double)m / 2) / gamma(0.5);
|
||||
*val = coeff * gamma(((double)m + 1.0) / 2.0) / sqrt(gamma(m + 0.50));
|
||||
}
|
||||
else { *val = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
static int maxabs(double* array, int N)
|
||||
{
|
||||
double maxval = 0.0;
|
||||
int index = -1;
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
const double temp = fabs(array[i]);
|
||||
if (temp >= maxval)
|
||||
{
|
||||
maxval = temp;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
double cdelta(int mother, double param, double psi0)
|
||||
{
|
||||
int N = 0;
|
||||
double s0 = 0;
|
||||
|
||||
double subscale = 8.0;
|
||||
const double dt = 0.25;
|
||||
if (mother == 0)
|
||||
{
|
||||
N = 16;
|
||||
s0 = dt / 4;
|
||||
}
|
||||
else if (mother == 1)
|
||||
{
|
||||
N = 16;
|
||||
s0 = dt / 4.0;
|
||||
}
|
||||
else if (mother == 2)
|
||||
{
|
||||
s0 = dt / 8.0;
|
||||
N = 256;
|
||||
if (param == 2.0)
|
||||
{
|
||||
subscale = 16.0;
|
||||
s0 = dt / 16.0;
|
||||
N = 2048;
|
||||
}
|
||||
}
|
||||
|
||||
const double dj = 1.0 / subscale;
|
||||
const int jtot = 16 * (int)subscale;
|
||||
|
||||
double* delta = (double*)malloc(sizeof(double) * N);
|
||||
double* wave = (double*)malloc(sizeof(double) * 2 * N * jtot);
|
||||
double* coi = (double*)malloc(sizeof(double) * N);
|
||||
double* scale = (double*)malloc(sizeof(double) * jtot);
|
||||
double* period = (double*)malloc(sizeof(double) * jtot);
|
||||
double* mval = (double*)malloc(sizeof(double) * N);
|
||||
|
||||
|
||||
delta[0] = 1;
|
||||
|
||||
for (int i = 1; i < N; ++i) { delta[i] = 0; }
|
||||
|
||||
for (int i = 0; i < jtot; ++i) { scale[i] = s0 * pow(2.0, (double)(i) * dj); }
|
||||
|
||||
cwavelet(delta, N, dt, mother, param, s0, dj, jtot, N, wave, scale, period, coi);
|
||||
|
||||
for (int i = 0; i < N; ++i) { mval[i] = 0; }
|
||||
|
||||
for (int j = 0; j < jtot; ++j)
|
||||
{
|
||||
const int iter = 2 * j * N;
|
||||
const double den = sqrt(scale[j]);
|
||||
for (int i = 0; i < N; ++i) { mval[i] += wave[iter + 2 * i] / den; }
|
||||
}
|
||||
|
||||
|
||||
const int maxarr = maxabs(mval, N);
|
||||
const double cdel = sqrt(dt) * dj * mval[maxarr] / psi0;
|
||||
|
||||
free(delta);
|
||||
free(wave);
|
||||
|
||||
free(scale);
|
||||
free(period);
|
||||
free(coi);
|
||||
free(mval);
|
||||
|
||||
return cdel;
|
||||
}
|
||||
|
||||
void icwavelet(double* wave, int N, double* scale, int jtot, double dt, double dj, double cdelta, double psi0, double* oup)
|
||||
{
|
||||
const double coeff = sqrt(dt) * dj / (cdelta * psi0);
|
||||
|
||||
for (int i = 0; i < N; ++i) { oup[i] = 0.0; }
|
||||
|
||||
for (int j = 0; j < jtot; ++j)
|
||||
{
|
||||
const int iter = 2 * j * N;
|
||||
const double den = sqrt(scale[j]);
|
||||
for (int i = 0; i < N; ++i) { oup[i] += wave[iter + 2 * i] / den; }
|
||||
}
|
||||
|
||||
for (int i = 0; i < N; ++i) { oup[i] *= coeff; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "wavefunc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int cwavelet(double* y, int N, double dt, int mother, double param, double s0, double dj, int jtot, int npad, double* wave, double* scale, double* period,
|
||||
double* coi);
|
||||
|
||||
void psi0(int mother, double param, double* val, int* real);
|
||||
|
||||
double factorial(int N);
|
||||
|
||||
double cdelta(int mother, double param, double psi0);
|
||||
|
||||
void icwavelet(double* wave, int N, double* scale, int jtot, double dt, double dj, double cdelta, double psi0, double* oup);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,299 @@
|
||||
#include "cwtmath.h"
|
||||
|
||||
static void nsfft_fd(const fft_object obj, fft_data* inp, fft_data* oup, const double lb, const double ub, double* w)
|
||||
{
|
||||
int i;
|
||||
|
||||
const int N = obj->N;
|
||||
const int L = N / 2;
|
||||
//w = (double*)malloc(sizeof(double)*N);
|
||||
|
||||
const int M = divideby(N, 2);
|
||||
|
||||
if (M == 0)
|
||||
{
|
||||
printf("The Non-Standard FFT Length must be a power of 2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
double* temp1 = (double*)malloc(sizeof(double) * L);
|
||||
double* temp2 = (double*)malloc(sizeof(double) * L);
|
||||
|
||||
const double delta = (ub - lb) / N;
|
||||
int j = -N;
|
||||
const double den = 2 * (ub - lb);
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
w[i] = (double)j / den;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
fft_exec(obj, inp, oup);
|
||||
|
||||
|
||||
for (i = 0; i < L; ++i)
|
||||
{
|
||||
temp1[i] = oup[i].re;
|
||||
temp2[i] = oup[i].im;
|
||||
}
|
||||
|
||||
for (i = 0; i < N - L; ++i)
|
||||
{
|
||||
oup[i].re = oup[i + L].re;
|
||||
oup[i].im = oup[i + L].im;
|
||||
}
|
||||
|
||||
for (i = 0; i < L; ++i)
|
||||
{
|
||||
oup[N - L + i].re = temp1[i];
|
||||
oup[N - L + i].im = temp2[i];
|
||||
}
|
||||
|
||||
const double plb = PI2 * lb;
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
const double tempr = oup[i].re;
|
||||
const double tempi = oup[i].im;
|
||||
const double theta = w[i] * plb;
|
||||
|
||||
oup[i].re = delta * (tempr * cos(theta) + tempi * sin(theta));
|
||||
oup[i].im = delta * (tempi * cos(theta) - tempr * sin(theta));
|
||||
}
|
||||
|
||||
|
||||
//free(w);
|
||||
free(temp1);
|
||||
free(temp2);
|
||||
}
|
||||
|
||||
static void nsfft_bk(fft_object obj, fft_data* inp, fft_data* oup, double lb, double ub, double* t)
|
||||
{
|
||||
int i;
|
||||
|
||||
const int N = obj->N;
|
||||
const int L = N / 2;
|
||||
|
||||
const int M = divideby(N, 2);
|
||||
|
||||
if (M == 0)
|
||||
{
|
||||
printf("The Non-Standard FFT Length must be a power of 2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
double* temp1 = (double*)malloc(sizeof(double) * L);
|
||||
double* temp2 = (double*)malloc(sizeof(double) * L);
|
||||
double* w = (double*)malloc(sizeof(double) * N);
|
||||
fft_data* inpt = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
|
||||
const double delta = (ub - lb) / N;
|
||||
int j = -N;
|
||||
const double den = 2 * (ub - lb);
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
w[i] = (double)j / den;
|
||||
j += 2;
|
||||
}
|
||||
|
||||
const double plb = PI2 * lb;
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
const double theta = w[i] * plb;
|
||||
|
||||
inpt[i].re = (inp[i].re * cos(theta) - inp[i].im * sin(theta)) / delta;
|
||||
inpt[i].im = (inp[i].im * cos(theta) + inp[i].re * sin(theta)) / delta;
|
||||
}
|
||||
|
||||
for (i = 0; i < L; ++i)
|
||||
{
|
||||
temp1[i] = inpt[i].re;
|
||||
temp2[i] = inpt[i].im;
|
||||
}
|
||||
|
||||
for (i = 0; i < N - L; ++i)
|
||||
{
|
||||
inpt[i].re = inpt[i + L].re;
|
||||
inpt[i].im = inpt[i + L].im;
|
||||
}
|
||||
|
||||
for (i = 0; i < L; ++i)
|
||||
{
|
||||
inpt[N - L + i].re = temp1[i];
|
||||
inpt[N - L + i].im = temp2[i];
|
||||
}
|
||||
|
||||
fft_exec(obj, inpt, oup);
|
||||
|
||||
for (i = 0; i < N; ++i) { t[i] = lb + i * delta; }
|
||||
|
||||
free(w);
|
||||
free(temp1);
|
||||
free(temp2);
|
||||
free(inpt);
|
||||
}
|
||||
|
||||
void nsfft_exec(fft_object obj, fft_data* inp, fft_data* oup, double lb, double ub, double* w)
|
||||
{
|
||||
if (obj->sgn == 1) { nsfft_fd(obj, inp, oup, lb, ub, w); }
|
||||
else if (obj->sgn == -1) { nsfft_bk(obj, inp, oup, lb, ub, w); }
|
||||
}
|
||||
|
||||
static double fix(double x)
|
||||
{
|
||||
// Rounds to the integer nearest to zero
|
||||
if (x >= 0.) { return floor(x); }
|
||||
return ceil(x);
|
||||
}
|
||||
|
||||
int nint(double N)
|
||||
{
|
||||
//const int i = (int)(N + 0.49999);
|
||||
//return i;
|
||||
return (int)(N + 0.49999);
|
||||
}
|
||||
|
||||
double gamma(double x)
|
||||
{
|
||||
/*
|
||||
* This C program code is based on W J Cody's fortran code.
|
||||
* http://www.netlib.org/specfun/gamma
|
||||
*
|
||||
* References:
|
||||
"An Overview of Software Development for Special Functions",
|
||||
W. J. Cody, Lecture Notes in Mathematics, 506,
|
||||
Numerical Analysis Dundee, 1975, G. A. Watson (ed.),
|
||||
Springer Verlag, Berlin, 1976.
|
||||
|
||||
Computer Approximations, Hart, Et. Al., Wiley and sons, New York, 1968.
|
||||
*/
|
||||
|
||||
// numerator and denominator coefficients for 1 <= x <= 2
|
||||
|
||||
double oup, yi, z;
|
||||
int i;
|
||||
|
||||
const double spi = 0.9189385332046727417803297;
|
||||
const double pi = 3.1415926535897932384626434;
|
||||
const double xmax = 171.624e+0;
|
||||
const double xinf = 1.79e308;
|
||||
const double eps = 2.22e-16;
|
||||
const double xninf = 1.79e-308;
|
||||
|
||||
double num[8] = {
|
||||
-1.71618513886549492533811e+0,
|
||||
2.47656508055759199108314e+1,
|
||||
-3.79804256470945635097577e+2,
|
||||
6.29331155312818442661052e+2,
|
||||
8.66966202790413211295064e+2,
|
||||
-3.14512729688483675254357e+4,
|
||||
-3.61444134186911729807069e+4,
|
||||
6.64561438202405440627855e+4
|
||||
};
|
||||
|
||||
double den[8] = {
|
||||
-3.08402300119738975254353e+1,
|
||||
3.15350626979604161529144e+2,
|
||||
-1.01515636749021914166146e+3,
|
||||
-3.10777167157231109440444e+3,
|
||||
2.25381184209801510330112e+4,
|
||||
4.75584627752788110767815e+3,
|
||||
-1.34659959864969306392456e+5,
|
||||
-1.15132259675553483497211e+5
|
||||
};
|
||||
|
||||
// Coefficients for Hart's Minimax approximation x >= 12
|
||||
|
||||
|
||||
double c[7] = {
|
||||
-1.910444077728e-03,
|
||||
8.4171387781295e-04,
|
||||
-5.952379913043012e-04,
|
||||
7.93650793500350248e-04,
|
||||
-2.777777777777681622553e-03,
|
||||
8.333333333333333331554247e-02,
|
||||
5.7083835261e-03
|
||||
};
|
||||
|
||||
double y = x;
|
||||
int swi = 0;
|
||||
double fact = 1.0;
|
||||
int n = 0;
|
||||
|
||||
|
||||
if (y < 0.)
|
||||
{
|
||||
// Negative x
|
||||
y = -x;
|
||||
yi = fix(y);
|
||||
oup = y - yi;
|
||||
|
||||
if (oup != 0.0)
|
||||
{
|
||||
if (yi != fix(yi * .5) * 2.) { swi = 1; }
|
||||
fact = -pi / sin(pi * oup);
|
||||
y += 1.;
|
||||
}
|
||||
else { return xinf; }
|
||||
}
|
||||
|
||||
if (y < eps)
|
||||
{
|
||||
if (y >= xninf) { oup = 1.0 / y; }
|
||||
else { return xinf; }
|
||||
}
|
||||
else if (y < 12.)
|
||||
{
|
||||
yi = y;
|
||||
if (y < 1.)
|
||||
{
|
||||
z = y;
|
||||
y += 1.;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (int)y - 1;
|
||||
y -= (double)n;
|
||||
z = y - 1.0;
|
||||
}
|
||||
double nsum = 0.;
|
||||
double dsum = 1.;
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
nsum = (nsum + num[i]) * z;
|
||||
dsum = dsum * z + den[i];
|
||||
}
|
||||
oup = nsum / dsum + 1.;
|
||||
|
||||
if (yi < y) { oup /= yi; }
|
||||
else if (yi > y)
|
||||
{
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
oup *= y;
|
||||
y += 1.;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y <= xmax)
|
||||
{
|
||||
const double y2 = y * y;
|
||||
double sum = c[6];
|
||||
for (i = 0; i < 6; ++i) { sum = sum / y2 + c[i]; }
|
||||
sum = sum / y - y + spi;
|
||||
sum += (y - .5) * log(y);
|
||||
oup = exp(sum);
|
||||
}
|
||||
else { return (xinf); }
|
||||
}
|
||||
|
||||
if (swi) { oup = -oup; }
|
||||
if (fact != 1.) { oup = fact / oup; }
|
||||
|
||||
return oup;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "wtmath.h"
|
||||
#include "hsfft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void nsfft_exec(fft_object obj, fft_data* inp, fft_data* oup, double lb, double ub,
|
||||
double* w);// lb -lower bound, ub - upper bound, w - time or frequency grid (Size N)
|
||||
|
||||
double gamma(double x);
|
||||
|
||||
int nint(double N);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* hsfft.h
|
||||
*
|
||||
* Created on: Apr 14, 2013
|
||||
* Author: Rafat Hussain
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PI2 6.28318530717958647692528676655900577
|
||||
|
||||
#ifndef fft_type
|
||||
#define fft_type double
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct fft_t
|
||||
{
|
||||
fft_type re;
|
||||
fft_type im;
|
||||
} fft_data;
|
||||
/*
|
||||
#define SADD(a,b) ((a)+(b))
|
||||
|
||||
#define SSUB(a,b) ((a)+(b))
|
||||
|
||||
#define SMUL(a,b) ((a)*(b))
|
||||
*/
|
||||
|
||||
typedef struct fft_set* fft_object;
|
||||
|
||||
fft_object fft_init(int N, int sgn);
|
||||
|
||||
struct fft_set
|
||||
{
|
||||
int N;
|
||||
int sgn;
|
||||
int factors[64];
|
||||
int lf;
|
||||
int lt;
|
||||
fft_data twiddle[1];
|
||||
};
|
||||
|
||||
void fft_exec(fft_object obj, fft_data* inp, fft_data* oup);
|
||||
|
||||
int divideby(int M, int d);
|
||||
|
||||
int dividebyN(int N);
|
||||
|
||||
//void arrrev(int M, int* arr);
|
||||
|
||||
int factors(int M, int* arr);
|
||||
|
||||
void twiddle(fft_data* vec, int N, int radix);
|
||||
|
||||
void longvectorN(fft_data* sig, int N, int* array, int tx);
|
||||
|
||||
void free_fft(fft_object object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* real.c
|
||||
*
|
||||
* Created on: Apr 20, 2013
|
||||
* Author: Rafat Hussain
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "real.h"
|
||||
|
||||
fft_real_object fft_real_init(int N, int sgn)
|
||||
{
|
||||
fft_real_object obj = (fft_real_object)malloc(sizeof(struct fft_real_set) + sizeof(fft_data) * (N / 2));
|
||||
obj->cobj = fft_init(N / 2, sgn);
|
||||
|
||||
for (int k = 0; k < N / 2; ++k)
|
||||
{
|
||||
const fft_type theta = PI2 * k / N;
|
||||
obj->twiddle2[k].re = cos(theta);
|
||||
obj->twiddle2[k].im = sin(theta);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void fft_r2c_exec(fft_real_object obj,fft_type* inp, fft_data* oup)
|
||||
{
|
||||
int i;
|
||||
const int N2 = obj->cobj->N;
|
||||
const int N = N2 * 2;
|
||||
|
||||
fft_data* cinp = (fft_data*)malloc(sizeof(fft_data) * N2);
|
||||
fft_data* coup = (fft_data*)malloc(sizeof(fft_data) * N2);
|
||||
|
||||
for (i = 0; i < N2; ++i)
|
||||
{
|
||||
cinp[i].re = inp[2 * i];
|
||||
cinp[i].im = inp[2 * i + 1];
|
||||
}
|
||||
|
||||
fft_exec(obj->cobj, cinp, coup);
|
||||
|
||||
oup[0].re = coup[0].re + coup[0].im;
|
||||
oup[0].im = 0.0;
|
||||
|
||||
for (i = 1; i < N2; ++i)
|
||||
{
|
||||
fft_type temp1 = coup[i].im + coup[N2 - i].im;
|
||||
fft_type temp2 = coup[N2 - i].re - coup[i].re;
|
||||
oup[i].re = (coup[i].re + coup[N2 - i].re + (temp1 * obj->twiddle2[i].re) + (temp2 * obj->twiddle2[i].im)) / 2.0;
|
||||
oup[i].im = (coup[i].im - coup[N2 - i].im + (temp2 * obj->twiddle2[i].re) - (temp1 * obj->twiddle2[i].im)) / 2.0;
|
||||
}
|
||||
|
||||
|
||||
oup[N2].re = coup[0].re - coup[0].im;
|
||||
oup[N2].im = 0.0;
|
||||
|
||||
for (i = 1; i < N2; ++i)
|
||||
{
|
||||
oup[N - i].re = oup[i].re;
|
||||
oup[N - i].im = -oup[i].im;
|
||||
}
|
||||
|
||||
|
||||
free(cinp);
|
||||
free(coup);
|
||||
}
|
||||
|
||||
void fft_c2r_exec(fft_real_object obj, fft_data* inp,fft_type* oup)
|
||||
{
|
||||
const int N2 = obj->cobj->N;
|
||||
|
||||
fft_data* cinp = (fft_data*)malloc(sizeof(fft_data) * N2);
|
||||
fft_data* coup = (fft_data*)malloc(sizeof(fft_data) * N2);
|
||||
|
||||
for (int i = 0; i < N2; ++i)
|
||||
{
|
||||
fft_type temp1 = -inp[i].im - inp[N2 - i].im;
|
||||
fft_type temp2 = -inp[N2 - i].re + inp[i].re;
|
||||
cinp[i].re = inp[i].re + inp[N2 - i].re + (temp1 * obj->twiddle2[i].re) - (temp2 * obj->twiddle2[i].im);
|
||||
cinp[i].im = inp[i].im - inp[N2 - i].im + (temp2 * obj->twiddle2[i].re) + (temp1 * obj->twiddle2[i].im);
|
||||
}
|
||||
|
||||
fft_exec(obj->cobj, cinp, coup);
|
||||
for (int i = 0; i < N2; ++i)
|
||||
{
|
||||
oup[2 * i] = coup[i].re;
|
||||
oup[2 * i + 1] = coup[i].im;
|
||||
}
|
||||
free(cinp);
|
||||
free(coup);
|
||||
}
|
||||
|
||||
void free_real_fft(fft_real_object object)
|
||||
{
|
||||
free_fft(object->cobj);
|
||||
free(object);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* real.h
|
||||
*
|
||||
* Created on: Apr 20, 2013
|
||||
* Author: Rafat Hussain
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hsfft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct fft_real_set* fft_real_object;
|
||||
|
||||
fft_real_object fft_real_init(int N, int sgn);
|
||||
|
||||
struct fft_real_set
|
||||
{
|
||||
fft_object cobj;
|
||||
fft_data twiddle2[1];
|
||||
};
|
||||
|
||||
void fft_r2c_exec(fft_real_object obj,fft_type* inp, fft_data* oup);
|
||||
|
||||
void fft_c2r_exec(fft_real_object obj, fft_data* inp,fft_type* oup);
|
||||
|
||||
void free_real_fft(fft_real_object object);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright (c) 2014, Rafat Hussain
|
||||
Copyright (c) 2016, Holger Nahrstaedt
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "conv.h"
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "math.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
int filtlength(const char* name);
|
||||
|
||||
int filtcoef(const char* name, double* lp1, double* hp1, double* lp2, double* hp2);
|
||||
|
||||
void copy_reverse(const double* in, const int N, double* out);
|
||||
void qmf_even(const double* in, const int N, double* out);
|
||||
void qmf_wrev(const double* in, const int N, double* out);
|
||||
void copy(const double* in, const int N, double* out);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,219 @@
|
||||
#include "wavefunc.h"
|
||||
|
||||
void meyer(const int N, const double lb, const double ub, double* phi, double* psi, double* tgrid)
|
||||
{
|
||||
int i;
|
||||
double theta, x, x2, x3, x4, v, cs;
|
||||
|
||||
const int M = divideby(N, 2);
|
||||
|
||||
if (M == 0)
|
||||
{
|
||||
printf("Size of Wavelet must be a power of 2");
|
||||
exit(1);
|
||||
}
|
||||
if (lb >= ub)
|
||||
{
|
||||
printf("upper bound must be greater than lower bound");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const fft_object obj = fft_init(N, -1);
|
||||
double* w = (double*)malloc(sizeof(double) * N);
|
||||
fft_data* phiw = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
fft_data* psiw = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
fft_data* oup = (fft_data*)malloc(sizeof(fft_data) * N);
|
||||
|
||||
const double delta = 2 * (ub - lb) / PI2;
|
||||
|
||||
double j = (double)N;
|
||||
j *= -1.0;
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
w[i] = j / delta;
|
||||
j += 2.0;
|
||||
psiw[i].re = psiw[i].im = 0.0;
|
||||
phiw[i].re = phiw[i].im = 0.0;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
const double wf = fabs(w[i]);
|
||||
if (wf <= PI2 / 3.0) { phiw[i].re = 1.0; }
|
||||
if (wf > PI2 / 3.0 && wf <= 2 * PI2 / 3.0)
|
||||
{
|
||||
x = (3 * wf / PI2) - 1.0;
|
||||
x2 = x * x;
|
||||
x3 = x2 * x;
|
||||
x4 = x3 * x;
|
||||
v = x4 * (35 - 84 * x + 70 * x2 - 20 * x3);
|
||||
theta = v * PI2 / 4.0;
|
||||
cs = cos(theta);
|
||||
const double sn = sin(theta);
|
||||
|
||||
phiw[i].re = cs;
|
||||
psiw[i].re = cos(w[i] / 2.0) * sn;
|
||||
psiw[i].im = sin(w[i] / 2.0) * sn;
|
||||
}
|
||||
if (wf > 2.0 * PI2 / 3.0 && wf <= 4 * PI2 / 3.0)
|
||||
{
|
||||
x = (1.5 * wf / PI2) - 1.0;
|
||||
x2 = x * x;
|
||||
x3 = x2 * x;
|
||||
x4 = x3 * x;
|
||||
v = x4 * (35 - 84 * x + 70 * x2 - 20 * x3);
|
||||
theta = v * PI2 / 4.0;
|
||||
cs = cos(theta);
|
||||
|
||||
psiw[i].re = cos(w[i] / 2.0) * cs;
|
||||
psiw[i].im = sin(w[i] / 2.0) * cs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsfft_exec(obj, phiw, oup, lb, ub, tgrid);
|
||||
|
||||
|
||||
for (i = 0; i < N; ++i) { phi[i] = oup[i].re / N; }
|
||||
|
||||
nsfft_exec(obj, psiw, oup, lb, ub, tgrid);
|
||||
|
||||
|
||||
for (i = 0; i < N; ++i) { psi[i] = oup[i].re / N; }
|
||||
|
||||
|
||||
free(oup);
|
||||
free(phiw);
|
||||
free(psiw);
|
||||
free(w);
|
||||
}
|
||||
|
||||
void gauss(int N, int p, double lb, double ub, double* psi, double* t)
|
||||
{
|
||||
double num, t2, t4;
|
||||
int i;
|
||||
|
||||
if (lb >= ub)
|
||||
{
|
||||
printf("upper bound must be greater than lower bound");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
t[0] = lb;
|
||||
t[N - 1] = ub;
|
||||
const double delta = (ub - lb) / (N - 1);
|
||||
for (i = 1; i < N - 1; ++i) { t[i] = lb + delta * i; }
|
||||
|
||||
const double den = sqrt(gamma(p + 0.5));
|
||||
|
||||
if ((p + 1) % 2 == 0) { num = 1.0; }
|
||||
else { num = -1.0; }
|
||||
|
||||
num /= den;
|
||||
|
||||
//printf("\n%g\n",num);
|
||||
|
||||
if (p == 1) { for (i = 0; i < N; ++i) { psi[i] = -t[i] * exp(- t[i] * t[i] / 2.0) * num; } }
|
||||
else if (p == 2)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = (-1.0 + t2) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 3)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = t[i] * (3.0 - t2) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 4)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = (t2 * t2 - 6.0 * t2 + 3.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 5)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = t[i] * (-t2 * t2 + 10.0 * t2 - 15.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 6)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = (t2 * t2 * t2 - 15.0 * t2 * t2 + 45.0 * t2 - 15.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 7)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
psi[i] = t[i] * (-t2 * t2 * t2 + 21.0 * t2 * t2 - 105.0 * t2 + 105.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 8)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
t4 = t2 * t2;
|
||||
psi[i] = (t4 * t4 - 28.0 * t4 * t2 + 210.0 * t4 - 420.0 * t2 + 105.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 9)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
t4 = t2 * t2;
|
||||
psi[i] = t[i] * (- t4 * t4 + 36.0 * t4 * t2 - 378.0 * t4 + 1260.0 * t2 - 945.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else if (p == 10)
|
||||
{
|
||||
for (i = 0; i < N; ++i)
|
||||
{
|
||||
t2 = t[i] * t[i];
|
||||
t4 = t2 * t2;
|
||||
psi[i] = (t4 * t4 * t2 - 45.0 * t4 * t4 + 630.0 * t4 * t2 - 3150.0 * t4 + 4725.0 * t2 - 945.0) * exp(- t2 / 2.0) * num;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("\n The Gaussian Derivative Wavelet is only available for Derivatives 1 to 10");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void mexhat(int N, double lb, double ub, double* psi, double* t) { gauss(N, 2, lb, ub, psi, t); }
|
||||
|
||||
void morlet(int N, double lb, double ub, double* psi, double* t)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (lb >= ub)
|
||||
{
|
||||
printf("upper bound must be greater than lower bound");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
t[0] = lb;
|
||||
t[N - 1] = ub;
|
||||
const double delta = (ub - lb) / (N - 1);
|
||||
for (i = 1; i < N - 1; ++i) { t[i] = lb + delta * i; }
|
||||
|
||||
for (i = 0; i < N; ++i) { psi[i] = exp(- t[i] * t[i] / 2.0) * cos(5 * t[i]); }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "cwtmath.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void meyer(const int N, const double lb, const double ub, double* phi, double* psi, double* tgrid);
|
||||
void gauss(int N, int p, double lb, double ub, double* psi, double* t);
|
||||
void mexhat(int N, double lb, double ub, double* psi, double* t);
|
||||
void morlet(int N, double lb, double ub, double* psi, double* t);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user