mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-18 11:40:33 +02:00
170 lines
5.6 KiB
C++
170 lines
5.6 KiB
C++
#ifndef STK_BLITSQUARE_H
|
|
#define STK_BLITSQUARE_H
|
|
|
|
#include "Generator.h"
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
namespace stk {
|
|
|
|
/***************************************************/
|
|
/*! \class BlitSquare
|
|
\brief STK band-limited square wave class.
|
|
|
|
This class generates a band-limited square wave signal. It is
|
|
derived in part from the approach reported by Stilson and Smith in
|
|
"Alias-Free Digital Synthesis of Classic Analog Waveforms", 1996.
|
|
The algorithm implemented in this class uses a SincM function with
|
|
an even M value to achieve a bipolar bandlimited impulse train.
|
|
This signal is then integrated to achieve a square waveform. The
|
|
integration process has an associated DC offset so a DC blocking
|
|
filter is applied at the output.
|
|
|
|
The user can specify both the fundamental frequency of the
|
|
waveform and the number of harmonics contained in the resulting
|
|
signal.
|
|
|
|
If nHarmonics is 0, then the signal will contain all harmonics up
|
|
to half the sample rate. Note, however, that this setting may
|
|
produce aliasing in the signal when the frequency is changing (no
|
|
automatic modification of the number of harmonics is performed by
|
|
the setFrequency() function). Also note that the harmonics of a
|
|
square wave fall at odd integer multiples of the fundamental, so
|
|
aliasing will happen with a lower fundamental than with the other
|
|
Blit waveforms. This class is not guaranteed to be well behaved
|
|
in the presence of significant aliasing.
|
|
|
|
Based on initial code of Robin Davies, 2005.
|
|
Modified algorithm code by Gary Scavone, 2005 - 2006.
|
|
*/
|
|
/***************************************************/
|
|
|
|
class BlitSquare: public Generator
|
|
{
|
|
public:
|
|
//! Default constructor that initializes BLIT frequency to 220 Hz.
|
|
BlitSquare( StkFloat frequency = 220.0 );
|
|
|
|
//! Class destructor.
|
|
~BlitSquare();
|
|
|
|
//! Resets the oscillator state and phase to 0.
|
|
void reset();
|
|
|
|
//! Set the phase of the signal.
|
|
/*!
|
|
Set the phase of the signal, in the range 0 to 1.
|
|
*/
|
|
void setPhase( StkFloat phase ) { phase_ = PI * phase; };
|
|
|
|
//! Get the current phase of the signal.
|
|
/*!
|
|
Get the phase of the signal, in the range [0 to 1.0).
|
|
*/
|
|
StkFloat getPhase() const { return phase_ / PI; };
|
|
|
|
//! Set the impulse train rate in terms of a frequency in Hz.
|
|
void setFrequency( StkFloat frequency );
|
|
|
|
//! Set the number of harmonics generated in the signal.
|
|
/*!
|
|
This function sets the number of harmonics contained in the
|
|
resulting signal. It is equivalent to (2 * M) + 1 in the BLIT
|
|
algorithm. The default value of 0 sets the algorithm for maximum
|
|
harmonic content (harmonics up to half the sample rate). This
|
|
parameter is not checked against the current sample rate and
|
|
fundamental frequency. Thus, aliasing can result if one or more
|
|
harmonics for a given fundamental frequency exceeds fs / 2. This
|
|
behavior was chosen over the potentially more problematic solution
|
|
of automatically modifying the M parameter, which can produce
|
|
audible clicks in the signal.
|
|
*/
|
|
void setHarmonics( unsigned int nHarmonics = 0 );
|
|
|
|
//! Return the last computed output value.
|
|
StkFloat lastOut( void ) const { return lastFrame_[0]; };
|
|
|
|
//! Compute and return one output sample.
|
|
StkFloat tick( void );
|
|
|
|
//! Fill a channel of the StkFrames object with computed outputs.
|
|
/*!
|
|
The \c channel argument must be less than the number of
|
|
channels in the StkFrames argument (the first channel is specified
|
|
by 0). However, range checking is only performed if _STK_DEBUG_
|
|
is defined during compilation, in which case an out-of-range value
|
|
will trigger an StkError exception.
|
|
*/
|
|
StkFrames& tick( StkFrames& frames, unsigned int channel = 0 );
|
|
|
|
protected:
|
|
|
|
void updateHarmonics( void );
|
|
|
|
unsigned int nHarmonics_;
|
|
unsigned int m_;
|
|
StkFloat rate_;
|
|
StkFloat phase_;
|
|
StkFloat p_;
|
|
StkFloat a_;
|
|
StkFloat lastBlitOutput_;
|
|
StkFloat dcbState_;
|
|
};
|
|
|
|
inline StkFloat BlitSquare :: tick( void )
|
|
{
|
|
StkFloat temp = lastBlitOutput_;
|
|
|
|
// A fully optimized version of this would replace the two sin calls
|
|
// with a pair of fast sin oscillators, for which stable fast
|
|
// two-multiply algorithms are well known. In the spirit of STK,
|
|
// which favors clarity over performance, the optimization has
|
|
// not been made here.
|
|
|
|
// Avoid a divide by zero, or use of a denomralized divisor
|
|
// at the sinc peak, which has a limiting value of 1.0.
|
|
StkFloat denominator = sin( phase_ );
|
|
if ( fabs( denominator ) < std::numeric_limits<StkFloat>::epsilon() ) {
|
|
// Inexact comparison safely distinguishes betwen *close to zero*, and *close to PI*.
|
|
if ( phase_ < 0.1f || phase_ > TWO_PI - 0.1f )
|
|
lastBlitOutput_ = a_;
|
|
else
|
|
lastBlitOutput_ = -a_;
|
|
}
|
|
else {
|
|
lastBlitOutput_ = sin( m_ * phase_ );
|
|
lastBlitOutput_ /= p_ * denominator;
|
|
}
|
|
|
|
lastBlitOutput_ += temp;
|
|
|
|
// Now apply DC blocker.
|
|
lastFrame_[0] = lastBlitOutput_ - dcbState_ + 0.999 * lastFrame_[0];
|
|
dcbState_ = lastBlitOutput_;
|
|
|
|
phase_ += rate_;
|
|
if ( phase_ >= TWO_PI ) phase_ -= TWO_PI;
|
|
|
|
return lastFrame_[0];
|
|
}
|
|
|
|
inline StkFrames& BlitSquare :: tick( StkFrames& frames, unsigned int channel )
|
|
{
|
|
#if defined(_STK_DEBUG_)
|
|
if ( channel >= frames.channels() ) {
|
|
oStream_ << "BlitSquare::tick(): channel and StkFrames arguments are incompatible!";
|
|
handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
#endif
|
|
|
|
StkFloat *samples = &frames[channel];
|
|
unsigned int hop = frames.channels();
|
|
for ( unsigned int i=0; i<frames.frames(); i++, samples += hop )
|
|
*samples = BlitSquare::tick();
|
|
|
|
return frames;
|
|
}
|
|
|
|
} // stk namespace
|
|
|
|
#endif
|