[DEV] add basic equalizer with biquad Not tested

This commit is contained in:
Edouard DUPIN 2015-04-21 21:08:38 +02:00
parent 68c2174fb0
commit 9a5490580a
14 changed files with 1267 additions and 0 deletions

64
.gitignore vendored Normal file
View File

@ -0,0 +1,64 @@
###################################
# folders
###################################
CVS
.svn
Object_*
doxygen/API/
doxygen/ALL/
###################################
# backup files
###################################
*~
*.swp
*.old
*.bck
###################################
# Compiled source #
###################################
*.com
*.class
*.dll
*.exe
*.o
*.so
*.pyc
tags
#ewol
out
ewol_debug
ewol_release
###################################
# Packages #
###################################
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
###################################
# Logs and databases #
###################################
*.log
*.sql
*.sqlite
###################################
# OS generated files #
###################################
.DS_Store?
ehthumbs.db
Icon?
Thumbs.db
Sources/libewol/ewol/os/AndroidAbstraction.cpp
org_ewol_EwolConstants.h

View File

@ -0,0 +1,9 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <etk/types.h>
#include <audio/algo/drain/debug.h>
#include <audio/algo/drain/BiQuad.h>

282
audio/algo/drain/BiQuad.h Normal file
View File

@ -0,0 +1,282 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_DRAIN_ALGO_BIQUAD_H__
#define __AUDIO_ALGO_DRAIN_ALGO_BIQUAD_H__
#include <etk/memory.h>
#include <audio/algo/drain/BiQuadType.h>
namespace audio {
namespace algo {
namespace drain {
template<typename TYPE> class BiQuad {
public:
BiQuad() {
reset();
// reset coefficients
m_a[0] = 1.0;
m_a[1] = 0.0;
m_a[2] = 0.0;
m_b[0] = 0.0;
m_b[1] = 0.0;
}
protected:
TYPE m_x[2]; //!< X history
TYPE m_y[2]; //!< Y histiry
TYPE m_a[3]; //!< A bi-Quad coef
TYPE m_b[2]; //!< B bi-Quad coef
public:
/**
* @brief Set the bi-quad value and type
* @param[in] _type Type of biquad.
* @param[in] _frequencyCut Cut Frequency. [0..sampleRate/2]
* @param[in] _qualityFactor Q factor of quality (good value of 0.707 ==> permit to not ower gain) limit [0.01 .. 10]
* @param[in] _gain Gain to apply (for notch, peak, lowShelf and highShelf) limit : -30, +30
* @param[in] _sampleRate Sample rate of the signal
*/
void setBiquad(enum audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain, float _sampleRate) {
reset();
if (_sampleRate < 1) {
m_a[0] = 1.0;
m_a[1] = 0.0;
m_a[2] = 0.0;
m_b[0] = 0.0;
m_b[1] = 0.0;
return;
}
if (_frequencyCut > _sampleRate/2) {
_frequencyCut = _sampleRate/2;
} else if (_frequencyCut < 0) {
_frequencyCut = 0;
}
if (_qualityFactor < 0.01) {
_qualityFactor = 0.01;
}
double norm;
double V = std11::pow(10.0, std11::abs(_gain) / 20.0);
double K = std11::tan(M_PI * _frequencyCut / _sampleRate);
switch (_type) {
case biQuadType_none:
m_a[0] = 1.0;
m_a[1] = 0.0;
m_a[2] = 0.0;
m_b[0] = 0.0;
m_b[1] = 0.0;
break;
case biQuadType_lowPass:
norm = 1.0 / (1.0 + K / _qualityFactor + K * K);
m_a[0] = K * K * norm;
m_a[1] = m_a[0] * 2.0;
m_a[2] = m_a[0];
m_b[0] = 2.0 * (K * K - 1.0) * norm;
m_b[1] = (1.0 - K / _qualityFactor + K * K) * norm;
break;
case biQuadType_highPass:
norm = 1.0 / (1.0 + K / _qualityFactor + K * K);
m_a[0] = 1.0 * norm;
m_a[1] = m_a[0] * -2.0;
m_a[2] = m_a[0];
m_b[0] = 2.0 * (K * K - 1.0) * norm;
m_b[1] = (1.0 - K / _qualityFactor + K * K) * norm;
break;
case biQuadType_bandPass:
norm = 1.0 / (1.0 + K / _qualityFactor + K * K);
m_a[0] = K / _qualityFactor * norm;
m_a[1] = 0.0;
m_a[2] = m_a[0] * -1.0;
m_b[0] = 2.0 * (K * K - 1.0) * norm;
m_b[1] = (1.0 - K / _qualityFactor + K * K) * norm;
break;
case biQuadType_notch:
norm = 1.0 / (1.0 + K / _qualityFactor + K * K);
m_a[0] = (1.0 + K * K) * norm;
m_a[1] = 2.0 * (K * K - 1.0) * norm;
m_a[2] = m_a[0];
m_b[0] = m_a[1];
m_b[1] = (1.0 - K / _qualityFactor + K * K) * norm;
break;
case biQuadType_peak:
if (_gain >= 0.0) {
norm = 1.0 / (1.0 + 1.0/_qualityFactor * K + K * K);
m_a[0] = (1.0 + V/_qualityFactor * K + K * K) * norm;
m_a[1] = 2.0 * (K * K - 1.0) * norm;
m_a[2] = (1.0 - V/_qualityFactor * K + K * K) * norm;
m_b[0] = m_a[1];
m_b[1] = (1.0 - 1.0/_qualityFactor * K + K * K) * norm;
} else {
norm = 1.0 / (1.0 + V/_qualityFactor * K + K * K);
m_a[0] = (1.0 + 1.0/_qualityFactor * K + K * K) * norm;
m_a[1] = 2.0 * (K * K - 1.0) * norm;
m_a[2] = (1.0 - 1.0/_qualityFactor * K + K * K) * norm;
m_b[0] = m_a[1];
m_b[1] = (1.0 - V/_qualityFactor * K + K * K) * norm;
}
break;
case biQuadType_lowShelf:
if (_gain >= 0) {
norm = 1.0 / (1.0 + M_SQRT2 * K + K * K);
m_a[0] = (1.0 + std11::sqrt(2.0*V) * K + V * K * K) * norm;
m_a[1] = 2.0 * (V * K * K - 1.0) * norm;
m_a[2] = (1.0 - std11::sqrt(2.0*V) * K + V * K * K) * norm;
m_b[0] = 2.0 * (K * K - 1.0) * norm;
m_b[1] = (1.0 - M_SQRT2 * K + K * K) * norm;
} else {
norm = 1.0 / (1.0 + std11::sqrt(2.0*V) * K + V * K * K);
m_a[0] = (1.0 + M_SQRT2 * K + K * K) * norm;
m_a[1] = 2.0 * (K * K - 1.0) * norm;
m_a[2] = (1.0 - M_SQRT2 * K + K * K) * norm;
m_b[0] = 2.0 * (V * K * K - 1.0) * norm;
m_b[1] = (1.0 - std11::sqrt(2.0*V) * K + V * K * K) * norm;
}
break;
case biQuadType_highShelf:
if (_gain >= 0) {
norm = 1.0 / (1.0 + M_SQRT2 * K + K * K);
m_a[0] = (V + std11::sqrt(2.0*V) * K + K * K) * norm;
m_a[1] = 2.0 * (K * K - V) * norm;
m_a[2] = (V - std11::sqrt(2.0*V) * K + K * K) * norm;
m_b[0] = 2.0 * (K * K - 1.0) * norm;
m_b[1] = (1.0 - M_SQRT2 * K + K * K) * norm;
} else {
norm = 1.0 / (V + std11::sqrt(2.0*V) * K + K * K);
m_a[0] = (1.0 + M_SQRT2 * K + K * K) * norm;
m_a[1] = 2.0 * (K * K - 1.0) * norm;
m_a[2] = (1.0 - M_SQRT2 * K + K * K) * norm;
m_b[0] = 2.0 * (K * K - V) * norm;
m_b[1] = (V - std11::sqrt(2.0*V) * K + K * K) * norm;
}
break;
}
}
/**
* @brief Set direct Coefficients
*/
void setBiquadCoef(TYPE _a0, TYPE _a1, TYPE _a2, TYPE _b0, TYPE _b1) {
m_a[0] = _a0;
m_a[1] = _a1;
m_a[2] = _a2;
m_b[0] = _b0;
m_b[1] = _b1;
reset();
}
/**
* @brief Get direct Coefficients
*/
void getBiquadCoef(TYPE& _a0, TYPE& _a1, TYPE& _a2, TYPE& _b0, TYPE& _b1) {
_a0 = m_a[0];
_a1 = m_a[1];
_a2 = m_a[2];
_b0 = m_b[0];
_b1 = m_b[1];
}
/**
* @brief Get direct Coefficients
*/
std::vector<TYPE> getCoef() {
std::vector<TYPE> out;
out.push_back(m_a[0]);
out.push_back(m_a[1]);
out.push_back(m_a[2]);
out.push_back(m_b[0]);
out.push_back(m_b[1]);
return out;
}
/**
* @brief Reset bequad filter (only history not value).
*/
void reset() {
m_x[0] = 0;
m_y[1] = 0;
m_x[0] = 0;
m_y[1] = 0;
}
protected:
/**
* @brief process single sample in float.
* @param[in] _sample Sample to process
* @return updataed value
*/
TYPE process(TYPE _sample) {
TYPE result;
// compute
result = m_a[0] * _sample
+ m_a[1] * m_x[0]
+ m_a[2] * m_x[1]
- m_b[0] * m_y[0]
- m_b[1] * m_y[1];
//update history of X
m_x[1] = m_x[0];
m_x[0] = _sample;
//update history of Y
m_y[1] = m_y[0];
m_y[0] = result;
return result;
}
public:
/**
* @brief Porcess function.
* param[in] _input Pointer on the input data.
* param[in,out] _output Poirter on the output data (can be the same as input (inplace availlable).
* param[in] _nbChunk Number of qample to process.
* param[in] _inputOffset Offset to add when read input data.
* param[in] _outputOffset Offset to add when write output data.
*/
void process(const TYPE* _input,
TYPE* _output,
size_t _nbChunk,
int32_t _inputOffset,
int32_t _outputOffset) {
for (size_t iii=0; iii<_nbChunk; ++iii) {
// process in float the biquad.
*_output = process(*_input);
// move to the sample on the same channel.
_input += _inputOffset;
_output += _outputOffset;
}
}
/**
* @brief calculate respond of the filter:
* @param[in] _sampleRate input qample rate
* @retrun list of frequency/power in dB
*/
std::vector<std::pair<float,float> > calculateTheory(double _sampleRate){
std::vector<std::pair<float,float> > out;
double norm;
bool buildLinear = true;
size_t len = 512;
for (size_t iii=0; iii < len; iii++) {
double w;
if (buildLinear == true) {
// 0 to pi, linear scale
w = iii / (len - 1.0) * M_PI;
} else {
// 0.001 to 1, times pi, log scale
w = std11::exp(std11::log(1.0 / 0.001) * iii / (len - 1.0)) * 0.001 * M_PI;
}
double freq = iii / (len - 1.0) * _sampleRate / 2.0;
double phi = std11::pow(std11::sin(w/2.0), 2.0);
double y = std11::log( std11::pow((m_a[0]+m_a[1]+m_a[2]).getDouble(), 2.0)
- 4.0*((m_a[0]*m_a[1]).getDouble() + 4.0*(m_a[0]*m_a[2]).getDouble() + (m_a[1]*m_a[2]).getDouble())*phi
+ 16.0*(m_a[0]*m_a[2]).getDouble()*phi*phi)
- std11::log( std11::pow(1.0+(m_b[0]+m_b[1]).getDouble(), 2.0)
- 4.0*((m_b[0]).getDouble() + 4.0*(m_b[1]).getDouble() + (m_b[0]*m_b[1]).getDouble())*phi
+ 16.0*m_b[1].getDouble()*phi*phi);
y = y * 10.0 / M_LN10;
if (y <= -200) {
y = -200.0;
}
//APPL_DEBUG("theory = " << freq << " power=" << y);
out.push_back(std::make_pair<float,float>(freq, y));
}
return out;
}
};
}
}
}
#endif

