[DEV] cotinue update
This commit is contained in:
parent
e83c228ca1
commit
e912b34365
@ -28,6 +28,7 @@
|
||||
|
||||
audio::algo::chunkware::Compressor::Compressor() :
|
||||
AttRelEnvelope(10.0, 100.0),
|
||||
m_isConfigured(false),
|
||||
m_threshdB(0.0),
|
||||
m_ratio(1.0),
|
||||
m_overThresholdEnvelopeDB(DC_OFFSET) {
|
||||
@ -42,12 +43,24 @@ void audio::algo::chunkware::Compressor::setRatio(double _ratio) {
|
||||
m_ratio = _ratio;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compressor::initRuntime() {
|
||||
void audio::algo::chunkware::Compressor::init() {
|
||||
m_overThresholdEnvelopeDB = DC_OFFSET;
|
||||
m_isConfigured = true;
|
||||
}
|
||||
|
||||
std::vector<enum audio::format> audio::algo::chunkware::Compressor::getSupportedFormat() {
|
||||
std::vector<enum audio::format> out = getNativeSupportedFormat();
|
||||
out.push_back(audio::format_int16);
|
||||
return out;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compressor::process(audio::format _format, void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel) {
|
||||
std::vector<enum audio::format> audio::algo::chunkware::Compressor::getNativeSupportedFormat() {
|
||||
std::vector<enum audio::format> out;
|
||||
out.push_back(audio::format_double);
|
||||
return out;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compressor::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);
|
||||
@ -57,12 +70,16 @@ void audio::algo::chunkware::Compressor::process(audio::format _format, void* _o
|
||||
{
|
||||
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) {
|
||||
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));
|
||||
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;
|
||||
@ -71,8 +88,7 @@ void audio::algo::chunkware::Compressor::process(audio::format _format, void* _o
|
||||
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]);
|
||||
processDouble(&output[iii*_nbChannel], &input[iii*_nbChannel], _nbChannel);
|
||||
//AA_CHUNK_INFO(" in=" << input[iii] << " => " << output[iii]);
|
||||
}
|
||||
}
|
||||
@ -84,45 +100,17 @@ void audio::algo::chunkware::Compressor::process(audio::format _format, void* _o
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
void audio::algo::chunkware::Compressor::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);
|
||||
}
|
||||
// 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
|
||||
processDouble(_out, _in, _nbChannel, keyLink);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Compressor::process(double& _in1, double& _in2) {
|
||||
// create sidechain
|
||||
double rect1 = std::abs(_in1); // rectify input
|
||||
double rect2 = std::abs(_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::Compressor::process(double& _in1, double& _in2, double _keyLinked) {
|
||||
_keyLinked = std::abs(_keyLinked); // rectify (just in case)
|
||||
void audio::algo::chunkware::Compressor::processDouble(double* _out, const double* _in, int8_t _nbChannel, double _keyLinked) {
|
||||
// convert key to dB
|
||||
_keyLinked += DC_OFFSET; // add DC offset to avoid log(0)
|
||||
double keydB = lin2dB(_keyLinked); // convert linear -> dB
|
||||
@ -145,7 +133,8 @@ void audio::algo::chunkware::Compressor::process(double& _in1, double& _in2, dou
|
||||
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;
|
||||
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||
_out[iii] = _in[iii] * gr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,46 @@ namespace audio {
|
||||
namespace algo {
|
||||
namespace chunkware {
|
||||
class Compressor : public audio::algo::chunkware::AttRelEnvelope {
|
||||
protected:
|
||||
bool m_isConfigured;
|
||||
public:
|
||||
Compressor();
|
||||
virtual ~Compressor() {}
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the Algorithm
|
||||
*/
|
||||
virtual void init();
|
||||
/**
|
||||
* @brief Get list of format suported in input.
|
||||
* @return list of supported format
|
||||
*/
|
||||
std::vector<enum audio::format> getSupportedFormat();
|
||||
/**
|
||||
* @brief Get list of algorithm format suported. No format convertion.
|
||||
* @return list of supported format
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel, double _value);
|
||||
/*
|
||||
void process(float* _out, const float* _in, int8_t _nbChannel);
|
||||
void process(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);
|
||||
void process(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);
|
||||
void process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||
*/
|
||||
protected:
|
||||
double m_threshdB;//!< threshold (dB)
|
||||
public:
|
||||
@ -53,18 +90,7 @@ namespace audio {
|
||||
virtual double getRatio() const {
|
||||
return m_ratio;
|
||||
}
|
||||
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);
|
||||
// runtime variables
|
||||
double m_overThresholdEnvelopeDB; //!< over-threshold envelope (dB)
|
||||
};
|
||||
|
@ -47,11 +47,12 @@ void audio::algo::chunkware::CompressorRms::initRuntime() {
|
||||
m_averageSuares = DC_OFFSET;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::CompressorRms::process(double& _in1, double& _in2) {
|
||||
void audio::algo::chunkware::CompressorRms::processDouble(double* _out, const double* _in, int8_t _nbChannel) {
|
||||
double sum = 0.0;
|
||||
// create sidechain
|
||||
double inSq1 = _in1 * _in1; // square input
|
||||
double inSq2 = _in2 * _in2;
|
||||
double sum = inSq1 + inSq2; // power summing
|
||||
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||
sum += _in[iii] * _in[iii]; // square input
|
||||
}
|
||||
sum += DC_OFFSET; // DC offset, to prevent denormal
|
||||
m_averager.run(sum, m_averageSuares); // average of squares
|
||||
double rms = sqrt(m_averageSuares); // rms (sort of ...)
|
||||
@ -62,5 +63,5 @@ void audio::algo::chunkware::CompressorRms::process(double& _in1, double& _in2)
|
||||
* giving comparable results.
|
||||
*/
|
||||
// rest of process
|
||||
audio::algo::chunkware::Compressor::process(_in1, _in2, rms);
|
||||
processDouble(_out, _in, _nbChannel, rms);
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ namespace audio {
|
||||
virtual double getWindow() const {
|
||||
return m_averager.getTc();
|
||||
}
|
||||
// runtime process
|
||||
virtual void initRuntime(); // call before runtime (in resume())
|
||||
void process(double& _in1, double& _in2); // compressor runtime process
|
||||
public:
|
||||
virtual void init();
|
||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||
protected:
|
||||
audio::algo::chunkware::EnvelopeDetector m_averager; //!< averager
|
||||
double m_averageSuares; //!< average of squares
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <audio/algo/chunkware/debug.h>
|
||||
|
||||
audio::algo::chunkware::Limiter::Limiter() :
|
||||
m_isConfigured(false),
|
||||
m_threshdB(0.0),
|
||||
m_threshold(1.0),
|
||||
m_peakHold(0),
|
||||
@ -39,8 +40,8 @@ audio::algo::chunkware::Limiter::Limiter() :
|
||||
m_bufferMask(BUFFER_SIZE-1),
|
||||
m_cursor(0) {
|
||||
setAttack(1.0);
|
||||
m_outputBuffer[ 0 ].resize(BUFFER_SIZE, 0.0);
|
||||
m_outputBuffer[ 1 ].resize(BUFFER_SIZE, 0.0);
|
||||
m_outputBuffer.resize(1);
|
||||
m_outputBuffer[0].resize(BUFFER_SIZE, 0.0);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::setThreshold(double _dB) {
|
||||
@ -64,13 +65,17 @@ void audio::algo::chunkware::Limiter::setSampleRate(double _sampleRate) {
|
||||
m_release.setSampleRate(_sampleRate);
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::initRuntime() {
|
||||
void audio::algo::chunkware::Limiter::init(int8_t _nbChannel) {
|
||||
m_peakTimer = 0;
|
||||
m_maxPeak = m_threshold;
|
||||
m_overThresholdEnvelope = m_threshold;
|
||||
m_cursor = 0;
|
||||
m_outputBuffer[ 0 ].assign(BUFFER_SIZE, 0.0);
|
||||
m_outputBuffer[ 1 ].assign(BUFFER_SIZE, 0.0);
|
||||
m_outputBuffer.resize(_nbChannel);
|
||||
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||
m_outputBuffer[iii].resize(BUFFER_SIZE, 0.0);
|
||||
m_outputBuffer[iii].assign(BUFFER_SIZE, 0.0);
|
||||
}
|
||||
m_isConfigured = true;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::FastEnvelope::setCoef() {
|
||||
@ -78,22 +83,41 @@ void audio::algo::chunkware::FastEnvelope::setCoef() {
|
||||
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) {
|
||||
std::vector<enum audio::format> audio::algo::chunkware::Limiter::getSupportedFormat() {
|
||||
std::vector<enum audio::format> out = getNativeSupportedFormat();
|
||||
out.push_back(audio::format_int16);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<enum audio::format> audio::algo::chunkware::Limiter::getNativeSupportedFormat() {
|
||||
std::vector<enum audio::format> out;
|
||||
out.push_back(audio::format_double);
|
||||
return out;
|
||||
}
|
||||
|
||||
void audio::algo::chunkware::Limiter::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);
|
||||
if (_nbChannel != m_outputBuffer.size()) {
|
||||
AA_CHUNK_ERROR("Can not compress with Other than nb channel configured ... channel: " << _nbChannel << " != " << m_outputBuffer.size());
|
||||
}
|
||||
if (m_isConfigured == false) {
|
||||
AA_CHUNK_ERROR("Algo is not initialized...");
|
||||
}
|
||||
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) {
|
||||
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));
|
||||
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;
|
||||
@ -102,114 +126,24 @@ void audio::algo::chunkware::Limiter::process(audio::format _format, void* _outp
|
||||
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]);
|
||||
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;
|
||||
AA_CHUNK_ERROR("Can not Limit with unsupported format : " << _format);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
void audio::algo::chunkware::Limiter::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);
|
||||
}
|
||||
// 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
|
||||
double rect1 = fabs(_in1); // rectify input
|
||||
double rect2 = fabs(_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) {
|
||||
keyLink = m_threshold;
|
||||
@ -262,16 +196,19 @@ void audio::algo::chunkware::Limiter::process(double& _in1, double& _in2) {
|
||||
// (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] = _in1;
|
||||
m_outputBuffer[1][m_cursor] = _in2;
|
||||
double delay[_nbChannel];
|
||||
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||
delay[iii] = m_outputBuffer[iii][delayIndex];
|
||||
// load current buffer index and advance current index
|
||||
// m_bufferMask wraps m_cursor index
|
||||
m_outputBuffer[iii][m_cursor] = _in[iii];
|
||||
}
|
||||
++m_cursor &= m_bufferMask;
|
||||
// output gain
|
||||
_in1 = delay1 * gR; // apply gain reduction to input
|
||||
_in2 = delay2 * gR;
|
||||
for (int8_t iii=0; iii<_nbChannel; ++iii) {
|
||||
// apply gain reduction to input
|
||||
_out[iii] = delay[iii] * 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
|
||||
@ -292,4 +229,4 @@ void audio::algo::chunkware::Limiter::process(double& _in1, double& _in2) {
|
||||
*
|
||||
* (... or replace with your favorite branchless clipper ...)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace audio {
|
||||
// class for faster attack/release
|
||||
class FastEnvelope : public audio::algo::chunkware::EnvelopeDetector {
|
||||
public:
|
||||
FastEnvelope(double _ms = 1.0, double _sampleRate = 44100.0) :
|
||||
FastEnvelope(double _ms = 1.0, double _sampleRate = 48000.0) :
|
||||
EnvelopeDetector(_ms, _sampleRate) {
|
||||
|
||||
}
|
||||
@ -56,7 +56,41 @@ namespace audio {
|
||||
public:
|
||||
Limiter();
|
||||
virtual ~Limiter() {}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Initialize the Algorithm
|
||||
* @param[in] _nbChannel Number of channel in the stream.
|
||||
*/
|
||||
virtual void init(int8_t _nbChannel);
|
||||
/**
|
||||
* @brief Get list of format suported in input.
|
||||
* @return list of supported format
|
||||
*/
|
||||
std::vector<enum audio::format> getSupportedFormat();
|
||||
/**
|
||||
* @brief Get list of algorithm format suported. No format convertion.
|
||||
* @return list of supported format
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
void process(void* _output, const void* _input, size_t _nbChunk, int8_t _nbChannel = 2, enum audio::format _format = audio::format_double);
|
||||
protected:
|
||||
void processDouble(double* _out, const double* _in, int8_t _nbChannel);
|
||||
/*
|
||||
void process(float* _out, const float* _in, int8_t _nbChannel);
|
||||
void process(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);
|
||||
void process(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);
|
||||
void process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||
*/
|
||||
protected:
|
||||
double m_threshdB; //!< threshold (dB)
|
||||
public:
|
||||
@ -91,49 +125,35 @@ namespace audio {
|
||||
protected:
|
||||
|
||||
public:
|
||||
// sample rate dependencies
|
||||
/**
|
||||
* @brief Set sample rate.
|
||||
* @param[in] _sampleRate New sample rate value.
|
||||
*/
|
||||
virtual void setSampleRate(double _sampleRate);
|
||||
/**
|
||||
* @brief Get current sample rate.
|
||||
* @return Vlue of the sample rate.
|
||||
*/
|
||||
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:
|
||||
/*
|
||||
void process(double* _out, const double* _in, int8_t _nbChannel);
|
||||
void process(float* _out, const float* _in, int8_t _nbChannel);
|
||||
void process(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);
|
||||
void process(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);
|
||||
void process(int32_64_t* _out, const int32_64_t* _in, int8_t _nbChannel);
|
||||
*/
|
||||
// limiter runtime process
|
||||
void process(double& _in1, double& _in2);
|
||||
void processMono(double& _in);
|
||||
private:
|
||||
// transfer function
|
||||
|
||||
double m_threshold; //!< threshold (linear)
|
||||
// max peak
|
||||
|
||||
unsigned int m_peakTimer; //!< peak hold timer
|
||||
uint32_t m_peakTimer; //!< peak hold timer
|
||||
double m_maxPeak; //!< max peak
|
||||
// attack/release envelope
|
||||
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
|
||||
// change this if you require more
|
||||
static const int BUFFER_SIZE = 1024; //!< buffer size (always a power of 2!)
|
||||
unsigned int m_bufferMask; //!< buffer mask
|
||||
unsigned int m_cursor; //!< cursor
|
||||
std::vector< double > m_outputBuffer[2]; //!< output buffer
|
||||
uint32_t m_bufferMask; //!< buffer mask
|
||||
uint32_t m_cursor; //!< cursor
|
||||
std::vector<std::vector<double> > m_outputBuffer; //!< output buffer
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ int main(int _argc, const char** _argv) {
|
||||
algo.setThreshold(0);
|
||||
algo.setAttack(0.1);
|
||||
algo.setRelease(2);
|
||||
algo.initRuntime();
|
||||
algo.init(1);
|
||||
int32_t lastPourcent = -1;
|
||||
for (int32_t iii=0; iii<output.size()/blockSize; ++iii) {
|
||||
if (lastPourcent != 100*iii / (output.size()/blockSize)) {
|
||||
@ -120,7 +120,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(audio::format_double, &output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1);
|
||||
algo.process(&output[iii*blockSize], &inputData[iii*blockSize], blockSize, 1, audio::format_double);
|
||||
if (perf == true) {
|
||||
std11::chrono::steady_clock::time_point timeEnd = std11::chrono::steady_clock::now();
|
||||
std11::chrono::nanoseconds time = timeEnd - timeStart;
|
||||
|
Loading…
Reference in New Issue
Block a user