Implement fade in to prevent Triple Osc from clicking (#5199)

This commit is contained in:
necrashter
2020-05-19 05:34:54 +03:00
committed by GitHub
parent b818234c15
commit 7f9b4c2401
4 changed files with 100 additions and 4 deletions

View File

@@ -132,6 +132,9 @@ public:
protected:
// fade in to prevent clicks
void applyFadeIn(sampleFrame * buf, NotePlayHandle * n);
// instruments may use this to apply a soft fade out at the end of
// notes - method does this only if really less or equal
// desiredReleaseFrames() frames are left

View File

@@ -49,6 +49,9 @@ public:
void * m_pluginData;
std::unique_ptr<BasicFilters<>> m_filter;
// length of the declicking fade in
fpp_t m_fadeInLength;
// specifies origin of NotePlayHandle
enum Origins
{

View File

@@ -364,6 +364,7 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
osc_l->update( _working_buffer + offset, frames, 0 );
osc_r->update( _working_buffer + offset, frames, 1 );
applyFadeIn(_working_buffer, _n);
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );

View File

@@ -23,8 +23,12 @@
*/
#include "Instrument.h"
#include "InstrumentTrack.h"
#include <cmath>
#include "DummyInstrument.h"
#include "InstrumentTrack.h"
#include "lmms_constants.h"
Instrument::Instrument(InstrumentTrack * _instrument_track,
@@ -78,8 +82,96 @@ bool Instrument::isFromTrack( const Track * _track ) const
return( m_instrumentTrack == _track );
}
// helper function for Instrument::applyFadeIn
static int countZeroCrossings(sampleFrame *buf, fpp_t start, fpp_t frames)
{
// zero point crossing counts of all channels
int zeroCrossings[DEFAULT_CHANNELS] = {0};
// maximum zero point crossing of all channels
int maxZeroCrossings = 0;
// determine the zero point crossing counts
for (fpp_t f = start; f < frames; ++f)
{
for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
{
// we don't want to count [-1, 0, 1] as two crossings
if ((buf[f - 1][ch] <= 0.0 && buf[f][ch] > 0.0) ||
(buf[f - 1][ch] >= 0.0 && buf[f][ch] < 0.0))
{
++zeroCrossings[ch];
if (zeroCrossings[ch] > maxZeroCrossings)
{
maxZeroCrossings = zeroCrossings[ch];
}
}
}
}
return maxZeroCrossings;
}
// helper function for Instrument::applyFadeIn
fpp_t getFadeInLength(float maxLength, fpp_t frames, int zeroCrossings)
{
// calculate the length of the fade in
// Length is inversely proportional to the max of zeroCrossings,
// because for low frequencies, we need a longer fade in to
// prevent clicking.
return (fpp_t) (maxLength / ((float) zeroCrossings / ((float) frames / 128.0f) + 1.0f));
}
void Instrument::applyFadeIn(sampleFrame * buf, NotePlayHandle * n)
{
const static float MAX_FADE_IN_LENGTH = 85.0;
f_cnt_t total = n->totalFramesPlayed();
if (total == 0)
{
const fpp_t frames = n->framesLeftForCurrentPeriod();
const f_cnt_t offset = n->offset();
// We need to skip the first sample because it almost always
// produces a zero crossing; it's not helpful while
// determining the fade in length. Hence 1
int maxZeroCrossings = countZeroCrossings(buf, offset + 1, offset + frames);
fpp_t length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, maxZeroCrossings);
n->m_fadeInLength = length;
// apply fade in
length = length < frames ? length : frames;
for (fpp_t f = 0; f < length; ++f)
{
for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
{
buf[offset + f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) f / (float) n->m_fadeInLength);
}
}
}
else if (total < n->m_fadeInLength)
{
const fpp_t frames = n->framesLeftForCurrentPeriod();
int new_zc = countZeroCrossings(buf, 1, frames);
fpp_t new_length = getFadeInLength(MAX_FADE_IN_LENGTH, frames, new_zc);
for (fpp_t f = 0; f < frames; ++f)
{
for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch)
{
float currentLength = n->m_fadeInLength * (1.0f - (float) f / frames) + new_length * ((float) f / frames);
buf[f][ch] *= 0.5 - 0.5 * cosf(F_PI * (float) (total + f) / currentLength);
if (total + f >= currentLength)
{
n->m_fadeInLength = currentLength;
return;
}
}
}
n->m_fadeInLength = new_length;
}
}
void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n )
{
@@ -109,6 +201,3 @@ QString Instrument::fullDisplayName() const
{
return instrumentTrack()->displayName();
}