75 static_assert(std::is_floating_point_v<T>,
"Oscillator requires float or double");
98 assert(sampleRate > 0.0);
99 sampleRate_ = sampleRate;
104 if (!syncOn_) primeMinBlep();
125 if (freq != freq)
return;
127 T nyquist =
static_cast<T
>(sampleRate_ * 0.5);
128 frequency_ = std::clamp(freq, T(0), nyquist);
138 if (w == waveform_)
return;
141 if (!syncOn_) primeMinBlep();
156 if (mode == antiAliasing_)
return;
157 antiAliasing_ = mode;
158 if (!syncOn_) primeMinBlep();
187 syncRatio_ = std::max(T(0), ratio);
207 phase_ = phase - std::floor(phase);
208 if (phase_ >= T(1)) phase_ -= T(1);
236 if (!syncOn_) primeMinBlep();
249 return nextSyncSample();
251 return nextMinBlepSample();
260 out =
fastSin(phase_ * twoPi<T>);
264 out = T(2) * phase_ - T(1);
265 out -= polyBlep(phase_, phaseInc_);
270 T raw = (phase_ < T(0.5)) ? T(1) : T(-1);
271 raw += polyBlep(phase_, phaseInc_);
274 T halfPhase = phase_ + T(0.5);
275 if (halfPhase >= T(1)) halfPhase -= T(1);
277 raw -= polyBlep(halfPhase, phaseInc_);
284 T raw = (phase_ < T(0.5)) ? T(1) : T(-1);
285 raw += polyBlep(phase_, phaseInc_);
287 T halfPhase = phase_ + T(0.5);
288 if (halfPhase >= T(1)) halfPhase -= T(1);
289 raw -= polyBlep(halfPhase, phaseInc_);
294 const double inc =
static_cast<double>(phaseInc_);
295 triState_ = inc *
static_cast<double>(raw) + (1.0 - inc) * triState_;
296 out =
static_cast<T
>(triState_) * triNorm_;
303 if (phase_ >= T(1)) phase_ -= T(1);
320 for (
size_t i = 0; i < numSamples; ++i)
336 const int nCh = buffer.getNumChannels();
337 const int nS = buffer.getNumSamples();
340 for (
int i = 0; i < nS; ++i)
344 T* ch0 = buffer.getChannel(0);
345 for (
int i = 0; i < nS; ++i)
347 for (
int ch = 1; ch < nCh; ++ch)
348 std::copy_n(ch0,
static_cast<size_t>(nS), buffer.getChannel(ch));
351 [[nodiscard]] T
getPhase() const noexcept {
return phase_; }
358 [[nodiscard]] T rawSlaveValue(T ph)
const noexcept
381 void seedTriangle() noexcept
383 T ph = syncOn_ ? slavePhase_ : phase_;
386 ph -= phaseInc_ * blepDelay_;
387 ph -= std::floor(ph);
389 const T ideal = (ph < T(0.5)) ? (T(4) * ph - T(1)) : (T(3) - T(4) * ph);
390 triState_ =
static_cast<double>(ideal * triExpectedPeak_);
405 void primeMinBlep() noexcept
410 || !(phaseInc_ > T(0)))
413 const T period = T(1) / phaseInc_;
414 const auto primeEdge = [&](T edgePhase, T jump)
noexcept {
417 T age = (phase_ - edgePhase) * period;
418 if (age < T(0)) age += period;
419 for (; age < static_cast<T>(kCorrLen); age += period)
420 scheduleMinBlep(jump, age);
424 primeEdge(T(0), T(-2));
428 primeEdge(T(0), T(2));
429 primeEdge(T(0.5), T(-2));
442 [[nodiscard]] T nextMinBlepSample() noexcept
444 T raw = rawSlaveValue(phase_) + corr_[
static_cast<size_t>(corrHead_)];
445 corr_[
static_cast<size_t>(corrHead_)] = T(0);
446 corrHead_ = (corrHead_ + 1) & kCorrMask;
455 out -= T(2) * phaseInc_ * blepDelay_;
461 const double inc =
static_cast<double>(phaseInc_);
462 triState_ = inc *
static_cast<double>(raw) + (1.0 - inc) * triState_;
463 out =
static_cast<T
>(triState_) * triNorm_;
466 const T old = phase_;
468 if (phaseInc_ > T(0))
473 const T alpha = (T(1) - old) / phaseInc_;
474 scheduleMinBlep(waveform_ ==
Waveform::Saw ? T(-2) : T(2), T(1) - alpha);
476 else if (waveform_ !=
Waveform::Saw && old < T(0.5) && phase_ >= T(0.5))
478 const T alpha = (T(0.5) - old) / phaseInc_;
479 scheduleMinBlep(T(-2), T(1) - alpha);
482 if (phase_ >= T(1)) phase_ -= T(1);
498 [[nodiscard]] T nextSyncSample() noexcept
500 const bool squareLike =
504 T raw = rawSlaveValue(slavePhase_) + corr_[
static_cast<size_t>(corrHead_)];
505 corr_[
static_cast<size_t>(corrHead_)] = T(0);
506 corrHead_ = (corrHead_ + 1) & kCorrMask;
512 out -= T(2) * slaveInc_ * blepDelay_;
520 const double inc =
static_cast<double>(slaveInc_);
521 triState_ = inc * (
static_cast<double>(raw) -
static_cast<double>(syncSquareDc_))
522 + (1.0 - inc) * triState_;
523 out =
static_cast<T
>(triState_) * triNorm_;
527 const T masterOld = phase_;
528 const T slaveOld = slavePhase_;
530 slavePhase_ += slaveInc_;
532 const bool masterWrap = phase_ >= T(1);
533 const T alphaSync = masterWrap ? (T(1) - masterOld) / phaseInc_ : T(2);
540 T alphaEdge = T(2), edgeJump = T(0);
541 if (slavePhase_ >= T(1))
543 alphaEdge = (T(1) - slaveOld) / slaveInc_;
546 else if (squareLike && slaveOld < T(0.5) && slavePhase_ >= T(0.5))
548 alphaEdge = (T(0.5) - slaveOld) / slaveInc_;
551 if (alphaEdge <= alphaSync && alphaEdge <= T(1))
552 scheduleMinBlep(edgeJump, T(1) - alphaEdge);
554 if (slavePhase_ >= T(1)) slavePhase_ -= T(1);
560 T atJump = slaveOld + alphaSync * slaveInc_;
561 atJump -= std::floor(atJump);
562 const T jump = rawSlaveValue(T(0)) - rawSlaveValue(atJump);
564 scheduleMinBlep(jump, T(1) - alphaSync);
567 slavePhase_ = (phase_ / std::max(phaseInc_, T(1e-12))) * slaveInc_;
568 slavePhase_ -= std::floor(slavePhase_);
580 void scheduleMinBlep(T jump, T frac)
noexcept
583 for (
int j = 0; j < kCorrLen; ++j)
584 corr_[
static_cast<size_t>((corrHead_ + j) & kCorrMask)] +=
585 jump * table.residual(
static_cast<T
>(j) + frac);
597 void reseedSlave() noexcept
599 const T r = slaveInc_ / std::max(phaseInc_, T(1e-12));
600 slavePhase_ = phase_ * r;
601 slavePhase_ -= std::floor(slavePhase_);
613 static inline T polyBlep(T phase, T inc)
noexcept
615 if (inc < T(1e-10))
return T(0);
620 return t + t - t * t - T(1);
622 else if (phase > T(1) - inc)
624 T t = (phase - T(1)) / inc;
625 return t * t + t + t + T(1);
631 void updatePhaseInc() noexcept
637 phaseInc_ = std::min(frequency_ /
static_cast<T
>(sampleRate_), T(0.5));
638 const bool wasOn = syncOn_;
639 syncOn_ = syncRatio_ > T(1.001) && phaseInc_ > T(0);
641 slaveInc_ = std::min(phaseInc_ * syncRatio_, T(0.5));
648 syncSquareDc_ = T(0);
649 if (syncOn_ && phaseInc_ > T(0))
651 const T r = slaveInc_ / phaseInc_;
652 const T f = r - std::floor(r);
653 syncSquareDc_ = (std::min(f, T(0.5)) - std::max(f - T(0.5), T(0))) / r;
658 if (syncOn_ && !wasOn)
667 void updateTriNorm() noexcept
670 const T inc = syncOn_ ? slaveInc_ : phaseInc_;
671 if (inc == triNormInc_)
676 if (inc > T(0) && inc < T(1))
682 T leakCoeff = T(1) - inc;
683 T halfPeriodSamples = T(0.5) / inc;
684 T q = std::pow(leakCoeff, halfPeriodSamples);
685 T expectedPeak = (T(1) - q) / (T(1) + q);
686 triExpectedPeak_ = expectedPeak;
687 triNorm_ = (expectedPeak > T(0.001)) ? T(1) / expectedPeak : T(4);
691 triExpectedPeak_ = T(0.25);
696 double sampleRate_ = 48000.0;
697 T frequency_ = T(440);
700 double triState_ = 0.0;
702 T triExpectedPeak_ = T(0.25);
703 T triNormInc_ = T(-1);
710 static constexpr int kCorrMask = kCorrLen - 1;
711 static_assert((kCorrLen & kCorrMask) == 0,
"minBLEP ring needs a power-of-two span");
713 bool syncOn_ =
false;
715 T slavePhase_ = T(0);
717 T syncSquareDc_ = T(0);
718 std::array<T, kCorrLen> corr_{};