andrewm@0
|
1 /*
|
andrewm@0
|
2 This code accompanies the textbook:
|
andrewm@0
|
3
|
andrewm@0
|
4 Digital Audio Effects: Theory, Implementation and Application
|
andrewm@0
|
5 Joshua D. Reiss and Andrew P. McPherson
|
andrewm@0
|
6
|
andrewm@0
|
7 ---
|
andrewm@0
|
8
|
andrewm@0
|
9 Auto-Wah: LFO or envelope-operated wah effect
|
andrewm@0
|
10 See textbook Chapter 4: Filter Effects
|
andrewm@0
|
11
|
andrewm@0
|
12 Code by Andrew McPherson, Brecht de Man and Joshua Reiss
|
andrewm@0
|
13
|
andrewm@0
|
14 ---
|
andrewm@0
|
15
|
andrewm@0
|
16 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
17 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
18 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
19 (at your option) any later version.
|
andrewm@0
|
20
|
andrewm@0
|
21 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
24 GNU General Public License for more details.
|
andrewm@0
|
25
|
andrewm@0
|
26 You should have received a copy of the GNU General Public License
|
andrewm@0
|
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
28 */
|
andrewm@0
|
29
|
andrewm@0
|
30 #include "PluginProcessor.h"
|
andrewm@0
|
31 #include "PluginEditor.h"
|
andrewm@0
|
32
|
andrewm@0
|
33 // The filter will produce a resonant peak of amplitude Q; bring everything
|
andrewm@0
|
34 // down somewhat to compensate, though try to maintain some perceptual balance
|
andrewm@0
|
35 // of being similar loudness. (This factor has been chosen somewhat arbitrarily.)
|
andrewm@0
|
36 const double kWahwahFilterGain = 0.5;
|
andrewm@0
|
37
|
andrewm@0
|
38 //==============================================================================
|
andrewm@0
|
39 AutoWahAudioProcessor::AutoWahAudioProcessor()
|
andrewm@0
|
40 {
|
andrewm@0
|
41 // Set default values:
|
andrewm@0
|
42 baseFrequency_ = 350.0;
|
andrewm@0
|
43 q_ = 5.0;
|
andrewm@0
|
44 lfoFrequency_ = 2.0;
|
andrewm@0
|
45 lfoWidth_ = 1000.0;
|
andrewm@0
|
46 envelopeWidth_ = 0.0;
|
andrewm@0
|
47 envelopeAttack_ = 0.005;
|
andrewm@0
|
48 envelopeDecay_ = 0.1;
|
andrewm@0
|
49
|
andrewm@0
|
50 // Initialise the filters later when we know how many channels
|
andrewm@0
|
51 wahFilters_ = 0;
|
andrewm@0
|
52 numWahFilters_ = 0;
|
andrewm@0
|
53 envelopes_ = 0;
|
andrewm@0
|
54 numEnvelopes_ = 0;
|
andrewm@0
|
55 attackMultiplier_ = 1.0;
|
andrewm@0
|
56 decayMultiplier_ = 0.0;
|
andrewm@0
|
57
|
andrewm@0
|
58 inverseSampleRate_ = 1.0/44100.0; // start with a sensible default
|
andrewm@0
|
59
|
andrewm@0
|
60 lastUIWidth_ = 550;
|
andrewm@0
|
61 lastUIHeight_ = 200;
|
andrewm@0
|
62 }
|
andrewm@0
|
63
|
andrewm@0
|
64 AutoWahAudioProcessor::~AutoWahAudioProcessor()
|
andrewm@0
|
65 {
|
andrewm@0
|
66 deallocateFilters();
|
andrewm@0
|
67 }
|
andrewm@0
|
68
|
andrewm@0
|
69 //==============================================================================
|
andrewm@0
|
70 const String AutoWahAudioProcessor::getName() const
|
andrewm@0
|
71 {
|
andrewm@0
|
72 return JucePlugin_Name;
|
andrewm@0
|
73 }
|
andrewm@0
|
74
|
andrewm@0
|
75 int AutoWahAudioProcessor::getNumParameters()
|
andrewm@0
|
76 {
|
andrewm@0
|
77 return kNumParameters;
|
andrewm@0
|
78 }
|
andrewm@0
|
79
|
andrewm@0
|
80 float AutoWahAudioProcessor::getParameter (int index)
|
andrewm@0
|
81 {
|
andrewm@0
|
82 // This method will be called by the host, probably on the audio thread, so
|
andrewm@0
|
83 // it's absolutely time-critical. Don't use critical sections or anything
|
andrewm@0
|
84 // UI-related, or anything at all that may block in any way!
|
andrewm@0
|
85 switch (index)
|
andrewm@0
|
86 {
|
andrewm@0
|
87 case kBaseFrequencyParam: return baseFrequency_;
|
andrewm@0
|
88 case kQParam: return q_;
|
andrewm@0
|
89 case kLFOFrequencyParam: return lfoFrequency_;
|
andrewm@0
|
90 case kLFOWidthParam: return lfoWidth_;
|
andrewm@0
|
91 case kEnvelopeWidthParam: return envelopeWidth_;
|
andrewm@0
|
92 case kEnvelopeAttackParam: return envelopeAttack_;
|
andrewm@0
|
93 case kEnvelopeDecayParam: return envelopeDecay_;
|
andrewm@0
|
94 default: return 0.0f;
|
andrewm@0
|
95 }
|
andrewm@0
|
96 }
|
andrewm@0
|
97
|
andrewm@0
|
98 void AutoWahAudioProcessor::setParameter (int index, float newValue)
|
andrewm@0
|
99 {
|
andrewm@0
|
100 // This method will be called by the host, probably on the audio thread, so
|
andrewm@0
|
101 // it's absolutely time-critical. Don't use critical sections or anything
|
andrewm@0
|
102 // UI-related, or anything at all that may block in any way!
|
andrewm@0
|
103
|
andrewm@0
|
104 switch (index)
|
andrewm@0
|
105 {
|
andrewm@0
|
106 case kBaseFrequencyParam:
|
andrewm@0
|
107 baseFrequency_ = newValue;
|
andrewm@0
|
108 break;
|
andrewm@0
|
109 case kQParam:
|
andrewm@0
|
110 q_ = newValue;
|
andrewm@0
|
111 break;
|
andrewm@0
|
112 case kLFOFrequencyParam:
|
andrewm@0
|
113 lfoFrequency_ = newValue;
|
andrewm@0
|
114 break;
|
andrewm@0
|
115 case kLFOWidthParam:
|
andrewm@0
|
116 lfoWidth_ = newValue;
|
andrewm@0
|
117 break;
|
andrewm@0
|
118 case kEnvelopeWidthParam:
|
andrewm@0
|
119 envelopeWidth_ = newValue;
|
andrewm@0
|
120 break;
|
andrewm@0
|
121 case kEnvelopeAttackParam:
|
andrewm@0
|
122 envelopeAttack_ = newValue;
|
andrewm@0
|
123 // See comment below for justification
|
andrewm@0
|
124 if(envelopeAttack_ == 0.0)
|
andrewm@0
|
125 attackMultiplier_ = 0.0;
|
andrewm@0
|
126 else
|
andrewm@0
|
127 attackMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeAttack_);
|
andrewm@0
|
128 break;
|
andrewm@0
|
129 case kEnvelopeDecayParam:
|
andrewm@0
|
130 envelopeDecay_ = newValue;
|
andrewm@0
|
131 // envelopeDecay_ sets the time constant tau. The decay is
|
andrewm@0
|
132 // given as e^-(t/tau) so after tau seconds, it will have
|
andrewm@0
|
133 // decayed to 1/e of its original value. tau*sampleRate samples
|
andrewm@0
|
134 // will have passed by then, each of which multiplies the signal
|
andrewm@0
|
135 // by decayMultiplier_.
|
andrewm@0
|
136 if(envelopeDecay_ == 0.0)
|
andrewm@0
|
137 decayMultiplier_ = 0.0;
|
andrewm@0
|
138 else
|
andrewm@0
|
139 decayMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeDecay_);
|
andrewm@0
|
140 break;
|
andrewm@0
|
141 default:
|
andrewm@0
|
142 break;
|
andrewm@0
|
143 }
|
andrewm@0
|
144 }
|
andrewm@0
|
145
|
andrewm@0
|
146 const String AutoWahAudioProcessor::getParameterName (int index)
|
andrewm@0
|
147 {
|
andrewm@0
|
148 switch (index)
|
andrewm@0
|
149 {
|
andrewm@0
|
150 case kBaseFrequencyParam: return "base frequency";
|
andrewm@0
|
151 case kQParam: return "Q";
|
andrewm@0
|
152 case kLFOFrequencyParam: return "LFO frequency";
|
andrewm@0
|
153 case kLFOWidthParam: return "LFO width";
|
andrewm@0
|
154 case kEnvelopeWidthParam: return "envelope width";
|
andrewm@0
|
155 case kEnvelopeAttackParam: return "envelope attack";
|
andrewm@0
|
156 case kEnvelopeDecayParam: return "envelope decay";
|
andrewm@0
|
157 default: break;
|
andrewm@0
|
158 }
|
andrewm@0
|
159
|
andrewm@0
|
160 return String::empty;
|
andrewm@0
|
161 }
|
andrewm@0
|
162
|
andrewm@0
|
163 const String AutoWahAudioProcessor::getParameterText (int index)
|
andrewm@0
|
164 {
|
andrewm@0
|
165 return String (getParameter (index), 2);
|
andrewm@0
|
166 }
|
andrewm@0
|
167
|
andrewm@0
|
168 const String AutoWahAudioProcessor::getInputChannelName (int channelIndex) const
|
andrewm@0
|
169 {
|
andrewm@0
|
170 return String (channelIndex + 1);
|
andrewm@0
|
171 }
|
andrewm@0
|
172
|
andrewm@0
|
173 const String AutoWahAudioProcessor::getOutputChannelName (int channelIndex) const
|
andrewm@0
|
174 {
|
andrewm@0
|
175 return String (channelIndex + 1);
|
andrewm@0
|
176 }
|
andrewm@0
|
177
|
andrewm@0
|
178 bool AutoWahAudioProcessor::isInputChannelStereoPair (int index) const
|
andrewm@0
|
179 {
|
andrewm@0
|
180 return true;
|
andrewm@0
|
181 }
|
andrewm@0
|
182
|
andrewm@0
|
183 bool AutoWahAudioProcessor::isOutputChannelStereoPair (int index) const
|
andrewm@0
|
184 {
|
andrewm@0
|
185 return true;
|
andrewm@0
|
186 }
|
andrewm@0
|
187
|
andrewm@0
|
188 bool AutoWahAudioProcessor::silenceInProducesSilenceOut() const
|
andrewm@0
|
189 {
|
andrewm@0
|
190 #if JucePlugin_SilenceInProducesSilenceOut
|
andrewm@0
|
191 return true;
|
andrewm@0
|
192 #else
|
andrewm@0
|
193 return false;
|
andrewm@0
|
194 #endif
|
andrewm@0
|
195 }
|
andrewm@0
|
196
|
andrewm@0
|
197 double AutoWahAudioProcessor::getTailLengthSeconds() const
|
andrewm@0
|
198 {
|
andrewm@0
|
199 return 0.0;
|
andrewm@0
|
200 }
|
andrewm@0
|
201
|
andrewm@0
|
202 bool AutoWahAudioProcessor::acceptsMidi() const
|
andrewm@0
|
203 {
|
andrewm@0
|
204 #if JucePlugin_WantsMidiInput
|
andrewm@0
|
205 return true;
|
andrewm@0
|
206 #else
|
andrewm@0
|
207 return false;
|
andrewm@0
|
208 #endif
|
andrewm@0
|
209 }
|
andrewm@0
|
210
|
andrewm@0
|
211 bool AutoWahAudioProcessor::producesMidi() const
|
andrewm@0
|
212 {
|
andrewm@0
|
213 #if JucePlugin_ProducesMidiOutput
|
andrewm@0
|
214 return true;
|
andrewm@0
|
215 #else
|
andrewm@0
|
216 return false;
|
andrewm@0
|
217 #endif
|
andrewm@0
|
218 }
|
andrewm@0
|
219
|
andrewm@0
|
220 int AutoWahAudioProcessor::getNumPrograms()
|
andrewm@0
|
221 {
|
andrewm@0
|
222 return 0;
|
andrewm@0
|
223 }
|
andrewm@0
|
224
|
andrewm@0
|
225 int AutoWahAudioProcessor::getCurrentProgram()
|
andrewm@0
|
226 {
|
andrewm@0
|
227 return 0;
|
andrewm@0
|
228 }
|
andrewm@0
|
229
|
andrewm@0
|
230 void AutoWahAudioProcessor::setCurrentProgram (int index)
|
andrewm@0
|
231 {
|
andrewm@0
|
232 }
|
andrewm@0
|
233
|
andrewm@0
|
234 const String AutoWahAudioProcessor::getProgramName (int index)
|
andrewm@0
|
235 {
|
andrewm@0
|
236 return String::empty;
|
andrewm@0
|
237 }
|
andrewm@0
|
238
|
andrewm@0
|
239 void AutoWahAudioProcessor::changeProgramName (int index, const String& newName)
|
andrewm@0
|
240 {
|
andrewm@0
|
241 }
|
andrewm@0
|
242
|
andrewm@0
|
243 //==============================================================================
|
andrewm@0
|
244 void AutoWahAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
andrewm@0
|
245 {
|
andrewm@0
|
246 // Use this method as the place to do any pre-playback
|
andrewm@0
|
247 // initialisation that you need..
|
andrewm@0
|
248
|
andrewm@0
|
249 allocateFilters();
|
andrewm@0
|
250 inverseSampleRate_ = 1.0 / sampleRate;
|
andrewm@0
|
251 if(envelopeDecay_ == 0.0)
|
andrewm@0
|
252 decayMultiplier_ = 0.0;
|
andrewm@0
|
253 else
|
andrewm@0
|
254 decayMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeDecay_);
|
andrewm@0
|
255 if(envelopeAttack_ == 0.0)
|
andrewm@0
|
256 attackMultiplier_ = 0.0;
|
andrewm@0
|
257 else
|
andrewm@0
|
258 attackMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeAttack_);
|
andrewm@0
|
259 }
|
andrewm@0
|
260
|
andrewm@0
|
261 void AutoWahAudioProcessor::releaseResources()
|
andrewm@0
|
262 {
|
andrewm@0
|
263 // When playback stops, you can use this as an opportunity to free up any
|
andrewm@0
|
264 // spare memory, etc.
|
andrewm@0
|
265
|
andrewm@0
|
266 deallocateFilters();
|
andrewm@0
|
267 }
|
andrewm@0
|
268
|
andrewm@0
|
269 void AutoWahAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
|
andrewm@0
|
270 {
|
andrewm@0
|
271 // Helpful information about this block of samples:
|
andrewm@0
|
272 const int numInputChannels = getNumInputChannels(); // How many input channels for our effect?
|
andrewm@0
|
273 const int numOutputChannels = getNumOutputChannels(); // How many output channels for our effect?
|
andrewm@0
|
274 const int numSamples = buffer.getNumSamples(); // How many samples in the buffer for this block?
|
andrewm@0
|
275 int channel;
|
andrewm@0
|
276 float ph;
|
andrewm@0
|
277
|
andrewm@0
|
278 // Go through each channel and put it through the resonant lowpass filter, updating
|
andrewm@0
|
279 // the coefficients as we go along. Each channel is processed identically in this effect.
|
andrewm@0
|
280
|
andrewm@0
|
281 for(channel = 0; channel < jmin(numInputChannels, numWahFilters_); ++channel)
|
andrewm@0
|
282 {
|
andrewm@0
|
283 // channelData is an array of length numSamples which contains the audio for one channel
|
b@1
|
284 float* channelData = buffer.getWritePointer(channel);
|
andrewm@0
|
285 ph = lfoPhase_;
|
andrewm@0
|
286
|
andrewm@0
|
287 for (int sample = 0; sample < numSamples; ++sample)
|
andrewm@0
|
288 {
|
andrewm@0
|
289 const float in = channelData[sample];
|
andrewm@0
|
290 float centreFrequency = baseFrequency_;
|
andrewm@0
|
291
|
andrewm@0
|
292 // Calculate the envelope of the signal. Do this even if we're not currently
|
andrewm@0
|
293 // changing the frequeny based on it, since it involves maintaining a history
|
andrewm@0
|
294 // of the signal's behaviour.
|
andrewm@0
|
295
|
andrewm@0
|
296 if(channel < numEnvelopes_) { // Safety check
|
andrewm@0
|
297 if(fabs(in) > envelopes_[channel]) {
|
andrewm@0
|
298 envelopes_[channel] += (1.0 - attackMultiplier_) * (fabs(in) - (double)envelopes_[channel]);
|
andrewm@0
|
299 }
|
andrewm@0
|
300 else
|
andrewm@0
|
301 envelopes_[channel] *= decayMultiplier_;
|
andrewm@0
|
302 }
|
andrewm@0
|
303
|
andrewm@0
|
304 // Calculate the centre frequency of the filter based on the LFO and the
|
andrewm@0
|
305 // signal envelope
|
andrewm@0
|
306 if(lfoWidth_ > 0.0) {
|
andrewm@0
|
307 centreFrequency += lfoWidth_ * (0.5f + 0.5f*sinf(2.0 * M_PI * ph));
|
andrewm@0
|
308 }
|
andrewm@0
|
309 if(envelopeWidth_ > 0.0 && channel < numEnvelopes_) {
|
andrewm@0
|
310 centreFrequency += envelopeWidth_ * envelopes_[channel];
|
andrewm@0
|
311 }
|
andrewm@0
|
312
|
andrewm@0
|
313 // Update filter coefficients (see ResonantLowpassFilter.cpp for calculation)
|
andrewm@0
|
314 wahFilters_[channel]->makeResonantLowpass(inverseSampleRate_,
|
andrewm@0
|
315 centreFrequency,
|
andrewm@0
|
316 q_,
|
andrewm@0
|
317 kWahwahFilterGain);
|
andrewm@0
|
318
|
andrewm@0
|
319 // Process one sample and store it back in place. See juce_IIRFilter.cpp for the
|
andrewm@0
|
320 // application of the IIR filter.
|
andrewm@0
|
321 channelData[sample] = wahFilters_[channel]->processSingleSampleRaw(in);
|
andrewm@0
|
322
|
andrewm@0
|
323 // Update the LFO phase, keeping it in the range 0-1
|
andrewm@0
|
324 ph += lfoFrequency_*inverseSampleRate_;
|
andrewm@0
|
325 if(ph >= 1.0)
|
andrewm@0
|
326 ph -= 1.0;
|
andrewm@0
|
327 }
|
andrewm@0
|
328 }
|
andrewm@0
|
329
|
andrewm@0
|
330 lfoPhase_ = ph;
|
andrewm@0
|
331
|
andrewm@0
|
332 // Go through the remaining channels. In case we have more outputs
|
andrewm@0
|
333 // than inputs, or there aren't enough filters, we'll clear any
|
andrewm@0
|
334 // remaining output channels (which could otherwise contain garbage)
|
andrewm@0
|
335 while(channel < numOutputChannels)
|
andrewm@0
|
336 {
|
andrewm@0
|
337 buffer.clear (channel++, 0, buffer.getNumSamples());
|
andrewm@0
|
338 }
|
andrewm@0
|
339 }
|
andrewm@0
|
340
|
andrewm@0
|
341 //==============================================================================
|
andrewm@0
|
342 bool AutoWahAudioProcessor::hasEditor() const
|
andrewm@0
|
343 {
|
andrewm@0
|
344 return true; // (change this to false if you choose to not supply an editor)
|
andrewm@0
|
345 }
|
andrewm@0
|
346
|
andrewm@0
|
347 AudioProcessorEditor* AutoWahAudioProcessor::createEditor()
|
andrewm@0
|
348 {
|
andrewm@0
|
349 return new AutoWahAudioProcessorEditor (this);
|
andrewm@0
|
350 }
|
andrewm@0
|
351
|
andrewm@0
|
352 //==============================================================================
|
andrewm@0
|
353 void AutoWahAudioProcessor::getStateInformation (MemoryBlock& destData)
|
andrewm@0
|
354 {
|
andrewm@0
|
355 // You should use this method to store your parameters in the memory block.
|
andrewm@0
|
356 // You could do that either as raw data, or use the XML or ValueTree classes
|
andrewm@0
|
357 // as intermediaries to make it easy to save and load complex data.
|
andrewm@0
|
358
|
andrewm@0
|
359 // Create an outer XML element..
|
andrewm@0
|
360 XmlElement xml("C4DMPLUGINSETTINGS");
|
andrewm@0
|
361
|
andrewm@0
|
362 // add some attributes to it..
|
andrewm@0
|
363 xml.setAttribute("uiWidth", lastUIWidth_);
|
andrewm@0
|
364 xml.setAttribute("uiHeight", lastUIHeight_);
|
andrewm@0
|
365 xml.setAttribute("baseFrequency", baseFrequency_);
|
andrewm@0
|
366 xml.setAttribute("q", q_);
|
andrewm@0
|
367 xml.setAttribute("lfoFrequency", lfoFrequency_);
|
andrewm@0
|
368 xml.setAttribute("lfoWidth", lfoWidth_);
|
andrewm@0
|
369 xml.setAttribute("envelopeWidth", envelopeWidth_);
|
andrewm@0
|
370 xml.setAttribute("envelopeAttack", envelopeAttack_);
|
andrewm@0
|
371 xml.setAttribute("envelopeDecay", envelopeDecay_);
|
andrewm@0
|
372
|
andrewm@0
|
373 // then use this helper function to stuff it into the binary blob and return it..
|
andrewm@0
|
374 copyXmlToBinary(xml, destData);
|
andrewm@0
|
375 }
|
andrewm@0
|
376
|
andrewm@0
|
377 void AutoWahAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
andrewm@0
|
378 {
|
andrewm@0
|
379 // You should use this method to restore your parameters from this memory block,
|
andrewm@0
|
380 // whose contents will have been created by the getStateInformation() call.
|
andrewm@0
|
381
|
andrewm@0
|
382 // This getXmlFromBinary() helper function retrieves our XML from the binary blob..
|
andrewm@0
|
383 ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
|
andrewm@0
|
384
|
andrewm@0
|
385 if(xmlState != 0)
|
andrewm@0
|
386 {
|
andrewm@0
|
387 // make sure that it's actually our type of XML object..
|
andrewm@0
|
388 if(xmlState->hasTagName("C4DMPLUGINSETTINGS"))
|
andrewm@0
|
389 {
|
andrewm@0
|
390 // ok, now pull out our parameters..
|
andrewm@0
|
391 lastUIWidth_ = xmlState->getIntAttribute("uiWidth", lastUIWidth_);
|
andrewm@0
|
392 lastUIHeight_ = xmlState->getIntAttribute("uiHeight", lastUIHeight_);
|
andrewm@0
|
393
|
andrewm@0
|
394 q_ = (float)xmlState->getDoubleAttribute("q", q_);
|
andrewm@0
|
395 baseFrequency_ = (float)xmlState->getDoubleAttribute("baseFrequency", baseFrequency_);
|
andrewm@0
|
396 lfoFrequency_ = (float)xmlState->getDoubleAttribute("lfoFrequency", lfoFrequency_);
|
andrewm@0
|
397 lfoWidth_ = (float)xmlState->getDoubleAttribute("lfoWidth", lfoWidth_);
|
andrewm@0
|
398 envelopeWidth_ = (float)xmlState->getDoubleAttribute("envelopeWidth", envelopeWidth_);
|
andrewm@0
|
399 envelopeAttack_ = (float)xmlState->getDoubleAttribute("envelopeAttack", envelopeAttack_);
|
andrewm@0
|
400 envelopeDecay_ = (float)xmlState->getDoubleAttribute("envelopeDecay", envelopeDecay_);
|
andrewm@0
|
401 inverseSampleRate_ = 1.0 / getSampleRate();
|
andrewm@0
|
402 if(envelopeDecay_ == 0.0)
|
andrewm@0
|
403 decayMultiplier_ = 0.0;
|
andrewm@0
|
404 else
|
andrewm@0
|
405 decayMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeDecay_);
|
andrewm@0
|
406 if(envelopeAttack_ == 0.0)
|
andrewm@0
|
407 attackMultiplier_ = 0.0;
|
andrewm@0
|
408 else
|
andrewm@0
|
409 attackMultiplier_ = pow(1.0 / M_E, inverseSampleRate_ / envelopeAttack_);
|
andrewm@0
|
410 }
|
andrewm@0
|
411 }
|
andrewm@0
|
412 }
|
andrewm@0
|
413
|
andrewm@0
|
414 void AutoWahAudioProcessor::allocateFilters()
|
andrewm@0
|
415 {
|
andrewm@0
|
416 // Prevent leaks from reallocation
|
andrewm@0
|
417 if(wahFilters_ != 0 || envelopes_ != 0)
|
andrewm@0
|
418 deallocateFilters();
|
andrewm@0
|
419
|
andrewm@0
|
420 // Create as many filters as we have input channels
|
andrewm@0
|
421 numWahFilters_ = getNumInputChannels();
|
andrewm@0
|
422 wahFilters_ = (ResonantLowpassFilter**)malloc(numWahFilters_ * sizeof(ResonantLowpassFilter*));
|
andrewm@0
|
423 if(wahFilters_ == 0)
|
andrewm@0
|
424 numWahFilters_ = 0;
|
andrewm@0
|
425 else {
|
andrewm@0
|
426 for(int i = 0; i < numWahFilters_; i++)
|
andrewm@0
|
427 wahFilters_[i] = new ResonantLowpassFilter;
|
andrewm@0
|
428 }
|
andrewm@0
|
429
|
andrewm@0
|
430 numEnvelopes_ = getNumInputChannels();
|
andrewm@0
|
431 envelopes_ = (double *)malloc(numEnvelopes_ * sizeof(double));
|
andrewm@0
|
432 if(envelopes_ == 0)
|
andrewm@0
|
433 numEnvelopes_ = 0;
|
andrewm@0
|
434 else {
|
andrewm@0
|
435 for(int i = 0; i < numEnvelopes_; i++)
|
andrewm@0
|
436 envelopes_[i] = 0.0;
|
andrewm@0
|
437 }
|
andrewm@0
|
438 }
|
andrewm@0
|
439
|
andrewm@0
|
440 void AutoWahAudioProcessor::deallocateFilters()
|
andrewm@0
|
441 {
|
andrewm@0
|
442 for(int i = 0; i < numWahFilters_; i++)
|
andrewm@0
|
443 delete wahFilters_[i];
|
andrewm@0
|
444 if(numWahFilters_ != 0)
|
andrewm@0
|
445 free(wahFilters_);
|
andrewm@0
|
446 numWahFilters_ = 0;
|
andrewm@0
|
447 wahFilters_ = 0;
|
andrewm@0
|
448 if(envelopes_ != 0)
|
andrewm@0
|
449 free(envelopes_);
|
andrewm@0
|
450 envelopes_ = 0;
|
andrewm@0
|
451 numEnvelopes_ = 0;
|
andrewm@0
|
452 }
|
andrewm@0
|
453
|
andrewm@0
|
454 //==============================================================================
|
andrewm@0
|
455 // This creates new instances of the plugin..
|
andrewm@0
|
456 AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
andrewm@0
|
457 {
|
andrewm@0
|
458 return new AutoWahAudioProcessor();
|
andrewm@0
|
459 }
|