[DEV] rename and rework lib to be compatible with other algo & rename river

This commit is contained in:
Edouard DUPIN 2015-04-23 21:17:45 +02:00
parent cb12079db1
commit f15b42e6e9
19 changed files with 703 additions and 405 deletions

View File

@ -1,93 +0,0 @@
/** @file
* @author Edouard DUPIN
* @author Fatima MARFOUQ
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/aec/debug.h>
#include <audio/algo/aec/Lms.h>
#include <audio/algo/aec/updateFilter.h>
#include <audio/algo/aec/convolution.h>
audio::algo::aec::Lms::Lms(void) :
m_filter(),
m_feedBack(),
m_mu(0.03f) {
setFilterSize(256);
}
audio::algo::aec::Lms::~Lms(void) {
}
void audio::algo::aec::Lms::reset(void) {
// simply reset filters.
setFilterSize(m_filter.size());
}
#define MAX_PROCESSING_BLOCK_SIZE (256)
bool audio::algo::aec::Lms::process(int16_t* _output, const int16_t* _feedback, const int16_t* _microphone, int32_t _nbSample) {
bool ret = false;
// due to the fact we allocate the data in the stack:
int32_t nbCycle = _nbSample/MAX_PROCESSING_BLOCK_SIZE;
if (_nbSample - int32_t(_nbSample/MAX_PROCESSING_BLOCK_SIZE)*MAX_PROCESSING_BLOCK_SIZE != 0 ) {
nbCycle++;
}
for (int32_t bbb=0; bbb<nbCycle; ++bbb) {
float output[MAX_PROCESSING_BLOCK_SIZE];
float feedback[MAX_PROCESSING_BLOCK_SIZE];
float microphone[MAX_PROCESSING_BLOCK_SIZE];
int32_t offset = bbb*MAX_PROCESSING_BLOCK_SIZE;
int32_t nbData = std::min(MAX_PROCESSING_BLOCK_SIZE,
_nbSample - offset);
for (size_t iii=0; iii<nbData; ++iii) {
microphone[iii] = float(_microphone[offset+iii])/32767.0f;
feedback[iii] = float(_feedback[offset+iii])/32767.0f;
}
ret = process(output, feedback, microphone, nbData);
for (size_t iii=0; iii<nbData; ++iii) {
_output[offset+iii] = int16_t(float(output[iii])*32767.0f);
}
}
return ret;
}
bool audio::algo::aec::Lms::process(float* _output, const float* _feedback, const float* _microphone, int32_t _nbSample) {
// add sample in the feedback history:
m_feedBack.resize(m_filter.size()+_nbSample, 0.0f);
memcpy(&m_feedBack[m_filter.size()], _feedback, _nbSample*sizeof(float));
for (int32_t iii=0; iii < _nbSample; iii++) {
_output[iii] = processValue(&m_feedBack[m_filter.size()+iii], _microphone[iii]);
}
// remove old value:
m_feedBack.erase(m_feedBack.begin(), m_feedBack.begin() + (m_feedBack.size()-m_filter.size()) );
return true;
}
float audio::algo::aec::Lms::processValue(float* _feedback, float _microphone) {
// Error calculation.
float convolutionValue = audio::algo::aec::convolution(_feedback, &m_filter[0], m_filter.size());
float error = _microphone - convolutionValue;
float out = std::avg(-1.0f, error, 1.0f);
audio::algo::aec::updateFilter(&m_filter[0], _feedback, error*m_mu, m_filter.size());
return out;
}
void audio::algo::aec::Lms::setFilterSize(size_t _sampleRate, std11::chrono::microseconds _time) {
setFilterSize((_sampleRate*_time.count())/1000000LL);
}
void audio::algo::aec::Lms::setFilterSize(size_t _nbSample) {
m_filter.clear();
m_feedBack.clear();
m_filter.resize(_nbSample, 0.0f);
m_feedBack.resize(_nbSample, 0.0f);
}
void audio::algo::aec::Lms::setMu(float _val) {
m_mu = _val;
}

View File

@ -1,98 +0,0 @@
/** @file
* @author Edouard DUPIN
* @author Fatima MARFOUQ
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/aec/debug.h>
#include <audio/algo/aec/Nlms.h>
#include <audio/algo/aec/updateFilter.h>
#include <audio/algo/aec/convolution.h>
#include <audio/algo/aec/power.h>
audio::algo::aec::Nlms::Nlms(void) :
m_filter(),
m_feedBack() {
setFilterSize(256);
}
audio::algo::aec::Nlms::~Nlms(void) {
}
void audio::algo::aec::Nlms::reset(void) {
// simply reset filters.
setFilterSize(m_filter.size());
}
#define MAX_PROCESSING_BLOCK_SIZE (256)
bool audio::algo::aec::Nlms::process(int16_t* _output, const int16_t* _feedback, const int16_t* _microphone, int32_t _nbSample) {
bool ret = false;
// due to the fact we allocate the data in the stack:
int32_t nbCycle = _nbSample/MAX_PROCESSING_BLOCK_SIZE;
if (_nbSample - int32_t(_nbSample/MAX_PROCESSING_BLOCK_SIZE)*MAX_PROCESSING_BLOCK_SIZE != 0 ) {
nbCycle++;
}
for (int32_t bbb=0; bbb<nbCycle; ++bbb) {
float output[MAX_PROCESSING_BLOCK_SIZE];
float feedback[MAX_PROCESSING_BLOCK_SIZE];
float microphone[MAX_PROCESSING_BLOCK_SIZE];
int32_t offset = bbb*MAX_PROCESSING_BLOCK_SIZE;
int32_t nbData = std::min(MAX_PROCESSING_BLOCK_SIZE,
_nbSample - offset);
for (size_t iii=0; iii<nbData; ++iii) {
microphone[iii] = float(_microphone[offset+iii])/32767.0f;
feedback[iii] = float(_feedback[offset+iii])/32767.0f;
}
ret = process(output, feedback, microphone, nbData);
for (size_t iii=0; iii<nbData; ++iii) {
_output[offset+iii] = int16_t(float(output[iii])*32767.0f);
}
}
return ret;
}
bool audio::algo::aec::Nlms::process(float* _output, const float* _feedback, const float* _microphone, int32_t _nbSample) {
// add sample in the feedback history:
m_feedBack.resize(m_filter.size()+_nbSample, 0.0f);
memcpy(&m_feedBack[m_filter.size()], _feedback, _nbSample*sizeof(float));
for (int32_t iii=0; iii < _nbSample; iii++) {
_output[iii] = processValue(&m_feedBack[m_filter.size()+iii], _microphone[iii]);
}
// remove old value:
m_feedBack.erase(m_feedBack.begin(), m_feedBack.begin() + (m_feedBack.size()-m_filter.size()) );
return true;
}
float audio::algo::aec::Nlms::processValue(float* _feedback, float _microphone) {
// Error calculation.
float convolutionValue = audio::algo::aec::convolution(_feedback, &m_filter[0], m_filter.size());
float error = _microphone - convolutionValue;
float out = std::avg(-1.0f, error, 1.0f);
// calculate mu:
float mu = audio::algo::aec::power(_feedback, m_filter.size());
//mu = *_feedback * *_feedback;
//AA_AEC_WARNING("Mu =" << mu);
if (mu <= 1.5f) {
// Not enought power in output
mu = 0.0001; // arbitrary
} else {
mu = 1.0f/mu;
//AA_AEC_WARNING("Mu =" << mu);
}
audio::algo::aec::updateFilter(&m_filter[0], _feedback, error*mu, m_filter.size());
return out;
}
void audio::algo::aec::Nlms::setFilterSize(size_t _sampleRate, std11::chrono::microseconds _time) {
setFilterSize((_sampleRate*_time.count())/1000000LL);
}
void audio::algo::aec::Nlms::setFilterSize(size_t _nbSample) {
m_filter.clear();
m_feedBack.clear();
m_filter.resize(_nbSample, 0.0f);
m_feedBack.resize(_nbSample, 0.0f);
}

View File

@ -1,45 +0,0 @@
/** @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 audio {
namespace algo {
namespace aec {
int32_t getLogId();
}
}
}
#define AA_AEC_BASE(info,data) TK_LOG_BASE(audio::algo::aec::getLogId(),info,data)
#define AA_AEC_CRITICAL(data) AA_AEC_BASE(1, data)
#define AA_AEC_ERROR(data) AA_AEC_BASE(2, data)
#define AA_AEC_WARNING(data) AA_AEC_BASE(3, data)
#ifdef DEBUG
#define AA_AEC_INFO(data) AA_AEC_BASE(4, data)
#define AA_AEC_DEBUG(data) AA_AEC_BASE(5, data)
#define AA_AEC_VERBOSE(data) AA_AEC_BASE(6, data)
#define AA_AEC_TODO(data) AA_AEC_BASE(4, "TODO : " << data)
#else
#define AA_AEC_INFO(data) do { } while(false)
#define AA_AEC_DEBUG(data) do { } while(false)
#define AA_AEC_VERBOSE(data) do { } while(false)
#define AA_AEC_TODO(data) do { } while(false)
#endif
#define AA_AEC_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)
#endif

215
audio/algo/river/Lms.cpp Normal file
View File

@ -0,0 +1,215 @@
/** @file
* @author Edouard DUPIN
* @author Fatima MARFOUQ
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/river/debug.h>
#include <audio/algo/river/Lms.h>
#include <audio/algo/river/updateFilter.h>
#include <audio/algo/river/convolution.h>
#define MAX_PROCESSING_BLOCK_SIZE (256)
namespace audio {
namespace algo {
namespace river {
class LmsPrivate {
private:
int8_t m_nbChannel;
float m_sampleRate;
enum audio::format m_format;
std::vector<std::vector<float> > m_filter; //!< Current filter
std::vector<std::vector<float> > m_feedBack; //!< Feedback history
float m_mu; //!< mu step size
public:
LmsPrivate() :
m_nbChannel(1),
m_sampleRate(48000),
m_format(audio::format_float),
m_filter(),
m_feedBack(),
m_mu(0.03f) {
setFilterSize(256);
}
~LmsPrivate(void) {
}
void reset(void) {
// simply reset filters.
setFilterSize(m_filter.size());
}
void init(int8_t _nbChannel, float _sampleRate, enum audio::format _format) {
m_nbChannel = _nbChannel;
m_sampleRate = _sampleRate;
m_format = _format;
setFilterSize(m_filter.size());
switch (m_format) {
case audio::format_int16:
case audio::format_float:
break;
default:
AA_RIVER_ERROR("Can not INIT LMS with unsupported format : " << m_format);
break;
}
}
void process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk) {
switch (m_format) {
case audio::format_int16:
processI16(reinterpret_cast<int16_t*>(_output),
reinterpret_cast<const int16_t*>(_inputFeedback),
reinterpret_cast<const int16_t*>(_input),
_nbChunk);
break;
case audio::format_float:
processFloat(reinterpret_cast<float*>(_output),
reinterpret_cast<const float*>(_inputFeedback),
reinterpret_cast<const float*>(_input),
_nbChunk);
break;
default:
AA_RIVER_ERROR("Can not Echo remove with unsupported format : " << m_format);
break;
}
}
bool processI16(int16_t* _output, const int16_t* _microphone, const int16_t* _feedback, int32_t _nbSample) {
bool ret = false;
int32_t processingSize = MAX_PROCESSING_BLOCK_SIZE*m_nbChannel;
// due to the fact we allocate the data in the stack:
int32_t nbCycle = _nbSample/processingSize;
if (_nbSample - int32_t(_nbSample/processingSize)*processingSize != 0 ) {
nbCycle++;
}
for (int32_t bbb=0; bbb<nbCycle; ++bbb) {
float output[processingSize];
float feedback[processingSize];
float microphone[processingSize];
int32_t offset = bbb*processingSize;
int32_t nbData = std::min(processingSize,
_nbSample - offset);
for (size_t iii=0; iii<nbData; ++iii) {
microphone[iii] = float(_microphone[offset+iii])/32767.0f;
feedback[iii] = float(_feedback[offset+iii])/32767.0f;
}
ret = processFloat(output, feedback, microphone, nbData);
for (size_t iii=0; iii<nbData; ++iii) {
_output[offset+iii] = int16_t(float(output[iii])*32767.0f);
}
}
return ret;
}
bool processFloat(float* _output, const float* _microphone, const float* _feedback, int32_t _nbSample) {
for (int8_t kkk=0; kkk<m_nbChannel; ++kkk) {
// add sample in the feedback history:
m_feedBack[kkk].resize(m_filter[kkk].size()+_nbSample, 0.0f);
memcpy(&m_feedBack[kkk][m_filter[kkk].size()], _feedback, _nbSample*sizeof(float));
for (int32_t iii=0; iii < _nbSample; iii++) {
_output[iii] = processValue(&m_feedBack[kkk][m_filter[kkk].size()+iii], _microphone[iii], m_filter[kkk]);
}
// remove old value:
m_feedBack[kkk].erase(m_feedBack[kkk].begin(), m_feedBack[kkk].begin() + (m_feedBack[kkk].size()-m_filter[kkk].size()) );
}
return true;
}
float processValue(float* _feedback, float _microphone, std::vector<float>& _filter) {
// Error calculation.
float convolutionValue = audio::algo::river::convolution(_feedback, &_filter[0], _filter.size());
float error = _microphone - convolutionValue;
float out = std::avg(-1.0f, error, 1.0f);
audio::algo::river::updateFilter(&_filter[0], _feedback, error*m_mu, _filter.size());
return out;
}
void setFilterTime(std11::chrono::microseconds _time) {
setFilterSize((m_sampleRate*_time.count())/1000000LL);
}
void setFilterSize(size_t _nbSample) {
m_filter.clear();
m_feedBack.clear();
m_filter.resize(m_nbChannel);
m_feedBack.resize(m_nbChannel);
for (int8_t kkk=0; kkk<m_nbChannel; ++kkk) {
m_filter[kkk].resize(_nbSample, 0.0f);
m_feedBack[kkk].resize(_nbSample, 0.0f);
}
}
void setMu(float _val) {
m_mu = _val;
}
};
}
}
}
audio::algo::river::Lms::Lms(void) {
}
audio::algo::river::Lms::~Lms(void) {
}
void audio::algo::river::Lms::reset(void) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->reset();
}
void audio::algo::river::Lms::init(int8_t _nbChannel, float _sampleRate, enum audio::format _format) {
if (m_private == nullptr) {
m_private = std::make_shared<audio::algo::river::LmsPrivate>();
}
m_private->init(_nbChannel, _sampleRate, _format);
}
std::vector<enum audio::format> audio::algo::river::Lms::getSupportedFormat() {
std::vector<enum audio::format> out = getNativeSupportedFormat();
out.push_back(audio::format_int16);
return out;
}
std::vector<enum audio::format> audio::algo::river::Lms::getNativeSupportedFormat() {
std::vector<enum audio::format> out;
out.push_back(audio::format_float);
return out;
}
void audio::algo::river::Lms::process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->process(_output, _input, _inputFeedback, _nbChunk);
}
void audio::algo::river::Lms::setFilterTime(std11::chrono::microseconds _time) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setFilterTime(_time);
}
void audio::algo::river::Lms::setFilterSize(size_t _nbSample) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setFilterSize(_nbSample);
}
void audio::algo::river::Lms::setMu(double _val) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setMu(_val);
}

View File

@ -5,14 +5,18 @@
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_AEC_LMS_H__
#define __AUDIO_ALGO_AEC_LMS_H__
#ifndef __AUDIO_ALGO_RIVER_LMS_H__
#define __AUDIO_ALGO_RIVER_LMS_H__
#include <etk/types.h>
#include <etk/chrono.h>
#include <audio/format.h>
#include <etk/memory.h>
namespace audio {
namespace algo {
namespace aec {
namespace river {
class LmsPrivate;
/**
* @brief Least Mean Square (LMS) algorithm "echo canceller"
* base on publication: http://www.arpapress.com/Volumes/Vol7Issue1/IJRRAS_7_1_05.pdf
@ -70,64 +74,60 @@ namespace audio {
/**
* @brief Constructor
*/
Lms(void);
Lms();
/**
* @brief Destructor
*/
~Lms(void);
virtual ~Lms();
public:
/**
* @brief Reset filter history and filter
* @brief Reset algorithm
*/
void reset(void);
virtual void reset();
/**
* @brief Process 16 bit LMS (input 16 bits)
* @param[in,out] _output output data of the LMS
* @param[in] _feedback Input feedback of the signal: x(n)
* @param[in] _microphone Input Microphone data: d(n)
* @brief Initialize the Algorithm
* @param[in] _nbChannel Number of channel in the stream.
* @param[in] _sampleRate Sample rate.
* @param[in] _format Input/output data format.
*/
bool process(int16_t* _output, const int16_t* _feedback, const int16_t* _microphone, int32_t _nbSample);
virtual void init(int8_t _nbChannel, float _sampleRate, enum audio::format _format = audio::format_float);
/**
* @brief Process float LMS
* @param[in,out] _output output data of the LMS
* @param[in] _feedback Input feedback of the signal: x(n)
* @param[in] _microphone Input Microphone data: d(n)
* @brief Get list of format suported in input.
* @return list of supported format
*/
bool process(float* _output, const float* _feedback, const float* _microphone, int32_t _nbSample);
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] _inputFeedback Input feedback data (all time MONO).
* @param[in] _nbChunk Number of chunk in the input buffer.
* @param[in] _nbChannel Number of channel in the stream.
*/
virtual void process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk);
protected:
/**
* @brief Process a single value of the LMS
* @param[in] _feedback Pointer on the feedback data (with history and at the n(th) position
* @param[in] _microphone Microphone single sample [-1..1]
* @return New output value [-1..1]
*/
float processValue(float* _feedback, float _microphone);
public:
std11::shared_ptr<LmsPrivate> m_private; // private data.
public: // parameter area:
/**
* @brief Set filter size with specifing the filter temporal size and his samplerate
* @param[in] _sampleRate Current sample rate to apply filter
* @param[in] _time Time of the filter size
*/
void setFilterSize(size_t _sampleRate, std11::chrono::microseconds _time);
virtual void setFilterTime(std11::chrono::microseconds _time);
/**
* @brief Set filter size in number of sample
* @param[in] _nbSample Sample size of the filter
*/
void setFilterSize(size_t _nbSample);
virtual void setFilterSize(size_t _nbSample);
/**
* @brief Set Mu value for basic LMS value
* @param[in] _val new mu value
*/
void setMu(float _val);
private:
std::vector<float> m_filter; //!< Current filter
std::vector<float> m_feedBack; //!< Feedback history
float m_mu; //!< mu step size
public:
// for debug only:
std::vector<float> getFilter() {
return m_filter;
}
virtual void setMu(double _val);
};
}
}

