Skip to content

Commit

Permalink
Implement fade in to prevent Triple Osc from clicking (#5199)
Browse files Browse the repository at this point in the history
  • Loading branch information
necrashter authored May 19, 2020
1 parent b818234 commit 7f9b4c2
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 4 deletions.
3 changes: 3 additions & 0 deletions include/Instrument.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ class LMMS_EXPORT Instrument : public Plugin


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
Expand Down
3 changes: 3 additions & 0 deletions include/NotePlayHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class LMMS_EXPORT NotePlayHandle : public PlayHandle, public Note
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
{
Expand Down
1 change: 1 addition & 0 deletions plugins/triple_oscillator/TripleOscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
97 changes: 93 additions & 4 deletions src/core/Instrument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -109,6 +201,3 @@ QString Instrument::fullDisplayName() const
{
return instrumentTrack()->displayName();
}



0 comments on commit 7f9b4c2

Please sign in to comment.