View File

@ -0,0 +1,38 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <etk/types.h>
#include <audio/algo/drain/debug.h>
#include <audio/algo/drain/BiQuadType.h>
static const char* listValues[] = {
"none",
"low-pass",
"high-pass",
"band-pass",
"notch",
"peak",
"low-shelf",
"high-shelf"
};
static int32_t listValuesSize = sizeof(listValues)/sizeof(char*);
namespace etk {
template<> std::string to_string<enum audio::algo::drain::biQuadType>(const enum audio::algo::drain::biQuadType& _variable) {
return listValues[_variable];
}
template <> bool from_string<enum audio::algo::drain::biQuadType>(enum audio::algo::drain::biQuadType& _variableRet, const std::string& _value) {
for (int32_t iii=0; iii<listValuesSize; ++iii) {
if (_value == listValues[iii]) {
_variableRet = static_cast<enum audio::algo::drain::biQuadType>(iii);
return true;
}
}
_variableRet = audio::algo::drain::biQuadType_none;
return false;
}
}

View File

@ -0,0 +1,29 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_DRAIN_ALGO_BIQUAD_TYPE_H__
#define __AUDIO_ALGO_DRAIN_ALGO_BIQUAD_TYPE_H__
#include <etk/memory.h>
namespace audio {
namespace algo {
namespace drain {
enum biQuadType {
biQuadType_none, //!< no filter (pass threw...)
biQuadType_lowPass, //!< low pass filter
biQuadType_highPass, //!< High pass filter
biQuadType_bandPass, //!< band pass filter
biQuadType_notch, //!< Notch Filter
biQuadType_peak, //!< Peaking band EQ filter
biQuadType_lowShelf, //!< Low shelf filter
biQuadType_highShelf, //!< High shelf filter
};
}
}
}
#endif