241
audio/algo/river/Nlms.cpp Normal file
View File

@ -0,0 +1,241 @@
/** @file
* @author Edouard DUPIN
* @author Fatima MARFOUQ
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/river/debug.h>
#include <audio/algo/river/Nlms.h>
#include <audio/algo/river/updateFilter.h>
#include <audio/algo/river/convolution.h>
#include <audio/algo/river/power.h>
/** @file
* @author Edouard DUPIN
* @author Fatima MARFOUQ
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/river/debug.h>
#include <audio/algo/river/Nlms.h>
#include <audio/algo/river/updateFilter.h>
#include <audio/algo/river/convolution.h>
#define MAX_PROCESSING_BLOCK_SIZE (256)
namespace audio {
namespace algo {
namespace river {
class NlmsPrivate {
private:
int8_t m_nbChannel;
float m_sampleRate;
enum audio::format m_format;
std::vector<std::vector<float> > m_filter; //!< Current filter
std::vector<std::vector<float> > m_feedBack; //!< Feedback history
float m_mu; //!< mu step size
public:
NlmsPrivate() :
m_nbChannel(1),
m_sampleRate(48000),
m_format(audio::format_float),
m_filter(),
m_feedBack(),
m_mu(0.03f) {
setFilterSize(256);
}
~NlmsPrivate(void) {
}
void reset(void) {
// simply reset filters.
setFilterSize(m_filter.size());
}
void init(int8_t _nbChannel, float _sampleRate, enum audio::format _format) {
m_nbChannel = _nbChannel;
m_sampleRate = _sampleRate;
m_format = _format;
setFilterSize(m_filter.size());
switch (m_format) {
case audio::format_int16:
case audio::format_float:
break;
default:
AA_RIVER_ERROR("Can not INIT LMS with unsupported format : " << m_format);
break;
}
}
void process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk) {
switch (m_format) {
case audio::format_int16:
processI16(reinterpret_cast<int16_t*>(_output),
reinterpret_cast<const int16_t*>(_inputFeedback),
reinterpret_cast<const int16_t*>(_input),
_nbChunk);
break;
case audio::format_float:
processFloat(reinterpret_cast<float*>(_output),
reinterpret_cast<const float*>(_inputFeedback),
reinterpret_cast<const float*>(_input),
_nbChunk);
break;
default:
AA_RIVER_ERROR("Can not Echo remove with unsupported format : " << m_format);
break;
}
}
bool processI16(int16_t* _output, const int16_t* _microphone, const int16_t* _feedback, int32_t _nbSample) {
bool ret = false;
int32_t processingSize = MAX_PROCESSING_BLOCK_SIZE*m_nbChannel;
// due to the fact we allocate the data in the stack:
int32_t nbCycle = _nbSample/processingSize;
if (_nbSample - int32_t(_nbSample/processingSize)*processingSize != 0 ) {
nbCycle++;
}
for (int32_t bbb=0; bbb<nbCycle; ++bbb) {
float output[processingSize];
float feedback[processingSize];
float microphone[processingSize];
int32_t offset = bbb*processingSize;
int32_t nbData = std::min(processingSize,
_nbSample - offset);
for (size_t iii=0; iii<nbData; ++iii) {
microphone[iii] = float(_microphone[offset+iii])/32767.0f;
feedback[iii] = float(_feedback[offset+iii])/32767.0f;
}
ret = processFloat(output, feedback, microphone, nbData);
for (size_t iii=0; iii<nbData; ++iii) {
_output[offset+iii] = int16_t(float(output[iii])*32767.0f);
}
}
return ret;
}
bool processFloat(float* _output, const float* _microphone, const float* _feedback, int32_t _nbSample) {
for (int8_t kkk=0; kkk<m_nbChannel; ++kkk) {
// add sample in the feedback history:
m_feedBack[kkk].resize(m_filter[kkk].size()+_nbSample, 0.0f);
memcpy(&m_feedBack[kkk][m_filter[kkk].size()], _feedback, _nbSample*sizeof(float));
for (int32_t iii=0; iii < _nbSample; iii++) {
_output[iii] = processValue(&m_feedBack[kkk][m_filter[kkk].size()+iii], _microphone[iii], m_filter[kkk]);
}
// remove old value:
m_feedBack[kkk].erase(m_feedBack[kkk].begin(), m_feedBack[kkk].begin() + (m_feedBack[kkk].size()-m_filter[kkk].size()) );
}
return true;
}
float processValue(float* _feedback, float _microphone, std::vector<float>& _filter) {
// Error calculation.
float convolutionValue = audio::algo::river::convolution(_feedback, &_filter[0], _filter.size());
float error = _microphone - convolutionValue;
float out = std::avg(-1.0f, error, 1.0f);
// calculate mu:
float mu = audio::algo::river::power(_feedback, _filter.size());
//mu = *_feedback * *_feedback;
//AA_RIVER_WARNING("Mu =" << mu);
if (mu <= 1.5f) {
// Not enought power in output
mu = 0.0001; // arbitrary
} else {
mu = 1.0f/mu;
//AA_RIVER_WARNING("Mu =" << mu);
}
audio::algo::river::updateFilter(&_filter[0], _feedback, error*m_mu, _filter.size());
return out;
}
void setFilterTime(std11::chrono::microseconds _time) {
setFilterSize((m_sampleRate*_time.count())/1000000LL);
}
void setFilterSize(size_t _nbSample) {
m_filter.clear();
m_feedBack.clear();
m_filter.resize(m_nbChannel);
m_feedBack.resize(m_nbChannel);
for (int8_t kkk=0; kkk<m_nbChannel; ++kkk) {
m_filter[kkk].resize(_nbSample, 0.0f);
m_feedBack[kkk].resize(_nbSample, 0.0f);
}
}
void setMu(float _val) {
m_mu = _val;
}
};
}
}
}
audio::algo::river::Nlms::Nlms(void) {
}
audio::algo::river::Nlms::~Nlms(void) {
}
void audio::algo::river::Nlms::reset(void) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->reset();
}
void audio::algo::river::Nlms::init(int8_t _nbChannel, float _sampleRate, enum audio::format _format) {
if (m_private == nullptr) {
m_private = std::make_shared<audio::algo::river::NlmsPrivate>();
}
m_private->init(_nbChannel, _sampleRate, _format);
}
std::vector<enum audio::format> audio::algo::river::Nlms::getSupportedFormat() {
std::vector<enum audio::format> out = getNativeSupportedFormat();
out.push_back(audio::format_int16);
return out;
}
std::vector<enum audio::format> audio::algo::river::Nlms::getNativeSupportedFormat() {
std::vector<enum audio::format> out;
out.push_back(audio::format_float);
return out;
}
void audio::algo::river::Nlms::process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->process(_output, _input, _inputFeedback, _nbChunk);
}
void audio::algo::river::Nlms::setFilterTime(std11::chrono::microseconds _time) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setFilterTime(_time);
}
void audio::algo::river::Nlms::setFilterSize(size_t _nbSample) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setFilterSize(_nbSample);
}
void audio::algo::river::Nlms::setMu(double _val) {
if (m_private == nullptr) {
AA_RIVER_ERROR("Algo is not initialized...");
}
m_private->setMu(_val);
}

View File

@ -5,14 +5,18 @@
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_AEC_NLMS_H__
#define __AUDIO_ALGO_AEC_NLMS_H__
#ifndef __AUDIO_ALGO_RIVER_NLMS_H__
#define __AUDIO_ALGO_RIVER_NLMS_H__
#include <etk/types.h>
#include <etk/chrono.h>
#include <audio/format.h>
#include <etk/memory.h>
namespace audio {
namespace algo {
namespace aec {
namespace river {
class NlmsPrivate;
/**
* @brief Least Mean Square (LMS) algorithm "echo canceller"
* base on publication: http://www.arpapress.com/Volumes/Vol7Issue1/IJRRAS_7_1_05.pdf
@ -70,58 +74,60 @@ namespace audio {
/**
* @brief Constructor
*/
Nlms(void);
Nlms();
/**
* @brief Destructor
*/
~Nlms(void);
virtual ~Nlms();
public:
/**
* @brief Reset filter history and filter
* @brief Reset algorithm
*/
void reset(void);
virtual void reset();
/**
* @brief Process 16 bit LMS (input 16 bits)
* @param[in,out] _output output data of the LMS
* @param[in] _feedback Input feedback of the signal: x(n)
* @param[in] _microphone Input Microphone data: d(n)
* @brief Initialize the Algorithm
* @param[in] _nbChannel Number of channel in the stream.
* @param[in] _sampleRate Sample rate.
* @param[in] _format Input/output data format.
*/
bool process(int16_t* _output, const int16_t* _feedback, const int16_t* _microphone, int32_t _nbSample);
virtual void init(int8_t _nbChannel, float _sampleRate, enum audio::format _format = audio::format_float);
/**
* @brief Process float LMS
* @param[in,out] _output output data of the LMS
* @param[in] _feedback Input feedback of the signal: x(n)
* @param[in] _microphone Input Microphone data: d(n)
* @brief Get list of format suported in input.
* @return list of supported format
*/
bool process(float* _output, const float* _feedback, const float* _microphone, int32_t _nbSample);
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] _inputFeedback Input feedback data (all time MONO).
* @param[in] _nbChunk Number of chunk in the input buffer.
* @param[in] _nbChannel Number of channel in the stream.
*/
virtual void process(void* _output, const void* _input, const void* _inputFeedback, size_t _nbChunk);
protected:
/**
* @brief Process a single value of the LMS
* @param[in] _feedback Pointer on the feedback data (with history and at the n(th) position
* @param[in] _microphone Microphone single sample [-1..1]
* @return New output value [-1..1]
*/
float processValue(float* _feedback, float _microphone);
public:
std11::shared_ptr<NlmsPrivate> m_private; // private data.
public: // parameter area:
/**
* @brief Set filter size with specifing the filter temporal size and his samplerate
* @param[in] _sampleRate Current sample rate to apply filter
* @param[in] _time Time of the filter size
*/
void setFilterSize(size_t _sampleRate, std11::chrono::microseconds _time);
virtual void setFilterTime(std11::chrono::microseconds _time);
/**
* @brief Set filter size in number of sample
* @param[in] _nbSample Sample size of the filter
*/
void setFilterSize(size_t _nbSample);
private:
std::vector<float> m_filter; //!< Current filter
std::vector<float> m_feedBack; //!< Feedback history
public:
// for debug only:
std::vector<float> getFilter() {
return m_filter;
}
virtual void setFilterSize(size_t _nbSample);
/**
* @brief Set Mu value for basic LMS value
* @param[in] _val new mu value
*/
virtual void setMu(double _val);
};
}
}

