Skip to content

Commit

Permalink
Merge pull request #187 from infrasonicaudio/osc-tri-polyblep-fix
Browse files Browse the repository at this point in the history
Fix polyblep triangle implementation
  • Loading branch information
stephenhensley committed Jul 7, 2023
2 parents 4f9a962 + c8ea8da commit 0b23cbb
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 30 deletions.
35 changes: 16 additions & 19 deletions Source/Synthesis/oscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,68 @@
using namespace daisysp;
static inline float Polyblep(float phase_inc, float t);

constexpr float TWO_PI_RECIP = 1.0f / TWOPI_F;

float Oscillator::Process()
{
float out, t;
switch(waveform_)
{
case WAVE_SIN: out = sinf(phase_); break;
case WAVE_SIN: out = sinf(phase_ * TWOPI_F); break;
case WAVE_TRI:
t = -1.0f + (2.0f * phase_ * TWO_PI_RECIP);
t = -1.0f + (2.0f * phase_);
out = 2.0f * (fabsf(t) - 0.5f);
break;
case WAVE_SAW:
out = -1.0f * (((phase_ * TWO_PI_RECIP * 2.0f)) - 1.0f);
break;
case WAVE_RAMP: out = ((phase_ * TWO_PI_RECIP * 2.0f)) - 1.0f; break;
case WAVE_SQUARE: out = phase_ < pw_rad_ ? (1.0f) : -1.0f; break;
case WAVE_SAW: out = -1.0f * (((phase_ * 2.0f)) - 1.0f); break;
case WAVE_RAMP: out = ((phase_ * 2.0f)) - 1.0f; break;
case WAVE_SQUARE: out = phase_ < pw_ ? (1.0f) : -1.0f; break;
case WAVE_POLYBLEP_TRI:
t = phase_ * TWO_PI_RECIP;
out = phase_ < PI_F ? 1.0f : -1.0f;
t = phase_;
out = phase_ < 0.5f ? 1.0f : -1.0f;
out += Polyblep(phase_inc_, t);
out -= Polyblep(phase_inc_, fmodf(t + 0.5f, 1.0f));
// Leaky Integrator:
// y[n] = A + x[n] + (1 - A) * y[n-1]
out = phase_inc_ * out + (1.0f - phase_inc_) * last_out_;
last_out_ = out;
out *= 4.f; // normalize amplitude after leaky integration
break;
case WAVE_POLYBLEP_SAW:
t = phase_ * TWO_PI_RECIP;
t = phase_;
out = (2.0f * t) - 1.0f;
out -= Polyblep(phase_inc_, t);
out *= -1.0f;
break;
case WAVE_POLYBLEP_SQUARE:
t = phase_ * TWO_PI_RECIP;
out = phase_ < pw_rad_ ? 1.0f : -1.0f;
t = phase_;
out = phase_ < pw_ ? 1.0f : -1.0f;
out += Polyblep(phase_inc_, t);
out -= Polyblep(phase_inc_, fmodf(t + (1.0f - pw_), 1.0f));
out *= 0.707f; // ?
break;
default: out = 0.0f; break;
}
phase_ += phase_inc_;
if(phase_ > TWOPI_F)
if(phase_ > 1.0f)
{
phase_ -= TWOPI_F;
phase_ -= 1.0f;
eoc_ = true;
}
else
{
eoc_ = false;
}
eor_ = (phase_ - phase_inc_ < PI_F && phase_ >= PI_F);
eor_ = (phase_ - phase_inc_ < 0.5f && phase_ >= 0.5f);

return out * amp_;
}

float Oscillator::CalcPhaseInc(float f)
{
return (TWOPI_F * f) * sr_recip_;
return f * sr_recip_;
}

static float Polyblep(float phase_inc, float t)
{
float dt = phase_inc * TWO_PI_RECIP;
float dt = phase_inc;
if(t < dt)
{
t /= dt;
Expand Down
17 changes: 6 additions & 11 deletions Source/Synthesis/oscillator.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class Oscillator
freq_ = 100.0f;
amp_ = 0.5f;
pw_ = 0.5f;
pw_rad_ = pw_ * TWOPI_F;
phase_ = 0.0f;
phase_inc_ = CalcPhaseInc(freq_);
waveform_ = WAVE_SIN;
Expand Down Expand Up @@ -75,11 +74,7 @@ class Oscillator
}
/** Sets the pulse width for WAVE_SQUARE and WAVE_POLYBLEP_SQUARE (range 0 - 1)
*/
inline void SetPw(const float pw)
{
pw_ = fclamp(pw, 0.0f, 1.0f);
pw_rad_ = pw_ * TWOPI_F;
}
inline void SetPw(const float pw) { pw_ = fclamp(pw, 0.0f, 1.0f); }

/** Returns true if cycle is at end of rise. Set during call to Process.
*/
Expand All @@ -91,28 +86,28 @@ class Oscillator

/** Returns true if cycle rising.
*/
inline bool IsRising() { return phase_ < PI_F; }
inline bool IsRising() { return phase_ < 0.5f; }

/** Returns true if cycle falling.
*/
inline bool IsFalling() { return phase_ >= PI_F; }
inline bool IsFalling() { return phase_ >= 0.5f; }

/** Processes the waveform to be generated, returning one sample. This should be called once per sample period.
*/
float Process();


/** Adds a value 0.0-1.0 (mapped to 0.0-TWO_PI) to the current phase. Useful for PM and "FM" synthesis.
/** Adds a value 0.0-1.0 (equivalent to 0.0-TWO_PI) to the current phase. Useful for PM and "FM" synthesis.
*/
void PhaseAdd(float _phase) { phase_ += (_phase * TWOPI_F); }
void PhaseAdd(float _phase) { phase_ += _phase; }
/** Resets the phase to the input argument. If no argumeNt is present, it will reset phase to 0.0;
*/
void Reset(float _phase = 0.0f) { phase_ = _phase; }

private:
float CalcPhaseInc(float f);
uint8_t waveform_;
float amp_, freq_, pw_, pw_rad_;
float amp_, freq_, pw_;
float sr_, sr_recip_, phase_, phase_inc_;
float last_out_, last_freq_;
bool eor_, eoc_;
Expand Down

0 comments on commit 0b23cbb

Please sign in to comment.