[DEV] update algo interface and add test
This commit is contained in:
parent
e912b34365
commit
715957ac75
@ -29,14 +29,14 @@
|
|||||||
audio::algo::chunkware::Compressor::Compressor() :
|
audio::algo::chunkware::Compressor::Compressor() :
|
||||||
AttRelEnvelope(10.0, 100.0),
|
AttRelEnvelope(10.0, 100.0),
|
||||||
m_isConfigured(false),
|
m_isConfigured(false),
|
||||||
m_threshdB(0.0),
|
m_thresholddB(0.0),
|
||||||
m_ratio(1.0),
|
m_ratio(1.0),
|
||||||
m_overThresholdEnvelopeDB(DC_OFFSET) {
|
m_overThresholdEnvelopeDB(DC_OFFSET) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Compressor::setThreshold(double _dB) {
|
void audio::algo::chunkware::Compressor::setThreshold(double _dB) {
|
||||||
m_threshdB = _dB;
|
m_thresholddB = _dB;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Compressor::setRatio(double _ratio) {
|
void audio::algo::chunkware::Compressor::setRatio(double _ratio) {
|
||||||
@ -115,7 +115,7 @@ void audio::algo::chunkware::Compressor::processDouble(double* _out, const doubl
|
|||||||
_keyLinked += DC_OFFSET; // add DC offset to avoid log(0)
|
_keyLinked += DC_OFFSET; // add DC offset to avoid log(0)
|
||||||
double keydB = lin2dB(_keyLinked); // convert linear -> dB
|
double keydB = lin2dB(_keyLinked); // convert linear -> dB
|
||||||
// threshold
|
// threshold
|
||||||
double overdB = keydB - m_threshdB; // delta over threshold
|
double overdB = keydB - m_thresholddB; // delta over threshold
|
||||||
if (overdB < 0.0) {
|
if (overdB < 0.0) {
|
||||||
overdB = 0.0;
|
overdB = 0.0;
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ namespace audio {
|
|||||||
* @brief Get list of format suported in input.
|
* @brief Get list of format suported in input.
|
||||||
* @return list of supported format
|
* @return list of supported format
|
||||||
*/
|
*/
|
||||||
std::vector<enum audio::format> getSupportedFormat();
|
virtual std::vector<enum audio::format> getSupportedFormat();
|
||||||
/**
|
/**
|
||||||
* @brief Get list of algorithm format suported. No format convertion.
|
* @brief Get list of algorithm format suported. No format convertion.
|
||||||
* @return list of supported format
|
* @return list of supported format
|
||||||
*/
|
*/
|
||||||
std::vector<enum audio::format> getNativeSupportedFormat();
|
virtual std::vector<enum audio::format> getNativeSupportedFormat();
|
||||||
/**
|
/**
|
||||||
* @brief Main input algo process.
|
* @brief Main input algo process.
|
||||||
* @param[in,out] _output Output data.
|
* @param[in,out] _output Output data.
|
||||||
@ -64,24 +64,24 @@ namespace audio {
|
|||||||
* @param[in] _nbChannel Number of channel in the stream.
|
* @param[in] _nbChannel Number of channel in the stream.
|
||||||
* @param[in] _format Input data format.
|
* @param[in] _format Input data format.
|
||||||
*/
|
*/
|
||||||
void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
virtual void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
||||||
protected:
|
protected:
|
||||||
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel, double _value);
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel, double _value);
|
||||||
/*
|
/*
|
||||||
void process(float* _out, const float* _in, int8_t _nbChannel);
|
virtual void processFloat(float* _out, const float* _in, int8_t _nbChannel);
|
||||||
void process(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
virtual void process16_16(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
||||||
void process(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
virtual void process16_32(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
virtual void process24_32(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
virtual void process32_32(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
virtual void process32_64(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||||
*/
|
*/
|
||||||
protected:
|
protected:
|
||||||
double m_threshdB;//!< threshold (dB)
|
double m_thresholddB;//!< threshold (dB)
|
||||||
public:
|
public:
|
||||||
virtual void setThreshold(double _dB);
|
virtual void setThreshold(double _dB);
|
||||||
virtual double getThreshold() const {
|
virtual double getThreshold() const {
|
||||||
return m_threshdB;
|
return m_thresholddB;
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
double m_ratio; //!< ratio (compression: < 1 ; expansion: > 1)
|
double m_ratio; //!< ratio (compression: < 1 ; expansion: > 1)
|
||||||
|
@ -45,7 +45,7 @@ namespace audio {
|
|||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
virtual void init();
|
virtual void init();
|
||||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||||
protected:
|
protected:
|
||||||
audio::algo::chunkware::EnvelopeDetector m_averager; //!< averager
|
audio::algo::chunkware::EnvelopeDetector m_averager; //!< averager
|
||||||
double m_averageSuares; //!< average of squares
|
double m_averageSuares; //!< average of squares
|
||||||
|
@ -25,45 +25,97 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <audio/algo/chunkware/Gate.h>
|
#include <audio/algo/chunkware/Gate.h>
|
||||||
|
#include <audio/algo/chunkware/debug.h>
|
||||||
|
|
||||||
audio::algo::chunkware::Gate::Gate() :
|
audio::algo::chunkware::Gate::Gate() :
|
||||||
AttRelEnvelope(1.0, 100.0),
|
AttRelEnvelope(1.0, 100.0),
|
||||||
m_threshdB(0.0),
|
m_thresholddB(0.0),
|
||||||
m_threshold(1.0),
|
m_threshold(1.0),
|
||||||
m_overThresholdEnvelope(DC_OFFSET) {
|
m_overThresholdEnvelope(DC_OFFSET) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Gate::setThresh(double _dB) {
|
void audio::algo::chunkware::Gate::setThreshold(double _dB) {
|
||||||
m_threshdB = _dB;
|
m_thresholddB = _dB;
|
||||||
m_threshold = dB2lin(_dB);
|
m_threshold = dB2lin(_dB);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Gate::initRuntime() {
|
|
||||||
|
void audio::algo::chunkware::Gate::init() {
|
||||||
m_overThresholdEnvelope = DC_OFFSET;
|
m_overThresholdEnvelope = DC_OFFSET;
|
||||||
|
m_isConfigured = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<enum audio::format> audio::algo::chunkware::Gate::getSupportedFormat() {
|
||||||
void audio::algo::chunkware::Gate::process(double& _in1, double& _in2) {
|
std::vector<enum audio::format> out = getNativeSupportedFormat();
|
||||||
// create sidechain
|
out.push_back(audio::format_int16);
|
||||||
double rect1 = fabs(_in1); // rectify input
|
return out;
|
||||||
double rect2 = fabs(_in2);
|
|
||||||
/* if desired, one could use another EnvelopeDetector to smooth
|
|
||||||
* the rectified signal.
|
|
||||||
*/
|
|
||||||
double link = std::max(rect1, rect2); // link channels with greater of 2
|
|
||||||
process(_in1, _in2, link); // rest of process
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Gate::process(double& _in1, double& _in2, double _keyLinked) {
|
std::vector<enum audio::format> audio::algo::chunkware::Gate::getNativeSupportedFormat() {
|
||||||
_keyLinked = fabs(_keyLinked); // rectify (just in case)
|
std::vector<enum audio::format> out;
|
||||||
|
out.push_back(audio::format_double);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio::algo::chunkware::Gate::process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel, enum audio::format _format) {
|
||||||
|
// TODO : Check init ...
|
||||||
|
if (_nbChannel != 1) {
|
||||||
|
AA_CHUNK_ERROR("Can not compress with Other than single channel: " << _nbChannel);
|
||||||
|
}
|
||||||
|
switch (_format) {
|
||||||
|
case audio::format_int16:
|
||||||
|
{
|
||||||
|
const int16_t* input = reinterpret_cast<const int16_t*>(_input);
|
||||||
|
int16_t* output = reinterpret_cast<int16_t*>(_output);
|
||||||
|
double vals[_nbChannel];
|
||||||
|
for (size_t iii=0; iii<_nbChunk ; ++iii) {
|
||||||
|
for (int8_t kkk=0; kkk<_nbChannel ; ++kkk) {
|
||||||
|
vals[kkk] = double(input[iii*_nbChannel+kkk]) / 32768.0;
|
||||||
|
}
|
||||||
|
processDouble(vals, vals, _nbChannel);
|
||||||
|
for (int8_t kkk=0; kkk<_nbChannel ; ++kkk) {
|
||||||
|
vals[kkk] *= 32768.0;
|
||||||
|
output[iii*_nbChannel+kkk] = int16_t(std::avg(-32768.0, vals[kkk], 32767.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case audio::format_double:
|
||||||
|
{
|
||||||
|
const double* input = reinterpret_cast<const double*>(_input);
|
||||||
|
double* output = reinterpret_cast<double*>(_output);
|
||||||
|
for (size_t iii=0; iii<_nbChunk ; ++iii) {
|
||||||
|
processDouble(&output[iii*_nbChannel], &input[iii*_nbChannel], _nbChannel);
|
||||||
|
//AA_CHUNK_INFO(" in=" << input[iii] << " => " << output[iii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
AA_CHUNK_ERROR("Can not compress with unsupported format : " << _format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio::algo::chunkware::Gate::processDouble(double* _out, const double* _in, int8_t _nbChannel) {
|
||||||
|
double keyLink = 0;
|
||||||
|
// get greater value;
|
||||||
|
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||||
|
double absValue = std::abs(_in[iii]);
|
||||||
|
keyLink = std::max(keyLink, absValue);
|
||||||
|
}
|
||||||
|
processDouble(_out, _in, _nbChannel, keyLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio::algo::chunkware::Gate::processDouble(double* _out, const double* _in, int8_t _nbChannel, double _keyLinked) {
|
||||||
// threshold
|
// threshold
|
||||||
// key over threshold (0.0 or 1.0)
|
// key over threshold (0.0 or 1.0)
|
||||||
double over = double(_keyLinked > m_threshold);
|
double over = double(_keyLinked > m_threshold);
|
||||||
// attack/release
|
// attack/release
|
||||||
over += DC_OFFSET; // add DC offset to avoid denormal
|
over += DC_OFFSET; // add DC offset to avoid denormal
|
||||||
audio::algo::chunkware::AttRelEnvelope::run(over, m_overThresholdEnvelope); // run attack/release
|
audio::algo::chunkware::AttRelEnvelope::run(over, m_overThresholdEnvelope); // run attack/release
|
||||||
over = m_overThresholdEnvelope - DC_OFFSET; // subtract DC offset
|
over = m_overThresholdEnvelope - DC_OFFSET; // subtract DC offset
|
||||||
/* REGARDING THE DC OFFSET: In this case, since the offset is added before
|
/* REGARDING THE DC OFFSET: In this case, since the offset is added before
|
||||||
* the attack/release processes, the envelope will never fall below the offset,
|
* the attack/release processes, the envelope will never fall below the offset,
|
||||||
* thereby avoiding denormals. However, to prevent the offset from causing
|
* thereby avoiding denormals. However, to prevent the offset from causing
|
||||||
@ -71,6 +123,7 @@ void audio::algo::chunkware::Gate::process(double& _in1, double& _in2, double _k
|
|||||||
* a minimum value of 0dB.
|
* a minimum value of 0dB.
|
||||||
*/
|
*/
|
||||||
// output gain
|
// output gain
|
||||||
_in1 *= over; // apply gain reduction to input
|
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||||
_in2 *= over;
|
_out[iii] = _in[iii] * over;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,29 +30,61 @@
|
|||||||
#include <etk/types.h>
|
#include <etk/types.h>
|
||||||
#include <audio/algo/chunkware/AttRelEnvelope.h>
|
#include <audio/algo/chunkware/AttRelEnvelope.h>
|
||||||
#include <audio/algo/chunkware/Gain.h>
|
#include <audio/algo/chunkware/Gain.h>
|
||||||
|
#include <audio/format.h>
|
||||||
|
|
||||||
namespace audio {
|
namespace audio {
|
||||||
namespace algo {
|
namespace algo {
|
||||||
namespace chunkware {
|
namespace chunkware {
|
||||||
class Gate : public AttRelEnvelope {
|
class Gate : public AttRelEnvelope {
|
||||||
|
protected:
|
||||||
|
bool m_isConfigured;
|
||||||
public:
|
public:
|
||||||
Gate();
|
Gate();
|
||||||
virtual ~Gate() {}
|
virtual ~Gate() {}
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Initialize the Algorithm
|
||||||
|
*/
|
||||||
|
virtual void init();
|
||||||
|
/**
|
||||||
|
* @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.
|
||||||
|
* @param[in] _nbChannel Number of channel in the stream.
|
||||||
|
* @param[in] _format Input data format.
|
||||||
|
*/
|
||||||
|
virtual void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
||||||
|
protected:
|
||||||
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||||
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel, double _value);
|
||||||
|
/*
|
||||||
|
virtual void processFloat(float* _out, const float* _in, int8_t _nbChannel);
|
||||||
|
virtual void process16_16(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
||||||
|
virtual void process16_32(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
||||||
|
virtual void process24_32(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
||||||
|
virtual void process32_32(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
||||||
|
virtual void process32_64(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||||
|
*/
|
||||||
|
public:
|
||||||
// parameters
|
// parameters
|
||||||
virtual void setThresh(double _dB);
|
virtual void setThreshold(double _dB);
|
||||||
virtual double getThresh() const {
|
virtual double getThreshold() const {
|
||||||
return m_threshdB;
|
return m_thresholddB;
|
||||||
}
|
}
|
||||||
// runtime
|
|
||||||
// call before runtime (in resume())
|
|
||||||
virtual void initRuntime();
|
|
||||||
// gate runtime process
|
|
||||||
void process(double& _in1, double& _in2);
|
|
||||||
// with stereo-linked key in
|
|
||||||
void process(double& _in1, double& _in2, double _keyLinked);
|
|
||||||
private:
|
private:
|
||||||
// transfer function
|
// transfer function
|
||||||
double m_threshdB; //!< threshold (dB)
|
double m_thresholddB; //!< threshold (dB)
|
||||||
double m_threshold; //!< threshold (linear)
|
double m_threshold; //!< threshold (linear)
|
||||||
// runtime variables
|
// runtime variables
|
||||||
double m_overThresholdEnvelope; //!< over-threshold envelope (linear)
|
double m_overThresholdEnvelope; //!< over-threshold envelope (linear)
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
audio::algo::chunkware::GateRms::GateRms() :
|
audio::algo::chunkware::GateRms::GateRms() :
|
||||||
m_averager(5.0),
|
m_averager(5.0),
|
||||||
m_averageSuares(DC_OFFSET) {
|
m_averageSquares(DC_OFFSET) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,24 +41,26 @@ void audio::algo::chunkware::GateRms::setWindow(double _ms) {
|
|||||||
m_averager.setTc(_ms);
|
m_averager.setTc(_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::GateRms::initRuntime() {
|
void audio::algo::chunkware::GateRms::init() {
|
||||||
audio::algo::chunkware::Gate::initRuntime();
|
m_averageSquares = DC_OFFSET;
|
||||||
m_averageSuares = DC_OFFSET;
|
audio::algo::chunkware::Gate::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::GateRms::process(double& _in1, double& _in2) {
|
void audio::algo::chunkware::GateRms::processDouble(double* _out, const double* _in, int8_t _nbChannel) {
|
||||||
|
double sum = 0.0;
|
||||||
// create sidechain
|
// create sidechain
|
||||||
double inSq1 = _in1 * _in1; // square input
|
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||||
double inSq2 = _in2 * _in2;
|
sum += _in[iii] * _in[iii]; // square input
|
||||||
double sum = inSq1 + inSq2; // power summing
|
}
|
||||||
sum += DC_OFFSET; // DC offset, to prevent denormal
|
sum += DC_OFFSET; // DC offset, to prevent denormal
|
||||||
m_averager.run(sum, m_averageSuares); // average of squares
|
m_averager.run(sum, m_averageSquares); // average of squares
|
||||||
double rms = sqrt(m_averageSuares); // rms (sort of ...)
|
double rms = sqrt(m_averageSquares); // rms (sort of ...)
|
||||||
/* REGARDING THE RMS AVERAGER: Ok, so this isn't a REAL RMS
|
/* REGARDING THE RMS AVERAGER: Ok, so this isn't a REAL RMS
|
||||||
* calculation. A true RMS is an FIR moving average. This
|
* calculation. A true RMS is an FIR moving average. This
|
||||||
* approximation is a 1-pole IIR. Nonetheless, in practice,
|
* approximation is a 1-pole IIR. Nonetheless, in practice,
|
||||||
* and in the interest of simplicity, this method will suffice,
|
* and in the interest of simplicity, this method will suffice,
|
||||||
* giving comparable results.
|
* giving comparable results.
|
||||||
*/
|
*/
|
||||||
audio::algo::chunkware::Gate::process(_in1, _in2, rms); // rest of process
|
// rest of process
|
||||||
|
audio::algo::chunkware::Gate::processDouble(_out, _in, _nbChannel, rms);
|
||||||
}
|
}
|
||||||
|
@ -42,14 +42,13 @@ namespace audio {
|
|||||||
virtual double getWindow() const {
|
virtual double getWindow() const {
|
||||||
return m_averager.getTc();
|
return m_averager.getTc();
|
||||||
}
|
}
|
||||||
// runtime process
|
public:
|
||||||
// call before runtime (in resume())
|
virtual void init();
|
||||||
virtual void initRuntime();
|
protected:
|
||||||
// gate runtime process
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||||
void process(double& _in1, double& _in2);
|
|
||||||
private:
|
private:
|
||||||
audio::algo::chunkware::EnvelopeDetector m_averager; //!< averager
|
audio::algo::chunkware::EnvelopeDetector m_averager; //!< averager
|
||||||
double m_averageSuares; //!< average of squares
|
double m_averageSquares; //!< average of squares
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
audio::algo::chunkware::Limiter::Limiter() :
|
audio::algo::chunkware::Limiter::Limiter() :
|
||||||
m_isConfigured(false),
|
m_isConfigured(false),
|
||||||
m_threshdB(0.0),
|
m_thresholddB(0.0),
|
||||||
m_threshold(1.0),
|
m_threshold(1.0),
|
||||||
m_peakHold(0),
|
m_peakHold(0),
|
||||||
m_peakTimer(0),
|
m_peakTimer(0),
|
||||||
@ -45,7 +45,7 @@ audio::algo::chunkware::Limiter::Limiter() :
|
|||||||
}
|
}
|
||||||
|
|
||||||
void audio::algo::chunkware::Limiter::setThreshold(double _dB) {
|
void audio::algo::chunkware::Limiter::setThreshold(double _dB) {
|
||||||
m_threshdB = _dB;
|
m_thresholddB = _dB;
|
||||||
m_threshold = dB2lin(_dB);
|
m_threshold = dB2lin(_dB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,12 +66,12 @@ namespace audio {
|
|||||||
* @brief Get list of format suported in input.
|
* @brief Get list of format suported in input.
|
||||||
* @return list of supported format
|
* @return list of supported format
|
||||||
*/
|
*/
|
||||||
std::vector<enum audio::format> getSupportedFormat();
|
virtual std::vector<enum audio::format> getSupportedFormat();
|
||||||
/**
|
/**
|
||||||
* @brief Get list of algorithm format suported. No format convertion.
|
* @brief Get list of algorithm format suported. No format convertion.
|
||||||
* @return list of supported format
|
* @return list of supported format
|
||||||
*/
|
*/
|
||||||
std::vector<enum audio::format> getNativeSupportedFormat();
|
virtual std::vector<enum audio::format> getNativeSupportedFormat();
|
||||||
/**
|
/**
|
||||||
* @brief Main input algo process.
|
* @brief Main input algo process.
|
||||||
* @param[in,out] _output Output data.
|
* @param[in,out] _output Output data.
|
||||||
@ -80,23 +80,23 @@ namespace audio {
|
|||||||
* @param[in] _nbChannel Number of channel in the stream.
|
* @param[in] _nbChannel Number of channel in the stream.
|
||||||
* @param[in] _format Input data format.
|
* @param[in] _format Input data format.
|
||||||
*/
|
*/
|
||||||
void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
virtual void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
||||||
protected:
|
protected:
|
||||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
virtual void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||||
/*
|
/*
|
||||||
void process(float* _out, const float* _in, int8_t _nbChannel);
|
virtual void processFloat(float* _out, const float* _in, int8_t _nbChannel);
|
||||||
void process(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
virtual void process16_16(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
||||||
void process(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
virtual void process16_32(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
virtual void process24_32(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
virtual void process32_32(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
||||||
void process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
virtual void process32_64(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||||
*/
|
*/
|
||||||
protected:
|
protected:
|
||||||
double m_threshdB; //!< threshold (dB)
|
double m_thresholddB; //!< threshold (dB)
|
||||||
public:
|
public:
|
||||||
virtual void setThreshold(double _dB);
|
virtual void setThreshold(double _dB);
|
||||||
virtual double getThreshold() const {
|
virtual double getThreshold() const {
|
||||||
return m_threshdB;
|
return m_thresholddB;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
219
test/main.cpp
219
test/main.cpp
@ -8,6 +8,7 @@
|
|||||||
#include <etk/etk.h>
|
#include <etk/etk.h>
|
||||||
#include <audio/algo/chunkware/Compressor.h>
|
#include <audio/algo/chunkware/Compressor.h>
|
||||||
#include <audio/algo/chunkware/Limiter.h>
|
#include <audio/algo/chunkware/Limiter.h>
|
||||||
|
#include <audio/algo/chunkware/Gate.h>
|
||||||
#include <etk/os/FSNode.h>
|
#include <etk/os/FSNode.h>
|
||||||
#include <etk/chrono.h>
|
#include <etk/chrono.h>
|
||||||
|
|
||||||
@ -37,16 +38,190 @@ static std::vector<int16_t> convert(const std::vector<double>& _data) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void performanceCompressor() {
|
||||||
|
std::vector<double> input;
|
||||||
|
input.resize(8192, 0);
|
||||||
|
std::vector<double> output;
|
||||||
|
output.resize(8192, 0);
|
||||||
|
double sampleRate = 48000.0;
|
||||||
|
{
|
||||||
|
double phase = 0;
|
||||||
|
double baseCycle = 2.0*M_PI/sampleRate * 1280.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_PRINT("Start compressor performance ...");
|
||||||
|
Performance perfo;
|
||||||
|
audio::algo::chunkware::Compressor algo;
|
||||||
|
algo.setThreshold(-10);
|
||||||
|
algo.setRatio(-5);
|
||||||
|
int32_t lastPourcent = -1;
|
||||||
|
for (int32_t iii=0; iii<4096; ++iii) {
|
||||||
|
perfo.tic();
|
||||||
|
algo.process(&output[0], &input[0], input.size(), 1, audio::format_double);
|
||||||
|
perfo.toc();
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
APPL_INFO("Performance Compressor (double): ");
|
||||||
|
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 ");
|
||||||
|
APPL_INFO(" min < avg < max= " << (float((perfo.getMinProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float((perfo.getMaxProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
void performanceLimiter() {
|
||||||
|
std::vector<double> input;
|
||||||
|
input.resize(8192, 0);
|
||||||
|
std::vector<double> output;
|
||||||
|
output.resize(8192, 0);
|
||||||
|
double sampleRate = 48000.0;
|
||||||
|
{
|
||||||
|
double phase = 0;
|
||||||
|
double baseCycle = 2.0*M_PI/sampleRate * 1280.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_PRINT("Start Limiter performance ...");
|
||||||
|
Performance perfo;
|
||||||
|
audio::algo::chunkware::Limiter algo;
|
||||||
|
algo.setSampleRate(48000);
|
||||||
|
algo.setThreshold(0);
|
||||||
|
algo.setAttack(0.1);
|
||||||
|
algo.setRelease(2);
|
||||||
|
algo.init(1);
|
||||||
|
int32_t lastPourcent = -1;
|
||||||
|
for (int32_t iii=0; iii<4096; ++iii) {
|
||||||
|
perfo.tic();
|
||||||
|
algo.process(&output[0], &input[0], input.size(), 1, audio::format_double);
|
||||||
|
perfo.toc();
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
APPL_INFO("Performance Limiter (double): ");
|
||||||
|
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 ");
|
||||||
|
APPL_INFO(" min < avg < max = " << (float((perfo.getMinProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float((perfo.getMaxProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
void performanceGate() {
|
||||||
|
std::vector<double> input;
|
||||||
|
input.resize(8192, 0);
|
||||||
|
std::vector<double> output;
|
||||||
|
output.resize(8192, 0);
|
||||||
|
double sampleRate = 48000.0;
|
||||||
|
{
|
||||||
|
double phase = 0;
|
||||||
|
double baseCycle = 2.0*M_PI/sampleRate * 1280.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_PRINT("Start Gate performance ...");
|
||||||
|
Performance perfo;
|
||||||
|
audio::algo::chunkware::Gate algo;
|
||||||
|
algo.setSampleRate(48000);
|
||||||
|
algo.setThreshold(0);
|
||||||
|
algo.setAttack(0.1);
|
||||||
|
algo.setRelease(2);
|
||||||
|
algo.init();
|
||||||
|
int32_t lastPourcent = -1;
|
||||||
|
for (int32_t iii=0; iii<4096; ++iii) {
|
||||||
|
perfo.tic();
|
||||||
|
algo.process(&output[0], &input[0], input.size(), 1, audio::format_double);
|
||||||
|
perfo.toc();
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
APPL_INFO("Performance Gate (double): ");
|
||||||
|
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 ");
|
||||||
|
APPL_INFO(" min < avg < max = " << (float((perfo.getMinProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "% < "
|
||||||
|
<< (float((perfo.getMaxProcessing().count()*sampleRate)/double(input.size()))/1000000000.0)*100.0 << "%");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int _argc, const char** _argv) {
|
int main(int _argc, const char** _argv) {
|
||||||
// the only one init for etk:
|
// the only one init for etk:
|
||||||
etk::init(_argc, _argv);
|
etk::init(_argc, _argv);
|
||||||
std::string inputName = "";
|
std::string inputName = "";
|
||||||
|
bool performance = false;
|
||||||
bool perf = false;
|
bool perf = false;
|
||||||
int64_t sampleRate = 48000;
|
int64_t sampleRate = 48000;
|
||||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||||
std::string data = _argv[iii];
|
std::string data = _argv[iii];
|
||||||
if (etk::start_with(data,"--in=")) {
|
if (etk::start_with(data,"--in=")) {
|
||||||
inputName = &data[5];
|
inputName = &data[5];
|
||||||
|
} else if (data == "--performance") {
|
||||||
|
performance = true;
|
||||||
} else if (data == "--perf") {
|
} else if (data == "--perf") {
|
||||||
perf = true;
|
perf = true;
|
||||||
} else if (etk::start_with(data,"--sample-rate=")) {
|
} else if (etk::start_with(data,"--sample-rate=")) {
|
||||||
@ -57,11 +232,19 @@ int main(int _argc, const char** _argv) {
|
|||||||
APPL_PRINT("Help : ");
|
APPL_PRINT("Help : ");
|
||||||
APPL_PRINT(" ./xxx --fb=file.raw --mic=file.raw");
|
APPL_PRINT(" ./xxx --fb=file.raw --mic=file.raw");
|
||||||
APPL_PRINT(" --in=YYY.raw inout file");
|
APPL_PRINT(" --in=YYY.raw inout 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(" --perf Enable performence test (little slower but real performence test)");
|
||||||
APPL_PRINT(" --sample-rate=XXXX Signal sample rate (default 48000)");
|
APPL_PRINT(" --sample-rate=XXXX Signal sample rate (default 48000)");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// PERFORMANCE test only ....
|
||||||
|
if (performance == true) {
|
||||||
|
performanceCompressor();
|
||||||
|
performanceLimiter();
|
||||||
|
performanceGate();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (inputName == "") {
|
if (inputName == "") {
|
||||||
APPL_ERROR("Can not Process missing parameters...");
|
APPL_ERROR("Can not Process missing parameters...");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
@ -74,12 +257,8 @@ int main(int _argc, const char** _argv) {
|
|||||||
output.resize(inputData.size(), 0);
|
output.resize(inputData.size(), 0);
|
||||||
// process in chunk of 256 samples
|
// process in chunk of 256 samples
|
||||||
int32_t blockSize = 256;
|
int32_t blockSize = 256;
|
||||||
// for CPU consomtion:
|
|
||||||
std11::chrono::nanoseconds totalTimeProcessing(0);
|
|
||||||
std11::chrono::nanoseconds minProcessing(99999999999999LL);
|
|
||||||
std11::chrono::nanoseconds maxProcessing(0);
|
|
||||||
int32_t totalIteration = 0;
|
|
||||||
|
|
||||||
|
Performance perfo;
|
||||||
/*
|
/*
|
||||||
audio::algo::chunkware::Compressor algo;
|
audio::algo::chunkware::Compressor algo;
|
||||||
algo.setThreshold(-10);
|
algo.setThreshold(-10);
|
||||||
@ -92,15 +271,10 @@ int main(int _argc, const char** _argv) {
|
|||||||
} else {
|
} else {
|
||||||
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
|
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(audio::format_double, &output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1);
|
algo.process(audio::format_double, &output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1);
|
||||||
if (perf == true) {
|
if (perf == true) {
|
||||||
std11::chrono::steady_clock::time_point timeEnd = std11::chrono::steady_clock::now();
|
perfo.toc();
|
||||||
std11::chrono::nanoseconds time = timeEnd - timeStart;
|
|
||||||
minProcessing = std::min(minProcessing, time);
|
|
||||||
maxProcessing = std::max(maxProcessing, time);
|
|
||||||
totalTimeProcessing += time;
|
|
||||||
totalIteration++;
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,15 +293,10 @@ int main(int _argc, const char** _argv) {
|
|||||||
} else {
|
} else {
|
||||||
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
|
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], &inputData[iii*blockSize], blockSize, 1, audio::format_double);
|
algo.process(&output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1, audio::format_double);
|
||||||
if (perf == true) {
|
if (perf == true) {
|
||||||
std11::chrono::steady_clock::time_point timeEnd = std11::chrono::steady_clock::now();
|
perfo.toc();
|
||||||
std11::chrono::nanoseconds time = timeEnd - timeStart;
|
|
||||||
minProcessing = std::min(minProcessing, time);
|
|
||||||
maxProcessing = std::max(maxProcessing, time);
|
|
||||||
totalTimeProcessing += time;
|
|
||||||
totalIteration++;
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,13 +305,13 @@ int main(int _argc, const char** _argv) {
|
|||||||
if (perf == true) {
|
if (perf == true) {
|
||||||
APPL_INFO("Performance Result: ");
|
APPL_INFO("Performance Result: ");
|
||||||
APPL_INFO(" blockSize=" << blockSize << " sample");
|
APPL_INFO(" blockSize=" << blockSize << " sample");
|
||||||
APPL_INFO(" min=" << minProcessing.count() << " ns");
|
APPL_INFO(" min=" << perfo.getMinProcessing().count() << " ns");
|
||||||
APPL_INFO(" max=" << maxProcessing.count() << " ns");
|
APPL_INFO(" max=" << perfo.getMaxProcessing().count() << " ns");
|
||||||
APPL_INFO(" avg=" << totalTimeProcessing.count()/totalIteration << " ns");
|
APPL_INFO(" avg=" << perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration() << " ns");
|
||||||
|
|
||||||
APPL_INFO(" min=" << (float((minProcessing.count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
APPL_INFO(" min=" << (float((perfo.getMinProcessing().count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
||||||
APPL_INFO(" max=" << (float((maxProcessing.count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
APPL_INFO(" max=" << (float((perfo.getMaxProcessing().count()*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
||||||
APPL_INFO(" avg=" << (float(((totalTimeProcessing.count()/totalIteration)*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
APPL_INFO(" avg=" << (float(((perfo.getTotalTimeProcessing().count()/perfo.getTotalIteration())*sampleRate)/blockSize)/1000000000.0)*100.0 << " %");
|
||||||
}
|
}
|
||||||
etk::FSNodeWriteAllDataType<int16_t>("output.raw", convert(output));
|
etk::FSNodeWriteAllDataType<int16_t>("output.raw", convert(output));
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user