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