View File

@ -0,0 +1,359 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/drain/Equalizer.h>
#include <audio/algo/drain/debug.h>
#include <audio/algo/drain/BiQuad.h>
#include <audio/types.h>
// see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
// see http://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
namespace audio {
namespace algo {
namespace drain {
class EqualizerPrivate {
protected:
float m_sampleRate;
int8_t m_nbChannel;
public:
/**
* @brief Constructor
*/
EqualizerPrivate() :
m_sampleRate(48000),
m_nbChannel(2) {
}
/**
* @brief Destructor
*/
virtual ~EqualizerPrivate() {
}
public:
/**
* @brief Initialize the Algorithm
* @param[in] _sampleRate Sample rate of the stream.
* @param[in] _nbChannel Number of channel in the stream.
*/
virtual void init(float _sampleRate=48000, int8_t _nbChannel=2) {
m_sampleRate = _sampleRate;
m_nbChannel = _nbChannel;
};
/**
* @brief Main input algo process.
* @param[in,out] _output Output data.
* @param[in] _input Input data.
* @param[in] _nbChunk Number of chunk in the input buffer.
*/
virtual void process(void* _output, const void* _input, size_t _nbChunk) = 0;
public:
/**
* @brief add a biquad with his value.
*/
virtual bool addBiquad(double _a0, double _a1, double _a2, double _b0, double _b1) = 0;
virtual bool addBiquad(int32_t _idChannel, double _a0, double _a1, double _a2, double _b0, double _b1) = 0;
/**
* @brief add a bi-quad value and type
* @param[in] _type Type of biquad.
* @param[in] _frequencyCut Cut Frequency. [0..sampleRate/2]
* @param[in] _qualityFactor Q factor of quality (good value of 0.707 ==> permit to not ower gain) limit [0.01 .. 10]
* @param[in] _gain Gain to apply (for notch, peak, lowShelf and highShelf) limit : -30, +30
* @param[in] _sampleRate Sample rate of the signal
*/
virtual bool addBiquad(audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) = 0;
virtual bool addBiquad(int32_t _idChannel, audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) = 0;
public:
// for debug & tools only
virtual std::vector<std::pair<float,float> > calculateTheory() = 0;
};
template<typename TYPE> class EqualizerPrivateType : public audio::algo::drain::EqualizerPrivate {
protected:
std::vector<std::vector<audio::algo::drain::BiQuad<TYPE> > > m_biquads;
public:
/**
* @brief Constructor
*/
EqualizerPrivateType() {
}
/**
* @brief Destructor
*/
virtual ~EqualizerPrivateType() {
}
virtual void init(float _sampleRate=48000, int8_t _nbChannel=2) {
audio::algo::drain::EqualizerPrivate::init(_sampleRate, _nbChannel);
m_biquads.clear();
m_biquads.resize(_nbChannel);
}
virtual void process(void* _output, const void* _input, size_t _nbChunk) {
for (size_t jjj=0; jjj<m_nbChannel; ++jjj) {
// get pointer on data:
const TYPE* input = reinterpret_cast<const TYPE*>(_input);
TYPE* output = reinterpret_cast<TYPE*>(_output);
// move to sample offset:
input += jjj;
output += jjj;
for (size_t iii=0; iii<m_biquads[jjj].size(); ++iii) {
m_biquads[jjj][iii].process(input, output, _nbChunk, m_nbChannel, m_nbChannel);
}
}
}
virtual bool addBiquad(double _a0, double _a1, double _a2, double _b0, double _b1) {
audio::algo::drain::BiQuad<TYPE> bq;
bq.setBiquadCoef(_a0, _a1, _a2, _b0, _b1);
// add this bequad for every Channel:
for (size_t iii=0; iii<m_biquads.size(); ++iii) {
m_biquads[iii].push_back(bq);
}
return true;
}
virtual bool addBiquad(audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) {
audio::algo::drain::BiQuad<TYPE> bq;
bq.setBiquad(_type, _frequencyCut, _qualityFactor, _gain, m_sampleRate);
// add this bequad for every Channel:
for (size_t iii=0; iii<m_biquads.size(); ++iii) {
m_biquads[iii].push_back(bq);
}
return true;
}
virtual bool addBiquad(int32_t _idChannel, double _a0, double _a1, double _a2, double _b0, double _b1) {
audio::algo::drain::BiQuad<TYPE> bq;
bq.setBiquadCoef(_a0, _a1, _a2, _b0, _b1);
if (_idChannel<m_biquads.size()) {
m_biquads[_idChannel].push_back(bq);
}
return true;
}
virtual bool addBiquad(int32_t _idChannel, audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) {
audio::algo::drain::BiQuad<TYPE> bq;
bq.setBiquad(_type, _frequencyCut, _qualityFactor, _gain, m_sampleRate);
if (_idChannel<m_biquads.size()) {
m_biquads[_idChannel].push_back(bq);
}
return true;
}
virtual std::vector<std::pair<float,float> > calculateTheory() {
std::vector<std::pair<float,float> > out;
for (size_t iii=0; iii<m_biquads[0].size(); ++iii) {
if (iii == 0) {
out = m_biquads[0][iii].calculateTheory(m_sampleRate);
} else {
std::vector<std::pair<float,float> > tmp = m_biquads[0][iii].calculateTheory(m_sampleRate);
for (size_t jjj=0; jjj< out.size(); ++jjj) {
out[jjj].second += tmp[jjj].second;
}
}
}
return out;
}
};
}
}
}
audio::algo::drain::Equalizer::Equalizer() {
}
audio::algo::drain::Equalizer::~Equalizer() {
}
void audio::algo::drain::Equalizer::init(float _sampleRate, int8_t _nbChannel, enum audio::format _format) {
switch (_format) {
default:
AA_DRAIN_CRITICAL("Request format for equalizer that not exist ... : " << _format);
break;
case audio::format_double:
{
std11::unique_ptr<EqualizerPrivateType<audio::double_t> > value(new EqualizerPrivateType<audio::double_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_float:
{
std11::unique_ptr<EqualizerPrivateType<audio::float_t> > value(new EqualizerPrivateType<audio::float_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int8:
{
std11::unique_ptr<EqualizerPrivateType<audio::int8_8_t> > value(new EqualizerPrivateType<audio::int8_8_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int8_on_int16:
{
std11::unique_ptr<EqualizerPrivateType<audio::int8_16_t> > value(new EqualizerPrivateType<audio::int8_16_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int16:
{
std11::unique_ptr<EqualizerPrivateType<audio::int16_16_t> > value(new EqualizerPrivateType<audio::int16_16_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int16_on_int32:
{
std11::unique_ptr<EqualizerPrivateType<audio::int16_32_t> > value(new EqualizerPrivateType<audio::int16_32_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int24_on_int32:
{
std11::unique_ptr<EqualizerPrivateType<audio::int24_32_t> > value(new EqualizerPrivateType<audio::int24_32_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int32:
{
std11::unique_ptr<EqualizerPrivateType<audio::int32_32_t> > value(new EqualizerPrivateType<audio::int32_32_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int32_on_int64:
{
std11::unique_ptr<EqualizerPrivateType<audio::int32_64_t> > value(new EqualizerPrivateType<audio::int32_64_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
case audio::format_int64:
{
std11::unique_ptr<EqualizerPrivateType<audio::int32_64_t> > value(new EqualizerPrivateType<audio::int32_64_t>());
if (value == nullptr) {
AA_DRAIN_ERROR("can not allocate private data...");
return;
}
value->init(_sampleRate, _nbChannel);
// net value:
m_private = std11::move(value);
}
break;
}
}
std::vector<enum audio::format> audio::algo::drain::Equalizer::getSupportedFormat() {
std::vector<enum audio::format> out = audio::algo::drain::Equalizer::getNativeSupportedFormat();
return out;
}
std::vector<enum audio::format> audio::algo::drain::Equalizer::getNativeSupportedFormat() {
std::vector<enum audio::format> out;
out.push_back(audio::format_float);
return out;
}
void audio::algo::drain::Equalizer::process(void* _output, const void* _input, size_t _nbChunk) {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return;
}
m_private->process(_output, _input, _nbChunk);
}
bool audio::algo::drain::Equalizer::addBiquad(double _a0, double _a1, double _a2, double _b0, double _b1) {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return false;
}
return m_private->addBiquad(_a0, _a1, _a2, _b0, _b1);
}
bool audio::algo::drain::Equalizer::addBiquad(int32_t _idChannel, double _a0, double _a1, double _a2, double _b0, double _b1) {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return false;
}
return m_private->addBiquad(_idChannel, _a0, _a1, _a2, _b0, _b1);
}
bool audio::algo::drain::Equalizer::addBiquad(audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return false;
}
return m_private->addBiquad(_type, _frequencyCut, _qualityFactor, _gain);
}
bool audio::algo::drain::Equalizer::addBiquad(int32_t _idChannel, audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain) {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return false;
}
return m_private->addBiquad(_idChannel, _type, _frequencyCut, _qualityFactor, _gain);
}
std::vector<std::pair<float,float> > audio::algo::drain::Equalizer::calculateTheory() {
if (m_private == nullptr) {
AA_DRAIN_ERROR("Equalizer does not init ...");
return std::vector<std::pair<float,float> >();
}
return m_private->calculateTheory();
}

View File

@ -0,0 +1,81 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_DRAIN_ALGO_EQUALIZER_H__
#define __AUDIO_ALGO_DRAIN_ALGO_EQUALIZER_H__
#include <etk/types.h>
#include <etk/memory.h>
#include <vector>
#include <audio/format.h>
#include <audio/algo/drain/BiQuadType.h>
namespace audio {
namespace algo {
namespace drain {
class EqualizerPrivate;
class Equalizer {
public:
/**
* @brief Constructor
*/
Equalizer();
/**
* @brief Destructor
*/
virtual ~Equalizer();
public:
/**
* @brief Initialize the Algorithm
* @param[in] _sampleRate Sample rate of the stream.
* @param[in] _nbChannel Number of channel in the stream.
* @param[in] _format Input data format.
*/
virtual void init(float _sampleRate=48000, int8_t _nbChannel=2, enum audio::format _format=audio::format_float);
/**
* @brief Get list of format suported in input.
* @return list of supported format
*/
virtual std::vector<enum audio::format> getSupportedFormat();
/**
* @brief Get list of algorithm format suported. No format convertion.
* @return list of supported format
*/
virtual std::vector<enum audio::format> getNativeSupportedFormat();
/**
* @brief Main input algo process.
* @param[in,out] _output Output data.
* @param[in] _input Input data.
* @param[in] _nbChunk Number of chunk in the input buffer.
*/
virtual void process(void* _output, const void* _input, size_t _nbChunk);
public:
/**
* @brief add a biquad with his value.
*/
bool addBiquad(double _a0, double _a1, double _a2, double _b0, double _b1);
bool addBiquad(int32_t _idChannel, double _a0, double _a1, double _a2, double _b0, double _b1);
/**
* @brief add a bi-quad value and type
* @param[in] _type Type of biquad.
* @param[in] _frequencyCut Cut Frequency. [0..sampleRate/2]
* @param[in] _qualityFactor Q factor of quality (good value of 0.707 ==> permit to not ower gain) limit [0.01 .. 10]
* @param[in] _gain Gain to apply (for notch, peak, lowShelf and highShelf) limit : -30, +30
* @param[in] _sampleRate Sample rate of the signal
*/
bool addBiquad(audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain);
bool addBiquad(int32_t _idChannel, audio::algo::drain::biQuadType _type, double _frequencyCut, double _qualityFactor, double _gain);
public:
// for debug & tools only
std::vector<std::pair<float,float> > calculateTheory();
protected:
std11::unique_ptr<EqualizerPrivate> m_private; //!< private data (abstract the type of the data flow).
};
}
}
}
#endif

View File

@ -0,0 +1,14 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "debug.h"
int32_t audio::algo::drain::getLogId() {
static int32_t g_val = etk::log::registerInstance("audio-algo-drain");
return g_val;
}

46
audio/algo/drain/debug.h Normal file
View File

@ -0,0 +1,46 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_DRAIN_DEBUG_H__
#define __AUDIO_ALGO_DRAIN_DEBUG_H__
#include <etk/log.h>
namespace audio {
namespace algo {
namespace drain {
int32_t getLogId();
}
}
}
#define AA_DRAIN_BASE(info,data) TK_LOG_BASE(audio::algo::drain::getLogId(),info,data)
#define AA_DRAIN_PRINT(data) AA_DRAIN_BASE(-1, data)
#define AA_DRAIN_CRITICAL(data) AA_DRAIN_BASE(1, data)
#define AA_DRAIN_ERROR(data) AA_DRAIN_BASE(2, data)
#define AA_DRAIN_WARNING(data) AA_DRAIN_BASE(3, data)
#ifdef DEBUG
#define AA_DRAIN_INFO(data) AA_DRAIN_BASE(4, data)
#define AA_DRAIN_DEBUG(data) AA_DRAIN_BASE(5, data)
#define AA_DRAIN_VERBOSE(data) AA_DRAIN_BASE(6, data)
#define AA_DRAIN_TODO(data) AA_DRAIN_BASE(4, "TODO : " << data)
#else
#define AA_DRAIN_INFO(data) do { } while(false)
#define AA_DRAIN_DEBUG(data) do { } while(false)
#define AA_DRAIN_VERBOSE(data) do { } while(false)
#define AA_DRAIN_TODO(data) do { } while(false)
#endif
#define AA_DRAIN_ASSERT(cond,data) \
do { \
if (!(cond)) { \
AA_DRAIN_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)
#endif

37
lutin_audio_algo_drain.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "audio_algo_drain : drain basic algorithm"
def get_licence():
# return a table with : "Licence Name", contamination if link static, contamination if link dynamic, "Licence string description / FileName / auto for classicle licence"]
return ["APACHE-2", False, False, "auto"]
def create(target):
myModule = module.Module(__file__, 'audio_algo_drain', 'LIBRARY')
myModule.add_src_file([
'audio/algo/drain/debug.cpp',
'audio/algo/drain/BiQuad.cpp',
'audio/algo/drain/BiQuadType.cpp',
'audio/algo/drain/Equalizer.cpp'
])
myModule.add_module_depend(['etk', 'audio'])
myModule.add_export_path(tools.get_current_path(__file__))
#myModule.set_licence(module.APACHE_2)
return myModule

View File

@ -0,0 +1,26 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "audio_algo_drain_test: test for speex ALGO"
def create(target):
myModule = module.Module(__file__, 'audio_algo_drain_test', 'BINARY')
myModule.add_src_file([
'test/main.cpp',
'test/debug.cpp'
])
myModule.add_module_depend(['audio_algo_drain'])
return myModule

13
test/debug.cpp Normal file
View File

@ -0,0 +1,13 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include "debug.h"
int32_t appl::getLogId() {
static int32_t g_val = etk::log::registerInstance("test-drain");
return g_val;
}

41
test/debug.h Normal file
View File

@ -0,0 +1,41 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#ifndef __APPL_DEBUG_H__
#define __APPL_DEBUG_H__
#include <etk/log.h>
namespace appl {
int32_t getLogId();
};
#define APPL_BASE(info,data) TK_LOG_BASE(appl::getLogId(),info,data)
#define APPL_PRINT(data) APPL_BASE(-1, data)
#define APPL_CRITICAL(data) APPL_BASE(1, data)
#define APPL_ERROR(data) APPL_BASE(2, data)
#define APPL_WARNING(data) APPL_BASE(3, data)
#define APPL_INFO(data) APPL_BASE(4, data)
#ifdef DEBUG
#define APPL_DEBUG(data) APPL_BASE(5, data)
#define APPL_VERBOSE(data) APPL_BASE(6, data)
#define APPL_TODO(data) APPL_BASE(4, "TODO : " << data)
#else
#define APPL_DEBUG(data) do { } while(false)
#define APPL_VERBOSE(data) do { } while(false)
#define APPL_TODO(data) do { } while(false)
#endif
#define APPL_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)
#endif

228
test/main.cpp Normal file
View File

@ -0,0 +1,228 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <test/debug.h>
#include <etk/etk.h>
#include <audio/algo/drain/Equalizer.h>
#include <etk/os/FSNode.h>
#include <etk/chrono.h>
#include <unistd.h>
#undef __class__
#define __class__ "test"
class Performance {
private:
std11::chrono::steady_clock::time_point m_timeStart;
std11::chrono::steady_clock::time_point m_timeStop;
std11::chrono::nanoseconds m_totalTimeProcessing;
std11::chrono::nanoseconds m_minProcessing;
std11::chrono::nanoseconds m_maxProcessing;
int32_t m_totalIteration;
public:
Performance() :
m_totalTimeProcessing(0),
m_minProcessing(99999999999999LL),
m_maxProcessing(0),
m_totalIteration(0) {
}
void tic() {
m_timeStart = std11::chrono::steady_clock::now();
}
void toc() {
m_timeStop = std11::chrono::steady_clock::now();
std11::chrono::nanoseconds time = m_timeStop - m_timeStart;
m_minProcessing = std::min(m_minProcessing, time);
m_maxProcessing = std::max(m_maxProcessing, time);
m_totalTimeProcessing += time;
m_totalIteration++;
}
std11::chrono::nanoseconds getTotalTimeProcessing() {
return m_totalTimeProcessing;
}
std11::chrono::nanoseconds getMinProcessing() {
return m_minProcessing;
}
std11::chrono::nanoseconds getMaxProcessing() {
return m_maxProcessing;
}
int32_t getTotalIteration() {
return m_totalIteration;
}
};
float performanceEqualizerType(audio::format _type) {
std::vector<float> input;
input.resize(1024, 0);
std::vector<float> output;
output.resize(input.size()*10, 0);
double sampleRate = 48000;
{
double phase = 0;
double baseCycle = 2.0*M_PI/sampleRate * 480.0;
for (int32_t iii=0; iii<input.size(); iii++) {
input[iii] = cos(phase) * 5.0;
phase += baseCycle;
if (phase >= 2*M_PI) {
phase -= 2*M_PI;
}
}
}
APPL_INFO("Start Equalizer (1 biquad) ... " << _type);
Performance perfo;
audio::algo::drain::Equalizer algo;
// configure in float
algo.init(48000, 1, _type);
// add basic biquad ...
algo.addBiquad(0.01, 0.02, 0.03, 0.05, 0.06);
// set 1024 test
for (int32_t iii=0; iii<1024; ++iii) {
perfo.tic();
size_t sizeOut = output.size();
algo.process(&output[0], &input[0], input.size());
perfo.toc();
usleep(1000);
}
APPL_INFO(" blockSize=" << input.size() << " sample");
APPL_INFO(" min < avg < max =" << perfo.getMinProcessing().count() << "ns < "
<< perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration() << "ns < "
<< perfo.getMaxProcessing().count() << "ns ");
float avg = (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRate)/double(input.size()))/1000000000.0)*100.0;
APPL_INFO(" min < avg < max= " << (float((perfo.getMinProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
<< avg << "% < "
<< (float((perfo.getMaxProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "%");
APPL_PRINT("type=" << _type << ": " << avg << "%");
return avg;
}
void performanceEqualizer() {
performanceEqualizerType(audio::format_double);
performanceEqualizerType(audio::format_float);
performanceEqualizerType(audio::format_int8);
performanceEqualizerType(audio::format_int8_on_int16);
performanceEqualizerType(audio::format_int16);
performanceEqualizerType(audio::format_int16_on_int32);
performanceEqualizerType(audio::format_int32);
performanceEqualizerType(audio::format_int32_on_int64);
performanceEqualizerType(audio::format_int64);
}
int main(int _argc, const char** _argv) {
// the only one init for etk:
etk::init(_argc, _argv);
std::string inputName = "";
std::string outputName = "output.raw";
bool performance = false;
bool perf = false;
int64_t sampleRateIn = 48000;
int64_t sampleRateOut = 48000;
int32_t nbChan = 1;
int32_t quality = 4;
std::string test = "";
for (int32_t iii=0; iii<_argc ; ++iii) {
std::string data = _argv[iii];
if (etk::start_with(data,"--in=")) {
inputName = &data[5];
} else if (etk::start_with(data,"--out=")) {
outputName = &data[6];
} else if (data == "--performance") {
performance = true;
} else if (data == "--perf") {
perf = true;
} else if (etk::start_with(data,"--test=")) {
data = &data[7];
sampleRateIn = etk::string_to_int32_t(data);
} else if (etk::start_with(data,"--sample-rate-in=")) {
data = &data[17];
sampleRateIn = etk::string_to_int32_t(data);
} else if (etk::start_with(data,"--sample-rate-out=")) {
data = &data[18];
sampleRateOut = etk::string_to_int32_t(data);
} else if (etk::start_with(data,"--nb=")) {
data = &data[5];
nbChan = etk::string_to_int32_t(data);
} else if (etk::start_with(data,"--quality=")) {
data = &data[10];
quality = etk::string_to_int32_t(data);
} else if ( data == "-h"
|| data == "--help") {
APPL_PRINT("Help : ");
APPL_PRINT(" ./xxx --fb=file.raw --mic=file.raw");
APPL_PRINT(" --in=YYY.raw input file");
APPL_PRINT(" --out=zzz.raw output file");
APPL_PRINT(" --performance Generate signal to force algo to maximum process time");
APPL_PRINT(" --perf Enable performence test (little slower but real performence test)");
APPL_PRINT(" --test=XXXX some test availlable ...");
APPL_PRINT(" EQUALIZER Test resampling data 16 bit mode");
APPL_PRINT(" --sample-rate=XXXX Input signal sample rate (default 48000)");
exit(0);
}
}
// PERFORMANCE test only ....
if (performance == true) {
performanceEqualizer();
return 0;
}
if (test == "EQUALIZER") {
/*
APPL_INFO("Start resampling test ... ");
if (inputName == "") {
APPL_ERROR("Can not Process missing parameters...");
exit(-1);
}
APPL_INFO("Read input:");
std::vector<int16_t> inputData = etk::FSNodeReadAllDataType<int16_t>(inputName);
APPL_INFO(" " << inputData.size() << " samples");
// resize output :
std::vector<int16_t> output;
output.resize(inputData.size()*sampleRateOut/sampleRateIn+5000, 0);
// process in chunk of 256 samples
int32_t blockSize = 256*nbChan;
Performance perfo;
audio::algo::speex::Resampler algo;
algo.init(nbChan, sampleRateIn, sampleRateOut, quality);
int32_t lastPourcent = -1;
size_t outputPosition = 0;
for (int32_t iii=0; iii<inputData.size()/blockSize; ++iii) {
if (lastPourcent != 100*iii / (inputData.size()/blockSize)) {
lastPourcent = 100*iii / (inputData.size()/blockSize);
APPL_INFO("Process : " << iii*blockSize << "/" << int32_t(inputData.size()/blockSize)*blockSize << " " << lastPourcent << "/100");
} else {
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(inputData.size()/blockSize)*blockSize);
}
size_t availlableSize = (output.size() - outputPosition) / nbChan;
perfo.tic();
algo.process(&output[outputPosition], availlableSize, &inputData[iii*blockSize], blockSize, audio::format_int16);
if (perf == true) {
perfo.toc();
usleep(1000);
}
outputPosition += availlableSize*nbChan;
}
if (perf == true) {
APPL_INFO("Performance Result: ");
APPL_INFO(" blockSize=" << blockSize << " sample");
APPL_INFO(" min=" << perfo.getMinProcessing().count() << " ns");
APPL_INFO(" max=" << perfo.getMaxProcessing().count() << " ns");
APPL_INFO(" avg=" << perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration() << " ns");
APPL_INFO(" min=" << (float((perfo.getMinProcessing().count()*sampleRateIn)/blockSize)/1000000000.0)*100.0 << " %");
APPL_INFO(" max=" << (float((perfo.getMaxProcessing().count()*sampleRateIn)/blockSize)/1000000000.0)*100.0 << " %");
APPL_INFO(" avg=" << (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRateIn)/blockSize)/1000000000.0)*100.0 << " %");
}
etk::FSNodeWriteAllDataType<int16_t>(outputName, output);
*/
}
}