LMMS
Loading...
Searching...
No Matches
juce_DryWetMixer.cpp
Go to the documentation of this file.
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 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28namespace dsp
29{
30
31//==============================================================================
32template <typename SampleType>
37
38template <typename SampleType>
39DryWetMixer<SampleType>::DryWetMixer (int maximumWetLatencyInSamplesIn)
40 : dryDelayLine (maximumWetLatencyInSamplesIn),
41 maximumWetLatencyInSamples (maximumWetLatencyInSamplesIn)
42{
43 dryDelayLine.setDelay (0);
44
45 update();
46 reset();
47}
48
49//==============================================================================
50template <typename SampleType>
56
57template <typename SampleType>
58void DryWetMixer<SampleType>::setWetMixProportion (SampleType newWetMixProportion)
59{
60 jassert (isPositiveAndNotGreaterThan (newWetMixProportion, 1.0));
61
62 mix = jlimit (static_cast<SampleType> (0.0), static_cast<SampleType> (1.0), newWetMixProportion);
63 update();
64}
65
66template <typename SampleType>
67void DryWetMixer<SampleType>::setWetLatency (SampleType wetLatencySamples)
68{
69 dryDelayLine.setDelay (wetLatencySamples);
70}
71
72//==============================================================================
73template <typename SampleType>
75{
76 jassert (spec.sampleRate > 0);
77 jassert (spec.numChannels > 0);
78
80
81 dryDelayLine.prepare (spec);
82 bufferDry.setSize ((int) spec.numChannels, (int) spec.maximumBlockSize, false, false, true);
83
84 update();
85 reset();
86}
87
88template <typename SampleType>
90{
91 dryVolume.reset (sampleRate, 0.05);
92 wetVolume.reset (sampleRate, 0.05);
93
94 dryDelayLine.reset();
95
97 bufferDry.setSize (bufferDry.getNumChannels(), fifo.getSize(), false, false, true);
98}
99
100//==============================================================================
101template <typename SampleType>
103{
104 jassert (drySamples.getNumChannels() <= (size_t) bufferDry.getNumChannels());
105 jassert (drySamples.getNumSamples() <= (size_t) fifo.getRemainingSpace());
106
107 auto offset = 0;
108
109 for (const auto& range : fifo.write ((int) drySamples.getNumSamples()))
110 {
111 if (range.getLength() == 0)
112 continue;
113
115 .getSubBlock ((size_t) range.getStart(), (size_t) range.getLength());
116
117 auto inputBlock = drySamples.getSubBlock ((size_t) offset, (size_t) range.getLength());
118
120 block.copyFrom (inputBlock);
121 else
122 dryDelayLine.process (ProcessContextNonReplacing<SampleType> (inputBlock, block));
123
124 offset += range.getLength();
125 }
126}
127
128template <typename SampleType>
130{
131 inOutBlock.multiplyBy (wetVolume);
132
133 jassert (inOutBlock.getNumSamples() <= (size_t) fifo.getNumReadable());
134
135 auto offset = 0;
136
137 for (const auto& range : fifo.read ((int) inOutBlock.getNumSamples()))
138 {
139 if (range.getLength() == 0)
140 continue;
141
143 .getSubBlock ((size_t) range.getStart(), (size_t) range.getLength());
144 block.multiplyBy (dryVolume);
145 inOutBlock.getSubBlock ((size_t) offset).add (block);
146
147 offset += range.getLength();
148 }
149}
150
151//==============================================================================
152template <typename SampleType>
154{
155 SampleType dryValue, wetValue;
156
157 switch (currentMixingRule)
158 {
159 case MixingRule::balanced:
160 dryValue = static_cast<SampleType> (2.0) * jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - mix);
161 wetValue = static_cast<SampleType> (2.0) * jmin (static_cast<SampleType> (0.5), mix);
162 break;
163
164 case MixingRule::linear:
165 dryValue = static_cast<SampleType> (1.0) - mix;
166 wetValue = mix;
167 break;
168
169 case MixingRule::sin3dB:
170 dryValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)));
171 wetValue = static_cast<SampleType> (std::sin (0.5 * MathConstants<double>::pi * mix));
172 break;
173
174 case MixingRule::sin4p5dB:
175 dryValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)), 1.5));
176 wetValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * mix), 1.5));
177 break;
178
179 case MixingRule::sin6dB:
180 dryValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * (1.0 - mix)), 2.0));
181 wetValue = static_cast<SampleType> (std::pow (std::sin (0.5 * MathConstants<double>::pi * mix), 2.0));
182 break;
183
184 case MixingRule::squareRoot3dB:
185 dryValue = std::sqrt (static_cast<SampleType> (1.0) - mix);
186 wetValue = std::sqrt (mix);
187 break;
188
189 case MixingRule::squareRoot4p5dB:
190 dryValue = static_cast<SampleType> (std::pow (std::sqrt (1.0 - mix), 1.5));
191 wetValue = static_cast<SampleType> (std::pow (std::sqrt (mix), 1.5));
192 break;
193
194 default:
195 dryValue = jmin (static_cast<SampleType> (0.5), static_cast<SampleType> (1.0) - mix);
196 wetValue = jmin (static_cast<SampleType> (0.5), mix);
197 break;
198 }
199
200 dryVolume.setTargetValue (dryValue);
201 wetVolume.setTargetValue (wetValue);
202}
203
204//==============================================================================
205template class DryWetMixer<float>;
206template class DryWetMixer<double>;
207
208
209//==============================================================================
210//==============================================================================
211#if JUCE_UNIT_TESTS
212
213struct DryWetMixerTests : public UnitTest
214{
215 DryWetMixerTests() : UnitTest ("DryWetMixer", UnitTestCategories::dsp) {}
216
217 enum class Kind { down, up };
218
219 static auto getRampBuffer (ProcessSpec spec, Kind kind)
220 {
221 AudioBuffer<float> buffer ((int) spec.numChannels, (int) spec.maximumBlockSize);
222
223 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
224 {
225 for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
226 {
227 const auto ramp = kind == Kind::up ? sample : spec.maximumBlockSize - sample;
228
229 buffer.setSample ((int) channel,
230 (int) sample,
231 jmap ((float) ramp, 0.0f, (float) spec.maximumBlockSize, 0.0f, 1.0f));
232 }
233 }
234
235 return buffer;
236 }
237
238 void runTest() override
239 {
240 constexpr ProcessSpec spec { 44100.0, 512, 2 };
241 constexpr auto numBlocks = 5;
242
243 const auto wetBuffer = getRampBuffer (spec, Kind::up);
244 const auto dryBuffer = getRampBuffer (spec, Kind::down);
245
246 for (auto maxLatency : { 0, 100, 200, 512 })
247 {
248 beginTest ("Mixer can push multiple small buffers");
249 {
250 DryWetMixer<float> mixer (maxLatency);
251 mixer.setWetMixProportion (0.5f);
252 mixer.prepare (spec);
253
254 for (auto block = 0; block < numBlocks; ++block)
255 {
256 // Push samples one-by-one
257 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
258 mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (sample, 1));
259
260 // Mix wet samples in one go
261 auto outputBlock = wetBuffer;
262 mixer.mixWetSamples ({ outputBlock });
263
264 // The output block should contain the wet and dry samples averaged
265 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
266 {
267 for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
268 {
269 const auto outputValue = outputBlock.getSample ((int) channel, (int) sample);
270 expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
271 }
272 }
273 }
274 }
275
276 beginTest ("Mixer can pop multiple small buffers");
277 {
278 DryWetMixer<float> mixer (maxLatency);
279 mixer.setWetMixProportion (0.5f);
280 mixer.prepare (spec);
281
282 for (auto block = 0; block < numBlocks; ++block)
283 {
284 // Push samples in one go
285 mixer.pushDrySamples ({ dryBuffer });
286
287 // Process wet samples one-by-one
288 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
289 {
290 AudioBuffer<float> outputBlock ((int) spec.numChannels, 1);
291 AudioBlock<const float> (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock);
292 mixer.mixWetSamples ({ outputBlock });
293
294 // The output block should contain the wet and dry samples averaged
295 for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
296 {
297 const auto outputValue = outputBlock.getSample ((int) channel, 0);
298 expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
299 }
300 }
301 }
302 }
303
304 beginTest ("Mixer can push and pop multiple small buffers");
305 {
306 DryWetMixer<float> mixer (maxLatency);
307 mixer.setWetMixProportion (0.5f);
308 mixer.prepare (spec);
309
310 for (auto block = 0; block < numBlocks; ++block)
311 {
312 // Push dry samples and process wet samples one-by-one
313 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
314 {
315 mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (sample, 1));
316
317 AudioBuffer<float> outputBlock ((int) spec.numChannels, 1);
318 AudioBlock<const float> (wetBuffer).getSubBlock (sample, 1).copyTo (outputBlock);
319 mixer.mixWetSamples ({ outputBlock });
320
321 // The output block should contain the wet and dry samples averaged
322 for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
323 {
324 const auto outputValue = outputBlock.getSample ((int) channel, 0);
325 expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
326 }
327 }
328 }
329 }
330
331 beginTest ("Mixer can push and pop full-sized blocks after encountering a shorter block");
332 {
333 DryWetMixer<float> mixer (maxLatency);
334 mixer.setWetMixProportion (0.5f);
335 mixer.prepare (spec);
336
337 constexpr auto shortBlockLength = spec.maximumBlockSize / 2;
338 AudioBuffer<float> shortBlock (spec.numChannels, shortBlockLength);
339 mixer.pushDrySamples (AudioBlock<const float> (dryBuffer).getSubBlock (shortBlockLength));
340 mixer.mixWetSamples ({ shortBlock });
341
342 for (auto block = 0; block < numBlocks; ++block)
343 {
344 // Push a full block of dry samples
345 mixer.pushDrySamples ({ dryBuffer });
346
347 // Mix a full block of wet samples
348 auto outputBlock = wetBuffer;
349 mixer.mixWetSamples ({ outputBlock });
350
351 // The output block should contain the wet and dry samples averaged
352 for (uint32_t sample = 0; sample < spec.maximumBlockSize; ++sample)
353 {
354 for (uint32_t channel = 0; channel < spec.numChannels; ++channel)
355 {
356 const auto outputValue = outputBlock.getSample ((int) channel, (int) sample);
357 expectWithinAbsoluteError (outputValue, 0.5f, 0.0001f);
358 }
359 }
360 }
361 }
362 }
363 }
364};
365
366static const DryWetMixerTests dryWetMixerTests;
367
368#endif
369
370} // namespace dsp
371} // namespace juce
Type jmin(const Type a, const Type b)
Definition MathsFunctions.h:60
Definition juce_SingleThreadedAbstractFifo.h:64
Definition juce_UnitTest.h:70
Definition juce_AudioBlock.h:70
AudioBlock getSubBlock(size_t newOffset, size_t newLength) const noexcept
Definition juce_AudioBlock.h:371
AudioBlock getSubsetChannelBlock(size_t channelStart, size_t numChannelsToUse) const noexcept
Definition juce_AudioBlock.h:259
constexpr size_t getNumChannels() const noexcept
Definition juce_AudioBlock.h:235
AudioBlock &JUCE_VECTOR_CALLTYPE multiplyBy(NumericType value) noexcept
Definition juce_AudioBlock.h:442
AudioBlock &JUCE_VECTOR_CALLTYPE add(NumericType value) noexcept
Definition juce_AudioBlock.h:396
AudioBlock & copyFrom(const AudioBlock< OtherSampleType > &src) noexcept
Definition juce_AudioBlock.h:314
constexpr size_t getNumSamples() const noexcept
Definition juce_AudioBlock.h:238
Definition juce_DryWetMixer.h:54
void pushDrySamples(const AudioBlock< const SampleType > drySamples)
Definition juce_DryWetMixer.cpp:102
SmoothedValue< SampleType, ValueSmoothingTypes::Linear > wetVolume
Definition juce_DryWetMixer.h:108
int maximumWetLatencyInSamples
Definition juce_DryWetMixer.h:116
void setWetMixProportion(SampleType newWetMixProportion)
Definition juce_DryWetMixer.cpp:58
void reset()
Definition juce_DryWetMixer.cpp:89
double sampleRate
Definition juce_DryWetMixer.h:115
AudioBuffer< SampleType > bufferDry
Definition juce_DryWetMixer.h:110
DelayLine< SampleType, DelayLineInterpolationTypes::Thiran > dryDelayLine
Definition juce_DryWetMixer.h:109
MixingRule currentMixingRule
Definition juce_DryWetMixer.h:114
SampleType mix
Definition juce_DryWetMixer.h:113
SingleThreadedAbstractFifo fifo
Definition juce_DryWetMixer.h:112
void setMixingRule(MixingRule newRule)
Definition juce_DryWetMixer.cpp:51
void setWetLatency(SampleType wetLatencyInSamples)
Definition juce_DryWetMixer.cpp:67
void prepare(const ProcessSpec &spec)
Definition juce_DryWetMixer.cpp:74
DryWetMixingRule MixingRule
Definition juce_DryWetMixer.h:57
SmoothedValue< SampleType, ValueSmoothingTypes::Linear > dryVolume
Definition juce_DryWetMixer.h:108
void update()
Definition juce_DryWetMixer.cpp:153
void mixWetSamples(AudioBlock< SampleType > wetSamples)
Definition juce_DryWetMixer.cpp:129
DryWetMixer()
Definition juce_DryWetMixer.cpp:33
#define jassert(expression)
unsigned int uint32_t
Definition mid.cpp:100
Definition audio_fx.h:36
Definition juce_UnitTestCategories.h:27
Definition juce_AudioBlock.h:29
JOCTET * buffer
Definition juce_JPEGLoader.cpp:302
Definition carla_juce.cpp:31
constexpr Type jmin(Type a, Type b)
Definition juce_MathsFunctions.h:106
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Definition juce_MathsFunctions.h:262
int nextPowerOfTwo(int n) noexcept
Definition juce_MathsFunctions.h:533
bool isPositiveAndNotGreaterThan(Type1 valueToTest, Type2 upperLimit) noexcept
Definition juce_MathsFunctions.h:298
static constexpr FloatType pi
Definition juce_MathsFunctions.h:382
Definition juce_ProcessContext.h:141
Definition juce_ProcessContext.h:38
uint32 numChannels
Definition juce_ProcessContext.h:46
double sampleRate
Definition juce_ProcessContext.h:40
uint32 maximumBlockSize
Definition juce_ProcessContext.h:43
signed int sample
Definition tap_dynamics_m.c:41