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 Compressor: dynamic range compression effect
|
andrewm@0
|
10 See textbook Chapter 6: Dynamics Processing
|
andrewm@0
|
11
|
andrewm@0
|
12 Code by Joshua Reiss, Brecht de Man and Andrew McPherson
|
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 CompressorAudioProcessor::CompressorAudioProcessor()
|
andrewm@0
|
33 // Initializer List
|
andrewm@0
|
34 :
|
andrewm@0
|
35 inputBuffer(1,1),
|
andrewm@0
|
36 nhost(0)
|
andrewm@0
|
37 {
|
andrewm@0
|
38 lastUIWidth = 850;
|
andrewm@0
|
39 lastUIHeight = 650;
|
andrewm@0
|
40 lastPosInfo.resetToDefault();
|
andrewm@0
|
41 }
|
andrewm@0
|
42 CompressorAudioProcessor::~CompressorAudioProcessor()
|
andrewm@0
|
43 {
|
andrewm@0
|
44 }
|
andrewm@0
|
45 //==============================================================================
|
andrewm@0
|
46 void CompressorAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
andrewm@0
|
47 {
|
andrewm@0
|
48 // Use this method as the place to do any pre-playback initialisation that you need.
|
andrewm@0
|
49 M = round(getNumInputChannels()/2);
|
andrewm@0
|
50 samplerate = (float)getSampleRate();
|
andrewm@0
|
51 bufferSize = getBlockSize();
|
andrewm@0
|
52 // Allocate a lot of dynamic memory here
|
andrewm@0
|
53 x_g .allocate(bufferSize, true);
|
andrewm@0
|
54 x_l .allocate(bufferSize, true);
|
andrewm@0
|
55 y_g .allocate(bufferSize, true);
|
andrewm@0
|
56 y_l .allocate(bufferSize, true);
|
andrewm@0
|
57 c .allocate(bufferSize, true);
|
andrewm@0
|
58 yL_prev=0;
|
andrewm@0
|
59 autoTime = false;
|
andrewm@0
|
60 compressorONOFF = false;
|
andrewm@0
|
61 resetAll();
|
andrewm@0
|
62 }
|
andrewm@0
|
63 void CompressorAudioProcessor::releaseResources()
|
andrewm@0
|
64 {
|
andrewm@0
|
65 // When playback stops, you can use this to free up any spare memory, etc.
|
andrewm@0
|
66 }
|
andrewm@0
|
67 void CompressorAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
|
andrewm@0
|
68 {
|
andrewm@0
|
69 if (compressorONOFF)
|
andrewm@0
|
70 {
|
andrewm@0
|
71 inputBuffer.setSize(M,bufferSize);
|
andrewm@0
|
72 inputBuffer.clear();
|
andrewm@0
|
73 for (int m = 0 ; m < M ; ++m)
|
andrewm@0
|
74 {
|
andrewm@0
|
75 if ( (threshold< 0) )
|
andrewm@0
|
76 {
|
andrewm@0
|
77 inputBuffer.clear(m,0,bufferSize);
|
andrewm@0
|
78 // Mix down left-right to analyse the input
|
andrewm@0
|
79 inputBuffer.addFrom(m,0,buffer,m*2,0,bufferSize,0.5);
|
andrewm@0
|
80 inputBuffer.addFrom(m,0,buffer,m*2+1,0,bufferSize,0.5);
|
andrewm@0
|
81 // compression : calculates the control voltage
|
andrewm@0
|
82 compressor(inputBuffer,m);
|
andrewm@0
|
83 // apply control voltage to the audio signal
|
andrewm@0
|
84 for (int i = 0 ; i < bufferSize ; ++i)
|
andrewm@0
|
85 {
|
b@1
|
86 buffer.getWritePointer(2*m+0)[i] *= c[i];
|
b@1
|
87 buffer.getWritePointer(2*m+1)[i] *= c[i];
|
andrewm@0
|
88 }
|
andrewm@0
|
89 inputBuffer.clear(m,0,bufferSize);
|
andrewm@0
|
90 // Mix down left-right to analyse the output
|
andrewm@0
|
91 inputBuffer.addFrom(m,0,buffer,m*2,0,bufferSize,0.5);
|
andrewm@0
|
92 inputBuffer.addFrom(m,0,buffer,m*2+1,0,bufferSize,0.5);
|
andrewm@0
|
93 }
|
andrewm@0
|
94 }
|
andrewm@0
|
95 }
|
andrewm@0
|
96 }
|
andrewm@0
|
97 // compressor functions
|
andrewm@0
|
98 void CompressorAudioProcessor::compressor(AudioSampleBuffer &buffer, int m)
|
andrewm@0
|
99 {
|
andrewm@0
|
100 alphaAttack = exp(-1/(0.001 * samplerate * tauAttack));
|
andrewm@0
|
101 alphaRelease= exp(-1/(0.001 * samplerate * tauRelease));
|
andrewm@0
|
102 for (int i = 0 ; i < bufferSize ; ++i)
|
andrewm@0
|
103 {
|
andrewm@0
|
104 //Level detection- estimate level using peak detector
|
b@1
|
105 if (fabs(buffer.getWritePointer(m)[i]) < 0.000001) x_g[i] =-120;
|
b@1
|
106 else x_g[i] =20*log10(fabs(buffer.getWritePointer(m)[i]));
|
andrewm@0
|
107 //Gain computer- static apply input/output curve
|
andrewm@0
|
108 if (x_g[i] >= threshold) y_g[i] = threshold+ (x_g[i] - threshold) / ratio;
|
andrewm@0
|
109 else y_g[i] = x_g[i];
|
andrewm@0
|
110 x_l[i] = x_g[i] - y_g[i];
|
andrewm@0
|
111 //Ballistics- smoothing of the gain
|
andrewm@0
|
112 if (x_l[0]>yL_prev) y_l[i]=alphaAttack * yL_prev+(1 - alphaAttack ) * x_l[i] ;
|
andrewm@0
|
113 else y_l[i]=alphaRelease* yL_prev+(1 - alphaRelease) * x_l[i] ;
|
andrewm@0
|
114 //find control
|
andrewm@0
|
115 c[i] = pow(10,(makeUpGain - y_l[i])/20);
|
andrewm@0
|
116 yL_prev=y_l[i];
|
andrewm@0
|
117 }
|
andrewm@0
|
118 }
|
andrewm@0
|
119 template <class T> const T& CompressorAudioProcessor::max( const T& a, const T& b )
|
andrewm@0
|
120 {
|
andrewm@0
|
121 return (a < b) ? b : a;
|
andrewm@0
|
122 }
|
andrewm@0
|
123 void CompressorAudioProcessor::resetAll()
|
andrewm@0
|
124 {
|
andrewm@0
|
125 tauAttack=0;tauRelease = 0;
|
andrewm@0
|
126 alphaAttack=0;alphaRelease = 0;
|
andrewm@0
|
127 threshold = 0;
|
andrewm@0
|
128 ratio= 1;
|
andrewm@0
|
129 makeUpGain= 0;
|
andrewm@0
|
130 yL_prev=0;
|
andrewm@0
|
131 for (int i = 0 ; i < bufferSize ; ++i)
|
andrewm@0
|
132 {
|
andrewm@0
|
133 x_g[i] = 0; y_g[i] = 0;
|
andrewm@0
|
134 x_l[i] = 0; y_l[i] = 0;
|
andrewm@0
|
135 c[i] = 0;
|
andrewm@0
|
136 }
|
andrewm@0
|
137 }
|
andrewm@0
|
138 //////////////////////////////////////////////
|
andrewm@0
|
139 float CompressorAudioProcessor::getThreshold()
|
andrewm@0
|
140 {
|
andrewm@0
|
141 return threshold;
|
andrewm@0
|
142 }
|
andrewm@0
|
143 float CompressorAudioProcessor::getRatio()
|
andrewm@0
|
144 {
|
andrewm@0
|
145 return ratio;
|
andrewm@0
|
146 }
|
andrewm@0
|
147 float CompressorAudioProcessor::getGain()
|
andrewm@0
|
148 {
|
andrewm@0
|
149 return makeUpGain;//problem?
|
andrewm@0
|
150 }
|
andrewm@0
|
151 float CompressorAudioProcessor::getAttackTime()
|
andrewm@0
|
152 {
|
andrewm@0
|
153 return tauAttack;
|
andrewm@0
|
154 }
|
andrewm@0
|
155 float CompressorAudioProcessor::getReleaseTime()
|
andrewm@0
|
156 {
|
andrewm@0
|
157 return tauRelease;
|
andrewm@0
|
158 }
|
andrewm@0
|
159 ////////////////////////////////////////////////////////
|
andrewm@0
|
160 void CompressorAudioProcessor::setThreshold(float T)
|
andrewm@0
|
161 {
|
andrewm@0
|
162 threshold= T;
|
andrewm@0
|
163 }
|
andrewm@0
|
164 void CompressorAudioProcessor::setGain(float G)
|
andrewm@0
|
165 {
|
andrewm@0
|
166 makeUpGain= G;
|
andrewm@0
|
167 }
|
andrewm@0
|
168 void CompressorAudioProcessor::setRatio(float R)
|
andrewm@0
|
169 {
|
andrewm@0
|
170 ratio= R;
|
andrewm@0
|
171 }
|
andrewm@0
|
172 void CompressorAudioProcessor::setAttackTime(float A)
|
andrewm@0
|
173 {
|
andrewm@0
|
174 tauAttack = A;
|
andrewm@0
|
175 }
|
andrewm@0
|
176 void CompressorAudioProcessor::setReleaseTime(float R)
|
andrewm@0
|
177 {
|
andrewm@0
|
178 tauRelease = R;
|
andrewm@0
|
179 }
|
andrewm@0
|
180 bool CompressorAudioProcessor::hasEditor() const
|
andrewm@0
|
181 {
|
andrewm@0
|
182 return true; // (change this to false if you choose to not supply an editor)
|
andrewm@0
|
183 }
|
andrewm@0
|
184 AudioProcessorEditor* CompressorAudioProcessor::createEditor()
|
andrewm@0
|
185 {
|
andrewm@0
|
186 return new CompressorAudioProcessorEditor (this);
|
andrewm@0
|
187 }
|
andrewm@0
|
188 //==============================================================================
|
andrewm@0
|
189 void CompressorAudioProcessor::getStateInformation (MemoryBlock& destData)
|
andrewm@0
|
190 {
|
andrewm@0
|
191 //Use this to store your parameters in memory block, either as raw data, or use XML or ValueTree classes as intermediaries to make it easy to save and load complex data.
|
andrewm@0
|
192 }
|
andrewm@0
|
193 void CompressorAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
andrewm@0
|
194 {
|
andrewm@0
|
195 // Use this to restore your parameters from this memory block, whose contents will have been created by the getStateInformation() call.
|
andrewm@0
|
196 }
|
andrewm@0
|
197 // This creates new instances of the plugin..
|
andrewm@0
|
198 AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
andrewm@0
|
199 {
|
andrewm@0
|
200 return new CompressorAudioProcessor();
|
andrewm@0
|
201 }
|
andrewm@0
|
202 int CompressorAudioProcessor::round(float inn)
|
andrewm@0
|
203 {
|
andrewm@0
|
204 if (inn > 0) return (int) (inn + 0.5);
|
andrewm@0
|
205 else return (int) (inn - 0.5);
|
andrewm@0
|
206 }
|
andrewm@0
|
207 const String CompressorAudioProcessor::getName() const
|
andrewm@0
|
208 {
|
andrewm@0
|
209 return JucePlugin_Name;
|
andrewm@0
|
210 }
|
andrewm@0
|
211 int CompressorAudioProcessor::getNumParameters()
|
andrewm@0
|
212 {
|
andrewm@0
|
213 return 0;
|
andrewm@0
|
214 }
|
andrewm@0
|
215 float CompressorAudioProcessor::getParameter (int index)
|
andrewm@0
|
216 {
|
andrewm@0
|
217 return 0.0f;
|
andrewm@0
|
218 }
|
andrewm@0
|
219 void CompressorAudioProcessor::setParameter (int index, float newValue)
|
andrewm@0
|
220 {
|
andrewm@0
|
221 }
|
andrewm@0
|
222 const String CompressorAudioProcessor::getParameterName (int index)
|
andrewm@0
|
223 {
|
andrewm@0
|
224 return String::empty;
|
andrewm@0
|
225 }
|
andrewm@0
|
226 const String CompressorAudioProcessor::getParameterText (int index)
|
andrewm@0
|
227 {
|
andrewm@0
|
228 return String::empty;
|
andrewm@0
|
229 }
|
andrewm@0
|
230 const String CompressorAudioProcessor::getInputChannelName (int channelIndex) const
|
andrewm@0
|
231 {
|
andrewm@0
|
232 return String (channelIndex + 1);
|
andrewm@0
|
233 }
|
andrewm@0
|
234 const String CompressorAudioProcessor::getOutputChannelName (int channelIndex) const
|
andrewm@0
|
235 {
|
andrewm@0
|
236 return String (channelIndex + 1);
|
andrewm@0
|
237 }
|
andrewm@0
|
238 bool CompressorAudioProcessor::isInputChannelStereoPair (int index) const
|
andrewm@0
|
239 {
|
andrewm@0
|
240 return true;
|
andrewm@0
|
241 }
|
andrewm@0
|
242 bool CompressorAudioProcessor::isOutputChannelStereoPair (int index) const
|
andrewm@0
|
243 {
|
andrewm@0
|
244 return true;
|
andrewm@0
|
245 }
|
andrewm@0
|
246 bool CompressorAudioProcessor::silenceInProducesSilenceOut() const
|
andrewm@0
|
247 {
|
andrewm@0
|
248 #if JucePlugin_SilenceInProducesSilenceOut
|
andrewm@0
|
249 return true;
|
andrewm@0
|
250 #else
|
andrewm@0
|
251 return false;
|
andrewm@0
|
252 #endif
|
andrewm@0
|
253 }
|
andrewm@0
|
254
|
andrewm@0
|
255 double CompressorAudioProcessor::getTailLengthSeconds() const
|
andrewm@0
|
256 {
|
andrewm@0
|
257 return 0.0;
|
andrewm@0
|
258 }
|
andrewm@0
|
259 bool CompressorAudioProcessor::acceptsMidi() const
|
andrewm@0
|
260 {
|
andrewm@0
|
261 #if JucePlugin_WantsMidiInput
|
andrewm@0
|
262 return true;
|
andrewm@0
|
263 #else
|
andrewm@0
|
264 return false;
|
andrewm@0
|
265 #endif
|
andrewm@0
|
266 }
|
andrewm@0
|
267 bool CompressorAudioProcessor::producesMidi() const
|
andrewm@0
|
268 {
|
andrewm@0
|
269 #if JucePlugin_ProducesMidiOutput
|
andrewm@0
|
270 return true;
|
andrewm@0
|
271 #else
|
andrewm@0
|
272 return false;
|
andrewm@0
|
273 #endif
|
andrewm@0
|
274 }
|
andrewm@0
|
275 int CompressorAudioProcessor::getNumPrograms()
|
andrewm@0
|
276 {
|
andrewm@0
|
277 return 0;
|
andrewm@0
|
278 }
|
andrewm@0
|
279 int CompressorAudioProcessor::getCurrentProgram()
|
andrewm@0
|
280 {
|
andrewm@0
|
281 return 0;
|
andrewm@0
|
282 }
|
andrewm@0
|
283 void CompressorAudioProcessor::setCurrentProgram (int index)
|
andrewm@0
|
284 {
|
andrewm@0
|
285 }
|
andrewm@0
|
286 const String CompressorAudioProcessor::getProgramName (int index)
|
andrewm@0
|
287 {
|
andrewm@0
|
288 return String::empty;
|
andrewm@0
|
289 }
|
andrewm@0
|
290 void CompressorAudioProcessor::changeProgramName (int index, const String& newName)
|
andrewm@0
|
291 {
|
andrewm@0
|
292 } |