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 Distortion: distortion effect using different characteristic curves
|
andrewm@0
|
10 See textbook Chapter 7: Overdrive, Distortion and Fuzz
|
andrewm@0
|
11
|
andrewm@0
|
12 Code by Brecht De Man, Joshua Reiss and Andrew McPherson
|
andrewm@0
|
13
|
andrewm@0
|
14 When using this code (or a modified version thereof), please cite:
|
andrewm@0
|
15
|
andrewm@0
|
16 Brecht De Man and Joshua D. Reiss, "Adaptive Control of Amplitude
|
andrewm@0
|
17 Distortion Effects," 53rd Conference of the Audio Engineering Society,
|
andrewm@0
|
18 2014.
|
andrewm@0
|
19
|
andrewm@0
|
20 ---
|
andrewm@0
|
21
|
andrewm@0
|
22 This program is free software: you can redistribute it and/or modify
|
andrewm@0
|
23 it under the terms of the GNU General Public License as published by
|
andrewm@0
|
24 the Free Software Foundation, either version 3 of the License, or
|
andrewm@0
|
25 (at your option) any later version.
|
andrewm@0
|
26
|
andrewm@0
|
27 This program is distributed in the hope that it will be useful,
|
andrewm@0
|
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
andrewm@0
|
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
andrewm@0
|
30 GNU General Public License for more details.
|
andrewm@0
|
31
|
andrewm@0
|
32 You should have received a copy of the GNU General Public License
|
andrewm@0
|
33 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
andrewm@0
|
34 */
|
andrewm@0
|
35
|
andrewm@0
|
36 #include "PluginProcessor.h"
|
andrewm@0
|
37 #include "PluginEditor.h"
|
andrewm@0
|
38
|
andrewm@0
|
39 #if JUCE_INTEL
|
andrewm@0
|
40 #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0;
|
andrewm@0
|
41 #else
|
andrewm@0
|
42 #define JUCE_SNAP_TO_ZERO(n)
|
andrewm@0
|
43 #endif
|
andrewm@0
|
44
|
andrewm@0
|
45 DistortionAudioProcessor::DistortionAudioProcessor()
|
andrewm@0
|
46 :
|
andrewm@0
|
47 _numChannels (1)
|
andrewm@0
|
48 ,_numSamples (1) // dummy - will be set in prepareToPlay
|
andrewm@0
|
49 ,_sampleRate (1) // dummy - will be set in prepareToPlay
|
andrewm@0
|
50 ,_typeNumber (_hardClipping) // standard
|
andrewm@0
|
51 ,_currentTrackBuffer (1,1)
|
andrewm@0
|
52 ,_lastUIWidth (850)
|
andrewm@0
|
53 ,_lastUIHeight (650)
|
andrewm@0
|
54
|
andrewm@0
|
55 {
|
andrewm@0
|
56 Reset();
|
andrewm@0
|
57 }
|
andrewm@0
|
58
|
andrewm@0
|
59 DistortionAudioProcessor::~DistortionAudioProcessor()
|
andrewm@0
|
60 {
|
andrewm@0
|
61 }
|
andrewm@0
|
62
|
andrewm@0
|
63 //-----------------------------------------------------------------------------
|
andrewm@0
|
64 // P R E P A R E T O P L A Y
|
andrewm@0
|
65 void DistortionAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
andrewm@0
|
66 {
|
andrewm@0
|
67 // If sample rate/block size changes or straight after construction
|
andrewm@0
|
68 if (_numSamples != samplesPerBlock || _sampleRate != sampleRate)
|
andrewm@0
|
69 {
|
andrewm@0
|
70 _sampleRate = sampleRate;
|
andrewm@0
|
71 _numSamples = samplesPerBlock;
|
andrewm@0
|
72 _numChannels = getNumInputChannels();
|
andrewm@0
|
73 }
|
andrewm@0
|
74 }
|
andrewm@0
|
75
|
andrewm@0
|
76
|
andrewm@0
|
77 //-----------------------------------------------------------------------------
|
andrewm@0
|
78 // P R O C E S S B L O C K
|
andrewm@0
|
79 void DistortionAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
|
andrewm@0
|
80 {
|
andrewm@0
|
81 float gain = powf(10.0f, _gainIndB/20.f);
|
andrewm@0
|
82
|
andrewm@0
|
83
|
andrewm@0
|
84 for (int channel = 0; channel < _numChannels; ++channel)
|
andrewm@0
|
85 {
|
andrewm@0
|
86 // Apply gain
|
andrewm@0
|
87 buffer.applyGain(channel, 0, buffer.getNumSamples(), gain);
|
andrewm@0
|
88
|
andrewm@0
|
89 // Put track audio data into _currentTrackBuffer
|
andrewm@0
|
90 float * originalData = new float;
|
b@1
|
91 originalData = buffer.getWritePointer(channel);
|
andrewm@0
|
92
|
andrewm@0
|
93 // Apply distortion (sample per sample)
|
andrewm@0
|
94 switch (_typeNumber) {
|
andrewm@0
|
95 case _hardClipping:
|
andrewm@0
|
96 {
|
andrewm@0
|
97 float threshold = 0.5f;
|
andrewm@0
|
98 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
|
andrewm@0
|
99 {
|
andrewm@0
|
100 if(originalData[sample] > threshold) // positive hard clipping
|
andrewm@0
|
101 {
|
andrewm@0
|
102 originalData[sample] = threshold;
|
andrewm@0
|
103 }
|
andrewm@0
|
104 else
|
andrewm@0
|
105 {
|
andrewm@0
|
106 if(originalData[sample] < - threshold) // negative hard clipping
|
andrewm@0
|
107 {
|
andrewm@0
|
108 originalData[sample] = - threshold;
|
andrewm@0
|
109 }
|
andrewm@0
|
110 }
|
andrewm@0
|
111 }
|
andrewm@0
|
112 break;
|
andrewm@0
|
113 }
|
andrewm@0
|
114
|
andrewm@0
|
115 case _softClipping:
|
andrewm@0
|
116 {
|
andrewm@0
|
117 float threshold1 = 1.0f/3.0f;
|
andrewm@0
|
118 float threshold2 = 2.0f/3.0f;
|
andrewm@0
|
119 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
|
andrewm@0
|
120 {
|
andrewm@0
|
121 if(originalData[sample] > threshold1)
|
andrewm@0
|
122 {
|
andrewm@0
|
123 if(originalData[sample] > threshold2) // positive clipping
|
andrewm@0
|
124 {
|
andrewm@0
|
125 originalData[sample] = 1.0f;
|
andrewm@0
|
126 }
|
andrewm@0
|
127 else // soft knee (positive)
|
andrewm@0
|
128 {
|
andrewm@0
|
129 originalData[sample] = (3.0f - (2.0f - 3.0f*originalData[sample])*(2.0f - 3.0f*originalData[sample]))/3.0f;
|
andrewm@0
|
130 }
|
andrewm@0
|
131 }
|
andrewm@0
|
132 else
|
andrewm@0
|
133 {
|
andrewm@0
|
134 if(originalData[sample] < -threshold1)
|
andrewm@0
|
135 {
|
andrewm@0
|
136 if(originalData[sample] < -threshold2) // negative clipping
|
andrewm@0
|
137 {
|
andrewm@0
|
138 originalData[sample] = -1.0f;
|
andrewm@0
|
139 }
|
andrewm@0
|
140 else // soft knee (negative)
|
andrewm@0
|
141 {
|
andrewm@0
|
142 originalData[sample] = - (3.0f - (2.0f + 3.0f*originalData[sample])*(2.0f + 3.0f*originalData[sample]))/3.0f;
|
andrewm@0
|
143 }
|
andrewm@0
|
144 }
|
andrewm@0
|
145 else // linear region (-1/3..1/3)
|
andrewm@0
|
146 {
|
andrewm@0
|
147 originalData[sample] *= 2.0f;
|
andrewm@0
|
148 }
|
andrewm@0
|
149 }
|
andrewm@0
|
150
|
andrewm@0
|
151 originalData[sample] /= 2.0f; // divide all by 2 to compensate for extra 6 dB gain boost
|
andrewm@0
|
152 }
|
andrewm@0
|
153 break;
|
andrewm@0
|
154 }
|
andrewm@0
|
155
|
andrewm@0
|
156 case _softClippingExp:
|
andrewm@0
|
157 {
|
andrewm@0
|
158 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
|
andrewm@0
|
159 {
|
andrewm@0
|
160 if (originalData[sample] > 0.0f) // positive
|
andrewm@0
|
161 {
|
andrewm@0
|
162 originalData[sample] = 1.0f - expf(-originalData[sample]);
|
andrewm@0
|
163 }
|
andrewm@0
|
164 else // negative
|
andrewm@0
|
165 {
|
andrewm@0
|
166 originalData[sample] = - 1.0f + expf(originalData[sample]);
|
andrewm@0
|
167 }
|
andrewm@0
|
168 }
|
andrewm@0
|
169 break;
|
andrewm@0
|
170 }
|
andrewm@0
|
171
|
andrewm@0
|
172 case _fullWaveRectifier:
|
andrewm@0
|
173 {
|
andrewm@0
|
174 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
|
andrewm@0
|
175 {
|
andrewm@0
|
176 originalData[sample] = fabs(originalData[sample]);
|
andrewm@0
|
177 }
|
andrewm@0
|
178 break;
|
andrewm@0
|
179 }
|
andrewm@0
|
180
|
andrewm@0
|
181 case _halfWaveRectifier:
|
andrewm@0
|
182 {
|
andrewm@0
|
183 for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
|
andrewm@0
|
184 {
|
andrewm@0
|
185 originalData[sample] = 0.5f*(fabs(originalData[sample])+originalData[sample]);
|
andrewm@0
|
186 }
|
andrewm@0
|
187 break;
|
andrewm@0
|
188 }
|
andrewm@0
|
189
|
andrewm@0
|
190 default:
|
andrewm@0
|
191 break;
|
andrewm@0
|
192 }
|
andrewm@0
|
193 }
|
andrewm@0
|
194 }
|
andrewm@0
|
195
|
andrewm@0
|
196
|
andrewm@0
|
197 //-----------------------------------------------------------------------------
|
andrewm@0
|
198 // R E S E T
|
andrewm@0
|
199 void DistortionAudioProcessor::Reset()
|
andrewm@0
|
200 {
|
andrewm@0
|
201 _gainIndB = 0.0f;
|
andrewm@0
|
202 _typeNumber = _hardClipping;
|
andrewm@0
|
203 }
|
andrewm@0
|
204
|
andrewm@0
|
205
|
andrewm@0
|
206 //-----------------------------------------------------------------------------
|
andrewm@0
|
207 //
|
andrewm@0
|
208 void DistortionAudioProcessor::releaseResources()
|
andrewm@0
|
209 {
|
andrewm@0
|
210 // When playback stops, you can use this to free up any spare memory, etc.
|
andrewm@0
|
211 }
|
andrewm@0
|
212
|
andrewm@0
|
213
|
andrewm@0
|
214 bool DistortionAudioProcessor::hasEditor() const
|
andrewm@0
|
215 {
|
andrewm@0
|
216 return true; // (change this to false if you choose to not supply an editor)
|
andrewm@0
|
217 }
|
andrewm@0
|
218
|
andrewm@0
|
219 AudioProcessorEditor* DistortionAudioProcessor::createEditor()
|
andrewm@0
|
220 {
|
andrewm@0
|
221 return new DistortionAudioProcessorEditor (this);
|
andrewm@0
|
222 }
|
andrewm@0
|
223
|
andrewm@0
|
224
|
andrewm@0
|
225 //==============================================================================
|
andrewm@0
|
226 void DistortionAudioProcessor::getStateInformation (MemoryBlock& destData)
|
andrewm@0
|
227 {
|
andrewm@0
|
228 // SAVE STATE INFO
|
andrewm@0
|
229 XmlElement xml("Distortion_XML");
|
andrewm@0
|
230
|
andrewm@0
|
231 // Knobs
|
andrewm@0
|
232 xml.setAttribute("_gain" ,_gainIndB);
|
andrewm@0
|
233
|
andrewm@0
|
234 // Combo box
|
andrewm@0
|
235 xml.setAttribute("_type" ,(int) _typeNumber);
|
andrewm@0
|
236
|
andrewm@0
|
237 // then use this helper function to stuff it into the binary blob and return it..
|
andrewm@0
|
238 copyXmlToBinary(xml, destData);
|
andrewm@0
|
239 }
|
andrewm@0
|
240
|
andrewm@0
|
241 void DistortionAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
andrewm@0
|
242 {
|
andrewm@0
|
243 // LOAD STATE INFO
|
andrewm@0
|
244 ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
|
andrewm@0
|
245
|
andrewm@0
|
246 // make sure that it's actually our type of XML object..
|
andrewm@0
|
247 if(xmlState->hasTagName("Distortion_XML"))
|
andrewm@0
|
248 {
|
andrewm@0
|
249
|
andrewm@0
|
250 // Knobs
|
andrewm@0
|
251 _gainIndB = (float) xmlState->getDoubleAttribute("_gain",true);
|
andrewm@0
|
252
|
andrewm@0
|
253 //Combo box
|
andrewm@0
|
254 _typeNumber = (DistortionAudioProcessor::Types) xmlState->getIntAttribute("_type",true);
|
andrewm@0
|
255 }
|
andrewm@0
|
256 }
|
andrewm@0
|
257
|
andrewm@0
|
258 // This creates new instances of the plugin..
|
andrewm@0
|
259 AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
andrewm@0
|
260 {
|
andrewm@0
|
261 return new DistortionAudioProcessor();
|
andrewm@0
|
262 }
|
andrewm@0
|
263
|
andrewm@0
|
264 const String DistortionAudioProcessor::getName() const
|
andrewm@0
|
265 {
|
andrewm@0
|
266 return JucePlugin_Name;
|
andrewm@0
|
267 }
|
andrewm@0
|
268
|
andrewm@0
|
269 bool DistortionAudioProcessor::silenceInProducesSilenceOut() const
|
andrewm@0
|
270 {
|
andrewm@0
|
271 return true;
|
andrewm@0
|
272 }
|
andrewm@0
|
273
|
andrewm@0
|
274 int DistortionAudioProcessor::getNumParameters()
|
andrewm@0
|
275 {
|
andrewm@0
|
276 return _PARAMtotalNumParams;
|
andrewm@0
|
277 }
|
andrewm@0
|
278
|
andrewm@0
|
279 float DistortionAudioProcessor::getParameter (int index) // externally accessible
|
andrewm@0
|
280 {
|
andrewm@0
|
281 switch (index)
|
andrewm@0
|
282 {
|
andrewm@0
|
283 case _PARAMdeviceReset: return 0.0f;
|
andrewm@0
|
284 case _PARAMgain: return (GetGainIndB());
|
andrewm@0
|
285 case _PARAMtype: return (GetType());
|
andrewm@0
|
286 default: return 0.0f;
|
andrewm@0
|
287 }
|
andrewm@0
|
288 }
|
andrewm@0
|
289
|
andrewm@0
|
290 void DistortionAudioProcessor::setParameter (int index, float newValue) // externally accessible
|
andrewm@0
|
291 {
|
andrewm@0
|
292 switch (index)
|
andrewm@0
|
293 {
|
andrewm@0
|
294 case _PARAMdeviceReset:
|
andrewm@0
|
295 Reset();
|
andrewm@0
|
296 break;
|
andrewm@0
|
297 case _PARAMgain:
|
andrewm@0
|
298 SetGainIndB(newValue);
|
andrewm@0
|
299 break;
|
andrewm@0
|
300 case _PARAMtype:
|
andrewm@0
|
301 SetType((DistortionAudioProcessor::Types) roundFloatToInt(newValue*_numberOfTypes));
|
andrewm@0
|
302 break;
|
andrewm@0
|
303 default:
|
andrewm@0
|
304 break;
|
andrewm@0
|
305 }
|
andrewm@0
|
306 }
|
andrewm@0
|
307
|
andrewm@0
|
308 const String DistortionAudioProcessor::getParameterName (int index) // externally accessible
|
andrewm@0
|
309 {
|
andrewm@0
|
310 switch (index)
|
andrewm@0
|
311 {
|
andrewm@0
|
312 case _PARAMdeviceReset: return "Reset";
|
andrewm@0
|
313 case _PARAMgain: return "Gain (dB)";
|
andrewm@0
|
314 case _PARAMtype: return "Type";
|
andrewm@0
|
315 default: break;
|
andrewm@0
|
316 }
|
andrewm@0
|
317 return String::empty;
|
andrewm@0
|
318 }
|
andrewm@0
|
319
|
andrewm@0
|
320 const String DistortionAudioProcessor::getParameterText (int index)
|
andrewm@0
|
321 {
|
andrewm@0
|
322 return String (getParameter (index), 2);
|
andrewm@0
|
323 }
|
andrewm@0
|
324
|
andrewm@0
|
325 const String DistortionAudioProcessor::getInputChannelName (int channelIndex) const
|
andrewm@0
|
326 {
|
andrewm@0
|
327 return String (channelIndex + 1);
|
andrewm@0
|
328 }
|
andrewm@0
|
329
|
andrewm@0
|
330 const String DistortionAudioProcessor::getOutputChannelName (int channelIndex) const
|
andrewm@0
|
331 {
|
andrewm@0
|
332 return String (channelIndex + 1);
|
andrewm@0
|
333 }
|
andrewm@0
|
334
|
andrewm@0
|
335 bool DistortionAudioProcessor::isInputChannelStereoPair (int index) const
|
andrewm@0
|
336 {
|
andrewm@0
|
337 return true;
|
andrewm@0
|
338 }
|
andrewm@0
|
339
|
andrewm@0
|
340 bool DistortionAudioProcessor::isOutputChannelStereoPair (int index) const
|
andrewm@0
|
341 {
|
andrewm@0
|
342 return true;
|
andrewm@0
|
343 }
|
andrewm@0
|
344
|
andrewm@0
|
345 bool DistortionAudioProcessor::acceptsMidi() const
|
andrewm@0
|
346 {
|
andrewm@0
|
347 #if JucePlugin_WantsMidiInput
|
andrewm@0
|
348 return true;
|
andrewm@0
|
349 #else
|
andrewm@0
|
350 return false;
|
andrewm@0
|
351 #endif
|
andrewm@0
|
352 }
|
andrewm@0
|
353
|
andrewm@0
|
354 bool DistortionAudioProcessor::producesMidi() const
|
andrewm@0
|
355 {
|
andrewm@0
|
356 #if JucePlugin_ProducesMidiOutput
|
andrewm@0
|
357 return true;
|
andrewm@0
|
358 #else
|
andrewm@0
|
359 return false;
|
andrewm@0
|
360 #endif
|
andrewm@0
|
361 }
|
andrewm@0
|
362
|
andrewm@0
|
363 int DistortionAudioProcessor::getNumPrograms()
|
andrewm@0
|
364 {
|
andrewm@0
|
365 return 0;
|
andrewm@0
|
366 }
|
andrewm@0
|
367
|
andrewm@0
|
368 int DistortionAudioProcessor::getCurrentProgram()
|
andrewm@0
|
369 {
|
andrewm@0
|
370 return 0;
|
andrewm@0
|
371 }
|
andrewm@0
|
372
|
andrewm@0
|
373 void DistortionAudioProcessor::setCurrentProgram (int index)
|
andrewm@0
|
374 {
|
andrewm@0
|
375 }
|
andrewm@0
|
376
|
andrewm@0
|
377 const String DistortionAudioProcessor::getProgramName (int index)
|
andrewm@0
|
378 {
|
andrewm@0
|
379 return String::empty;
|
andrewm@0
|
380 }
|
andrewm@0
|
381
|
andrewm@0
|
382 void DistortionAudioProcessor::changeProgramName (int index, const String& newName)
|
andrewm@0
|
383 {
|
andrewm@0
|
384 }
|