mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-04 22:26:07 -05:00
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 #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).
215 lines
4.9 KiB
C++
215 lines
4.9 KiB
C++
/*
|
|
* Oscillator.h - declaration of class Oscillator
|
|
*
|
|
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
|
*
|
|
* This file is part of LMMS - https://lmms.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program (see COPYING); if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#ifndef OSCILLATOR_H
|
|
#define OSCILLATOR_H
|
|
|
|
#include "lmmsconfig.h"
|
|
|
|
#include <math.h>
|
|
|
|
#ifdef LMMS_HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include "SampleBuffer.h"
|
|
#include "lmms_constants.h"
|
|
|
|
class IntModel;
|
|
|
|
|
|
class LMMS_EXPORT Oscillator
|
|
{
|
|
MM_OPERATORS
|
|
public:
|
|
enum WaveShapes
|
|
{
|
|
SineWave,
|
|
TriangleWave,
|
|
SawWave,
|
|
SquareWave,
|
|
MoogSawWave,
|
|
ExponentialWave,
|
|
WhiteNoise,
|
|
UserDefinedWave,
|
|
NumWaveShapes
|
|
} ;
|
|
|
|
enum ModulationAlgos
|
|
{
|
|
PhaseModulation,
|
|
AmplitudeModulation,
|
|
SignalMix,
|
|
SynchronizedBySubOsc,
|
|
FrequencyModulation,
|
|
NumModulationAlgos
|
|
} ;
|
|
|
|
|
|
Oscillator( const IntModel * _wave_shape_model,
|
|
const IntModel * _mod_algo_model,
|
|
const float & _freq,
|
|
const float & _detuning,
|
|
const float & _phase_offset,
|
|
const float & _volume,
|
|
Oscillator * _m_subOsc = NULL );
|
|
virtual ~Oscillator()
|
|
{
|
|
delete m_subOsc;
|
|
}
|
|
|
|
|
|
inline void setUserWave( const SampleBuffer * _wave )
|
|
{
|
|
m_userWave = _wave;
|
|
}
|
|
|
|
void update( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
|
|
// now follow the wave-shape-routines...
|
|
|
|
static inline sample_t sinSample( const float _sample )
|
|
{
|
|
return sinf( _sample * F_2PI );
|
|
}
|
|
|
|
static inline sample_t triangleSample( const float _sample )
|
|
{
|
|
const float ph = absFraction( _sample );
|
|
if( ph <= 0.25f )
|
|
{
|
|
return ph * 4.0f;
|
|
}
|
|
else if( ph <= 0.75f )
|
|
{
|
|
return 2.0f - ph * 4.0f;
|
|
}
|
|
return ph * 4.0f - 4.0f;
|
|
}
|
|
|
|
static inline sample_t sawSample( const float _sample )
|
|
{
|
|
return -1.0f + absFraction( _sample ) * 2.0f;
|
|
}
|
|
|
|
static inline sample_t squareSample( const float _sample )
|
|
{
|
|
return ( absFraction( _sample ) > 0.5f ) ? -1.0f : 1.0f;
|
|
}
|
|
|
|
static inline sample_t moogSawSample( const float _sample )
|
|
{
|
|
const float ph = absFraction( _sample );
|
|
if( ph < 0.5f )
|
|
{
|
|
return -1.0f + ph * 4.0f;
|
|
}
|
|
return 1.0f - 2.0f * ph;
|
|
}
|
|
|
|
static inline sample_t expSample( const float _sample )
|
|
{
|
|
float ph = absFraction( _sample );
|
|
if( ph > 0.5f )
|
|
{
|
|
ph = 1.0f - ph;
|
|
}
|
|
return -1.0f + 8.0f * ph * ph;
|
|
}
|
|
|
|
static inline sample_t noiseSample( const float )
|
|
{
|
|
// Precise implementation
|
|
// return 1.0f - rand() * 2.0f / RAND_MAX;
|
|
|
|
// Fast implementation
|
|
return 1.0f - fast_rand() * 2.0f / FAST_RAND_MAX;
|
|
}
|
|
|
|
inline sample_t userWaveSample( const float _sample ) const
|
|
{
|
|
return m_userWave->userWaveSample( _sample );
|
|
}
|
|
|
|
|
|
private:
|
|
const IntModel * m_waveShapeModel;
|
|
const IntModel * m_modulationAlgoModel;
|
|
const float & m_freq;
|
|
const float & m_detuning;
|
|
const float & m_volume;
|
|
const float & m_ext_phaseOffset;
|
|
Oscillator * m_subOsc;
|
|
float m_phaseOffset;
|
|
float m_phase;
|
|
const SampleBuffer * m_userWave;
|
|
|
|
|
|
void updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
void updatePM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
void updateAM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
void updateMix( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
void updateSync( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
void updateFM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
|
|
float syncInit( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
inline bool syncOk( float _osc_coeff );
|
|
|
|
template<WaveShapes W>
|
|
void updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
template<WaveShapes W>
|
|
void updatePM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
template<WaveShapes W>
|
|
void updateAM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
template<WaveShapes W>
|
|
void updateMix( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
template<WaveShapes W>
|
|
void updateSync( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
template<WaveShapes W>
|
|
void updateFM( sampleFrame * _ab, const fpp_t _frames,
|
|
const ch_cnt_t _chnl );
|
|
|
|
template<WaveShapes W>
|
|
inline sample_t getSample( const float _sample );
|
|
|
|
inline void recalcPhase();
|
|
|
|
} ;
|
|
|
|
|
|
#endif
|