[DEV] start to work
This commit is contained in:
parent
abce0fc900
commit
cdfb632a9e
@ -9,6 +9,7 @@
|
||||
#include <drain/Algo.h>
|
||||
|
||||
// see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
// see http://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/
|
||||
|
||||
drain::Equalizer::Equalizer() {
|
||||
|
||||
@ -18,17 +19,18 @@ void drain::Equalizer::init() {
|
||||
drain::Algo::init();
|
||||
drain::Algo::m_type = "Equalizer";
|
||||
m_supportedFormat.push_back(audio::format_int16);
|
||||
m_supportedFormat.push_back(audio::format_float);
|
||||
m_type = drain::filterType_none;
|
||||
m_gain = 0;
|
||||
m_frequency = 1000;
|
||||
m_bandWidth = 200;
|
||||
m_gain = 6;
|
||||
m_frequencyCut = 1000;
|
||||
m_qualityFactor = 0.707;
|
||||
configureBiQuad();
|
||||
// reset coefficients
|
||||
m_b[0] = 1.0;
|
||||
m_b[1] = 0.0;
|
||||
m_b[2] = 0.0;
|
||||
m_a[0] = 0.0;
|
||||
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;
|
||||
}
|
||||
|
||||
std11::shared_ptr<drain::Equalizer> drain::Equalizer::create() {
|
||||
@ -58,46 +60,58 @@ bool drain::Equalizer::process(std11::chrono::system_clock::time_point& _time,
|
||||
if (_input == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (size_t jjj=0; jjj<getOutputFormat().getMap().size(); ++jjj) {
|
||||
// get pointer on data:
|
||||
int16_t* data = static_cast<int16_t*>(_input);
|
||||
// move to sample offset:
|
||||
data += jjj;
|
||||
for (size_t iii=0; iii<_inputNbChunk; ++iii) {
|
||||
// process in float the biquad.
|
||||
float out = processFloat(*data, m_history[jjj]);
|
||||
// Limit output.
|
||||
out = std::avg(-32768.0f, out, 32767.0f);
|
||||
*data = static_cast<int16_t>(out);
|
||||
// move to the sample on the same channel.
|
||||
data += getOutputFormat().getMap().size();
|
||||
if (getOutputFormat().getFormat() == audio::format_float) {
|
||||
for (size_t jjj=0; jjj<getOutputFormat().getMap().size(); ++jjj) {
|
||||
// get pointer on data:
|
||||
float* data = static_cast<float*>(_input);
|
||||
// move to sample offset:
|
||||
data += jjj;
|
||||
for (size_t iii=0; iii<_inputNbChunk; ++iii) {
|
||||
// process in float the biquad.
|
||||
*data = processFloat(*data, m_history[jjj]);
|
||||
// move to the sample on the same channel.
|
||||
data += getOutputFormat().getMap().size();
|
||||
}
|
||||
}
|
||||
} else if (getOutputFormat().getFormat() == audio::format_int16) {
|
||||
for (size_t jjj=0; jjj<getOutputFormat().getMap().size(); ++jjj) {
|
||||
// get pointer on data:
|
||||
int16_t* data = static_cast<int16_t*>(_input);
|
||||
// move to sample offset:
|
||||
data += jjj;
|
||||
for (size_t iii=0; iii<_inputNbChunk; ++iii) {
|
||||
// process in float the biquad.
|
||||
float out = processFloat(*data, m_history[jjj]);
|
||||
// Limit output.
|
||||
out = std::avg(-32768.0f, out, 32767.0f);
|
||||
*data = static_cast<int16_t>(out);
|
||||
// move to the sample on the same channel.
|
||||
data += getOutputFormat().getMap().size();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drain::Equalizer::setParameter(const std::string& _parameter, const std::string& _value) {
|
||||
//DRAIN_WARNING("set : " << _parameter << " " << _value);
|
||||
if (_parameter == "type") {
|
||||
if (_value == "none") {
|
||||
m_type = drain::filterType_none;
|
||||
} else if (_value == "LPF") {
|
||||
m_type = drain::filterType_LPF;
|
||||
m_type = drain::filterType_lowPass;
|
||||
} else if (_value == "HPF") {
|
||||
m_type = drain::filterType_HPF;
|
||||
m_type = drain::filterType_highPass;
|
||||
} else if (_value == "BPF") {
|
||||
m_type = drain::filterType_BPF;
|
||||
m_type = drain::filterType_bandPass;
|
||||
} else if (_value == "NOTCH") {
|
||||
m_type = drain::filterType_NOTCH;
|
||||
} else if (_value == "APF") {
|
||||
m_type = drain::filterType_APF;
|
||||
m_type = drain::filterType_notch;
|
||||
} else if (_value == "PeakingEQ") {
|
||||
m_type = drain::filterType_PeakingEQ;
|
||||
m_type = drain::filterType_peak;
|
||||
} else if (_value == "LSH") {
|
||||
m_type = drain::filterType_LSH;
|
||||
m_type = drain::filterType_lowShelf;
|
||||
} else if (_value == "HSH") {
|
||||
m_type = drain::filterType_HSH;
|
||||
} else if (_value == "EQU"){
|
||||
m_type = drain::filterType_EQU;
|
||||
m_type = drain::filterType_highShelf;
|
||||
} else {
|
||||
DRAIN_ERROR("Can not set equalizer type : " << _value);
|
||||
return false;
|
||||
@ -105,17 +119,21 @@ bool drain::Equalizer::setParameter(const std::string& _parameter, const std::st
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "gain") {
|
||||
m_gain = etk::string_to_int32_t(_value);
|
||||
m_gain = etk::string_to_double(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "frequency") {
|
||||
m_frequency = etk::string_to_int32_t(_value);
|
||||
m_frequencyCut = etk::string_to_double(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "band-width") {
|
||||
m_bandWidth = etk::string_to_int32_t(_value);
|
||||
} else if (_parameter == "quality") {
|
||||
m_qualityFactor = etk::string_to_double(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "reset") {
|
||||
m_history.clear();
|
||||
m_history.resize(getOutputFormat().getMap().size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -131,116 +149,162 @@ std::string drain::Equalizer::getParameterProperty(const std::string& _parameter
|
||||
float drain::Equalizer::processFloat(float _sample, drain::BGHistory& _history) {
|
||||
float result;
|
||||
// compute
|
||||
result = m_b[0] * _sample
|
||||
+ m_b[1] * _history.m_x[0]
|
||||
+ m_b[2] * _history.m_x[1]
|
||||
- m_a[0] * _history.m_y[0]
|
||||
- m_a[1] * _history.m_y[1];
|
||||
result = m_a[0] * _sample
|
||||
+ m_a[1] * _history.m_x[0]
|
||||
+ m_a[2] * _history.m_x[1]
|
||||
- m_b[0] * _history.m_y[0]
|
||||
- m_b[1] * _history.m_y[1];
|
||||
//update history of X
|
||||
_history.m_x[0] = _history.m_x[1];
|
||||
_history.m_x[1] = _sample;
|
||||
_history.m_x[1] = _history.m_x[0];
|
||||
_history.m_x[0] = _sample;
|
||||
//update history of Y
|
||||
_history.m_y[0] = _history.m_y[1];
|
||||
_history.m_y[1] = result;
|
||||
_history.m_y[1] = _history.m_y[0];
|
||||
_history.m_y[0] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool drain::Equalizer::configureBiQuad() {
|
||||
// reset biQuad.
|
||||
m_b[0] = 1.0;
|
||||
m_b[1] = 0.0;
|
||||
m_b[2] = 0.0;
|
||||
m_a[0] = 0.0;
|
||||
m_a[1] = 0.0;
|
||||
if (m_type == filterType_none) {
|
||||
return true;
|
||||
}
|
||||
double a0, a1, a2, b0, b1, b2;
|
||||
/* setup variables */
|
||||
double A = std::pow(10, m_gain /40); // used for peaking and shelving EQ filters only
|
||||
double w0 = 2.0 * M_PI * double(m_frequency) / double(getOutputFormat().getFrequency());
|
||||
|
||||
// 2*sqrt(A)*alpha = sin(w0) * sqrt( (A^2 + 1)*(1/S - 1) + 2*A )
|
||||
// is a handy intermediate variable for shelving EQ filters.
|
||||
double alpha = std::sin(w0) * std::sqrt((A*A+1.0)*(1.0/m_bandWidth - 1.0) + 2*A);
|
||||
alpha /= 2.0*std::sqrt(A);
|
||||
|
||||
|
||||
switch (m_type) {
|
||||
case drain::filterType_LPF:
|
||||
b0 = (1.0 - std::cos(w0)) * 0.5;
|
||||
b1 = 1.0 - std::cos(w0);
|
||||
b2 = (1.0 - std::cos(w0)) * 0.5;
|
||||
a0 = 1.0 + alpha;
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - alpha;
|
||||
break;
|
||||
case drain::filterType_HPF:
|
||||
b0 = (1.0 + std::cos(w0)) * 0.5;
|
||||
b1 = -(1.0 + std::cos(w0));
|
||||
b2 = (1.0 + std::cos(w0)) * 0.5;
|
||||
a0 = 1.0 + alpha;
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - alpha;
|
||||
break;
|
||||
case drain::filterType_BPF: // constant 0dB peak gain
|
||||
b0 = alpha;
|
||||
b1 = 0.0;
|
||||
b2 = -alpha;
|
||||
a0 = 1.0 + alpha;
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - alpha;
|
||||
break;
|
||||
case drain::filterType_NOTCH:
|
||||
b0 = 1.0;
|
||||
b1 = -2.0 * std::cos(w0);
|
||||
b2 = 1.0;
|
||||
a0 = 1.0 + alpha;
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - alpha;
|
||||
break;
|
||||
case drain::filterType_APF:
|
||||
b0 = 1.0 - alpha;
|
||||
b1 = -2.0 * std::cos(w0);
|
||||
b2 = 1.0 + alpha;
|
||||
a0 = 1.0 + alpha;
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - alpha;
|
||||
break;
|
||||
case drain::filterType_PeakingEQ:
|
||||
b0 = 1.0 + (alpha * A);
|
||||
b1 = -2.0 * std::cos(w0);
|
||||
b2 = 1.0 - (alpha * A);
|
||||
a0 = 1.0 + (alpha /A);
|
||||
a1 = -2.0 * std::cos(w0);
|
||||
a2 = 1.0 - (alpha /A);
|
||||
break;
|
||||
case drain::filterType_LSH:
|
||||
b0 = A*( (A+1.0) - (A-1.0)*std::cos(w0) + 2.0*std::sqrt(A)*alpha );
|
||||
b1 = 2.0*A*( (A-1.0) - (A+1.0)*std::cos(w0) );
|
||||
b2 = A*( (A+1.0) - (A-1.0)*std::cos(w0) - 2.0*std::sqrt(A)*alpha );
|
||||
a0 = (A+1.0) + (A-1.0)*std::cos(w0) + 2.0*std::sqrt(A)*alpha;
|
||||
a1 = -2.0*( (A-1.0) + (A+1.0)*std::cos(w0) );
|
||||
a2 = (A+1.0) + (A-1.0)*std::cos(w0) - 2.0*std::sqrt(A)*alpha;
|
||||
break;
|
||||
case drain::filterType_HSH:
|
||||
b0 = A*( (A+1.0) + (A-1.0) * std::cos(w0) + 2.0*std::sqrt(A)*alpha );
|
||||
b1 = -2.0*A*( (A-1.0) + (A+1.0) * std::cos(w0) );
|
||||
b2 = A*( (A+1.0) + (A-1.0) * std::cos(w0) - 2.0*std::sqrt(A)*alpha );
|
||||
a0 = (A+1.0) - (A-1.0) * std::cos(w0) + 2.0*std::sqrt(A)*alpha;
|
||||
a1 = 2.0*( (A-1.0) - (A+1.0) * std::cos(w0) );
|
||||
a2 = (A+1.0) - (A-1.0) * std::cos(w0) - 2.0*std::sqrt(A)*alpha;
|
||||
break;
|
||||
default:
|
||||
DRAIN_CRITICAL("Impossible case ...");
|
||||
return false;
|
||||
}
|
||||
// precalculate coefficients:
|
||||
m_b[0] = b0 /a0;
|
||||
m_b[1] = b1 /a0;
|
||||
m_b[2] = b2 /a0;
|
||||
m_a[0] = a1 /a0;
|
||||
m_a[1] = a2 /a0;
|
||||
calcBiquad(m_type, m_frequencyCut, m_qualityFactor, m_gain);
|
||||
return true;
|
||||
}
|
||||
|
||||
void drain::Equalizer::calcBiquad(enum drain::filterType _type, double _frequencyCut, double _qualityFactor, double _gain) {
|
||||
m_type = _type;
|
||||
m_frequencyCut = _frequencyCut;
|
||||
m_qualityFactor = _qualityFactor;
|
||||
m_gain = _gain;
|
||||
|
||||
if (getOutputFormat().getFrequency() < 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 (m_frequencyCut > getOutputFormat().getFrequency()/2) {
|
||||
m_frequencyCut = getOutputFormat().getFrequency()/2;
|
||||
} else if (m_frequencyCut < 0) {
|
||||
m_frequencyCut = 0;
|
||||
}
|
||||
if (m_qualityFactor < 0.01) {
|
||||
m_qualityFactor = 0.01;
|
||||
}
|
||||
switch (m_type) {
|
||||
case filterType_lowPass:
|
||||
case filterType_highPass:
|
||||
case filterType_bandPass:
|
||||
case filterType_notch:
|
||||
// Quality : USE IT
|
||||
// Gain : Not USE IT
|
||||
break;
|
||||
case filterType_peak:
|
||||
// Quality : USE IT
|
||||
// Gain : USE IT
|
||||
break;
|
||||
case filterType_lowShelf:
|
||||
case filterType_highShelf:
|
||||
// Quality : NOT USE IT
|
||||
// Gain : USE IT
|
||||
break;
|
||||
default:
|
||||
// Quality : USE IT
|
||||
// Gain : USE IT
|
||||
break;
|
||||
}
|
||||
double norm;
|
||||
double V = std::pow(10.0, std::abs(m_gain) / 20.0);
|
||||
double K = std::tan(M_PI * m_frequencyCut / getOutputFormat().getFrequency());
|
||||
switch (m_type) {
|
||||
case filterType_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 filterType_lowPass:
|
||||
norm = 1 / (1 + K / m_qualityFactor + K * K);
|
||||
m_a[0] = K * K * norm;
|
||||
m_a[1] = 2 * m_a[0];
|
||||
m_a[2] = m_a[0];
|
||||
m_b[0] = 2 * (K * K - 1) * norm;
|
||||
m_b[1] = (1 - K / m_qualityFactor + K * K) * norm;
|
||||
break;
|
||||
case filterType_highPass:
|
||||
norm = 1 / (1 + K / m_qualityFactor + K * K);
|
||||
m_a[0] = 1 * norm;
|
||||
m_a[1] = -2 * m_a[0];
|
||||
m_a[2] = m_a[0];
|
||||
m_b[0] = 2 * (K * K - 1) * norm;
|
||||
m_b[1] = (1 - K / m_qualityFactor + K * K) * norm;
|
||||
break;
|
||||
case filterType_bandPass:
|
||||
norm = 1 / (1 + K / m_qualityFactor + K * K);
|
||||
m_a[0] = K / m_qualityFactor * norm;
|
||||
m_a[1] = 0;
|
||||
m_a[2] = -m_a[0];
|
||||
m_b[0] = 2 * (K * K - 1) * norm;
|
||||
m_b[1] = (1 - K / m_qualityFactor + K * K) * norm;
|
||||
break;
|
||||
case filterType_notch:
|
||||
norm = 1 / (1 + K / m_qualityFactor + K * K);
|
||||
m_a[0] = (1 + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - 1) * norm;
|
||||
m_a[2] = m_a[0];
|
||||
m_b[0] = m_a[1];
|
||||
m_b[1] = (1 - K / m_qualityFactor + K * K) * norm;
|
||||
break;
|
||||
case filterType_peak:
|
||||
if (m_gain >= 0) {
|
||||
norm = 1 / (1 + 1/m_qualityFactor * K + K * K);
|
||||
m_a[0] = (1 + V/m_qualityFactor * K + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - 1) * norm;
|
||||
m_a[2] = (1 - V/m_qualityFactor * K + K * K) * norm;
|
||||
m_b[0] = m_a[1];
|
||||
m_b[1] = (1 - 1/m_qualityFactor * K + K * K) * norm;
|
||||
} else {
|
||||
norm = 1 / (1 + V/m_qualityFactor * K + K * K);
|
||||
m_a[0] = (1 + 1/m_qualityFactor * K + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - 1) * norm;
|
||||
m_a[2] = (1 - 1/m_qualityFactor * K + K * K) * norm;
|
||||
m_b[0] = m_a[1];
|
||||
m_b[1] = (1 - V/m_qualityFactor * K + K * K) * norm;
|
||||
}
|
||||
break;
|
||||
case filterType_lowShelf:
|
||||
if (m_gain >= 0) {
|
||||
norm = 1 / (1 + M_SQRT2 * K + K * K);
|
||||
m_a[0] = (1 + std::sqrt(2*V) * K + V * K * K) * norm;
|
||||
m_a[1] = 2 * (V * K * K - 1) * norm;
|
||||
m_a[2] = (1 - std::sqrt(2*V) * K + V * K * K) * norm;
|
||||
m_b[0] = 2 * (K * K - 1) * norm;
|
||||
m_b[1] = (1 - M_SQRT2 * K + K * K) * norm;
|
||||
} else {
|
||||
norm = 1 / (1 + std::sqrt(2*V) * K + V * K * K);
|
||||
m_a[0] = (1 + M_SQRT2 * K + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - 1) * norm;
|
||||
m_a[2] = (1 - M_SQRT2 * K + K * K) * norm;
|
||||
m_b[0] = 2 * (V * K * K - 1) * norm;
|
||||
m_b[1] = (1 - std::sqrt(2*V) * K + V * K * K) * norm;
|
||||
}
|
||||
break;
|
||||
case filterType_highShelf:
|
||||
if (m_gain >= 0) {
|
||||
norm = 1 / (1 + M_SQRT2 * K + K * K);
|
||||
m_a[0] = (V + std::sqrt(2*V) * K + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - V) * norm;
|
||||
m_a[2] = (V - std::sqrt(2*V) * K + K * K) * norm;
|
||||
m_b[0] = 2 * (K * K - 1) * norm;
|
||||
m_b[1] = (1 - M_SQRT2 * K + K * K) * norm;
|
||||
} else {
|
||||
norm = 1 / (V + std::sqrt(2*V) * K + K * K);
|
||||
m_a[0] = (1 + M_SQRT2 * K + K * K) * norm;
|
||||
m_a[1] = 2 * (K * K - 1) * norm;
|
||||
m_a[2] = (1 - M_SQRT2 * K + K * K) * norm;
|
||||
m_b[0] = 2 * (K * K - V) * norm;
|
||||
m_b[1] = (V - std::sqrt(2*V) * K + K * K) * norm;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,14 @@
|
||||
|
||||
namespace drain {
|
||||
enum filterType {
|
||||
filterType_none, //!< no filter
|
||||
filterType_LPF, //!< low pass filter
|
||||
filterType_HPF, //!< High pass filter
|
||||
filterType_BPF, //!< band pass filter
|
||||
filterType_NOTCH, //!< Notch Filter
|
||||
filterType_APF, //!< All pass Filter
|
||||
filterType_PeakingEQ, //!< Peaking band EQ filter
|
||||
filterType_LSH, //!< Low shelf filter
|
||||
filterType_HSH, //!< High shelf filter
|
||||
filterType_EQU //!< Equalizer
|
||||
filterType_none, //!< no filter (pass threw...)
|
||||
filterType_lowPass, //!< low pass filter
|
||||
filterType_highPass, //!< High pass filter
|
||||
filterType_bandPass, //!< band pass filter
|
||||
filterType_notch, //!< Notch Filter
|
||||
filterType_peak, //!< Peaking band EQ filter
|
||||
filterType_lowShelf, //!< Low shelf filter
|
||||
filterType_highShelf, //!< High shelf filter
|
||||
};
|
||||
class BGHistory {
|
||||
public:
|
||||
@ -64,18 +62,33 @@ namespace drain {
|
||||
//-----------------------------------------
|
||||
// START parameters:
|
||||
enum filterType m_type; //!< current filter type.
|
||||
float m_gain; //!< Gain to apply in dB
|
||||
float m_frequency; //!< Frequency to apply filter
|
||||
float m_bandWidth; //!< Band With to apply filter
|
||||
float m_gain; //!< Gain to apply in dB ??? limit : -30, +30
|
||||
float m_frequencyCut; //!< Frequency to apply filter ???? LIMIT : [0..sampleRate/2]
|
||||
// good value of 0.707 ==> permit to not ower gain
|
||||
float m_qualityFactor; //!< Quality factor ??? limit [0.01 .. 10]
|
||||
// END parameters:
|
||||
//-----------------------------------------
|
||||
float m_a[2]; //!< A bi-Quad coef
|
||||
float m_b[3]; //!< B bi-Quad coef
|
||||
float m_a[3]; //!< A bi-Quad coef
|
||||
float m_b[2]; //!< B bi-Quad coef
|
||||
std::vector<BGHistory> m_history;
|
||||
/**
|
||||
* @brief Configure the current biquad.
|
||||
*/
|
||||
bool configureBiQuad();
|
||||
public:
|
||||
/**
|
||||
* @brief Configure the current biquad.
|
||||
*/
|
||||
void calcBiquad(enum drain::filterType _type, double _frequencyCut, double _qualityFactor, double _gain);
|
||||
std::vector<float> getCoef() {
|
||||
std::vector<float> 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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
appl::Windows::Windows() :
|
||||
m_sampleRate(48000),
|
||||
m_type(drain::filterType::filterType_LPF),
|
||||
m_cutFrequency(2000),
|
||||
m_gain(2),
|
||||
m_bandWidth(200) {
|
||||
m_type(drain::filterType::filterType_lowPass),
|
||||
m_cutFrequency(8000.0),
|
||||
m_gain(6.0),
|
||||
m_bandWidth(0.1) {
|
||||
addObjectType("appl::Windows");
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ void appl::Windows::init() {
|
||||
subBind(ewol::widget::Entry, "frequency", signalModify, shared_from_this(), &appl::Windows::onCallbackFrequency);
|
||||
subBind(ewol::widget::Entry, "band-width", signalModify, shared_from_this(), &appl::Windows::onCallbackBandWidth);
|
||||
subBind(ewol::widget::Button, "display", signalPressed, shared_from_this(), &appl::Windows::onCallbackStart);
|
||||
m_displayer = std11::dynamic_pointer_cast<appl::widget::DisplayFrequency>(getSubObjectNamed("displayer"));
|
||||
}
|
||||
|
||||
void appl::Windows::onCallbackSampleRate(const std::string& _value) {
|
||||
@ -63,7 +64,7 @@ void appl::Windows::onCallbackType(const std::string& _value) {
|
||||
std::vector<std::string> list;
|
||||
list.push_back("HPF");
|
||||
list.push_back("LPF");
|
||||
if (etk::isIn(etk::string_to_int32_t(_value), list) == true) {
|
||||
if (etk::isIn(_value, list) == true) {
|
||||
ewol::parameterSetOnObjectNamed("type-valid", "color", "green");
|
||||
} else {
|
||||
ewol::parameterSetOnObjectNamed("type-valid", "color", "red");
|
||||
@ -82,30 +83,161 @@ void appl::Windows::onCallbackBandWidth(const std::string& _value) {
|
||||
APPL_INFO("BandWidth " << _value);
|
||||
}
|
||||
|
||||
#include <river/debug.h>
|
||||
|
||||
void appl::Windows::onCallbackStart() {
|
||||
APPL_INFO("start ");
|
||||
m_data.clear();
|
||||
int32_t iii = 200;
|
||||
while (iii < m_sampleRate/2) {
|
||||
APPl_INFO("TEST : " << iii);
|
||||
// create equalizer
|
||||
std11::shared_ptr<drain::Equalizer> eq = drain::Equalizer::create();
|
||||
// configure parameter
|
||||
eq->setParameter("type", "LPF");
|
||||
eq->setParameter("gain", etk::to_string(m_gain));
|
||||
eq->setParameter("frequency", etk::to_string(m_cutFrequency));
|
||||
eq->setParameter("band-width", etk::to_string(m_bandWidth));
|
||||
// configure input
|
||||
|
||||
// configure outpur
|
||||
int32_t iii = 10;
|
||||
std::vector<audio::channel> map;
|
||||
map.push_back(audio::channel_frontCenter);
|
||||
drain::IOFormatInterface format(map, audio::format_int16, m_sampleRate);
|
||||
// create equalizer
|
||||
std11::shared_ptr<drain::Equalizer> eq = drain::Equalizer::create();
|
||||
// configure input
|
||||
eq->setInputFormat(format);
|
||||
// configure output
|
||||
eq->setOutputFormat(format);
|
||||
// configure parameter
|
||||
/*
|
||||
eq->setParameter("type", "APF");
|
||||
eq->setParameter("gain", etk::to_string(m_gain));
|
||||
eq->setParameter("frequency", etk::to_string(m_cutFrequency));
|
||||
eq->setParameter("band-width", etk::to_string(m_bandWidth));
|
||||
*/
|
||||
//eq->calcBiquad(drain::filterType_none, m_cutFrequency, 0.707, m_gain);
|
||||
eq->calcBiquad(drain::filterType_lowPass, m_cutFrequency, 0.707, m_gain);
|
||||
//eq->calcBiquad(drain::filterType_highPass, m_cutFrequency, 0.707, m_gain);
|
||||
std::vector<std::pair<float,float> > theory = calculateTheory(48000, eq->getCoef());
|
||||
m_displayer->clear();
|
||||
m_displayer->setValue(theory);
|
||||
std::vector<std::pair<float,float> > pratic;
|
||||
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 = std::exp(std::log(1.0 / 0.001) * iii / (len - 1.0)) * 0.001 * M_PI;
|
||||
//}
|
||||
double freq = iii / (len - 1.0) * 48000 / 2.0;
|
||||
|
||||
//while (iii < m_sampleRate/2) {
|
||||
std::vector<int16_t> data;
|
||||
//APPL_INFO("TEST : " << iii);
|
||||
// To reset filter
|
||||
eq->setParameter("reset", "");
|
||||
// create sinus
|
||||
|
||||
data.resize(4096, 0);
|
||||
double m_phase = 0;
|
||||
double baseCycle = 2.0*M_PI/double(m_sampleRate) * double(freq);
|
||||
for (int32_t iii=0; iii<data.size(); iii++) {
|
||||
data[iii] = cos(m_phase) * 30000;
|
||||
m_phase += baseCycle;
|
||||
if (m_phase >= 2*M_PI) {
|
||||
m_phase -= 2*M_PI;
|
||||
}
|
||||
}
|
||||
// process
|
||||
|
||||
iii += 200;
|
||||
int16_t* output = nullptr;
|
||||
void* outputVoid = nullptr;
|
||||
size_t outputNbChunk = 0;
|
||||
std11::chrono::system_clock::time_point time;
|
||||
RIVER_SAVE_FILE_MACRO(int16_t,"aaa_test_INPUT.raw",&data[0],data.size());
|
||||
eq->process(time, &data[0], data.size(), outputVoid, outputNbChunk);
|
||||
output = static_cast<int16_t*>(outputVoid);
|
||||
RIVER_SAVE_FILE_MACRO(int16_t,"aaa_test_OUTPUT.raw",output,outputNbChunk);
|
||||
int16_t value = 0;
|
||||
for (size_t iii=20; iii<outputNbChunk-20; ++iii) {
|
||||
value = std::max(value, output[iii]);
|
||||
}
|
||||
float gain = 20.0 * std::log10(double(value)/30000.0);
|
||||
APPL_INFO("LEVEL " << iii << " out = " << value << " % : " << gain);
|
||||
pratic.push_back(std::make_pair<float, float>(iii,float(value)/30000.0f));
|
||||
iii += 10;
|
||||
}
|
||||
|
||||
/*
|
||||
while (iii < m_sampleRate/2) {
|
||||
std::vector<float> data;
|
||||
//APPL_INFO("TEST : " << iii);
|
||||
// To reset filter
|
||||
eq->setParameter("reset", "");
|
||||
// create sinus
|
||||
data.resize(4096, 0);
|
||||
double m_phase = 0;
|
||||
double baseCycle = 2.0*M_PI/double(m_sampleRate) * double(iii);
|
||||
for (int32_t iii=0; iii<data.size(); iii++) {
|
||||
data[iii] = cos(m_phase);
|
||||
m_phase += baseCycle;
|
||||
if (m_phase >= 2*M_PI) {
|
||||
m_phase -= 2*M_PI;
|
||||
}
|
||||
}
|
||||
// process
|
||||
float* output = nullptr;
|
||||
void* outputVoid = nullptr;
|
||||
size_t outputNbChunk = 0;
|
||||
std11::chrono::system_clock::time_point time;
|
||||
RIVER_SAVE_FILE_MACRO(float,"aaa_test_INPUT_F.raw",&data[0],data.size());
|
||||
eq->process(time, &data[0], data.size(), outputVoid, outputNbChunk);
|
||||
output = static_cast<float*>(outputVoid);
|
||||
RIVER_SAVE_FILE_MACRO(float,"aaa_test_OUTPUT_F.raw",output,outputNbChunk);
|
||||
double value = 0;
|
||||
for (size_t iii=0; iii<outputNbChunk; ++iii) {
|
||||
value += std::abs(output[iii]);
|
||||
}
|
||||
value /= (outputNbChunk);
|
||||
float gain = 20.0 * std::log10(float(value));
|
||||
APPL_INFO("LEVEL " << iii << " out = " << value << " % : " << gain);
|
||||
pratic.push_back(std::make_pair<float, float>(iii,float(value)));
|
||||
iii += 10;
|
||||
}
|
||||
*/
|
||||
m_displayer->setValue(pratic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<float,float> > appl::Windows::calculateTheory(double _sampleRate, std::vector<float> _coef) {
|
||||
std::vector<std::pair<float,float> > out;
|
||||
double norm;
|
||||
float m_a[3];
|
||||
float m_b[2];
|
||||
|
||||
m_a[0] = _coef[0];
|
||||
m_a[1] = _coef[1];
|
||||
m_a[2] = _coef[2];
|
||||
m_b[0] = _coef[3];
|
||||
m_b[1] = _coef[4];
|
||||
|
||||
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 = std::exp(std::log(1.0 / 0.001) * iii / (len - 1.0)) * 0.001 * M_PI;
|
||||
}
|
||||
double phi = std::pow(std::sin(w/2.0), 2.0);
|
||||
double y = std::log( std::pow(m_a[0]+m_a[1]+m_a[2], 2.0)
|
||||
- 4.0*(m_a[0]*m_a[1] + 4.0*m_a[0]*m_a[2] + m_a[1]*m_a[2])*phi
|
||||
+ 16.0*m_a[0]*m_a[2]*phi*phi)
|
||||
- std::log( std::pow(1.0+m_b[0]+m_b[1], 2.0)
|
||||
- 4.0*(m_b[0] + 4.0*m_b[1] + m_b[0]*m_b[1])*phi
|
||||
+ 16.0*m_b[1]*phi*phi);
|
||||
y = y * 10.0 / M_LN10;
|
||||
|
||||
if (y <= -200) {
|
||||
y = -200.0;
|
||||
}
|
||||
|
||||
APPL_INFO("theory = " << iii / (len - 1.0) * _sampleRate / 2.0 << " power=" << y);
|
||||
out.push_back(std::make_pair<float,float>(iii / (len - 1.0) * _sampleRate / 2.0, y + 0.5));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <ewol/widget/Layer.h>
|
||||
#include <ewol/widget/Composer.h>
|
||||
#include <drain/Equalizer.h>
|
||||
#include <appl/widget/DisplayFrequency.h>
|
||||
|
||||
namespace appl {
|
||||
class Windows : public ewol::widget::Windows {
|
||||
@ -23,6 +24,7 @@ namespace appl {
|
||||
DECLARE_FACTORY(Windows);
|
||||
protected:
|
||||
std::shared_ptr<ewol::widget::Composer> m_gui;
|
||||
std::shared_ptr<appl::widget::DisplayFrequency> m_displayer;
|
||||
void onCallbackSampleRate(const std::string& _value);
|
||||
void onCallbackType(const std::string& _value);
|
||||
void onCallbackGain(const std::string& _value);
|
||||
@ -35,7 +37,7 @@ namespace appl {
|
||||
float m_cutFrequency;
|
||||
float m_gain;
|
||||
float m_bandWidth;
|
||||
std::vector<std::pair<float,float> > m_data;
|
||||
std::vector<std::pair<float,float> > calculateTheory(double _sampleRate, std::vector<float> _coef);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,7 @@ appl::widget::DisplayFrequency::~DisplayFrequency() {
|
||||
|
||||
|
||||
void appl::widget::DisplayFrequency::setValue(const std::vector<std::pair<float,float> >& _data) {
|
||||
m_data = _data;
|
||||
m_data.push_back(_data);
|
||||
markToRedraw();
|
||||
}
|
||||
|
||||
@ -62,25 +62,67 @@ void appl::widget::DisplayFrequency::onRegenerateDisplay() {
|
||||
}
|
||||
// remove previous data
|
||||
m_draw.clear();
|
||||
|
||||
m_borderSize = m_size * 0.05;
|
||||
|
||||
|
||||
|
||||
// set background
|
||||
m_draw.setColor(etk::color::black);
|
||||
m_draw.setPos(vec2(0,0));
|
||||
m_draw.rectangleWidth(m_size);
|
||||
m_draw.setColor(etk::color::gray);
|
||||
m_draw.setPos(m_borderSize);
|
||||
m_draw.setThickness(1);
|
||||
vec2 plop = m_size-m_borderSize;
|
||||
m_draw.lineTo(vec2(m_borderSize.x(), plop.y()));
|
||||
m_draw.lineTo(m_size-m_borderSize);
|
||||
m_draw.lineTo(vec2(plop.x(), m_borderSize.y()));
|
||||
|
||||
m_draw.lineTo(m_borderSize);
|
||||
|
||||
if (m_data.size() == 0) {
|
||||
return;
|
||||
}
|
||||
// set all the line:
|
||||
m_draw.setColor(etk::color::white);
|
||||
m_draw.setThickness(1);
|
||||
|
||||
float ratioX = m_size.x() / (m_frequencyMax - m_frequencyMin);
|
||||
float ratioY = m_size.y() / (m_gainMax - m_gainMin);
|
||||
m_draw.setPos(vec2(ratioX*(m_data[0].first - m_gainMin),
|
||||
ratioY*(m_data[0].second - m_gainMin)));
|
||||
float baseX = 0;
|
||||
for (size_t iii=1; iii<m_data.size(); ++iii) {
|
||||
m_draw.lineTo(vec2(ratioX*(m_data[0].first - m_gainMin),
|
||||
ratioY*(m_data[0].second - m_gainMin)));
|
||||
// calculate min and Max :
|
||||
m_gainMax = -999999999.9;
|
||||
m_gainMin = 9999999999.9;
|
||||
m_frequencyMin = 99999999.0;
|
||||
m_frequencyMax = -99999999.0;
|
||||
for (size_t kkk=0; kkk<m_data.size(); kkk++) {
|
||||
for (size_t iii=0; iii<m_data[kkk].size(); ++iii) {
|
||||
m_gainMax = std::max(m_gainMax, m_data[kkk][iii].second);
|
||||
m_gainMin = std::min(m_gainMin, m_data[kkk][iii].second);
|
||||
m_frequencyMax = std::max(m_frequencyMax, m_data[kkk][iii].first);
|
||||
m_frequencyMin = std::min(m_frequencyMin, m_data[kkk][iii].first);
|
||||
}
|
||||
}
|
||||
m_gainMin = -20;
|
||||
// set all the line:
|
||||
m_draw.setThickness(1);
|
||||
APPL_ERROR("---------------------------");
|
||||
for (size_t kkk=0; kkk<m_data.size(); ++kkk) {
|
||||
APPL_ERROR("kjhkjhkj " << kkk << " " << m_data.size());
|
||||
if (kkk == 0) {
|
||||
m_draw.setColor(etk::color::green);
|
||||
} else if (kkk == 0) {
|
||||
m_draw.setColor(etk::color::orange);
|
||||
} else {
|
||||
m_draw.setColor(etk::color::red);
|
||||
}
|
||||
|
||||
float ratioX = (m_size.x()-m_borderSize.x()*2.0) / (m_frequencyMax - m_frequencyMin);
|
||||
float ratioY = (m_size.y()-m_borderSize.y()*2.0) / (m_gainMax - m_gainMin);
|
||||
m_draw.setPos( m_borderSize
|
||||
+ vec2(ratioX*(m_data[kkk][0].first - m_frequencyMin),
|
||||
ratioY*(m_data[kkk][0].second - m_gainMin)));
|
||||
float baseX = 0;
|
||||
for (size_t iii=1; iii<m_data[kkk].size(); ++iii) {
|
||||
m_draw.lineTo( m_borderSize
|
||||
+ vec2(ratioX*(m_data[kkk][iii].first - m_frequencyMin),
|
||||
ratioY*(m_data[kkk][iii].second - m_gainMin)));
|
||||
}
|
||||
}
|
||||
// TODO : Draw text ...
|
||||
|
||||
}
|
||||
|
@ -28,8 +28,11 @@ namespace appl {
|
||||
//! @brief destructor
|
||||
virtual ~DisplayFrequency();
|
||||
private:
|
||||
std::vector<std::pair<float,float> > m_data; //!< data that might be displayed
|
||||
std::vector<std::vector<std::pair<float,float> > > m_data; //!< data that might be displayed
|
||||
public:
|
||||
void clear() {
|
||||
m_data.clear();
|
||||
}
|
||||
void setValue(const std::vector<std::pair<float,float> >& _data);
|
||||
private:
|
||||
float m_gainMin; //!< display minimum gain value
|
||||
@ -44,6 +47,8 @@ namespace appl {
|
||||
public: // herited function
|
||||
virtual void onDraw();
|
||||
virtual void onRegenerateDisplay();
|
||||
private:
|
||||
vec2 m_borderSize;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ def create(target):
|
||||
'appl/widget/DisplayFrequency.cpp',
|
||||
])
|
||||
# add Library dependency name
|
||||
myModule.add_module_depend(['ewol', 'drain'])
|
||||
myModule.add_module_depend(['ewol', 'drain', 'river'])
|
||||
# add application C flags
|
||||
myModule.compile_flags_CC([
|
||||
"-DPROJECT_NAME=\"\\\""+myModule.name+"\\\"\""])
|
||||
|
Loading…
Reference in New Issue
Block a user