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     A 6 stage phaser that modulates first order all-pass filters to create sweeping
33     notches in the magnitude frequency response.
34 
35     This audio effect can be controlled with standard phaser parameters: the speed
36     and depth of the LFO controlling the frequency response, a mix control, a
37     feedback control, and the centre frequency of the modulation.
38 
39     @tags{DSP}
40 */
41 template <typename SampleType>
42 class Phaser
43 {
44 public:
45     //==============================================================================
46     /** Constructor. */
47     Phaser();
48 
49     //==============================================================================
50     /** Sets the rate (in Hz) of the LFO modulating the phaser all-pass filters. This
51         rate must be lower than 100 Hz.
52     */
53     void setRate (SampleType newRateHz);
54 
55     /** Sets the volume (between 0 and 1) of the LFO modulating the phaser all-pass
56         filters.
57     */
58     void setDepth (SampleType newDepth);
59 
60     /** Sets the centre frequency (in Hz) of the phaser all-pass filters modulation.
61     */
62     void setCentreFrequency (SampleType newCentreHz);
63 
64     /** Sets the feedback volume (between -1 and 1) of the phaser. Negative can be
65         used to get specific phaser sounds.
66     */
67     void setFeedback (SampleType newFeedback);
68 
69     /** Sets the amount of dry and wet signal in the output of the phaser (between 0
70         for full dry and 1 for full wet).
71     */
72     void setMix (SampleType newMix);
73 
74     //==============================================================================
75     /** Initialises the processor. */
76     void prepare (const ProcessSpec& spec);
77 
78     /** Resets the internal state variables of the processor. */
79     void reset();
80 
81     //==============================================================================
82     /** Processes the input and output samples supplied in the processing context. */
83     template <typename ProcessContext>
process(const ProcessContext & context)84     void process (const ProcessContext& context) noexcept
85     {
86         const auto& inputBlock = context.getInputBlock();
87         auto& outputBlock      = context.getOutputBlock();
88         const auto numChannels = outputBlock.getNumChannels();
89         const auto numSamples  = outputBlock.getNumSamples();
90 
91         jassert (inputBlock.getNumChannels() == numChannels);
92         jassert (inputBlock.getNumChannels() == lastOutput.size());
93         jassert (inputBlock.getNumSamples()  == numSamples);
94 
95         if (context.isBypassed)
96         {
97             outputBlock.copyFrom (inputBlock);
98             return;
99         }
100 
101         int numSamplesDown = 0;
102         auto counter = updateCounter;
103 
104         for (size_t i = 0; i < numSamples; ++i)
105         {
106             if (counter == 0)
107                 numSamplesDown++;
108 
109             counter++;
110 
111             if (counter == maxUpdateCounter)
112                 counter = 0;
113         }
114 
115         if (numSamplesDown > 0)
116         {
117             auto freqBlock = AudioBlock<SampleType>(bufferFrequency).getSubBlock (0, (size_t) numSamplesDown);
118             auto contextFreq = ProcessContextReplacing<SampleType> (freqBlock);
119             freqBlock.clear();
120 
121             osc.process (contextFreq);
122             freqBlock.multiplyBy (oscVolume);
123         }
124 
125         auto* freqSamples = bufferFrequency.getWritePointer (0);
126 
127         for (int i = 0; i < numSamplesDown; ++i)
128         {
129             auto lfo = jlimit (static_cast<SampleType> (0.0),
130                                static_cast<SampleType> (1.0),
131                                freqSamples[i] + normCentreFrequency);
132 
133             freqSamples[i] = mapToLog10 (lfo, static_cast<SampleType> (20.0),
134                                          static_cast<SampleType> (jmin (20000.0, 0.49 * sampleRate)));
135         }
136 
137         auto currentFrequency = filters[0]->getCutoffFrequency();
138         dryWet.pushDrySamples (inputBlock);
139 
140         for (size_t channel = 0; channel < numChannels; ++channel)
141         {
142             counter = updateCounter;
143             int k = 0;
144 
145             auto* inputSamples  = inputBlock .getChannelPointer (channel);
146             auto* outputSamples = outputBlock.getChannelPointer (channel);
147 
148             for (size_t i = 0; i < numSamples; ++i)
149             {
150                 auto input = inputSamples[i];
151                 auto output = input - lastOutput[channel];
152 
153                 if (i == 0 && counter != 0)
154                     for (int n = 0; n < numStages; ++n)
155                         filters[n]->setCutoffFrequency (currentFrequency);
156 
157                 if (counter == 0)
158                 {
159                     for (int n = 0; n < numStages; ++n)
160                         filters[n]->setCutoffFrequency (freqSamples[k]);
161 
162                     k++;
163                 }
164 
165                 for (int n = 0; n < numStages; ++n)
166                     output = filters[n]->processSample ((int) channel, output);
167 
168                 outputSamples[i] = output;
169                 lastOutput[channel] = output * feedbackVolume[channel].getNextValue();
170 
171                 counter++;
172 
173                 if (counter == maxUpdateCounter)
174                     counter = 0;
175             }
176         }
177 
178         dryWet.mixWetSamples (outputBlock);
179         updateCounter = (updateCounter + (int) numSamples) % maxUpdateCounter;
180     }
181 
182 private:
183     //==============================================================================
184     void update();
185 
186     //==============================================================================
187     Oscillator<SampleType> osc;
188     OwnedArray<FirstOrderTPTFilter<SampleType>> filters;
189     SmoothedValue<SampleType, ValueSmoothingTypes::Linear> oscVolume;
190     std::vector<SmoothedValue<SampleType, ValueSmoothingTypes::Linear>> feedbackVolume { 2 };
191     DryWetMixer<SampleType> dryWet;
192     std::vector<SampleType> lastOutput { 2 };
193     AudioBuffer<SampleType> bufferFrequency;
194     SampleType normCentreFrequency = 0.5;
195     double sampleRate = 44100.0;
196 
197     int updateCounter = 0;
198     static constexpr int maxUpdateCounter = 4;
199 
200     SampleType rate = 1.0, depth = 0.5, feedback = 0.0, mix = 0.5;
201     SampleType centreFrequency = 1300.0;
202     static constexpr int numStages = 6;
203 };
204 
205 } // namespace dsp
206 } // namespace juce
207