OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
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
23namespace juce
24{
25
26struct 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
255static 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())
virtual void runTest()=0
Random getRandom() const