2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
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.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
26 //==============================================================================
28 Performs a simple reverb effect on a stream of audio data.
30 This is a simple stereo reverb, based on the technique and tunings used in FreeVerb.
31 Use setSampleRate() to prepare it, and then call processStereo() or processMono() to
32 apply the reverb to your audio data.
34 @see ReverbAudioSource
41 //==============================================================================
44 setParameters (Parameters());
45 setSampleRate (44100.0);
48 //==============================================================================
49 /** Holds the parameters being used by a Reverb object. */
52 float roomSize
= 0.5f
; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */
53 float damping
= 0.5f
; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */
54 float wetLevel
= 0.33f
; /**< Wet level, 0 to 1.0 */
55 float dryLevel
= 0.4f
; /**< Dry level, 0 to 1.0 */
56 float width
= 1.0f
; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */
57 float freezeMode
= 0.0f
; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5
58 put the reverb into a continuous feedback loop. */
61 //==============================================================================
62 /** Returns the reverb's current parameters. */
63 const Parameters
& getParameters() const noexcept
{ return parameters
; }
65 /** Applies a new set of parameters to the reverb.
66 Note that this doesn't attempt to lock the reverb, so if you call this in parallel with
67 the process method, you may get artifacts.
69 void setParameters (const Parameters
& newParams
)
71 const float wetScaleFactor
= 3.0f
;
72 const float dryScaleFactor
= 2.0f
;
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
));
79 gain
= isFrozen (newParams
.freezeMode
) ? 0.0f
: 0.015f
;
80 parameters
= newParams
;
84 //==============================================================================
85 /** Sets the sample rate that will be used for the reverb.
86 You must call this before the process methods, in order to tell it the correct sample rate.
88 void setSampleRate (const double sampleRate
)
90 jassert (sampleRate
> 0);
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
;
97 for (int i
= 0; i
< numCombs
; ++i
)
99 comb
[0][i
].setSize ((intSampleRate
* combTunings
[i
]) / 44100);
100 comb
[1][i
].setSize ((intSampleRate
* (combTunings
[i
] + stereoSpread
)) / 44100);
103 for (int i
= 0; i
< numAllPasses
; ++i
)
105 allPass
[0][i
].setSize ((intSampleRate
* allPassTunings
[i
]) / 44100);
106 allPass
[1][i
].setSize ((intSampleRate
* (allPassTunings
[i
] + stereoSpread
)) / 44100);
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
);
117 /** Clears the reverb's buffers. */
120 for (int j
= 0; j
< numChannels
; ++j
)
122 for (int i
= 0; i
< numCombs
; ++i
)
125 for (int i
= 0; i
< numAllPasses
; ++i
)
126 allPass
[j
][i
].clear();
130 //==============================================================================
131 /** Applies the reverb to two stereo channels of audio data. */
132 void processStereo (float* const left
, float* const right
, const int numSamples
) noexcept
134 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
135 jassert (left
!= nullptr && right
!= nullptr);
137 for (int i
= 0; i
< numSamples
; ++i
)
139 const float input
= (left
[i
] + right
[i
]) * gain
;
140 float outL
= 0, outR
= 0;
142 const float damp
= damping
.getNextValue();
143 const float feedbck
= feedback
.getNextValue();
145 for (int j
= 0; j
< numCombs
; ++j
) // accumulate the comb filters in parallel
147 outL
+= comb
[0][j
].process (input
, damp
, feedbck
);
148 outR
+= comb
[1][j
].process (input
, damp
, feedbck
);
151 for (int j
= 0; j
< numAllPasses
; ++j
) // run the allpass filters in series
153 outL
= allPass
[0][j
].process (outL
);
154 outR
= allPass
[1][j
].process (outR
);
157 const float dry
= dryGain
.getNextValue();
158 const float wet1
= wetGain1
.getNextValue();
159 const float wet2
= wetGain2
.getNextValue();
161 left
[i
] = outL
* wet1
+ outR
* wet2
+ left
[i
] * dry
;
162 right
[i
] = outR
* wet1
+ outL
* wet2
+ right
[i
] * dry
;
164 JUCE_END_IGNORE_WARNINGS_MSVC
167 /** Applies the reverb to a single mono channel of audio data. */
168 void processMono (float* const samples
, const int numSamples
) noexcept
170 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
171 jassert (samples
!= nullptr);
173 for (int i
= 0; i
< numSamples
; ++i
)
175 const float input
= samples
[i
] * gain
;
178 const float damp
= damping
.getNextValue();
179 const float feedbck
= feedback
.getNextValue();
181 for (int j
= 0; j
< numCombs
; ++j
) // accumulate the comb filters in parallel
182 output
+= comb
[0][j
].process (input
, damp
, feedbck
);
184 for (int j
= 0; j
< numAllPasses
; ++j
) // run the allpass filters in series
185 output
= allPass
[0][j
].process (output
);
187 const float dry
= dryGain
.getNextValue();
188 const float wet1
= wetGain1
.getNextValue();
190 samples
[i
] = output
* wet1
+ samples
[i
] * dry
;
192 JUCE_END_IGNORE_WARNINGS_MSVC
196 //==============================================================================
197 static bool isFrozen (const float freezeMode
) noexcept
{ return freezeMode
>= 0.5f
; }
199 void updateDamping() noexcept
201 const float roomScaleFactor
= 0.28f
;
202 const float roomOffset
= 0.7f
;
203 const float dampScaleFactor
= 0.4f
;
205 if (isFrozen (parameters
.freezeMode
))
206 setDamping (0.0f
, 1.0f
);
208 setDamping (parameters
.damping
* dampScaleFactor
,
209 parameters
.roomSize
* roomScaleFactor
+ roomOffset
);
212 void setDamping (const float dampingToUse
, const float roomSizeToUse
) noexcept
214 damping
.setTargetValue (dampingToUse
);
215 feedback
.setTargetValue (roomSizeToUse
);
218 //==============================================================================
222 CombFilter() noexcept
{}
224 void setSize (const int size
)
226 if (size
!= bufferSize
)
229 buffer
.malloc (size
);
236 void clear() noexcept
239 buffer
.clear ((size_t) bufferSize
);
242 float process (const float input
, const float damp
, const float feedbackLevel
) noexcept
244 const float output
= buffer
[bufferIndex
];
245 last
= (output
* (1.0f
- damp
)) + (last
* damp
);
246 JUCE_UNDENORMALISE (last
);
248 float temp
= input
+ (last
* feedbackLevel
);
249 JUCE_UNDENORMALISE (temp
);
250 buffer
[bufferIndex
] = temp
;
251 bufferIndex
= (bufferIndex
+ 1) % bufferSize
;
256 HeapBlock
<float> buffer
;
257 int bufferSize
= 0, bufferIndex
= 0;
260 JUCE_DECLARE_NON_COPYABLE (CombFilter
)
263 //==============================================================================
267 AllPassFilter() noexcept
{}
269 void setSize (const int size
)
271 if (size
!= bufferSize
)
274 buffer
.malloc (size
);
281 void clear() noexcept
283 buffer
.clear ((size_t) bufferSize
);
286 float process (const float input
) noexcept
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
;
297 HeapBlock
<float> buffer
;
298 int bufferSize
= 0, bufferIndex
= 0;
300 JUCE_DECLARE_NON_COPYABLE (AllPassFilter
)
303 //==============================================================================
304 enum { numCombs
= 8, numAllPasses
= 4, numChannels
= 2 };
306 Parameters parameters
;
309 CombFilter comb
[numChannels
][numCombs
];
310 AllPassFilter allPass
[numChannels
][numAllPasses
];
312 SmoothedValue
<float> damping
, feedback
, dryGain
, wetGain1
, wetGain2
;
314 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb
)