OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
juce_SmoothedValue.h
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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
23namespace juce
24{
25
26//==============================================================================
35template <typename SmoothedValueType>
37{
38private:
39 //==============================================================================
40 template <typename T> struct FloatTypeHelper;
41
42 template <template <typename> class SmoothedValueClass, typename FloatType>
43 struct FloatTypeHelper <SmoothedValueClass <FloatType>>
44 {
45 using Type = FloatType;
46 };
47
48 template <template <typename, typename> class SmoothedValueClass, typename FloatType, typename SmoothingType>
49 struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
50 {
51 using Type = FloatType;
52 };
53
54public:
55 using FloatType = typename FloatTypeHelper<SmoothedValueType>::Type;
56
57 //==============================================================================
59 SmoothedValueBase() = default;
60
61 virtual ~SmoothedValueBase() {}
62
63 //==============================================================================
65 bool isSmoothing() const noexcept { return countdown > 0; }
66
68 FloatType getCurrentValue() const noexcept { return currentValue; }
69
70 //==============================================================================
72 FloatType getTargetValue() const noexcept { return target; }
73
77 void setCurrentAndTargetValue (FloatType newValue)
78 {
79 target = currentValue = newValue;
80 countdown = 0;
81 }
82
83 //==============================================================================
89 void applyGain (FloatType* samples, int numSamples) noexcept
90 {
91 jassert (numSamples >= 0);
92
93 if (isSmoothing())
94 {
95 for (int i = 0; i < numSamples; ++i)
96 samples[i] *= getNextSmoothedValue();
97 }
98 else
99 {
100 FloatVectorOperations::multiply (samples, target, numSamples);
101 }
102 }
103
110 void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
111 {
112 jassert (numSamples >= 0);
113
114 if (isSmoothing())
115 {
116 for (int i = 0; i < numSamples; ++i)
117 samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
118 }
119 else
120 {
122 }
123 }
124
126 void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
127 {
128 jassert (numSamples >= 0);
129
130 if (isSmoothing())
131 {
132 if (buffer.getNumChannels() == 1)
133 {
134 auto* samples = buffer.getWritePointer (0);
135
136 for (int i = 0; i < numSamples; ++i)
137 samples[i] *= getNextSmoothedValue();
138 }
139 else
140 {
141 for (auto i = 0; i < numSamples; ++i)
142 {
143 auto gain = getNextSmoothedValue();
144
145 for (int channel = 0; channel < buffer.getNumChannels(); channel++)
146 buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
147 }
148 }
149 }
150 else
151 {
152 buffer.applyGain (0, numSamples, target);
153 }
154 }
155
156private:
157 //==============================================================================
158 FloatType getNextSmoothedValue() noexcept
159 {
160 return static_cast <SmoothedValueType*> (this)->getNextValue();
161 }
162
163protected:
164 //==============================================================================
165 FloatType currentValue = 0;
166 FloatType target = currentValue;
167 int countdown = 0;
168};
169
170//==============================================================================
180namespace ValueSmoothingTypes
181{
187 struct Linear {};
188
194 struct Multiplicative {};
195}
196
197//==============================================================================
227template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
228class SmoothedValue : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
229{
230public:
231 //==============================================================================
234 : SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
235 {
236 }
237
239 SmoothedValue (FloatType initialValue) noexcept
240 {
241 // Multiplicative smoothed values cannot ever reach 0!
242 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
243
244 // Visual Studio can't handle base class initialisation with CRTP
245 this->currentValue = initialValue;
246 this->target = this->currentValue;
247 }
248
249 //==============================================================================
254 void reset (double sampleRate, double rampLengthInSeconds) noexcept
255 {
256 jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
257 reset ((int) std::floor (rampLengthInSeconds * sampleRate));
258 }
259
263 void reset (int numSteps) noexcept
264 {
265 stepsToTarget = numSteps;
266 this->setCurrentAndTargetValue (this->target);
267 }
268
269 //==============================================================================
273 void setTargetValue (FloatType newValue) noexcept
274 {
275 if (newValue == this->target)
276 return;
277
278 if (stepsToTarget <= 0)
279 {
280 this->setCurrentAndTargetValue (newValue);
281 return;
282 }
283
284 // Multiplicative smoothed values cannot ever reach 0!
285 jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
286
287 this->target = newValue;
288 this->countdown = stepsToTarget;
289
290 setStepSize();
291 }
292
293 //==============================================================================
298 {
299 if (! this->isSmoothing())
300 return this->target;
301
302 --(this->countdown);
303
304 if (this->isSmoothing())
305 setNextValue();
306 else
307 this->currentValue = this->target;
308
309 return this->currentValue;
310 }
311
312 //==============================================================================
318 FloatType skip (int numSamples) noexcept
319 {
320 if (numSamples >= this->countdown)
321 {
322 this->setCurrentAndTargetValue (this->target);
323 return this->target;
324 }
325
326 skipCurrentValue (numSamples);
327
328 this->countdown -= numSamples;
329 return this->currentValue;
330 }
331
332 //==============================================================================
343 JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
344 {
345 if (force)
346 {
347 this->setCurrentAndTargetValue (newValue);
348 return;
349 }
350
351 setTargetValue (newValue);
352 })
353
354private:
355 //==============================================================================
356 template <typename T>
357 using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
358
359 template <typename T>
360 using MultiplicativeVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value, void>::type;
361
362 //==============================================================================
363 template <typename T = SmoothingType>
364 LinearVoid<T> setStepSize() noexcept
365 {
366 step = (this->target - this->currentValue) / (FloatType) this->countdown;
367 }
368
369 template <typename T = SmoothingType>
370 MultiplicativeVoid<T> setStepSize()
371 {
372 step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
373 }
374
375 //==============================================================================
376 template <typename T = SmoothingType>
377 LinearVoid<T> setNextValue() noexcept
378 {
379 this->currentValue += step;
380 }
381
382 template <typename T = SmoothingType>
383 MultiplicativeVoid<T> setNextValue() noexcept
384 {
385 this->currentValue *= step;
386 }
387
388 //==============================================================================
389 template <typename T = SmoothingType>
390 LinearVoid<T> skipCurrentValue (int numSamples) noexcept
391 {
392 this->currentValue += step * (FloatType) numSamples;
393 }
394
395 template <typename T = SmoothingType>
396 MultiplicativeVoid<T> skipCurrentValue (int numSamples)
397 {
398 this->currentValue *= (FloatType) std::pow (step, numSamples);
399 }
400
401 //==============================================================================
402 FloatType step = FloatType();
403 int stepsToTarget = 0;
404};
405
406template <typename FloatType>
407using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
408
409
410//==============================================================================
411//==============================================================================
412#if JUCE_UNIT_TESTS
413
414template <class SmoothedValueType>
415class CommonSmoothedValueTests : public UnitTest
416{
417public:
419 : UnitTest ("CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
420 {}
421
422 void runTest() override
423 {
424 beginTest ("Initial state");
425 {
427
428 auto value = sv.getCurrentValue();
429 expectEquals (sv.getTargetValue(), value);
430
431 sv.getNextValue();
432 expectEquals (sv.getCurrentValue(), value);
433 expect (! sv.isSmoothing());
434 }
435
436 beginTest ("Resetting");
437 {
438 auto initialValue = 15.0f;
439
441 sv.reset (3);
442 expectEquals (sv.getCurrentValue(), initialValue);
443
444 auto targetValue = initialValue + 1.0f;
445 sv.setTargetValue (targetValue);
446 expectEquals (sv.getTargetValue(), targetValue);
447 expectEquals (sv.getCurrentValue(), initialValue);
448 expect (sv.isSmoothing());
449
450 auto currentValue = sv.getNextValue();
451 expect (currentValue > initialValue);
452 expectEquals (sv.getCurrentValue(), currentValue);
453 expectEquals (sv.getTargetValue(), targetValue);
454 expect (sv.isSmoothing());
455
456 sv.reset (5);
457
458 expectEquals (sv.getCurrentValue(), targetValue);
459 expectEquals (sv.getTargetValue(), targetValue);
460 expect (! sv.isSmoothing());
461
462 sv.getNextValue();
463 expectEquals (sv.getCurrentValue(), targetValue);
464
465 sv.setTargetValue (1.5f);
466 sv.getNextValue();
467
468 float newStart = 0.2f;
469 sv.setCurrentAndTargetValue (newStart);
470 expectEquals (sv.getNextValue(), newStart);
471 expectEquals (sv.getTargetValue(), newStart);
472 expectEquals (sv.getCurrentValue(), newStart);
473 expect (! sv.isSmoothing());
474 }
475
476 beginTest ("Sample rate");
477 {
479 auto svTime = svSamples;
480
481 auto numSamples = 12;
482
483 svSamples.reset (numSamples);
484 svTime.reset (numSamples * 2, 1.0);
485
486 for (int i = 0; i < numSamples; ++i)
487 {
488 svTime.skip (1);
489 expectWithinAbsoluteError (svSamples.getNextValue(),
490 svTime.getNextValue(),
491 1.0e-7f);
492 }
493 }
494
495 beginTest ("Block processing");
496 {
497 SmoothedValueType sv (1.0f);
498
499 sv.reset (12);
500 sv.setTargetValue (2.0f);
501
502 const auto numSamples = 15;
503
504 AudioBuffer<float> referenceData (1, numSamples);
505
506 for (int i = 0; i < numSamples; ++i)
507 referenceData.setSample (0, i, sv.getNextValue());
508
509 expect (referenceData.getSample (0, 0) > 0);
510 expect (referenceData.getSample (0, 10) < sv.getTargetValue());
511 expectWithinAbsoluteError (referenceData.getSample (0, 11),
512 sv.getTargetValue(),
513 1.0e-7f);
514
515 auto getUnitData = [] (int numSamplesToGenerate)
516 {
517 AudioBuffer<float> result (1, numSamplesToGenerate);
518
519 for (int i = 0; i < numSamplesToGenerate; ++i)
520 result.setSample (0, i, 1.0f);
521
522 return result;
523 };
524
525 auto compareData = [this](const AudioBuffer<float>& test,
526 const AudioBuffer<float>& reference)
527 {
528 for (int i = 0; i < test.getNumSamples(); ++i)
529 expectWithinAbsoluteError (test.getSample (0, i),
530 reference.getSample (0, i),
531 1.0e-7f);
532 };
533
534 auto testData = getUnitData (numSamples);
535 sv.setCurrentAndTargetValue (1.0f);
536 sv.setTargetValue (2.0f);
537 sv.applyGain (testData.getWritePointer (0), numSamples);
539
540 testData = getUnitData (numSamples);
541 AudioBuffer<float> destData (1, numSamples);
542 sv.setCurrentAndTargetValue (1.0f);
543 sv.setTargetValue (2.0f);
544 sv.applyGain (destData.getWritePointer (0),
545 testData.getReadPointer (0),
546 numSamples);
548 compareData (testData, getUnitData (numSamples));
549
550 testData = getUnitData (numSamples);
551 sv.setCurrentAndTargetValue (1.0f);
552 sv.setTargetValue (2.0f);
553 sv.applyGain (testData, numSamples);
555 }
556
557 beginTest ("Skip");
558 {
560
561 sv.reset (12);
562 sv.setCurrentAndTargetValue (1.0f);
563 sv.setTargetValue (2.0f);
564
565 Array<float> reference;
566
567 for (int i = 0; i < 15; ++i)
568 reference.add (sv.getNextValue());
569
570 sv.setCurrentAndTargetValue (1.0f);
571 sv.setTargetValue (2.0f);
572
573 expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
574 expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
575 expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
576 sv.skip (3);
577 expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
578 expectEquals (sv.skip (300), sv.getTargetValue());
579 expectEquals (sv.getCurrentValue(), sv.getTargetValue());
580 }
581
582 beginTest ("Negative");
583 {
585
586 auto numValues = 12;
587 sv.reset (numValues);
588
589 std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
590 { -100.0f, -3.0f } };
591
592 for (auto range : ranges)
593 {
594 auto start = range.first, end = range.second;
595
596 sv.setCurrentAndTargetValue (start);
597 sv.setTargetValue (end);
598
599 auto val = sv.skip (numValues / 2);
600
601 if (end > start)
602 expect (val > start && val < end);
603 else
604 expect (val < start && val > end);
605
606 auto nextVal = sv.getNextValue();
607 expect (end > start ? (nextVal > val) : (nextVal < val));
608
609 auto endVal = sv.skip (500);
610 expectEquals (endVal, end);
611 expectEquals (sv.getNextValue(), end);
612 expectEquals (sv.getCurrentValue(), end);
613
614 sv.setCurrentAndTargetValue (start);
615 sv.setTargetValue (end);
616
617 SmoothedValueType positiveSv { -start };
618 positiveSv.reset (numValues);
619 positiveSv.setTargetValue (-end);
620
621 for (int i = 0; i < numValues + 2; ++i)
622 expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
623 }
624 }
625 }
626};
627
628#endif
629
630} // namespace juce
ElementType * end() noexcept
Definition juce_Array.h:344
Array()=default
static void JUCE_CALLTYPE multiply(float *dest, const float *src, int numValues) noexcept
bool isSmoothing() const noexcept
void applyGain(FloatType *samples, int numSamples) noexcept
void applyGain(AudioBuffer< FloatType > &buffer, int numSamples) noexcept
void setCurrentAndTargetValue(FloatType newValue)
FloatType getTargetValue() const noexcept
FloatType getCurrentValue() const noexcept
void applyGain(FloatType *samplesOut, const FloatType *samplesIn, int numSamples) noexcept
FloatType skip(int numSamples) noexcept
FloatType getNextValue() noexcept
(void setValue(FloatType newValue, bool force=false) noexcept, { if(force) { this->setCurrentAndTargetValue(newValue);return;} setTargetValue(newValue);}) private typename std::enable_if< std::is_same< T, ValueSmoothingTypes::Multiplicative >::value, void >::type MultiplicativeVoid
SmoothedValue(FloatType initialValue) noexcept
void reset(double sampleRate, double rampLengthInSeconds) noexcept
void reset(int numSteps) noexcept
void setTargetValue(FloatType newValue) noexcept