#pragma once #include #include enum class Waveform { Sine = 0, Saw, Square, Triangle, Noise, Pulse25, Pulse12, ReverseSaw, FullRectSine, HalfRectSine, Stair4, Stair8, SoftSine, Sinc, ExpPulse, DoubleSine, SuperSaw, NumWaveforms }; class Oscillator { public: void prepare(double sampleRate) { this->sampleRate = sampleRate; phase = 0.0; phaseIncrement = 0.0; for (int i = 0; i < numSuperSaws; ++i) superSawPhase[i] = i * (1.0f / static_cast(numSuperSaws)); } void setFrequency(float freq) { frequency = freq; if (sampleRate > 0.0) phaseIncrement = frequency / static_cast(sampleRate); } void setWaveform(Waveform wf) { waveform = wf; } void setLevel(float l) { level = std::clamp(l, 0.0f, 1.0f); } void setPan(float p) { pan = std::clamp(p, -1.0f, 1.0f); } void setSemitone(float s) { semitoneOffset = s; } void setFineTune(float f) { fineTune = f; } void setPhase(float p) { phase = p; } float getFrequency() const { return frequency; } void process(float* leftOut, float* rightOut) { float output = 0.0f; switch (waveform) { case Waveform::Sine: output = std::sin(phase * 2.0f * pi); break; case Waveform::Saw: output = 2.0f * (phase - 0.5f); break; case Waveform::Square: output = (phase < 0.5f) ? 1.0f : -1.0f; break; case Waveform::Triangle: output = 4.0f * std::abs(phase - 0.5f) - 1.0f; break; case Waveform::Noise: output = random.nextFloat() * 2.0f - 1.0f; break; case Waveform::Pulse25: output = (phase < 0.25f) ? 1.0f : -1.0f; break; case Waveform::Pulse12: output = (phase < 0.12f) ? 1.0f : -1.0f; break; case Waveform::ReverseSaw: output = 2.0f * phase - 1.0f; break; case Waveform::FullRectSine: output = std::fabs(std::sin(phase * 2.0f * pi)); break; case Waveform::HalfRectSine: output = std::max(0.0f, std::sin(phase * 2.0f * pi)); break; case Waveform::Stair4: { float s = std::floor(phase * 4.0f); output = -1.0f + 2.0f * ((s + 0.5f) / 4.0f); break; } case Waveform::Stair8: { float s = std::floor(phase * 8.0f); output = -1.0f + 2.0f * ((s + 0.5f) / 8.0f); break; } case Waveform::SoftSine: output = std::tanh(3.0f * std::sin(phase * 2.0f * pi)) / std::tanh(3.0f); break; case Waveform::Sinc: { float x = phase * 2.0f * pi * 6.0f; output = (x != 0.0f) ? std::sin(x) / x : 1.0f; break; } case Waveform::ExpPulse: output = (phase < 0.5f) ? std::exp(-phase * 10.0f) : -std::exp(-(phase - 0.5f) * 10.0f); break; case Waveform::DoubleSine: output = 0.5f * std::sin(phase * 2.0f * pi) + 0.5f * std::sin(phase * 4.0f * pi); break; case Waveform::SuperSaw: { static constexpr int numSaws = 7; static constexpr float detunes[numSaws] = { -12.0f, -8.0f, -4.0f, 0.0f, 4.0f, 8.0f, 12.0f }; output = 0.0f; for (int i = 0; i < numSaws; ++i) { superSawPhase[i] += phaseIncrement * std::pow(2.0f, detunes[i] / 1200.0f); if (superSawPhase[i] >= 1.0f) superSawPhase[i] -= 1.0f; output += 2.0f * (superSawPhase[i] - 0.5f); } output /= static_cast(numSaws); break; } default: output = 0.0f; break; } phase += phaseIncrement; if (phase >= 1.0f) phase -= 1.0f; output *= level; float leftGain = std::sqrt((1.0f - pan) * 0.5f); float rightGain = std::sqrt((1.0f + pan) * 0.5f); *leftOut += output * leftGain; *rightOut += output * rightGain; } float getCurrentPhase() const { return phase; } private: static constexpr int numSuperSaws = 7; static constexpr float pi = 3.14159265358979323846f; float frequency = 440.0f; float phase = 0.0f; float phaseIncrement = 0.0f; float superSawPhase[numSuperSaws] = { 0.0f }; float level = 0.7f; float pan = 0.0f; float semitoneOffset = 0.0f; float fineTune = 0.0f; double sampleRate = 44100.0; Waveform waveform = Waveform::Saw; juce::Random random; };