[DEV] Continue rework
This commit is contained in:
parent
72a2776a6f
commit
1d1b5e9393
@ -26,7 +26,7 @@
|
||||
#include <audio/algo/chunkware/Compressor.h>
|
||||
#include <audio/algo/chunkware/debug.h>
|
||||
|
||||
audio::algo::chunkware::Compresssor::Compresssor() :
|
||||
audio::algo::chunkware::Compressor::Compressor() :
|
||||
AttRelEnvelope(10.0, 100.0),
|
||||
m_threshdB(0.0),
|
||||
m_ratio(1.0),
|
||||
@ -34,23 +34,86 @@ audio::algo::chunkware::Compresssor::Compresssor() :
|
||||
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compresssor::setThresh(double _dB) {
|
||||
void audio::algo::chunkware::Compressor::setThreshold(double _dB) {
|
||||
m_threshdB = _dB;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compresssor::setRatio(double _ratio) {
|
||||
AA_CHUNK_ASSERT(_ratio > 0.0, "input function error");
|
||||
void audio::algo::chunkware::Compressor::setRatio(double _ratio) {
|
||||
m_ratio = _ratio;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compresssor::initRuntime() {
|
||||
void audio::algo::chunkware::Compressor::initRuntime() {
|
||||
m_overThresholdEnvelopeDB = DC_OFFSET;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compresssor::process(double& _in1, double& _in2) {
|
||||
|
||||
void audio::algo::chunkware::Compressor::process(audio::format _format, void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel) {
|
||||
// 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);
|
||||
for (size_t iii=0; iii<_nbChunk ; ++iii) {
|
||||
double val = input[iii];
|
||||
val /= 32768.0;
|
||||
processMono(val);
|
||||
val *= 32768.0;
|
||||
output[iii] = int16_t(std::avg(-32768.0, val*32768.0, 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) {
|
||||
output[iii] = input[iii];
|
||||
processMono(output[iii]);
|
||||
//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::Compressor::processMono(double& _in) {
|
||||
double rect1 = std::abs(_in); // rectify input
|
||||
rect1 += DC_OFFSET;
|
||||
double keydB = lin2dB(rect1);
|
||||
// threshold
|
||||
double overdB = keydB - m_threshdB; //delta over threshold
|
||||
if ( overdB < 0.0 ) {
|
||||
overdB = 0.0;
|
||||
}
|
||||
// attack/release
|
||||
overdB += DC_OFFSET; // add DC offset to avoid denormal
|
||||
AttRelEnvelope::run(overdB, m_overThresholdEnvelopeDB); // run attack/release envelope
|
||||
overdB = m_overThresholdEnvelopeDB - DC_OFFSET; // subtract DC offset
|
||||
/* 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,
|
||||
* thereby avoiding denormals. However, to prevent the offset from causing
|
||||
* constant gain reduction, we must subtract it from the envelope, yielding
|
||||
* a minimum value of 0dB.
|
||||
*/
|
||||
// transfer function
|
||||
double gr = overdB * ( m_ratio - 1.0 ); // gain reduction (dB)
|
||||
gr = dB2lin(gr); // convert dB -> linear
|
||||
// output gain
|
||||
_in *= gr; // apply gain reduction to input
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compressor::process(double& _in1, double& _in2) {
|
||||
// create sidechain
|
||||
double rect1 = fabs(_in1); // rectify input
|
||||
double rect2 = fabs(_in2);
|
||||
double rect1 = std::abs(_in1); // rectify input
|
||||
double rect2 = std::abs(_in2);
|
||||
/* if desired, one could use another EnvelopeDetector to smooth
|
||||
* the rectified signal.
|
||||
*/
|
||||
@ -58,15 +121,16 @@ void audio::algo::chunkware::Compresssor::process(double& _in1, double& _in2) {
|
||||
process(_in1, _in2, link); // rest of process
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compresssor::process(double& _in1, double& _in2, double _keyLinked) {
|
||||
_keyLinked = fabs(_keyLinked); // rectify (just in case)
|
||||
void audio::algo::chunkware::Compressor::process(double& _in1, double& _in2, double _keyLinked) {
|
||||
_keyLinked = std::abs(_keyLinked); // rectify (just in case)
|
||||
// convert key to dB
|
||||
_keyLinked += DC_OFFSET; // add DC offset to avoid log(0)
|
||||
double keydB = lin2dB(_keyLinked); // convert linear -> dB
|
||||
_keyLinked += DC_OFFSET; // add DC offset to avoid log(0)
|
||||
double keydB = lin2dB(_keyLinked); // convert linear -> dB
|
||||
// threshold
|
||||
double overdB = keydB - m_threshdB; // delta over threshold
|
||||
if (overdB < 0.0)
|
||||
double overdB = keydB - m_threshdB; // delta over threshold
|
||||
if (overdB < 0.0) {
|
||||
overdB = 0.0;
|
||||
}
|
||||
// attack/release
|
||||
overdB += DC_OFFSET; // add DC offset to avoid denormal
|
||||
audio::algo::chunkware::AttRelEnvelope::run(overdB, m_overThresholdEnvelopeDB); // run attack/release envelope
|
||||
@ -78,8 +142,8 @@ void audio::algo::chunkware::Compresssor::process(double& _in1, double& _in2, do
|
||||
* a minimum value of 0dB.
|
||||
*/
|
||||
// transfer function
|
||||
double gr = overdB * (m_ratio - 1.0); // gain reduction (dB)
|
||||
gr = dB2lin(gr); // convert dB -> linear
|
||||
double gr = overdB * (m_ratio - 1.0); // gain reduction (dB)
|
||||
gr = dB2lin(gr); // convert dB -> linear
|
||||
// output gain
|
||||
_in1 *= gr; // apply gain reduction to input
|
||||
_in2 *= gr;
|
||||
|
@ -28,36 +28,43 @@
|
||||
#define __AUDIO_ALGO_CHUNKWARE_COMPRESSOR_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
#include <audio/format.h>
|
||||
#include <audio/algo/chunkware/AttRelEnvelope.h>
|
||||
#include <audio/algo/chunkware/Gain.h>
|
||||
|
||||
namespace audio {
|
||||
namespace algo {
|
||||
namespace chunkware {
|
||||
class Compresssor : public audio::algo::chunkware::AttRelEnvelope {
|
||||
class Compressor : public audio::algo::chunkware::AttRelEnvelope {
|
||||
public:
|
||||
Compresssor();
|
||||
virtual ~Compresssor() {}
|
||||
// parameters
|
||||
virtual void setThresh(double _dB);
|
||||
virtual void setRatio(double _dB);
|
||||
virtual double getThresh() const {
|
||||
Compressor();
|
||||
virtual ~Compressor() {}
|
||||
protected:
|
||||
double m_threshdB;//!< threshold (dB)
|
||||
public:
|
||||
virtual void setThreshold(double _dB);
|
||||
virtual double getThreshold() const {
|
||||
return m_threshdB;
|
||||
}
|
||||
protected:
|
||||
double m_ratio; //!< ratio (compression: < 1 ; expansion: > 1)
|
||||
public:
|
||||
virtual void setRatio(double _dB);
|
||||
virtual double getRatio() const {
|
||||
return m_ratio;
|
||||
}
|
||||
// runtime
|
||||
public:
|
||||
void process(audio::format _format, void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel);
|
||||
// call before runtime (in resume())
|
||||
virtual void initRuntime();
|
||||
protected:
|
||||
// runtime
|
||||
// compressor runtime process
|
||||
void processMono(double& _in);
|
||||
// compressor runtime process
|
||||
void process(double& _in1, double& _in2);
|
||||
// with stereo-linked key in
|
||||
void process(double& _in1, double& _in2, double _keyLinked);
|
||||
private:
|
||||
// transfer function
|
||||
double m_threshdB;//!< threshold (dB)
|
||||
double m_ratio; //!< ratio (compression: < 1 ; expansion: > 1)
|
||||
// runtime variables
|
||||
double m_overThresholdEnvelopeDB; //!< over-threshold envelope (dB)
|
||||
};
|
||||
|
@ -27,27 +27,27 @@
|
||||
#include <audio/algo/chunkware/CompressorRms.h>
|
||||
|
||||
|
||||
audio::algo::chunkware::CompresssorRms::CompresssorRms() :
|
||||
audio::algo::chunkware::CompressorRms::CompressorRms() :
|
||||
m_averager(5.0),
|
||||
m_averageSuares(DC_OFFSET) {
|
||||
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::CompresssorRms::setSampleRate(double _sampleRate) {
|
||||
audio::algo::chunkware::Compresssor::setSampleRate(_sampleRate);
|
||||
void audio::algo::chunkware::CompressorRms::setSampleRate(double _sampleRate) {
|
||||
audio::algo::chunkware::Compressor::setSampleRate(_sampleRate);
|
||||
m_averager.setSampleRate(_sampleRate);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::CompresssorRms::setWindow(double _ms) {
|
||||
void audio::algo::chunkware::CompressorRms::setWindow(double _ms) {
|
||||
m_averager.setTc(_ms);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::CompresssorRms::initRuntime() {
|
||||
audio::algo::chunkware::Compresssor::initRuntime();
|
||||
void audio::algo::chunkware::CompressorRms::initRuntime() {
|
||||
audio::algo::chunkware::Compressor::initRuntime();
|
||||
m_averageSuares = DC_OFFSET;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::CompresssorRms::process(double& _in1, double& _in2) {
|
||||
void audio::algo::chunkware::CompressorRms::process(double& _in1, double& _in2) {
|
||||
// create sidechain
|
||||
double inSq1 = _in1 * _in1; // square input
|
||||
double inSq2 = _in2 * _in2;
|
||||
@ -62,5 +62,5 @@ void audio::algo::chunkware::CompresssorRms::process(double& _in1, double& _in2)
|
||||
* giving comparable results.
|
||||
*/
|
||||
// rest of process
|
||||
audio::algo::chunkware::Compresssor::process(_in1, _in2, rms);
|
||||
audio::algo::chunkware::Compressor::process(_in1, _in2, rms);
|
||||
}
|
||||
|
@ -32,10 +32,10 @@
|
||||
namespace audio {
|
||||
namespace algo {
|
||||
namespace chunkware {
|
||||
class CompresssorRms : public audio::algo::chunkware::Compresssor {
|
||||
class CompressorRms : public audio::algo::chunkware::Compressor {
|
||||
public:
|
||||
CompresssorRms();
|
||||
virtual ~CompresssorRms() {}
|
||||
CompressorRms();
|
||||
virtual ~CompressorRms() {}
|
||||
// sample rate
|
||||
virtual void setSampleRate(double _sampleRate);
|
||||
// RMS window
|
||||
|
@ -43,7 +43,7 @@ audio::algo::chunkware::Limiter::Limiter() :
|
||||
m_outputBuffer[ 1 ].resize(BUFFER_SIZE, 0.0);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::setThresh(double _dB) {
|
||||
void audio::algo::chunkware::Limiter::setThreshold(double _dB) {
|
||||
m_threshdB = _dB;
|
||||
m_threshold = dB2lin(_dB);
|
||||
}
|
||||
@ -73,11 +73,136 @@ void audio::algo::chunkware::Limiter::initRuntime() {
|
||||
m_outputBuffer[ 1 ].assign(BUFFER_SIZE, 0.0);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::FastEnvelope::setCoef() {
|
||||
void audio::algo::chunkware::FastEnvelope::setCoef() {
|
||||
// rises to 99% of in value over duration of time constant
|
||||
m_coefficient = pow(0.01, (1000.0 / (m_timeMs * m_sampleRate)));
|
||||
m_coefficient = std::pow(0.01, (1000.0 / (m_timeMs * m_sampleRate)));
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::process(audio::format _format, void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel) {
|
||||
// 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);
|
||||
for (size_t iii=0; iii<_nbChunk ; ++iii) {
|
||||
double val = input[iii];
|
||||
val /= 32768.0;
|
||||
processMono(val);
|
||||
val *= 32768.0;
|
||||
output[iii] = int16_t(std::avg(-32768.0, val*32768.0, 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) {
|
||||
output[iii] = input[iii];
|
||||
processMono(output[iii]);
|
||||
//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::Limiter::processMono(double& _in) {
|
||||
double keyLink = std::abs(_in); // rectify input
|
||||
// threshold
|
||||
// we always want to feed the sidechain AT LEATS the threshold value
|
||||
if (keyLink < m_threshold) {
|
||||
keyLink = m_threshold;
|
||||
}
|
||||
// test:
|
||||
// a) whether peak timer has "expired"
|
||||
// b) whether new peak is greater than previous max peak
|
||||
if ((++m_peakTimer >= m_peakHold) || (keyLink > m_maxPeak)) {
|
||||
// if either condition is met:
|
||||
m_peakTimer = 0; // reset peak timer
|
||||
m_maxPeak = keyLink; // assign new peak to max peak
|
||||
}
|
||||
/* REGARDING THE MAX PEAK: This method assumes that the only important
|
||||
* sample in a look-ahead buffer would be the highest peak. As such,
|
||||
* instead of storing all samples in a look-ahead buffer, it only stores
|
||||
* the max peak, and compares all incoming samples to that one.
|
||||
* The max peak has a hold time equal to what the look-ahead buffer
|
||||
* would have been, which is tracked by a timer (counter). When this
|
||||
* timer expires, the sample would have exited from the buffer. Therefore,
|
||||
* a new sample must be assigned to the max peak. We assume that the next
|
||||
* highest sample in our theoretical buffer is the current input sample.
|
||||
* In reality, we know this is probably NOT the case, and that there has
|
||||
* been another sample, slightly lower than the one before it, that has
|
||||
* passed the input. If we do not account for this possibility, our gain
|
||||
* reduction could be insufficient, resulting in an "over" at the output.
|
||||
* To remedy this, we simply apply a suitably long release stage in the
|
||||
* envelope follower.
|
||||
*/
|
||||
// attack/release
|
||||
if (m_maxPeak > m_overThresholdEnvelope) {
|
||||
// run attack phase
|
||||
m_attack.run(m_maxPeak, m_overThresholdEnvelope);
|
||||
} else {
|
||||
// run release phase
|
||||
m_release.run(m_maxPeak, m_overThresholdEnvelope);
|
||||
}
|
||||
/* REGARDING THE ATTACK: This limiter achieves "look-ahead" detection
|
||||
* by allowing the envelope follower to attack the max peak, which is
|
||||
* held for the duration of the attack phase -- unless a new, higher
|
||||
* peak is detected. The output signal is buffered so that the gain
|
||||
* reduction is applied in advance of the "offending" sample.
|
||||
*/
|
||||
|
||||
/* NOTE: a DC offset is not necessary for the envelope follower,
|
||||
* as neither the max peak nor envelope should fall below the
|
||||
* threshold (which is assumed to be around 1.0 linear).
|
||||
*/
|
||||
// gain reduction
|
||||
double gR = m_threshold / m_overThresholdEnvelope;
|
||||
// unload current buffer index
|
||||
// (m_cursor - delay) & m_bufferMask gets sample from [delay] samples ago
|
||||
// m_bufferMask variable wraps index
|
||||
unsigned int delayIndex = (m_cursor - m_peakHold) & m_bufferMask;
|
||||
double delay1 = m_outputBuffer[0][delayIndex];
|
||||
//double delay2 = m_outputBuffer[1][delayIndex];
|
||||
// load current buffer index and advance current index
|
||||
// m_bufferMask wraps m_cursor index
|
||||
m_outputBuffer[0][m_cursor] = _in;
|
||||
//m_outputBuffer[1][m_cursor] = _in2;
|
||||
++m_cursor &= m_bufferMask;
|
||||
// output gain
|
||||
_in = delay1 * gR; // apply gain reduction to input
|
||||
//_in2 = delay2 * gR;
|
||||
/* REGARDING THE GAIN REDUCTION: Due to the logarithmic nature
|
||||
* of the attack phase, the sidechain will never achieve "full"
|
||||
* attack. (Actually, it is only guaranteed to achieve 99% of
|
||||
* the input value over the given time constant.) As such, the
|
||||
* limiter cannot achieve "brick-wall" limiting. There are 2
|
||||
* workarounds:
|
||||
*
|
||||
* 1) Set the threshold slightly lower than the desired threshold.
|
||||
* i.e. 0.0dB -> -0.1dB or even -0.5dB
|
||||
*
|
||||
* 2) Clip the output at the threshold, as such:
|
||||
*
|
||||
* if (in1 > m_threshold) in1 = m_threshold;
|
||||
* else if (in1 < -m_threshold) in1 = -m_threshold;
|
||||
*
|
||||
* if (in2 > m_threshold) in2 = m_threshold;
|
||||
* else if (in2 < -m_threshold) in2 = -m_threshold;
|
||||
*
|
||||
* (... or replace with your favorite branchless clipper ...)
|
||||
*/
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::process(double& _in1, double& _in2) {
|
||||
// create sidechain
|
||||
@ -86,8 +211,9 @@ void audio::algo::chunkware::Limiter::process(double& _in1, double& _in2) {
|
||||
double keyLink = std::max(rect1, rect2); // link channels with greater of 2
|
||||
// threshold
|
||||
// we always want to feed the sidechain AT LEATS the threshold value
|
||||
if (keyLink < m_threshold)
|
||||
if (keyLink < m_threshold) {
|
||||
keyLink = m_threshold;
|
||||
}
|
||||
// test:
|
||||
// a) whether peak timer has "expired"
|
||||
// b) whether new peak is greater than previous max peak
|
||||
|
@ -28,68 +28,102 @@
|
||||
#define __AUDIO_ALGO_CHUNKWARE_LIMITER_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
#include <audio/format.h>
|
||||
#include <audio/algo/chunkware/AttRelEnvelope.h>
|
||||
#include <audio/algo/chunkware/Gain.h>
|
||||
#include <etk/chrono.h>
|
||||
#include <vector>
|
||||
|
||||
namespace audio {
|
||||
namespace algo {
|
||||
namespace chunkware {
|
||||
// class for faster attack/release
|
||||
class FastEnvelope : public audio::algo::chunkware::EnvelopeDetector {
|
||||
public:
|
||||
FastEnvelope(double _ms = 1.0, double _sampleRate = 44100.0) :
|
||||
EnvelopeDetector(_ms, _sampleRate) {
|
||||
|
||||
}
|
||||
virtual ~FastEnvelope() {}
|
||||
protected:
|
||||
// override setCoef() - coefficient calculation
|
||||
virtual void setCoef();
|
||||
};
|
||||
|
||||
class Limiter {
|
||||
protected:
|
||||
bool m_isConfigured;
|
||||
public:
|
||||
Limiter();
|
||||
virtual ~Limiter() {}
|
||||
// parameters
|
||||
virtual void setThresh(double _dB);
|
||||
virtual void setAttack(double _ms);
|
||||
virtual void setRelease(double _ms);
|
||||
virtual double getThresh() const {
|
||||
|
||||
protected:
|
||||
double m_threshdB; //!< threshold (dB)
|
||||
public:
|
||||
virtual void setThreshold(double _dB);
|
||||
virtual double getThreshold() const {
|
||||
return m_threshdB;
|
||||
}
|
||||
|
||||
protected:
|
||||
std11::chrono::microseconds m_attackTime; //!< attaque time in ms.
|
||||
public:
|
||||
virtual void setAttack(double _ms);
|
||||
virtual double getAttack() const {
|
||||
return m_attack.getTc();
|
||||
}
|
||||
|
||||
protected:
|
||||
std11::chrono::microseconds m_releaseTime; //!< attaque time in ms.
|
||||
public:
|
||||
virtual void setRelease(double _ms);
|
||||
virtual double getRelease() const {
|
||||
return m_release.getTc();
|
||||
}
|
||||
// latency
|
||||
protected:
|
||||
unsigned int m_peakHold; //!< peak hold (samples)
|
||||
public:
|
||||
virtual const unsigned int getLatency() const {
|
||||
return m_peakHold;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
// sample rate dependencies
|
||||
virtual void setSampleRate(double _sampleRate);
|
||||
virtual void setSampleRate(double _sampleRate);
|
||||
virtual double getSampleRate() {
|
||||
return m_attack.getSampleRate();
|
||||
}
|
||||
// runtime
|
||||
// call before runtime (in resume())
|
||||
virtual void initRuntime();
|
||||
void process(audio::format _format, void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel);
|
||||
protected:
|
||||
process(double* _out, const double* _in, int8_t _nbChannel);
|
||||
process(float* _out, const float* _in, int8_t _nbChannel);
|
||||
process(int16_16_t* _out, const int16_16_t* _in, int8_t _nbChannel);
|
||||
process(int16_32_t* _out, const int16_32_t* _in, int8_t _nbChannel);
|
||||
process(int24_32_t* _out, const int24_32_t* _in, int8_t _nbChannel);
|
||||
process(int32_32_t* _out, const int32_32_t* _in, int8_t _nbChannel);
|
||||
process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||
// limiter runtime process
|
||||
void process(double& _in1, double& _in2);
|
||||
protected:
|
||||
// class for faster attack/release
|
||||
class FastEnvelope : public audio::algo::chunkware::EnvelopeDetector {
|
||||
public:
|
||||
FastEnvelope(double _ms = 1.0, double _sampleRate = 44100.0) :
|
||||
EnvelopeDetector(_ms, _sampleRate) {
|
||||
|
||||
}
|
||||
virtual ~FastEnvelope() {}
|
||||
protected:
|
||||
// override setCoef() - coefficient calculation
|
||||
virtual void setCoef();
|
||||
};
|
||||
void processMono(double& _in);
|
||||
private:
|
||||
// transfer function
|
||||
double m_threshdB; //!< threshold (dB)
|
||||
|
||||
double m_threshold; //!< threshold (linear)
|
||||
// max peak
|
||||
unsigned int m_peakHold; //!< peak hold (samples)
|
||||
|
||||
unsigned int m_peakTimer; //!< peak hold timer
|
||||
double m_maxPeak; //!< max peak
|
||||
// attack/release envelope
|
||||
audio::algo::chunkware::Limiter::FastEnvelope m_attack; //!< attack
|
||||
audio::algo::chunkware::Limiter::FastEnvelope m_release; //!< release
|
||||
audio::algo::chunkware::FastEnvelope m_attack; //!< attack
|
||||
audio::algo::chunkware::FastEnvelope m_release; //!< release
|
||||
|
||||
|
||||
double m_overThresholdEnvelope; //!< over-threshold envelope (linear)
|
||||
// buffer
|
||||
// BUFFER_SIZE default can handle up to ~10ms at 96kHz
|
||||
|
@ -19,7 +19,7 @@ def create(target):
|
||||
'audio/algo/chunkware/GateRms.cpp',
|
||||
'audio/algo/chunkware/Limiter.cpp'
|
||||
])
|
||||
myModule.add_module_depend(['etk'])
|
||||
myModule.add_module_depend(['etk', 'audio'])
|
||||
myModule.add_export_path(tools.get_current_path(__file__))
|
||||
# return module
|
||||
return myModule
|
||||
|
@ -6,7 +6,8 @@
|
||||
|
||||
#include <test/debug.h>
|
||||
#include <etk/etk.h>
|
||||
#include <audio/algo/chunkware/.h>
|
||||
#include <audio/algo/chunkware/Compressor.h>
|
||||
#include <audio/algo/chunkware/Limiter.h>
|
||||
#include <etk/os/FSNode.h>
|
||||
#include <etk/chrono.h>
|
||||
|
||||
@ -21,6 +22,8 @@ static std::vector<double> convert(const std::vector<int16_t>& _data) {
|
||||
for (size_t iii=0; iii<_data.size(); ++iii) {
|
||||
out[iii] = _data[iii];
|
||||
out[iii] /= 32768.0;
|
||||
out[iii] *= 2.1;
|
||||
//APPL_INFO(" in=" << _data[iii] << " => " << out[iii]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@ -43,7 +46,7 @@ int main(int _argc, const char** _argv) {
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
if (etk::start_with(data,"--in=")) {
|
||||
fbName = &data[5];
|
||||
inputName = &data[5];
|
||||
} else if (data == "--perf") {
|
||||
perf = true;
|
||||
} else if (etk::start_with(data,"--sample-rate=")) {
|
||||
@ -77,14 +80,10 @@ int main(int _argc, const char** _argv) {
|
||||
std11::chrono::nanoseconds maxProcessing(0);
|
||||
int32_t totalIteration = 0;
|
||||
|
||||
|
||||
audio::algo::aec::Lms algo;
|
||||
if (filterSize != 0) {
|
||||
algo.setFilterSize(filterSize);
|
||||
}
|
||||
if (mu != 0.0f) {
|
||||
algo.setMu(mu);
|
||||
}
|
||||
/*
|
||||
audio::algo::chunkware::Compressor algo;
|
||||
algo.setThreshold(-10);
|
||||
algo.setRatio(-5);
|
||||
int32_t lastPourcent = -1;
|
||||
for (int32_t iii=0; iii<output.size()/blockSize; ++iii) {
|
||||
if (lastPourcent != 100*iii / (output.size()/blockSize)) {
|
||||
@ -94,7 +93,7 @@ int main(int _argc, const char** _argv) {
|
||||
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
|
||||
}
|
||||
std11::chrono::steady_clock::time_point timeStart = std11::chrono::steady_clock::now();
|
||||
algo.process(&output[iii*blockSize], &fbData[iii*blockSize], &micData[iii*blockSize], blockSize);
|
||||
algo.process(audio::format_double, &output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1);
|
||||
if (perf == true) {
|
||||
std11::chrono::steady_clock::time_point timeEnd = std11::chrono::steady_clock::now();
|
||||
std11::chrono::nanoseconds time = timeEnd - timeStart;
|
||||
@ -102,9 +101,38 @@ int main(int _argc, const char** _argv) {
|
||||
maxProcessing = std::max(maxProcessing, time);
|
||||
totalTimeProcessing += time;
|
||||
totalIteration++;
|
||||
usleep(10000);
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
*/
|
||||
audio::algo::chunkware::Limiter algo;
|
||||
algo.setSampleRate(48000);
|
||||
algo.setThreshold(0);
|
||||
algo.setAttack(0.1);
|
||||
algo.setRelease(2);
|
||||
algo.initRuntime();
|
||||
int32_t lastPourcent = -1;
|
||||
for (int32_t iii=0; iii<output.size()/blockSize; ++iii) {
|
||||
if (lastPourcent != 100*iii / (output.size()/blockSize)) {
|
||||
lastPourcent = 100*iii / (output.size()/blockSize);
|
||||
APPL_INFO("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize << " " << lastPourcent << "/100");
|
||||
} else {
|
||||
APPL_VERBOSE("Process : " << iii*blockSize << "/" << int32_t(output.size()/blockSize)*blockSize);
|
||||
}
|
||||
std11::chrono::steady_clock::time_point timeStart = std11::chrono::steady_clock::now();
|
||||
algo.process(audio::format_double, &output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1);
|
||||
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(1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (perf == true) {
|
||||
APPL_INFO("Performance Result: ");
|
||||
APPL_INFO(" blockSize=" << blockSize << " sample");
|
||||
|
Loading…
Reference in New Issue
Block a user