OpenShot Library | OpenShotAudio  0.2.2
juce_audio_basics/utilities/juce_Reverb.h
1 
2 /** @weakgroup juce_audio_basics-utilities
3  * @{
4  */
5 /*
6  ==============================================================================
7 
8  This file is part of the JUCE library.
9  Copyright (c) 2017 - ROLI Ltd.
10 
11  JUCE is an open source library subject to commercial or open-source
12  licensing.
13 
14  The code included in this file is provided under the terms of the ISC license
15  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
16  To use, copy, modify, and/or distribute this software for any purpose with or
17  without fee is hereby granted provided that the above copyright notice and
18  this permission notice appear in all copies.
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
30 //==============================================================================
31 /**
32  Performs a simple reverb effect on a stream of audio data.
33 
34  This is a simple stereo reverb, based on the technique and tunings used in FreeVerb.
35  Use setSampleRate() to prepare it, and then call processStereo() or processMono() to
36  apply the reverb to your audio data.
37 
38  @see ReverbAudioSource
39 
40  @tags{Audio}
41 */
42 class Reverb
43 {
44 public:
45  //==============================================================================
46  Reverb()
47  {
49  setSampleRate (44100.0);
50  }
51 
52  //==============================================================================
53  /** Holds the parameters being used by a Reverb object. */
54  struct Parameters
55  {
56  float roomSize = 0.5f; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
57  float damping = 0.5f; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
58  float wetLevel = 0.33f; /**< Wet level, 0 to 1.0 */
59  float dryLevel = 0.4f; /**< Dry level, 0 to 1.0 */
60  float width = 1.0f; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
61  float freezeMode = 0.0f; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
62  put the reverb into a continuous feedback loop. */
63  };
64 
65  //==============================================================================
66  /** Returns the reverb's current parameters. */
67  const Parameters& getParameters() const noexcept { return parameters; }
68 
69  /** Applies a new set of parameters to the reverb.
70  Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
71  the process method, you may get artifacts.
72  */
73  void setParameters (const Parameters& newParams)
74  {
75  const float wetScaleFactor = 3.0f;
76  const float dryScaleFactor = 2.0f;
77 
78  const float wet = newParams.wetLevel * wetScaleFactor;
79  dryGain.setTargetValue (newParams.dryLevel * dryScaleFactor);
80  wetGain1.setTargetValue (0.5f * wet * (1.0f + newParams.width));
81  wetGain2.setTargetValue (0.5f * wet * (1.0f - newParams.width));
82 
83  gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
84  parameters = newParams;
85  updateDamping();
86  }
87 
88  //==============================================================================
89  /** Sets the sample rate that will be used for the reverb.
90  You must call this before the process methods, in order to tell it the correct sample rate.
91  */
92  void setSampleRate (const double sampleRate)
93  {
94  jassert (sampleRate > 0);
95 
96  static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
97  static const short allPassTunings[] = { 556, 441, 341, 225 };
98  const int stereoSpread = 23;
99  const int intSampleRate = (int) sampleRate;
100 
101  for (int i = 0; i < numCombs; ++i)
102  {
103  comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
104  comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
105  }
106 
107  for (int i = 0; i < numAllPasses; ++i)
108  {
109  allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
110  allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
111  }
112 
113  const double smoothTime = 0.01;
114  damping .reset (sampleRate, smoothTime);
115  feedback.reset (sampleRate, smoothTime);
116  dryGain .reset (sampleRate, smoothTime);
117  wetGain1.reset (sampleRate, smoothTime);
118  wetGain2.reset (sampleRate, smoothTime);
119  }
120 
121  /** Clears the reverb's buffers. */
122  void reset()
123  {
124  for (int j = 0; j < numChannels; ++j)
125  {
126  for (int i = 0; i < numCombs; ++i)
127  comb[j][i].clear();
128 
129  for (int i = 0; i < numAllPasses; ++i)
130  allPass[j][i].clear();
131  }
132  }
133 
134  //==============================================================================
135  /** Applies the reverb to two stereo channels of audio data. */
136  void processStereo (float* const left, float* const right, const int numSamples) noexcept
137  {
138  jassert (left != nullptr && right != nullptr);
139 
140  for (int i = 0; i < numSamples; ++i)
141  {
142  const float input = (left[i] + right[i]) * gain;
143  float outL = 0, outR = 0;
144 
145  const float damp = damping.getNextValue();
146  const float feedbck = feedback.getNextValue();
147 
148  for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
149  {
150  outL += comb[0][j].process (input, damp, feedbck);
151  outR += comb[1][j].process (input, damp, feedbck);
152  }
153 
154  for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
155  {
156  outL = allPass[0][j].process (outL);
157  outR = allPass[1][j].process (outR);
158  }
159 
160  const float dry = dryGain.getNextValue();
161  const float wet1 = wetGain1.getNextValue();
162  const float wet2 = wetGain2.getNextValue();
163 
164  left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
165  right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
166  }
167  }
168 
169  /** Applies the reverb to a single mono channel of audio data. */
170  void processMono (float* const samples, const int numSamples) noexcept
171  {
172  jassert (samples != nullptr);
173 
174  for (int i = 0; i < numSamples; ++i)
175  {
176  const float input = samples[i] * gain;
177  float output = 0;
178 
179  const float damp = damping.getNextValue();
180  const float feedbck = feedback.getNextValue();
181 
182  for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
183  output += comb[0][j].process (input, damp, feedbck);
184 
185  for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
186  output = allPass[0][j].process (output);
187 
188  const float dry = dryGain.getNextValue();
189  const float wet1 = wetGain1.getNextValue();
190 
191  samples[i] = output * wet1 + samples[i] * dry;
192  }
193  }
194 
195 private:
196  //==============================================================================
197  static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; }
198 
199  void updateDamping() noexcept
200  {
201  const float roomScaleFactor = 0.28f;
202  const float roomOffset = 0.7f;
203  const float dampScaleFactor = 0.4f;
204 
205  if (isFrozen (parameters.freezeMode))
206  setDamping (0.0f, 1.0f);
207  else
208  setDamping (parameters.damping * dampScaleFactor,
209  parameters.roomSize * roomScaleFactor + roomOffset);
210  }
211 
212  void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
213  {
214  damping.setTargetValue (dampingToUse);
215  feedback.setTargetValue (roomSizeToUse);
216  }
217 
218  //==============================================================================
219  class CombFilter
220  {
221  public:
222  CombFilter() noexcept {}
223 
224  void setSize (const int size)
225  {
226  if (size != bufferSize)
227  {
228  bufferIndex = 0;
229  buffer.malloc (size);
230  bufferSize = size;
231  }
232 
233  clear();
234  }
235 
236  void clear() noexcept
237  {
238  last = 0;
239  buffer.clear ((size_t) bufferSize);
240  }
241 
242  float process (const float input, const float damp, const float feedbackLevel) noexcept
243  {
244  const float output = buffer[bufferIndex];
245  last = (output * (1.0f - damp)) + (last * damp);
246  JUCE_UNDENORMALISE (last);
247 
248  float temp = input + (last * feedbackLevel);
249  JUCE_UNDENORMALISE (temp);
250  buffer[bufferIndex] = temp;
251  bufferIndex = (bufferIndex + 1) % bufferSize;
252  return output;
253  }
254 
255  private:
256  HeapBlock<float> buffer;
257  int bufferSize = 0, bufferIndex = 0;
258  float last = 0.0f;
259 
260  JUCE_DECLARE_NON_COPYABLE (CombFilter)
261  };
262 
263  //==============================================================================
264  class AllPassFilter
265  {
266  public:
267  AllPassFilter() noexcept {}
268 
269  void setSize (const int size)
270  {
271  if (size != bufferSize)
272  {
273  bufferIndex = 0;
274  buffer.malloc (size);
275  bufferSize = size;
276  }
277 
278  clear();
279  }
280 
281  void clear() noexcept
282  {
283  buffer.clear ((size_t) bufferSize);
284  }
285 
286  float process (const float input) noexcept
287  {
288  const float bufferedValue = buffer [bufferIndex];
289  float temp = input + (bufferedValue * 0.5f);
290  JUCE_UNDENORMALISE (temp);
291  buffer [bufferIndex] = temp;
292  bufferIndex = (bufferIndex + 1) % bufferSize;
293  return bufferedValue - input;
294  }
295 
296  private:
297  HeapBlock<float> buffer;
298  int bufferSize = 0, bufferIndex = 0;
299 
300  JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
301  };
302 
303  //==============================================================================
304  enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
305 
306  Parameters parameters;
307  float gain;
308 
309  CombFilter comb [numChannels][numCombs];
310  AllPassFilter allPass [numChannels][numAllPasses];
311 
312  SmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
313 
314  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
315 };
316 
317 } // namespace juce
318 
319 /** @}*/
float roomSize
Room size, 0 to 1.0, where 1.0 is big, 0 is small.
void processStereo(float *const left, float *const right, const int numSamples) noexcept
Applies the reverb to two stereo channels of audio data.
const Parameters & getParameters() const noexcept
Returns the reverb&#39;s current parameters.
float width
Reverb width, 0 to 1.0, where 1.0 is very wide.
float freezeMode
Freeze mode - values < 0.5 are "normal" mode, values > 0.5 put the reverb into a continuous feedback ...
void setSampleRate(const double sampleRate)
Sets the sample rate that will be used for the reverb.
float damping
Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped.
Performs a simple reverb effect on a stream of audio data.
void processMono(float *const samples, const int numSamples) noexcept
Applies the reverb to a single mono channel of audio data.
void reset()
Clears the reverb&#39;s buffers.
void setParameters(const Parameters &newParams)
Applies a new set of parameters to the reverb.
Holds the parameters being used by a Reverb object.