OpenShot Library | OpenShotAudio  0.2.2
juce_SmoothedValue.h
1 
2 /** @weakgroup juce_audio_basics-utilities
3  * @{
4  */
5 /*
6  ==============================================================================
7 
8  This file is part of the JUCE library.
9  Copyright (c) 2017 - ROLI Ltd.
10 
11  JUCE is an open source library subject to commercial or open-source
12  licensing.
13 
14  The code included in this file is provided under the terms of the ISC license
15  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
16  To use, copy, modify, and/or distribute this software for any purpose with or
17  without fee is hereby granted provided that the above copyright notice and
18  this permission notice appear in all copies.
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
30 //==============================================================================
31 /**
32  A base class for the smoothed value classes.
33 
34  This class is used to provide common functionality to the SmoothedValue and
35  dsp::LogRampedValue classes.
36 
37  @tags{Audio}
38 */
39 template <typename SmoothedValueType>
41 {
42 private:
43  //==============================================================================
44  template <typename T> struct FloatTypeHelper;
45 
46  template <template <typename> class SmoothedValueClass, typename FloatType>
47  struct FloatTypeHelper <SmoothedValueClass <FloatType>>
48  {
49  using Type = FloatType;
50  };
51 
52  template <template <typename, typename> class SmoothedValueClass, typename FloatType, typename SmoothingType>
53  struct FloatTypeHelper <SmoothedValueClass <FloatType, SmoothingType>>
54  {
55  using Type = FloatType;
56  };
57 
58 public:
59  using FloatType = typename FloatTypeHelper<SmoothedValueType>::Type;
60 
61  //==============================================================================
62  /** Constructor. */
63  SmoothedValueBase() = default;
64 
65  virtual ~SmoothedValueBase() {}
66 
67  //==============================================================================
68  /** Returns true if the current value is currently being interpolated. */
69  bool isSmoothing() const noexcept { return countdown > 0; }
70 
71  /** Returns the current value of the ramp. */
72  FloatType getCurrentValue() const noexcept { return currentValue; }
73 
74  //==============================================================================
75  /** Returns the target value towards which the smoothed value is currently moving. */
76  FloatType getTargetValue() const noexcept { return target; }
77 
78  /** Sets the current value and the target value.
79  @param newValue the new value to take
80  */
81  void setCurrentAndTargetValue (FloatType newValue)
82  {
83  target = currentValue = newValue;
84  countdown = 0;
85  }
86 
87  //==============================================================================
88  /** Applies a smoothed gain to a stream of samples
89  S[i] *= gain
90  @param samples Pointer to a raw array of samples
91  @param numSamples Length of array of samples
92  */
93  void applyGain (FloatType* samples, int numSamples) noexcept
94  {
95  jassert (numSamples >= 0);
96 
97  if (isSmoothing())
98  {
99  for (int i = 0; i < numSamples; ++i)
100  samples[i] *= getNextSmoothedValue();
101  }
102  else
103  {
104  FloatVectorOperations::multiply (samples, target, numSamples);
105  }
106  }
107 
108  /** Computes output as a smoothed gain applied to a stream of samples.
109  Sout[i] = Sin[i] * gain
110  @param samplesOut A pointer to a raw array of output samples
111  @param samplesIn A pointer to a raw array of input samples
112  @param numSamples The length of the array of samples
113  */
114  void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept
115  {
116  jassert (numSamples >= 0);
117 
118  if (isSmoothing())
119  {
120  for (int i = 0; i < numSamples; ++i)
121  samplesOut[i] = samplesIn[i] * getNextSmoothedValue();
122  }
123  else
124  {
125  FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples);
126  }
127  }
128 
129  /** Applies a smoothed gain to a buffer */
130  void applyGain (AudioBuffer<FloatType>& buffer, int numSamples) noexcept
131  {
132  jassert (numSamples >= 0);
133 
134  if (isSmoothing())
135  {
136  if (buffer.getNumChannels() == 1)
137  {
138  auto* samples = buffer.getWritePointer (0);
139 
140  for (int i = 0; i < numSamples; ++i)
141  samples[i] *= getNextSmoothedValue();
142  }
143  else
144  {
145  for (auto i = 0; i < numSamples; ++i)
146  {
147  auto gain = getNextSmoothedValue();
148 
149  for (int channel = 0; channel < buffer.getNumChannels(); channel++)
150  buffer.setSample (channel, i, buffer.getSample (channel, i) * gain);
151  }
152  }
153  }
154  else
155  {
156  buffer.applyGain (0, numSamples, target);
157  }
158  }
159 
160 private:
161  //==============================================================================
162  FloatType getNextSmoothedValue() noexcept
163  {
164  return static_cast <SmoothedValueType*> (this)->getNextValue();
165  }
166 
167 protected:
168  //==============================================================================
169  FloatType currentValue = 0;
170  FloatType target = currentValue;
171  int countdown = 0;
172 };
173 
174 //==============================================================================
175 /**
176  A namespace containing a set of types used for specifying the smoothing
177  behaviour of the SmoothedValue class.
178 
179  For example:
180  @code
181  SmoothedValue<float, ValueSmoothingTypes::Multiplicative> frequency (1.0f);
182  @endcode
183 */
184 namespace ValueSmoothingTypes
185 {
186  /**
187  Used to indicate a linear smoothing between values.
188 
189  @tags{Audio}
190  */
191  struct Linear {};
192 
193  /**
194  Used to indicate a smoothing between multiplicative values.
195 
196  @tags{Audio}
197  */
198  struct Multiplicative {};
199 }
200 
201 //==============================================================================
202 /**
203  A utility class for values that need smoothing to avoid audio glitches.
204 
205  A ValueSmoothingTypes::Linear template parameter selects linear smoothing,
206  which increments the SmoothedValue linearly towards its target value.
207 
208  @code
209  SmoothedValue<float, ValueSmoothingTypes::Linear> yourSmoothedValue;
210  @endcode
211 
212  A ValueSmoothingTypes::Multiplicative template parameter selects
213  multiplicative smoothing increments towards the target value.
214 
215  @code
216  SmoothedValue<float, ValueSmoothingTypes::Multiplicative> yourSmoothedValue;
217  @endcode
218 
219  Multiplicative smoothing is useful when you are dealing with
220  exponential/logarithmic values like volume in dB or frequency in Hz. For
221  example a 12 step ramp from 440.0 Hz (A4) to 880.0 Hz (A5) will increase the
222  frequency with an equal temperament tuning across the octave. A 10 step
223  smoothing from 1.0 (0 dB) to 3.16228 (10 dB) will increase the value in
224  increments of 1 dB.
225 
226  Note that when you are using multiplicative smoothing you cannot ever reach a
227  target value of zero!
228 
229  @tags{Audio}
230 */
231 template <typename FloatType, typename SmoothingType = ValueSmoothingTypes::Linear>
232 class SmoothedValue : public SmoothedValueBase <SmoothedValue <FloatType, SmoothingType>>
233 {
234 public:
235  //==============================================================================
236  /** Constructor. */
237  SmoothedValue() noexcept
238  : SmoothedValue ((FloatType) (std::is_same<SmoothingType, ValueSmoothingTypes::Linear>::value ? 0 : 1))
239  {
240  }
241 
242  /** Constructor. */
243  SmoothedValue (FloatType initialValue) noexcept
244  {
245  // Multiplicative smoothed values cannot ever reach 0!
246  jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && initialValue == 0));
247 
248  // Visual Studio can't handle base class initialisation with CRTP
249  this->currentValue = initialValue;
250  this->target = this->currentValue;
251  }
252 
253  //==============================================================================
254  /** Reset to a new sample rate and ramp length.
255  @param sampleRate The sample rate
256  @param rampLengthInSeconds The duration of the ramp in seconds
257  */
258  void reset (double sampleRate, double rampLengthInSeconds) noexcept
259  {
260  jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
261  reset ((int) std::floor (rampLengthInSeconds * sampleRate));
262  }
263 
264  /** Set a new ramp length directly in samples.
265  @param numSteps The number of samples over which the ramp should be active
266  */
267  void reset (int numSteps) noexcept
268  {
269  stepsToTarget = numSteps;
270  this->setCurrentAndTargetValue (this->target);
271  }
272 
273  //==============================================================================
274  /** Set the next value to ramp towards.
275  @param newValue The new target value
276  */
277  void setTargetValue (FloatType newValue) noexcept
278  {
279  if (newValue == this->target)
280  return;
281 
282  if (stepsToTarget <= 0)
283  {
284  this->setCurrentAndTargetValue (newValue);
285  return;
286  }
287 
288  // Multiplicative smoothed values cannot ever reach 0!
289  jassert (! (std::is_same<SmoothingType, ValueSmoothingTypes::Multiplicative>::value && newValue == 0));
290 
291  this->target = newValue;
292  this->countdown = stepsToTarget;
293 
294  setStepSize();
295  }
296 
297  //==============================================================================
298  /** Compute the next value.
299  @returns Smoothed value
300  */
301  FloatType getNextValue() noexcept
302  {
303  if (! this->isSmoothing())
304  return this->target;
305 
306  --(this->countdown);
307 
308  if (this->isSmoothing())
309  setNextValue();
310  else
311  this->currentValue = this->target;
312 
313  return this->currentValue;
314  }
315 
316  //==============================================================================
317  /** Skip the next numSamples samples.
318  This is identical to calling getNextValue numSamples times. It returns
319  the new current value.
320  @see getNextValue
321  */
322  FloatType skip (int numSamples) noexcept
323  {
324  if (numSamples >= this->countdown)
325  {
326  this->setCurrentAndTargetValue (this->target);
327  return this->target;
328  }
329 
330  skipCurrentValue (numSamples);
331 
332  this->countdown -= numSamples;
333  return this->currentValue;
334  }
335 
336  //==============================================================================
337  /** THIS FUNCTION IS DEPRECATED.
338 
339  Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
340 
341  lsv.setValue (x, false); -> lsv.setTargetValue (x);
342  lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
343 
344  @param newValue The new target value
345  @param force If true, the value will be set immediately, bypassing the ramp
346  */
347  JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
348  {
349  if (force)
350  {
351  this->setCurrentAndTargetValue (newValue);
352  return;
353  }
354 
355  setTargetValue (newValue);
356  })
357 
358 private:
359  //==============================================================================
360  template <typename T>
361  using LinearVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Linear>::value, void>::type;
362 
363  template <typename T>
364  using MultiplicativeVoid = typename std::enable_if <std::is_same <T, ValueSmoothingTypes::Multiplicative>::value, void>::type;
365 
366  //==============================================================================
367  template <typename T = SmoothingType>
368  LinearVoid<T> setStepSize() noexcept
369  {
370  step = (this->target - this->currentValue) / (FloatType) this->countdown;
371  }
372 
373  template <typename T = SmoothingType>
374  MultiplicativeVoid<T> setStepSize()
375  {
376  step = std::exp ((std::log (std::abs (this->target)) - std::log (std::abs (this->currentValue))) / this->countdown);
377  }
378 
379  //==============================================================================
380  template <typename T = SmoothingType>
381  LinearVoid<T> setNextValue() noexcept
382  {
383  this->currentValue += step;
384  }
385 
386  template <typename T = SmoothingType>
387  MultiplicativeVoid<T> setNextValue() noexcept
388  {
389  this->currentValue *= step;
390  }
391 
392  //==============================================================================
393  template <typename T = SmoothingType>
394  LinearVoid<T> skipCurrentValue (int numSamples) noexcept
395  {
396  this->currentValue += step * (FloatType) numSamples;
397  }
398 
399  template <typename T = SmoothingType>
400  MultiplicativeVoid<T> skipCurrentValue (int numSamples)
401  {
402  this->currentValue *= (FloatType) std::pow (step, numSamples);
403  }
404 
405  //==============================================================================
406  FloatType step = FloatType();
407  int stepsToTarget = 0;
408 };
409 
410 template <typename FloatType>
412 
413 
414 //==============================================================================
415 //==============================================================================
416 #if JUCE_UNIT_TESTS
417 
418 template <class SmoothedValueType>
419 class CommonSmoothedValueTests : public UnitTest
420 {
421 public:
422  CommonSmoothedValueTests()
423  : UnitTest ("CommonSmoothedValueTests", UnitTestCategories::smoothedValues)
424  {}
425 
426  void runTest() override
427  {
428  beginTest ("Initial state");
429  {
430  SmoothedValueType sv;
431 
432  auto value = sv.getCurrentValue();
433  expectEquals (sv.getTargetValue(), value);
434 
435  sv.getNextValue();
436  expectEquals (sv.getCurrentValue(), value);
437  expect (! sv.isSmoothing());
438  }
439 
440  beginTest ("Resetting");
441  {
442  auto initialValue = 15.0f;
443 
444  SmoothedValueType sv (initialValue);
445  sv.reset (3);
446  expectEquals (sv.getCurrentValue(), initialValue);
447 
448  auto targetValue = initialValue + 1.0f;
449  sv.setTargetValue (targetValue);
450  expectEquals (sv.getTargetValue(), targetValue);
451  expectEquals (sv.getCurrentValue(), initialValue);
452  expect (sv.isSmoothing());
453 
454  auto currentValue = sv.getNextValue();
455  expect (currentValue > initialValue);
456  expectEquals (sv.getCurrentValue(), currentValue);
457  expectEquals (sv.getTargetValue(), targetValue);
458  expect (sv.isSmoothing());
459 
460  sv.reset (5);
461 
462  expectEquals (sv.getCurrentValue(), targetValue);
463  expectEquals (sv.getTargetValue(), targetValue);
464  expect (! sv.isSmoothing());
465 
466  sv.getNextValue();
467  expectEquals (sv.getCurrentValue(), targetValue);
468 
469  sv.setTargetValue (1.5f);
470  sv.getNextValue();
471 
472  float newStart = 0.2f;
473  sv.setCurrentAndTargetValue (newStart);
474  expectEquals (sv.getNextValue(), newStart);
475  expectEquals (sv.getTargetValue(), newStart);
476  expectEquals (sv.getCurrentValue(), newStart);
477  expect (! sv.isSmoothing());
478  }
479 
480  beginTest ("Sample rate");
481  {
482  SmoothedValueType svSamples { 3.0f };
483  auto svTime = svSamples;
484 
485  auto numSamples = 12;
486 
487  svSamples.reset (numSamples);
488  svTime.reset (numSamples * 2, 1.0);
489 
490  for (int i = 0; i < numSamples; ++i)
491  {
492  svTime.skip (1);
493  expectWithinAbsoluteError (svSamples.getNextValue(),
494  svTime.getNextValue(),
495  1.0e-7f);
496  }
497  }
498 
499  beginTest ("Block processing");
500  {
501  SmoothedValueType sv (1.0f);
502 
503  sv.reset (12);
504  sv.setTargetValue (2.0f);
505 
506  const auto numSamples = 15;
507 
508  AudioBuffer<float> referenceData (1, numSamples);
509 
510  for (int i = 0; i < numSamples; ++i)
511  referenceData.setSample (0, i, sv.getNextValue());
512 
513  expect (referenceData.getSample (0, 0) > 0);
514  expect (referenceData.getSample (0, 10) < sv.getTargetValue());
515  expectWithinAbsoluteError (referenceData.getSample (0, 11),
516  sv.getTargetValue(),
517  1.0e-7f);
518 
519  auto getUnitData = [] (int numSamplesToGenerate)
520  {
521  AudioBuffer<float> result (1, numSamplesToGenerate);
522 
523  for (int i = 0; i < numSamplesToGenerate; ++i)
524  result.setSample (0, i, 1.0f);
525 
526  return result;
527  };
528 
529  auto compareData = [this](const AudioBuffer<float>& test,
530  const AudioBuffer<float>& reference)
531  {
532  for (int i = 0; i < test.getNumSamples(); ++i)
533  expectWithinAbsoluteError (test.getSample (0, i),
534  reference.getSample (0, i),
535  1.0e-7f);
536  };
537 
538  auto testData = getUnitData (numSamples);
539  sv.setCurrentAndTargetValue (1.0f);
540  sv.setTargetValue (2.0f);
541  sv.applyGain (testData.getWritePointer (0), numSamples);
542  compareData (testData, referenceData);
543 
544  testData = getUnitData (numSamples);
545  AudioBuffer<float> destData (1, numSamples);
546  sv.setCurrentAndTargetValue (1.0f);
547  sv.setTargetValue (2.0f);
548  sv.applyGain (destData.getWritePointer (0),
549  testData.getReadPointer (0),
550  numSamples);
551  compareData (destData, referenceData);
552  compareData (testData, getUnitData (numSamples));
553 
554  testData = getUnitData (numSamples);
555  sv.setCurrentAndTargetValue (1.0f);
556  sv.setTargetValue (2.0f);
557  sv.applyGain (testData, numSamples);
558  compareData (testData, referenceData);
559  }
560 
561  beginTest ("Skip");
562  {
563  SmoothedValueType sv;
564 
565  sv.reset (12);
566  sv.setCurrentAndTargetValue (1.0f);
567  sv.setTargetValue (2.0f);
568 
569  Array<float> reference;
570 
571  for (int i = 0; i < 15; ++i)
572  reference.add (sv.getNextValue());
573 
574  sv.setCurrentAndTargetValue (1.0f);
575  sv.setTargetValue (2.0f);
576 
577  expectWithinAbsoluteError (sv.skip (1), reference[0], 1.0e-6f);
578  expectWithinAbsoluteError (sv.skip (1), reference[1], 1.0e-6f);
579  expectWithinAbsoluteError (sv.skip (2), reference[3], 1.0e-6f);
580  sv.skip (3);
581  expectWithinAbsoluteError (sv.getCurrentValue(), reference[6], 1.0e-6f);
582  expectEquals (sv.skip (300), sv.getTargetValue());
583  expectEquals (sv.getCurrentValue(), sv.getTargetValue());
584  }
585 
586  beginTest ("Negative");
587  {
588  SmoothedValueType sv;
589 
590  auto numValues = 12;
591  sv.reset (numValues);
592 
593  std::vector<std::pair<float, float>> ranges = { { -1.0f, -2.0f },
594  { -100.0f, -3.0f } };
595 
596  for (auto range : ranges)
597  {
598  auto start = range.first, end = range.second;
599 
600  sv.setCurrentAndTargetValue (start);
601  sv.setTargetValue (end);
602 
603  auto val = sv.skip (numValues / 2);
604 
605  if (end > start)
606  expect (val > start && val < end);
607  else
608  expect (val < start && val > end);
609 
610  auto nextVal = sv.getNextValue();
611  expect (end > start ? (nextVal > val) : (nextVal < val));
612 
613  auto endVal = sv.skip (500);
614  expectEquals (endVal, end);
615  expectEquals (sv.getNextValue(), end);
616  expectEquals (sv.getCurrentValue(), end);
617 
618  sv.setCurrentAndTargetValue (start);
619  sv.setTargetValue (end);
620 
621  SmoothedValueType positiveSv { -start };
622  positiveSv.reset (numValues);
623  positiveSv.setTargetValue (-end);
624 
625  for (int i = 0; i < numValues + 2; ++i)
626  expectEquals (sv.getNextValue(), -positiveSv.getNextValue());
627  }
628  }
629  }
630 };
631 
632 #endif
633 
634 } // namespace juce
635 
636 /** @}*/
FloatType getNextValue() noexcept
Compute the next value.
FloatType getTargetValue() const noexcept
Returns the target value towards which the smoothed value is currently moving.
Used to indicate a linear smoothing between values.
A utility class for values that need smoothing to avoid audio glitches.
FloatType getCurrentValue() const noexcept
Returns the current value of the ramp.
void reset(int numSteps) noexcept
Set a new ramp length directly in samples.
FloatType skip(int numSamples) noexcept
Skip the next numSamples samples.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition: juce_Array.h:422
void reset(double sampleRate, double rampLengthInSeconds) noexcept
Reset to a new sample rate and ramp length.
STL namespace.
Type getSample(int channel, int sampleIndex) const noexcept
Returns a sample from the buffer.
static void JUCE_CALLTYPE multiply(float *dest, const float *src, int numValues) noexcept
Multiplies the destination values by the source values.
SmoothedValueBase()=default
Constructor.
bool isSmoothing() const noexcept
Returns true if the current value is currently being interpolated.
This is a base class for classes that perform a unit test.
Definition: juce_UnitTest.h:73
A multi-channel buffer containing floating point audio samples.
Used to indicate a smoothing between multiplicative values.
A base class for the smoothed value classes.
SmoothedValue() noexcept
Constructor.
Holds a resizable array of primitive or copy-by-value objects.
Definition: juce_Array.h:59
SmoothedValue(FloatType initialValue) noexcept
Constructor.
void applyGain(AudioBuffer< FloatType > &buffer, int numSamples) noexcept
Applies a smoothed gain to a buffer.
void setTargetValue(FloatType newValue) noexcept
Set the next value to ramp towards.
void setCurrentAndTargetValue(FloatType newValue)
Sets the current value and the target value.
Type * getWritePointer(int channelNumber) noexcept
Returns a writeable pointer to one of the buffer&#39;s channels.
void setSample(int destChannel, int destSample, Type newValue) noexcept
Sets a sample in the buffer.
int getNumSamples() const noexcept
Returns the number of samples allocated in each of the buffer&#39;s channels.
void applyGain(FloatType *samplesOut, const FloatType *samplesIn, int numSamples) noexcept
Computes output as a smoothed gain applied to a stream of samples.
void applyGain(FloatType *samples, int numSamples) noexcept
Applies a smoothed gain to a stream of samples S[i] *= gain.
(void setValue(Type 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
THIS FUNCTION IS DEPRECATED.