OpenShot Audio Library | OpenShotAudio  0.6.0
juce_audio_basics/utilities/juce_Reverb.h
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2022 - Raw Material Software Limited
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 //==============================================================================
38 class Reverb
39 {
40 public:
41  //==============================================================================
42  Reverb()
43  {
45  setSampleRate (44100.0);
46  }
47 
48  //==============================================================================
50  struct Parameters
51  {
52  float roomSize = 0.5f;
53  float damping = 0.5f;
54  float wetLevel = 0.33f;
55  float dryLevel = 0.4f;
56  float width = 1.0f;
57  float freezeMode = 0.0f;
59  };
60 
61  //==============================================================================
63  const Parameters& getParameters() const noexcept { return parameters; }
64 
69  void setParameters (const Parameters& newParams)
70  {
71  const float wetScaleFactor = 3.0f;
72  const float dryScaleFactor = 2.0f;
73 
74  const float wet = newParams.wetLevel * wetScaleFactor;
75  dryGain.setTargetValue (newParams.dryLevel * dryScaleFactor);
76  wetGain1.setTargetValue (0.5f * wet * (1.0f + newParams.width));
77  wetGain2.setTargetValue (0.5f * wet * (1.0f - newParams.width));
78 
79  gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f;
80  parameters = newParams;
81  updateDamping();
82  }
83 
84  //==============================================================================
88  void setSampleRate (const double sampleRate)
89  {
90  jassert (sampleRate > 0);
91 
92  static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz)
93  static const short allPassTunings[] = { 556, 441, 341, 225 };
94  const int stereoSpread = 23;
95  const int intSampleRate = (int) sampleRate;
96 
97  for (int i = 0; i < numCombs; ++i)
98  {
99  comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100);
100  comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100);
101  }
102 
103  for (int i = 0; i < numAllPasses; ++i)
104  {
105  allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100);
106  allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100);
107  }
108 
109  const double smoothTime = 0.01;
110  damping .reset (sampleRate, smoothTime);
111  feedback.reset (sampleRate, smoothTime);
112  dryGain .reset (sampleRate, smoothTime);
113  wetGain1.reset (sampleRate, smoothTime);
114  wetGain2.reset (sampleRate, smoothTime);
115  }
116 
118  void reset()
119  {
120  for (int j = 0; j < numChannels; ++j)
121  {
122  for (int i = 0; i < numCombs; ++i)
123  comb[j][i].clear();
124 
125  for (int i = 0; i < numAllPasses; ++i)
126  allPass[j][i].clear();
127  }
128  }
129 
130  //==============================================================================
132  void processStereo (float* const left, float* const right, const int numSamples) noexcept
133  {
134  JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
135  jassert (left != nullptr && right != nullptr);
136 
137  for (int i = 0; i < numSamples; ++i)
138  {
139  // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
140  const float input = (left[i] + right[i]) * gain;
141  float outL = 0, outR = 0;
142 
143  const float damp = damping.getNextValue();
144  const float feedbck = feedback.getNextValue();
145 
146  for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel
147  {
148  outL += comb[0][j].process (input, damp, feedbck);
149  outR += comb[1][j].process (input, damp, feedbck);
150  }
151 
152  for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series
153  {
154  outL = allPass[0][j].process (outL);
155  outR = allPass[1][j].process (outR);
156  }
157 
158  const float dry = dryGain.getNextValue();
159  const float wet1 = wetGain1.getNextValue();
160  const float wet2 = wetGain2.getNextValue();
161 
162  left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
163  right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
164  }
165  JUCE_END_IGNORE_WARNINGS_MSVC
166  }
167 
169  void processMono (float* const samples, const int numSamples) noexcept
170  {
171  JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
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  JUCE_END_IGNORE_WARNINGS_MSVC
194  }
195 
196 private:
197  //==============================================================================
198  static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; }
199 
200  void updateDamping() noexcept
201  {
202  const float roomScaleFactor = 0.28f;
203  const float roomOffset = 0.7f;
204  const float dampScaleFactor = 0.4f;
205 
206  if (isFrozen (parameters.freezeMode))
207  setDamping (0.0f, 1.0f);
208  else
209  setDamping (parameters.damping * dampScaleFactor,
210  parameters.roomSize * roomScaleFactor + roomOffset);
211  }
212 
213  void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept
214  {
215  damping.setTargetValue (dampingToUse);
216  feedback.setTargetValue (roomSizeToUse);
217  }
218 
219  //==============================================================================
220  class CombFilter
221  {
222  public:
223  CombFilter() noexcept {}
224 
225  void setSize (const int size)
226  {
227  if (size != bufferSize)
228  {
229  bufferIndex = 0;
230  buffer.malloc (size);
231  bufferSize = size;
232  }
233 
234  clear();
235  }
236 
237  void clear() noexcept
238  {
239  last = 0;
240  buffer.clear ((size_t) bufferSize);
241  }
242 
243  float process (const float input, const float damp, const float feedbackLevel) noexcept
244  {
245  const float output = buffer[bufferIndex];
246  last = (output * (1.0f - damp)) + (last * damp);
247  JUCE_UNDENORMALISE (last);
248 
249  float temp = input + (last * feedbackLevel);
250  JUCE_UNDENORMALISE (temp);
251  buffer[bufferIndex] = temp;
252  bufferIndex = (bufferIndex + 1) % bufferSize;
253  return output;
254  }
255 
256  private:
257  HeapBlock<float> buffer;
258  int bufferSize = 0, bufferIndex = 0;
259  float last = 0.0f;
260 
261  JUCE_DECLARE_NON_COPYABLE (CombFilter)
262  };
263 
264  //==============================================================================
265  class AllPassFilter
266  {
267  public:
268  AllPassFilter() noexcept {}
269 
270  void setSize (const int size)
271  {
272  if (size != bufferSize)
273  {
274  bufferIndex = 0;
275  buffer.malloc (size);
276  bufferSize = size;
277  }
278 
279  clear();
280  }
281 
282  void clear() noexcept
283  {
284  buffer.clear ((size_t) bufferSize);
285  }
286 
287  float process (const float input) noexcept
288  {
289  const float bufferedValue = buffer [bufferIndex];
290  float temp = input + (bufferedValue * 0.5f);
291  JUCE_UNDENORMALISE (temp);
292  buffer [bufferIndex] = temp;
293  bufferIndex = (bufferIndex + 1) % bufferSize;
294  return bufferedValue - input;
295  }
296 
297  private:
298  HeapBlock<float> buffer;
299  int bufferSize = 0, bufferIndex = 0;
300 
301  JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
302  };
303 
304  //==============================================================================
305  enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
306 
307  Parameters parameters;
308  float gain;
309 
310  CombFilter comb [numChannels][numCombs];
311  AllPassFilter allPass [numChannels][numAllPasses];
312 
313  SmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
314 
315  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
316 };
317 
318 } // namespace juce
void processMono(float *const samples, const int numSamples) noexcept
void processStereo(float *const left, float *const right, const int numSamples) noexcept
void setParameters(const Parameters &newParams)
void setSampleRate(const double sampleRate)
const Parameters & getParameters() const noexcept
FloatType getNextValue() noexcept
void reset(double sampleRate, double rampLengthInSeconds) noexcept
void setTargetValue(FloatType newValue) noexcept