OpenShot Audio Library | OpenShotAudio  0.3.0
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 
23 namespace juce
24 {
25 
26 //==============================================================================
35 template <typename SmoothedValueType>
37 {
38 private:
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 
54 public:
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  {
121  FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
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 
156 private:
157  //==============================================================================
158  FloatType getNextSmoothedValue() noexcept
159  {
160  return static_cast <SmoothedValueType*> (this)->getNextValue();
161  }
162 
163 protected:
164  //==============================================================================
165  FloatType currentValue = 0;
166  FloatType target = currentValue;
167  int countdown = 0;
168 };
169 
170 //==============================================================================
180 namespace ValueSmoothingTypes
181 {
187  struct Linear {};
188 
194  struct Multiplicative {};
195 }
196 
197 //==============================================================================
227 template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
228 class SmoothedValue : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
229 {
230 public:
231  //==============================================================================
233  SmoothedValue() noexcept
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  //==============================================================================
297  FloatType getNextValue() noexcept
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 
354 private:
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 
406 template <typename FloatType>
407 using LinearSmoothedValue = SmoothedValue <FloatType, ValueSmoothingTypes::Linear>;
408 
409 
410 //==============================================================================
411 //==============================================================================
412 #if JUCE_UNIT_TESTS
413 
414 template <class SmoothedValueType>
415 class CommonSmoothedValueTests : public UnitTest
416 {
417 public:
418  CommonSmoothedValueTests()
419  : UnitTest ("CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
420  {}
421 
422  void runTest() override
423  {
424  beginTest ("Initial state");
425  {
426  SmoothedValueType sv;
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 
440  SmoothedValueType sv (initialValue);
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  {
478  SmoothedValueType svSamples { 3.0f };
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);
538  compareData (testData, referenceData);
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);
547  compareData (destData, referenceData);
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);
554  compareData (testData, referenceData);
555  }
556 
557  beginTest ("Skip");
558  {
559  SmoothedValueType sv;
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  {
584  SmoothedValueType sv;
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
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