View File

@ -5,9 +5,9 @@
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/aec/convolution.h>
#include <audio/algo/river/convolution.h>
float audio::algo::aec::convolution(float* _dataMinus, float* _dataPlus, size_t _count) {
float audio::algo::river::convolution(float* _dataMinus, float* _dataPlus, size_t _count) {
float out = 0.0f;
for (size_t iii = 0; iii < _count; ++iii) {
out += *_dataMinus-- * *_dataPlus++;

View File

@ -5,14 +5,14 @@
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_AEC_CONVOLUTION_H__
#define __AUDIO_ALGO_AEC_CONVOLUTION_H__
#ifndef __AUDIO_ALGO_RIVER_CONVOLUTION_H__
#define __AUDIO_ALGO_RIVER_CONVOLUTION_H__
#include <etk/types.h>
namespace audio {
namespace algo {
namespace aec {
namespace river {
float convolution(float* _dataMinus, float* _dataPlus, size_t _count);
}
}

View File

@ -7,8 +7,8 @@
#include "debug.h"
int32_t audio::algo::aec::getLogId() {
static int32_t g_val = etk::log::registerInstance("audio-algo-aec");
int32_t audio::algo::river::getLogId() {
static int32_t g_val = etk::log::registerInstance("audio-algo-river");
return g_val;
}

45
audio/algo/river/debug.h Normal file
View File

@ -0,0 +1,45 @@
/** @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 audio {
namespace algo {
namespace river {
int32_t getLogId();
}
}
}
#define AA_RIVER_BASE(info,data) TK_LOG_BASE(audio::algo::river::getLogId(),info,data)
#define AA_RIVER_CRITICAL(data) AA_RIVER_BASE(1, data)
#define AA_RIVER_ERROR(data) AA_RIVER_BASE(2, data)
#define AA_RIVER_WARNING(data) AA_RIVER_BASE(3, data)
#ifdef DEBUG
#define AA_RIVER_INFO(data) AA_RIVER_BASE(4, data)
#define AA_RIVER_DEBUG(data) AA_RIVER_BASE(5, data)
#define AA_RIVER_VERBOSE(data) AA_RIVER_BASE(6, data)
#define AA_RIVER_TODO(data) AA_RIVER_BASE(4, "TODO : " << data)
#else
#define AA_RIVER_INFO(data) do { } while(false)
#define AA_RIVER_DEBUG(data) do { } while(false)
#define AA_RIVER_VERBOSE(data) do { } while(false)
#define AA_RIVER_TODO(data) do { } while(false)
#endif
#define AA_RIVER_ASSERT(cond,data) \
do { \
if (!(cond)) { \
APPL_CRITICAL(data); \
assert(!#cond); \
} \
} while (0)
#endif

View File

@ -5,10 +5,10 @@
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/aec/power.h>
#include <audio/algo/river/power.h>
float audio::algo::aec::power(float* _data, int32_t _count) {
float audio::algo::river::power(float* _data, int32_t _count) {
float out = 0;
for (size_t iii = 0; iii < _count; ++iii) {
out += *_data * *_data;

View File

@ -5,14 +5,14 @@
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_AEC_POWER_H__
#define __AUDIO_ALGO_AEC_POWER_H__
#ifndef __AUDIO_ALGO_RIVER_POWER_H__
#define __AUDIO_ALGO_RIVER_POWER_H__
#include <etk/types.h>
namespace audio {
namespace algo {
namespace aec {
namespace river {
float power(float* _data, int32_t _count);
}
}

View File

@ -5,10 +5,10 @@
* @license APACHE v2.0 (see license file)
*/
#include <audio/algo/aec/updateFilter.h>
#include <audio/algo/river/updateFilter.h>
void audio::algo::aec::updateFilter(float* _filter, float* _data, float _value, int32_t _count) {
void audio::algo::river::updateFilter(float* _filter, float* _data, float _value, int32_t _count) {
for (size_t iii = 0; iii < _count; ++iii) {
*(_filter++) += *_data-- * _value;
}

View File

@ -5,14 +5,14 @@
* @license APACHE v2.0 (see license file)
*/
#ifndef __AUDIO_ALGO_AEC_UPDATE_FILTER_H__
#define __AUDIO_ALGO_AEC_UPDATE_FILTER_H__
#ifndef __AUDIO_ALGO_RIVER_UPDATE_FILTER_H__
#define __AUDIO_ALGO_RIVER_UPDATE_FILTER_H__
#include <etk/types.h>
namespace audio {
namespace algo {
namespace aec {
namespace river {
void updateFilter(float* _filter, float* _data, float _value, int32_t _count);
}
}

View File

@ -1,32 +0,0 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "audio_algo_aec : AEC basic algo"
def create(target):
myModule = module.Module(__file__, 'audio_algo_aec', 'LIBRARY')
myModule.add_src_file([
'audio/algo/aec/debug.cpp',
'audio/algo/aec/convolution.cpp',
'audio/algo/aec/power.cpp',
'audio/algo/aec/updateFilter.cpp',
'audio/algo/aec/Lms.cpp',
'audio/algo/aec/Nlms.cpp'
])
myModule.add_module_depend(['etk'])
myModule.add_export_path(tools.get_current_path(__file__))
# return module
return myModule

32
lutin_audio_algo_river.py Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/python
import lutinModule as module
import lutinTools as tools
import lutinDebug as debug
def get_desc():
return "audio_algo_river : AEC basic algo"
def create(target):
myModule = module.Module(__file__, 'audio_algo_river', 'LIBRARY')
myModule.add_src_file([
'audio/algo/river/debug.cpp',
'audio/algo/river/convolution.cpp',
'audio/algo/river/power.cpp',
'audio/algo/river/updateFilter.cpp',
'audio/algo/river/Lms.cpp',
'audio/algo/river/Nlms.cpp'
])
myModule.add_module_depend(['etk', 'audio'])
myModule.add_export_path(tools.get_current_path(__file__))
# return module
return myModule

View File

@ -8,12 +8,12 @@ def get_desc():
def create(target):
myModule = module.Module(__file__, 'audio_algo_aec_test', 'BINARY')
myModule = module.Module(__file__, 'audio_algo_river_test', 'BINARY')
myModule.add_src_file([
'test/main.cpp',
'test/debug.cpp'
])
myModule.add_module_depend(['audio_algo_aec'])
myModule.add_module_depend(['audio_algo_river'])
return myModule

View File

@ -6,8 +6,8 @@
#include <test/debug.h>
#include <etk/etk.h>
#include <audio/algo/aec/Lms.h>
#include <audio/algo/aec/Nlms.h>
#include <audio/algo/river/Lms.h>
#include <audio/algo/river/Nlms.h>
#include <etk/os/FSNode.h>
#include <etk/chrono.h>
#include <etk/thread/tools.h>
@ -17,6 +17,51 @@
#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;
}
};
int main(int _argc, const char** _argv) {
// the only one init for etk:
etk::init(_argc, _argv);
@ -67,6 +112,9 @@ int main(int _argc, const char** _argv) {
APPL_ERROR("Can not Process missing parameters...");
exit(-1);
}
Performance perfo;
APPL_INFO("Read FeedBack:");
std::vector<int16_t> fbData = etk::FSNodeReadAllDataType<int16_t>(fbName);
APPL_INFO(" " << fbData.size() << " samples");
@ -78,17 +126,12 @@ int main(int _argc, const char** _argv) {
output.resize(std::min(fbData.size(), micData.size()), 0);
// process in chunk of 256 samples
int32_t blockSize = 256;
// end filter :
std::vector<float> filter;
std11::chrono::nanoseconds totalTimeProcessing(0);
std11::chrono::nanoseconds minProcessing(99999999999999LL);
std11::chrono::nanoseconds maxProcessing(0);
int32_t totalIteration = 0;
if (nlms == false) {
APPL_PRINT("***********************");
APPL_PRINT("** LMS **");
APPL_PRINT("***********************");
audio::algo::aec::Lms algo;
audio::algo::river::Lms algo;
algo.init(1, sampleRate, audio::format_float);
if (filterSize != 0) {
algo.setFilterSize(filterSize);
}
@ -103,24 +146,19 @@ int main(int _argc, const char** _argv) {
} else {
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
}
std11::chrono::steady_clock::time_point timeStart = std11::chrono::steady_clock::now();
perfo.tic();
algo.process(&output[iii*blockSize], &fbData[iii*blockSize], &micData[iii*blockSize], blockSize);
if (perf == true) {
std11::chrono::steady_clock::time_point timeEnd = std11::chrono::steady_clock::now();
std11::chrono::nanoseconds time = timeEnd - timeStart;
minProcessing = std::min(minProcessing, time);
maxProcessing = std::max(maxProcessing, time);
totalTimeProcessing += time;
totalIteration++;
usleep(10000);
perfo.toc();
usleep(1000);
}
}
filter = algo.getFilter();
} else {
APPL_PRINT("***********************");
APPL_PRINT("** NLMS (power) **");
APPL_PRINT("***********************");
audio::algo::aec::Nlms algo;
audio::algo::river::Nlms algo;
algo.init(1, sampleRate, audio::format_float);
if (filterSize != 0) {
algo.setFilterSize(filterSize);
}
@ -132,38 +170,27 @@ int main(int _argc, const char** _argv) {
} else {
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
}
perfo.tic();
algo.process(&output[iii*blockSize], &fbData[iii*blockSize], &micData[iii*blockSize], blockSize);
if (perf == true) {
perfo.toc();
usleep(1000);
}
}
filter = algo.getFilter();
}
APPL_PRINT("Process done");
if (perf == true) {
APPL_PRINT("Performance Result: ");
APPL_PRINT(" blockSize=" << blockSize << " sample");
APPL_PRINT(" total data time=" << int64_t(totalIteration)*int64_t(blockSize)*1000000000LL/int64_t(sampleRate) << " ns");
APPL_PRINT(" min=" << minProcessing.count() << " ns");
APPL_PRINT(" max=" << maxProcessing.count() << " ns");
APPL_PRINT(" avg=" << totalTimeProcessing.count()/totalIteration << " ns");
APPL_PRINT(" " << sampleRate << " Hz");
APPL_PRINT(" min=" << (float((minProcessing.count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" max=" << (float((maxProcessing.count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" 48000 Hz");
sampleRate = 48000;
APPL_PRINT(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" 32000 Hz");
sampleRate = 32000;
APPL_PRINT(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" 16000 Hz");
sampleRate = 16000;
APPL_PRINT(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_PRINT(" 8000 Hz");
sampleRate = 8000;
APPL_PRINT(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
APPL_INFO(" blockSize=" << blockSize << " 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(blockSize))/1000000000.0)*100.0;
APPL_INFO(" min < avg < max= " << (float((perfo.getMinProcessing().count()*sampleRate)/double(blockSize))/1000000000.0)*100.0 << "% < "
<< avg << "% < "
<< (float((perfo.getMaxProcessing().count()*sampleRate)/double(blockSize))/1000000000.0)*100.0 << "%");
APPL_PRINT("float : " << sampleRate << " : " << avg << "%");
}
etk::FSNodeWriteAllDataType<int16_t>("output.raw", output);
etk::FSNodeWriteAllDataType<float>("filter.raw", filter);
}