Implement fade in to prevent Triple Osc from clicking (#5199)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user