OpenShot Audio Library | OpenShotAudio  0.6.0
juce_ADSR_test.cpp
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  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 struct ADSRTests final : public UnitTest
27 {
28  ADSRTests() : UnitTest ("ADSR", UnitTestCategories::audio) {}
29 
30  void runTest() override
31  {
32  constexpr double sampleRate = 44100.0;
33  const ADSR::Parameters parameters { 0.1f, 0.1f, 0.5f, 0.1f };
34 
35  ADSR adsr;
36  adsr.setSampleRate (sampleRate);
37  adsr.setParameters (parameters);
38 
39  beginTest ("Idle");
40  {
41  adsr.reset();
42 
43  expect (! adsr.isActive());
44  expectEquals (adsr.getNextSample(), 0.0f);
45  }
46 
47  beginTest ("Attack");
48  {
49  adsr.reset();
50 
51  adsr.noteOn();
52  expect (adsr.isActive());
53 
54  auto buffer = getTestBuffer (sampleRate, parameters.attack);
55  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
56 
57  expect (isIncreasing (buffer));
58  }
59 
60  beginTest ("Decay");
61  {
62  adsr.reset();
63 
64  adsr.noteOn();
65  advanceADSR (adsr, roundToInt (parameters.attack * sampleRate));
66 
67  auto buffer = getTestBuffer (sampleRate, parameters.decay);
68  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
69 
70  expect (isDecreasing (buffer));
71  }
72 
73  beginTest ("Sustain");
74  {
75  adsr.reset();
76 
77  adsr.noteOn();
78  advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay + 0.01) * sampleRate));
79 
80  auto random = getRandom();
81 
82  for (int numTests = 0; numTests < 100; ++numTests)
83  {
84  const auto sustainLevel = random.nextFloat();
85  const auto sustainLength = jmax (0.1f, random.nextFloat());
86 
87  adsr.setParameters ({ parameters.attack, parameters.decay, sustainLevel, parameters.release });
88 
89  auto buffer = getTestBuffer (sampleRate, sustainLength);
90  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
91 
92  expect (isSustained (buffer, sustainLevel));
93  }
94  }
95 
96  beginTest ("Release");
97  {
98  adsr.reset();
99 
100  adsr.noteOn();
101  advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate));
102  adsr.noteOff();
103 
104  auto buffer = getTestBuffer (sampleRate, parameters.release);
105  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
106 
107  expect (isDecreasing (buffer));
108  }
109 
110  beginTest ("Zero-length attack jumps to decay");
111  {
112  adsr.reset();
113  adsr.setParameters ({ 0.0f, parameters.decay, parameters.sustain, parameters.release });
114 
115  adsr.noteOn();
116 
117  auto buffer = getTestBuffer (sampleRate, parameters.decay);
118  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
119 
120  expect (isDecreasing (buffer));
121  }
122 
123  beginTest ("Zero-length decay jumps to sustain");
124  {
125  adsr.reset();
126  adsr.setParameters ({ parameters.attack, 0.0f, parameters.sustain, parameters.release });
127 
128  adsr.noteOn();
129  advanceADSR (adsr, roundToInt (parameters.attack * sampleRate));
130  adsr.getNextSample();
131 
132  expectEquals (adsr.getNextSample(), parameters.sustain);
133 
134  auto buffer = getTestBuffer (sampleRate, 1);
135  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
136 
137  expect (isSustained (buffer, parameters.sustain));
138  }
139 
140  beginTest ("Zero-length attack and decay jumps to sustain");
141  {
142  adsr.reset();
143  adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release });
144 
145  adsr.noteOn();
146 
147  expectEquals (adsr.getNextSample(), parameters.sustain);
148 
149  auto buffer = getTestBuffer (sampleRate, 1);
150  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
151 
152  expect (isSustained (buffer, parameters.sustain));
153  }
154 
155  beginTest ("Zero-length attack and decay releases correctly");
156  {
157  adsr.reset();
158  adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release });
159 
160  adsr.noteOn();
161  adsr.noteOff();
162 
163  auto buffer = getTestBuffer (sampleRate, parameters.release);
164  adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
165 
166  expect (isDecreasing (buffer));
167  }
168 
169  beginTest ("Zero-length release resets to idle");
170  {
171  adsr.reset();
172  adsr.setParameters ({ parameters.attack, parameters.decay, parameters.sustain, 0.0f });
173 
174  adsr.noteOn();
175  advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate));
176  adsr.noteOff();
177 
178  expect (! adsr.isActive());
179  }
180  }
181 
182  static void advanceADSR (ADSR& adsr, int numSamplesToAdvance)
183  {
184  while (--numSamplesToAdvance >= 0)
185  adsr.getNextSample();
186  }
187 
188  static AudioBuffer<float> getTestBuffer (double sampleRate, float lengthInSeconds)
189  {
190  AudioBuffer<float> buffer { 2, roundToInt (lengthInSeconds * sampleRate) };
191 
192  for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
193  for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
194  buffer.setSample (channel, sample, 1.0f);
195 
196  return buffer;
197  }
198 
199  static bool isIncreasing (const AudioBuffer<float>& b)
200  {
201  jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
202 
203  for (int channel = 0; channel < b.getNumChannels(); ++channel)
204  {
205  float previousSample = -1.0f;
206 
207  for (int sample = 0; sample < b.getNumSamples(); ++sample)
208  {
209  const auto currentSample = b.getSample (channel, sample);
210 
211  if (currentSample <= previousSample)
212  return false;
213 
214  previousSample = currentSample;
215  }
216  }
217 
218  return true;
219  }
220 
221  static bool isDecreasing (const AudioBuffer<float>& b)
222  {
223  jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
224 
225  for (int channel = 0; channel < b.getNumChannels(); ++channel)
226  {
227  float previousSample = std::numeric_limits<float>::max();
228 
229  for (int sample = 0; sample < b.getNumSamples(); ++sample)
230  {
231  const auto currentSample = b.getSample (channel, sample);
232 
233  if (currentSample >= previousSample)
234  return false;
235 
236  previousSample = currentSample;
237  }
238  }
239 
240  return true;
241  }
242 
243  static bool isSustained (const AudioBuffer<float>& b, float sustainLevel)
244  {
245  jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
246 
247  for (int channel = 0; channel < b.getNumChannels(); ++channel)
248  if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float> { sustainLevel, sustainLevel })
249  return false;
250 
251  return true;
252  }
253 };
254 
255 static ADSRTests adsrTests;
256 
257 } // namespace juce
void expectEquals(ValueType actual, ValueType expected, String failureMessage=String())
UnitTest(const String &name, const String &category=String())
void beginTest(const String &testName)
void expect(bool testResult, const String &failureMessage=String())
Random getRandom() const