[DEV] add equalizer
This commit is contained in:
parent
d8dbded424
commit
ccaa044058
@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AIRT_ALGO_CORE_ALGO_H__
|
||||
#define __AIRT_ALGO_CORE_ALGO_H__
|
||||
#ifndef __DRAIN_ALGO_CORE_ALGO_H__
|
||||
#define __DRAIN_ALGO_CORE_ALGO_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AIRT_ALGO_AUTO_LOG_IN_OUT_H__
|
||||
#define __AIRT_ALGO_AUTO_LOG_IN_OUT_H__
|
||||
#ifndef __DRAIN_ALGO_AUTO_LOG_IN_OUT_H__
|
||||
#define __DRAIN_ALGO_AUTO_LOG_IN_OUT_H__
|
||||
|
||||
#include <string>
|
||||
#include "debug.h"
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_CHANNEL_REORDER_H__
|
||||
#define __AIRT_ALGO_CHANNEL_REORDER_H__
|
||||
#ifndef __DRAIN_ALGO_CHANNEL_REORDER_H__
|
||||
#define __DRAIN_ALGO_CHANNEL_REORDER_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_END_POINT_H__
|
||||
#define __AIRT_ALGO_END_POINT_H__
|
||||
#ifndef __DRAIN_ALGO_END_POINT_H__
|
||||
#define __DRAIN_ALGO_END_POINT_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_END_POINT_CALLBACK_H__
|
||||
#define __AIRT_ALGO_END_POINT_CALLBACK_H__
|
||||
#ifndef __DRAIN_ALGO_END_POINT_CALLBACK_H__
|
||||
#define __DRAIN_ALGO_END_POINT_CALLBACK_H__
|
||||
|
||||
#include <drain/EndPoint.h>
|
||||
#include <etk/functional.h>
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_END_POINT_READ_H__
|
||||
#define __AIRT_ALGO_END_POINT_READ_H__
|
||||
#ifndef __DRAIN_ALGO_END_POINT_READ_H__
|
||||
#define __DRAIN_ALGO_END_POINT_READ_H__
|
||||
|
||||
#include <drain/EndPoint.h>
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_ALGO_END_POINT_WRITE_H__
|
||||
#define __AIRT_ALGO_ALGO_END_POINT_WRITE_H__
|
||||
#ifndef __DRAIN_ALGO_ALGO_END_POINT_WRITE_H__
|
||||
#define __DRAIN_ALGO_ALGO_END_POINT_WRITE_H__
|
||||
|
||||
#include <drain/EndPoint.h>
|
||||
#include <etk/functional.h>
|
||||
|
246
drain/Equalizer.cpp
Normal file
246
drain/Equalizer.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <drain/debug.h>
|
||||
#include <drain/Equalizer.h>
|
||||
#include <drain/Algo.h>
|
||||
|
||||
// see http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
|
||||
drain::Equalizer::Equalizer() {
|
||||
|
||||
}
|
||||
|
||||
void drain::Equalizer::init() {
|
||||
drain::Algo::init();
|
||||
drain::Algo::m_type = "Equalizer";
|
||||
m_supportedFormat.push_back(audio::format_int16);
|
||||
m_type = drain::filterType_none;
|
||||
m_gain = 0;
|
||||
m_frequency = 1000;
|
||||
m_bandWidth = 200;
|
||||
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[1] = 0.0;
|
||||
}
|
||||
|
||||
std11::shared_ptr<drain::Equalizer> drain::Equalizer::create() {
|
||||
std11::shared_ptr<drain::Equalizer> tmp(new drain::Equalizer());
|
||||
tmp->init();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
drain::Equalizer::~Equalizer() {
|
||||
|
||||
}
|
||||
|
||||
void drain::Equalizer::configurationChange() {
|
||||
drain::Algo::configurationChange();
|
||||
// Resize the configuration ouput of algirithm
|
||||
m_history.clear();
|
||||
m_history.resize(getOutputFormat().getMap().size());
|
||||
}
|
||||
|
||||
bool drain::Equalizer::process(std11::chrono::system_clock::time_point& _time,
|
||||
void* _input,
|
||||
size_t _inputNbChunk,
|
||||
void*& _output,
|
||||
size_t& _outputNbChunk) {
|
||||
_outputNbChunk = _inputNbChunk;
|
||||
_output = _input;
|
||||
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();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drain::Equalizer::setParameter(const std::string& _parameter, const std::string& _value) {
|
||||
if (_parameter == "type") {
|
||||
if (_value == "none") {
|
||||
m_type = drain::filterType_none;
|
||||
} else if (_value == "LPF") {
|
||||
m_type = drain::filterType_LPF;
|
||||
} else if (_value == "HPF") {
|
||||
m_type = drain::filterType_HPF;
|
||||
} else if (_value == "BPF") {
|
||||
m_type = drain::filterType_BPF;
|
||||
} else if (_value == "NOTCH") {
|
||||
m_type = drain::filterType_NOTCH;
|
||||
} else if (_value == "APF") {
|
||||
m_type = drain::filterType_APF;
|
||||
} else if (_value == "PeakingEQ") {
|
||||
m_type = drain::filterType_PeakingEQ;
|
||||
} else if (_value == "LSH") {
|
||||
m_type = drain::filterType_LSH;
|
||||
} else if (_value == "HSH") {
|
||||
m_type = drain::filterType_HSH;
|
||||
} else if (_value == "EQU"){
|
||||
m_type = drain::filterType_EQU;
|
||||
} else {
|
||||
DRAIN_ERROR("Can not set equalizer type : " << _value);
|
||||
return false;
|
||||
}
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "gain") {
|
||||
m_gain = etk::string_to_int32_t(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "frequency") {
|
||||
m_frequency = etk::string_to_int32_t(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
} else if (_parameter == "band-width") {
|
||||
m_bandWidth = etk::string_to_int32_t(_value);
|
||||
configureBiQuad();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string drain::Equalizer::getParameter(const std::string& _parameter) const {
|
||||
return "error";
|
||||
}
|
||||
|
||||
std::string drain::Equalizer::getParameterProperty(const std::string& _parameter) const {
|
||||
return "error";
|
||||
}
|
||||
|
||||
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];
|
||||
//update history of X
|
||||
_history.m_x[0] = _history.m_x[1];
|
||||
_history.m_x[1] = _sample;
|
||||
//update history of Y
|
||||
_history.m_y[0] = _history.m_y[1];
|
||||
_history.m_y[1] = 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;
|
||||
return true;
|
||||
}
|
||||
|
82
drain/Equalizer.h
Normal file
82
drain/Equalizer.h
Normal file
@ -0,0 +1,82 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __DRAIN_ALGO_EQUALIZER_H__
|
||||
#define __DRAIN_ALGO_EQUALIZER_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
#include <etk/memory.h>
|
||||
|
||||
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
|
||||
};
|
||||
class BGHistory {
|
||||
public:
|
||||
BGHistory() {
|
||||
m_x[0] = 0;
|
||||
m_y[1] = 0;
|
||||
m_x[0] = 0;
|
||||
m_y[1] = 0;
|
||||
}
|
||||
float m_x[2]; //!< X history
|
||||
float m_y[2]; //!< Y histiry
|
||||
};
|
||||
class Equalizer : public Algo {
|
||||
protected:
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
Equalizer();
|
||||
void init();
|
||||
public:
|
||||
static std11::shared_ptr<Equalizer> create();
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~Equalizer();
|
||||
protected:
|
||||
virtual void configurationChange();
|
||||
public:
|
||||
virtual bool process(std11::chrono::system_clock::time_point& _time,
|
||||
void* _input,
|
||||
size_t _inputNbChunk,
|
||||
void*& _output,
|
||||
size_t& _outputNbChunk);
|
||||
virtual bool setParameter(const std::string& _parameter, const std::string& _value);
|
||||
virtual std::string getParameter(const std::string& _parameter) const;
|
||||
virtual std::string getParameterProperty(const std::string& _parameter) const;
|
||||
|
||||
protected:
|
||||
float processFloat(float _sample, drain::BGHistory& _history);
|
||||
//-----------------------------------------
|
||||
// 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
|
||||
// END parameters:
|
||||
//-----------------------------------------
|
||||
float m_a[2]; //!< A bi-Quad coef
|
||||
float m_b[3]; //!< B bi-Quad coef
|
||||
std::vector<BGHistory> m_history;
|
||||
/**
|
||||
* @brief Configure the current biquad.
|
||||
*/
|
||||
bool configureBiQuad();
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -3,8 +3,8 @@
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
#ifndef __AIRT_ALGO_FORMAT_UPDATE_H__
|
||||
#define __AIRT_ALGO_FORMAT_UPDATE_H__
|
||||
#ifndef __DRAIN_ALGO_FORMAT_UPDATE_H__
|
||||
#define __DRAIN_ALGO_FORMAT_UPDATE_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
|
||||
|
@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AIRT_ALGO_IO_FORMAT_INTERFACE_H__
|
||||
#define __AIRT_ALGO_IO_FORMAT_INTERFACE_H__
|
||||
#ifndef __DRAIN_ALGO_IO_FORMAT_INTERFACE_H__
|
||||
#define __DRAIN_ALGO_IO_FORMAT_INTERFACE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __AIRT_ALGO_PROCESS_H__
|
||||
#define __AIRT_ALGO_PROCESS_H__
|
||||
#ifndef __DRAIN_ALGO_PROCESS_H__
|
||||
#define __DRAIN_ALGO_PROCESS_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_RESAMPLER_H__
|
||||
#define __AIRT_ALGO_RESAMPLER_H__
|
||||
#ifndef __DRAIN_ALGO_RESAMPLER_H__
|
||||
#define __DRAIN_ALGO_RESAMPLER_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
#ifdef HAVE_SPEEX_DSP_RESAMPLE
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_VOLUME_H__
|
||||
#define __AIRT_ALGO_VOLUME_H__
|
||||
#ifndef __DRAIN_ALGO_VOLUME_H__
|
||||
#define __DRAIN_ALGO_VOLUME_H__
|
||||
|
||||
#include <drain/Algo.h>
|
||||
#ifdef HAVE_SPEEX_DSP_RESAMPLE
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @license APACHE v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRT_ALGO_CORE_H__
|
||||
#define __AIRT_ALGO_CORE_H__
|
||||
#ifndef __DRAIN_ALGO_CORE_H__
|
||||
#define __DRAIN_ALGO_CORE_H__
|
||||
|
||||
#include <string>
|
||||
#include <audio/format.h>
|
||||
|
@ -25,7 +25,8 @@ def create(target):
|
||||
'drain/Resampler.cpp',
|
||||
'drain/Volume.cpp',
|
||||
'drain/IOFormatInterface.cpp',
|
||||
'drain/AutoLogInOut.cpp'
|
||||
'drain/AutoLogInOut.cpp',
|
||||
'drain/Equalizer.cpp'
|
||||
])
|
||||
|
||||
# TODO: myModule.add_optional_module_depend('speexdsp', "HAVE_SPEEX_DSP_RESAMPLE")
|
||||
|
Loading…
x
Reference in New Issue
Block a user