Skip to content

Commit

Permalink
Fix for issue LMMS#3816 - FM or heavy PM in TripleOscillator makes ou…
Browse files Browse the repository at this point in the history
…tputs odd for some target waveforms. (LMMS#5651)

The internal waveforms of the class Oscillator produces the wrong amplitude when the input is a
negative phase. When doing PM or FM, negative phases may occur. When a negative phase is e.g. passed
to the the saw sample, it produces values less than -1.0, hence going out of range.

Converted all fraction calls to absFraction calls.

Removed the +2 in the function Oscillator::recalcPhase. The comment here was that it was needed to avoid
negative phases in case of PM. But by converting fraction to absFraction in the waveforms, negative phases
are not an issue anymore. On top of that the m_phase variable gains about 2 extra bits in precision.
As side effect of that, it improves the behaviour of the issue LMMS#2047 - TripleOscillator: Oscillators are getting out of sync.
Though I did not investigate it thoroughly over different notes and samplerates.

Add documentation to the fraction and absFraction functions in lmms_math.h as it was not immediately clear by the name what the
functions do. Correct the implementation of the functions in case the flag __INTEL_COMPILER is set. (floorf rounds always down).
  • Loading branch information
DigArtRoks authored Sep 10, 2020
1 parent 8f57457 commit 0aef73f
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 9 deletions.
10 changes: 5 additions & 5 deletions include/Oscillator.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class LMMS_EXPORT Oscillator

static inline sample_t triangleSample( const float _sample )
{
const float ph = fraction( _sample );
const float ph = absFraction( _sample );
if( ph <= 0.25f )
{
return ph * 4.0f;
Expand All @@ -111,17 +111,17 @@ class LMMS_EXPORT Oscillator

static inline sample_t sawSample( const float _sample )
{
return -1.0f + fraction( _sample ) * 2.0f;
return -1.0f + absFraction( _sample ) * 2.0f;
}

static inline sample_t squareSample( const float _sample )
{
return ( fraction( _sample ) > 0.5f ) ? -1.0f : 1.0f;
return ( absFraction( _sample ) > 0.5f ) ? -1.0f : 1.0f;
}

static inline sample_t moogSawSample( const float _sample )
{
const float ph = fraction( _sample );
const float ph = absFraction( _sample );
if( ph < 0.5f )
{
return -1.0f + ph * 4.0f;
Expand All @@ -131,7 +131,7 @@ class LMMS_EXPORT Oscillator

static inline sample_t expSample( const float _sample )
{
float ph = fraction( _sample );
float ph = absFraction( _sample );
if( ph > 0.5f )
{
ph = 1.0f - ph;
Expand Down
23 changes: 21 additions & 2 deletions include/lmms_math.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,41 @@ using namespace std;

static inline float absFraction( const float _x )
{
return( _x - ( _x >= 0.0f ? floorf( _x ) : floorf( _x ) - 1 ) );
return( _x - floorf( _x ) );
}

static inline float fraction( const float _x )
{
return( _x - floorf( _x ) );
return( _x - floorf( _x ) - ( _x >= 0.0f ? 0.0 : 1.0 ) );
}

#else

/*!
* @brief Returns the wrapped fractional part of a float, a value between 0.0f and 1.0f.
*
* absFraction( 2.3) => 0.3
* absFraction(-2.3) => 0.7
*
* Note that this not the same as the absolute value of the fraction (as the function name suggests).
* If the result is interpreted as a phase of an oscillator, it makes that negative phases are
* converted to positive phases.
*/
static inline float absFraction( const float _x )
{
return( _x - ( _x >= 0.0f ? static_cast<int>( _x ) :
static_cast<int>( _x ) - 1 ) );
}

/*!
* @brief Returns the fractional part of a float, a value between -1.0f and 1.0f.
*
* fraction( 2.3) => 0.3
* fraction(-2.3) => -0.3
*
* Note that if the return value is used as a phase of an oscillator, that the oscillator must support
* negative phases.
*/
static inline float fraction( const float _x )
{
return( _x - static_cast<int>( _x ) );
Expand Down
3 changes: 1 addition & 2 deletions src/core/Oscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,7 @@ inline void Oscillator::recalcPhase()
m_phaseOffset = m_ext_phaseOffset;
m_phase += m_phaseOffset;
}
m_phase = absFraction( m_phase )+2; // make sure we're not running
// negative when doing PM
m_phase = absFraction( m_phase );
}


Expand Down

0 comments on commit 0aef73f

Please sign in to comment.