diff --git a/modules/juce_dsp/juce_dsp.h b/modules/juce_dsp/juce_dsp.h index c0b5d737ab..e42dbaf6b3 100644 --- a/modules/juce_dsp/juce_dsp.h +++ b/modules/juce_dsp/juce_dsp.h @@ -241,6 +241,7 @@ namespace juce #include "maths/juce_SpecialFunctions.h" #include "maths/juce_Matrix.h" +#include "maths/juce_Phase.h" #include "maths/juce_Polynomial.h" #include "maths/juce_FastMathApproximations.h" #include "maths/juce_LookupTable.h" diff --git a/modules/juce_dsp/maths/juce_Phase.h b/modules/juce_dsp/maths/juce_Phase.h new file mode 100644 index 0000000000..11817196cf --- /dev/null +++ b/modules/juce_dsp/maths/juce_Phase.h @@ -0,0 +1,66 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ +namespace dsp +{ + +/** + Represents an increasing phase value between 0 and 2*pi. + + This represents a value which can be incremented, and which wraps back to 0 when it + goes past 2 * pi. +*/ +template +struct Phase +{ + /** Resets the phase to 0. */ + void reset() noexcept { phase = 0; } + + /** Returns the current value, and increments the phase by the given increment. + The increment must be a positive value, it can't go backwards! + The new value of the phase after calling this function will be (phase + increment) % (2 * pi). + */ + Type advance (Type increment) noexcept + { + jassert (increment >= 0); // cannot run this value backwards! + + auto last = phase; + auto next = last + increment; + + while (next >= MathConstants::twoPi) + next -= MathConstants::twoPi; + + phase = next; + return last; + } + + Type phase = 0; +}; + +} // namespace dsp +} // namespace juce diff --git a/modules/juce_dsp/processors/juce_Oscillator.h b/modules/juce_dsp/processors/juce_Oscillator.h index d3cf2f51c3..0363a161ea 100644 --- a/modules/juce_dsp/processors/juce_Oscillator.h +++ b/modules/juce_dsp/processors/juce_Oscillator.h @@ -50,7 +50,8 @@ public: If lookup table is not zero, then the function will be approximated with a lookup table. */ - Oscillator (const std::function& function, size_t lookupTableNumPoints = 0) + Oscillator (const std::function& function, + size_t lookupTableNumPoints = 0) { initialise (function, lookupTableNumPoints); } @@ -59,13 +60,14 @@ public: bool isInitialised() const noexcept { return static_cast (generator); } /** Initialises the oscillator with a waveform. */ - void initialise (const std::function& function, size_t lookupTableNumPoints = 0) + void initialise (const std::function& function, + size_t lookupTableNumPoints = 0) { if (lookupTableNumPoints != 0) { auto* table = new LookupTableTransform (function, - static_cast (-1.0 * MathConstants::pi), - static_cast (MathConstants::pi), + -MathConstants::pi, + MathConstants::pi, lookupTableNumPoints); lookupTable = table; @@ -97,7 +99,7 @@ public: /** Resets the internal state of the oscillator */ void reset() noexcept { - pos = 0.0; + phase.reset(); if (sampleRate > 0) frequency.reset (sampleRate, 0.05); @@ -108,11 +110,8 @@ public: SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType) noexcept { jassert (isInitialised()); - auto increment = static_cast (MathConstants::twoPi) * frequency.getNextValue() / sampleRate; - auto value = generator (pos - static_cast (MathConstants::pi)); - pos = std::fmod (pos + increment, static_cast (MathConstants::twoPi)); - - return value; + auto increment = MathConstants::twoPi * frequency.getNextValue() / sampleRate; + return generator (phase.advance (increment) - MathConstants::pi); } /** Processes the input and output buffers supplied in the processing context. */ @@ -128,19 +127,15 @@ public: auto len = outBlock.getNumSamples(); auto numChannels = outBlock.getNumChannels(); - auto baseIncrement = static_cast (MathConstants::twoPi) / sampleRate; + auto baseIncrement = MathConstants::twoPi / sampleRate; if (frequency.isSmoothing()) { auto* buffer = rampBuffer.getRawDataPointer(); for (size_t i = 0; i < len; ++i) - { - buffer[i] = pos - static_cast (MathConstants::pi); - - pos = std::fmod (pos + (baseIncrement * frequency.getNextValue()), - static_cast (MathConstants::twoPi)); - } + buffer[i] = phase.advance (baseIncrement * frequency.getNextValue()) + - MathConstants::pi; for (size_t ch = 0; ch < numChannels; ++ch) { @@ -153,21 +148,18 @@ public: else { auto freq = baseIncrement * frequency.getNextValue(); + auto p = phase; for (size_t ch = 0; ch < numChannels; ++ch) { - auto p = pos; + p = phase; auto* dst = outBlock.getChannelPointer (ch); for (size_t i = 0; i < len; ++i) - { - dst[i] = generator (p - static_cast (MathConstants::pi)); - p = std::fmod (p + freq, static_cast (MathConstants::twoPi)); - } + dst[i] = generator (p.advance (freq) - MathConstants::pi); } - pos = std::fmod (pos + freq * static_cast (len), - static_cast (MathConstants::twoPi)); + phase = p; } } @@ -176,8 +168,9 @@ private: std::function generator; ScopedPointer> lookupTable; Array rampBuffer; - LinearSmoothedValue frequency {static_cast (440.0)}; - NumericType sampleRate = 48000.0, pos = 0.0; + LinearSmoothedValue frequency { static_cast (440.0) }; + NumericType sampleRate = 48000.0; + Phase phase; }; } // namespace dsp