1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 namespace dsp 29 { 30 31 /** 32 Generates a signal based on a user-supplied function. 33 34 @tags{DSP} 35 */ 36 template <typename SampleType> 37 class Oscillator 38 { 39 public: 40 /** The NumericType is the underlying primitive type used by the SampleType (which 41 could be either a primitive or vector) 42 */ 43 using NumericType = typename SampleTypeHelpers::ElementType<SampleType>::Type; 44 45 /** Creates an uninitialised oscillator. Call initialise before first use. */ 46 Oscillator() = default; 47 48 /** Creates an oscillator with a periodic input function (-pi..pi). 49 50 If lookup table is not zero, then the function will be approximated 51 with a lookup table. 52 */ 53 Oscillator (const std::function<NumericType (NumericType)>& function, 54 size_t lookupTableNumPoints = 0) 55 { 56 initialise (function, lookupTableNumPoints); 57 } 58 59 /** Returns true if the Oscillator has been initialised. */ isInitialised()60 bool isInitialised() const noexcept { return static_cast<bool> (generator); } 61 62 /** Initialises the oscillator with a waveform. */ 63 void initialise (const std::function<NumericType (NumericType)>& function, 64 size_t lookupTableNumPoints = 0) 65 { 66 if (lookupTableNumPoints != 0) 67 { 68 auto* table = new LookupTableTransform<NumericType> (function, 69 -MathConstants<NumericType>::pi, 70 MathConstants<NumericType>::pi, 71 lookupTableNumPoints); 72 73 lookupTable.reset (table); 74 generator = [table] (NumericType x) { return (*table) (x); }; 75 } 76 else 77 { 78 generator = function; 79 } 80 } 81 82 //============================================================================== 83 /** Sets the frequency of the oscillator. */ 84 void setFrequency (NumericType newFrequency, bool force = false) noexcept 85 { 86 if (force) 87 { 88 frequency.setCurrentAndTargetValue (newFrequency); 89 return; 90 } 91 92 frequency.setTargetValue (newFrequency); 93 } 94 95 /** Returns the current frequency of the oscillator. */ getFrequency()96 NumericType getFrequency() const noexcept { return frequency.getTargetValue(); } 97 98 //============================================================================== 99 /** Called before processing starts. */ prepare(const ProcessSpec & spec)100 void prepare (const ProcessSpec& spec) noexcept 101 { 102 sampleRate = static_cast<NumericType> (spec.sampleRate); 103 rampBuffer.resize ((int) spec.maximumBlockSize); 104 105 reset(); 106 } 107 108 /** Resets the internal state of the oscillator */ reset()109 void reset() noexcept 110 { 111 phase.reset(); 112 113 if (sampleRate > 0) 114 frequency.reset (sampleRate, 0.05); 115 } 116 117 //============================================================================== 118 /** Returns the result of processing a single sample. */ processSample(SampleType input)119 SampleType JUCE_VECTOR_CALLTYPE processSample (SampleType input) noexcept 120 { 121 jassert (isInitialised()); 122 auto increment = MathConstants<NumericType>::twoPi * frequency.getNextValue() / sampleRate; 123 return input + generator (phase.advance (increment) - MathConstants<NumericType>::pi); 124 } 125 126 /** Processes the input and output buffers supplied in the processing context. */ 127 template <typename ProcessContext> process(const ProcessContext & context)128 void process (const ProcessContext& context) noexcept 129 { 130 jassert (isInitialised()); 131 auto&& outBlock = context.getOutputBlock(); 132 auto&& inBlock = context.getInputBlock(); 133 134 // this is an output-only processor 135 jassert (outBlock.getNumSamples() <= static_cast<size_t> (rampBuffer.size())); 136 137 auto len = outBlock.getNumSamples(); 138 auto numChannels = outBlock.getNumChannels(); 139 auto inputChannels = inBlock.getNumChannels(); 140 auto baseIncrement = MathConstants<NumericType>::twoPi / sampleRate; 141 142 if (context.isBypassed) 143 context.getOutputBlock().clear(); 144 145 if (frequency.isSmoothing()) 146 { 147 auto* buffer = rampBuffer.getRawDataPointer(); 148 149 for (size_t i = 0; i < len; ++i) 150 buffer[i] = phase.advance (baseIncrement * frequency.getNextValue()) 151 - MathConstants<NumericType>::pi; 152 153 if (! context.isBypassed) 154 { 155 size_t ch; 156 157 if (context.usesSeparateInputAndOutputBlocks()) 158 { 159 for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch) 160 { 161 auto* dst = outBlock.getChannelPointer (ch); 162 auto* src = inBlock.getChannelPointer (ch); 163 164 for (size_t i = 0; i < len; ++i) 165 dst[i] = src[i] + generator (buffer[i]); 166 } 167 } 168 else 169 { 170 for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch) 171 { 172 auto* dst = outBlock.getChannelPointer (ch); 173 174 for (size_t i = 0; i < len; ++i) 175 dst[i] += generator (buffer[i]); 176 } 177 } 178 179 for (; ch < numChannels; ++ch) 180 { 181 auto* dst = outBlock.getChannelPointer (ch); 182 183 for (size_t i = 0; i < len; ++i) 184 dst[i] = generator (buffer[i]); 185 } 186 } 187 } 188 else 189 { 190 auto freq = baseIncrement * frequency.getNextValue(); 191 auto p = phase; 192 193 if (context.isBypassed) 194 { 195 frequency.skip (static_cast<int> (len)); 196 p.advance (freq * static_cast<NumericType> (len)); 197 } 198 else 199 { 200 size_t ch; 201 202 if (context.usesSeparateInputAndOutputBlocks()) 203 { 204 for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch) 205 { 206 p = phase; 207 auto* dst = outBlock.getChannelPointer (ch); 208 auto* src = inBlock.getChannelPointer (ch); 209 210 for (size_t i = 0; i < len; ++i) 211 dst[i] = src[i] + generator (p.advance (freq) - MathConstants<NumericType>::pi); 212 } 213 } 214 else 215 { 216 for (ch = 0; ch < jmin (numChannels, inputChannels); ++ch) 217 { 218 p = phase; 219 auto* dst = outBlock.getChannelPointer (ch); 220 221 for (size_t i = 0; i < len; ++i) 222 dst[i] += generator (p.advance (freq) - MathConstants<NumericType>::pi); 223 } 224 } 225 226 for (; ch < numChannels; ++ch) 227 { 228 p = phase; 229 auto* dst = outBlock.getChannelPointer (ch); 230 231 for (size_t i = 0; i < len; ++i) 232 dst[i] = generator (p.advance (freq) - MathConstants<NumericType>::pi); 233 } 234 } 235 236 phase = p; 237 } 238 } 239 240 private: 241 //============================================================================== 242 std::function<NumericType (NumericType)> generator; 243 std::unique_ptr<LookupTableTransform<NumericType>> lookupTable; 244 Array<NumericType> rampBuffer; 245 SmoothedValue<NumericType> frequency { static_cast<NumericType> (440.0) }; 246 NumericType sampleRate = 48000.0; 247 Phase<NumericType> phase; 248 }; 249 250 } // namespace dsp 251 } // namespace juce 252