Wrapping up the ADSR envelope and tests
This commit is contained in:
parent
989acdaac6
commit
b909e468bf
8 changed files with 491 additions and 36 deletions
|
|
@ -75,6 +75,7 @@ set(COMMON_SOURCES
|
|||
sources/Synth.cpp
|
||||
sources/FilePool.cpp
|
||||
sources/Region.cpp
|
||||
sources/ADSREnvelope.cpp
|
||||
sources/Parser.cpp
|
||||
)
|
||||
|
||||
|
|
@ -110,6 +111,7 @@ set(TEST_SOURCES
|
|||
tests/FilesT.cpp
|
||||
tests/OnePoleFilterT.cpp
|
||||
tests/RegionActivationT.cpp
|
||||
tests/ADSREnvelopeT.cpp
|
||||
tests/MainT.cpp
|
||||
)
|
||||
|
||||
|
|
@ -168,4 +170,7 @@ add_executable(bm_looping benchmarks/BM_looping.cpp sources/SIMDSSE.cpp)
|
|||
target_link_libraries(bm_looping benchmark absl::span absl::algorithm)
|
||||
|
||||
add_executable(bm_ramp benchmarks/BM_ramp.cpp sources/SIMDSSE.cpp)
|
||||
target_link_libraries(bm_ramp benchmark absl::span absl::algorithm)
|
||||
target_link_libraries(bm_ramp benchmark absl::span absl::algorithm)
|
||||
|
||||
add_executable(bm_ADSR benchmarks/BM_ADSR.cpp sources/SIMDSSE.cpp)
|
||||
target_link_libraries(bm_ADSR benchmark absl::span absl::algorithm)
|
||||
45
benchmarks/BM_ADSR.cpp
Normal file
45
benchmarks/BM_ADSR.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include <benchmark/benchmark.h>
|
||||
#include "../sources/ADSREnvelope.h"
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <numeric>
|
||||
|
||||
constexpr int fixedAmount { 12 };
|
||||
constexpr int envelopeSize { 2 << 16 };
|
||||
constexpr int attack = static_cast<int>(envelopeSize / 4) - fixedAmount;
|
||||
constexpr int decay = attack;
|
||||
constexpr int release = attack;
|
||||
constexpr int releaseTime = envelopeSize - static_cast<int>(envelopeSize / 4);
|
||||
|
||||
|
||||
static void Point(benchmark::State& state) {
|
||||
std::vector<float> output(state.range(0));
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
for (auto _ : state) {
|
||||
envelope.reset(attack, release, 1.0, fixedAmount, decay, fixedAmount);
|
||||
envelope.startRelease(releaseTime);
|
||||
for(int offset = 0; offset < envelopeSize; offset += state.range(0))
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
benchmark::DoNotOptimize(output);
|
||||
}
|
||||
state.counters["Per block"] = benchmark::Counter(envelopeSize / state.range(0), benchmark::Counter::kIsIterationInvariantRate | benchmark::Counter::kInvert);
|
||||
}
|
||||
|
||||
static void Block(benchmark::State& state) {
|
||||
std::vector<float> output(state.range(0));
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
for (auto _ : state) {
|
||||
envelope.reset(attack, release, 1.0, fixedAmount, decay, fixedAmount);
|
||||
envelope.startRelease(releaseTime);
|
||||
for(int offset = 0; offset < envelopeSize; offset += state.range(0))
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
benchmark::DoNotOptimize(output);
|
||||
}
|
||||
|
||||
state.counters["Per block"] = benchmark::Counter(envelopeSize / state.range(0), benchmark::Counter::kIsIterationInvariantRate | benchmark::Counter::kInvert);
|
||||
}
|
||||
|
||||
BENCHMARK(Point)->RangeMultiplier(2)->Range((2<<6), (2<<11));
|
||||
BENCHMARK(Block)->RangeMultiplier(2)->Range((2<<6), (2<<11));
|
||||
BENCHMARK_MAIN();
|
||||
217
sources/ADSREnvelope.cpp
Normal file
217
sources/ADSREnvelope.cpp
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
#include "Globals.h"
|
||||
#include "SIMDHelpers.h"
|
||||
#include "Helpers.h"
|
||||
#include "ADSREnvelope.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
|
||||
template<class Type>
|
||||
void ADSREnvelope<Type>::reset(int attack, int release, Type sustain, int delay, int decay, int hold, Type start, Type depth) noexcept
|
||||
{
|
||||
ASSERT(start <= 1.0f);
|
||||
ASSERT(sustain <= 1.0f);
|
||||
|
||||
sustain = std::clamp<Type>(sustain, 0.0, 1.0);
|
||||
start = std::clamp<Type>(start, 0.0, 1.0);
|
||||
|
||||
currentState = State::Done;
|
||||
this->delay = delay;
|
||||
this->attack = attack;
|
||||
this->decay = decay;
|
||||
this->release = release;
|
||||
this->hold = hold;
|
||||
this->start = depth * start;
|
||||
this->sustain = depth * sustain;
|
||||
this->peak = depth;
|
||||
releaseDelay = 0;
|
||||
shouldRelease = false;
|
||||
step = 0.0;
|
||||
currentValue = this->start;
|
||||
currentState = State::Delay;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
Type ADSREnvelope<Type>::getNextValue() noexcept
|
||||
{
|
||||
if (shouldRelease && releaseDelay-- == 0)
|
||||
{
|
||||
currentState = State::Release;
|
||||
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
|
||||
}
|
||||
|
||||
switch(currentState)
|
||||
{
|
||||
case State::Delay:
|
||||
if (delay-- > 0)
|
||||
return start;
|
||||
|
||||
currentState = State::Attack;
|
||||
step = (1.0 - currentValue) / (attack > 0 ? attack : 1);
|
||||
[[fallthrough]];
|
||||
case State::Attack:
|
||||
if (attack-- > 0)
|
||||
{
|
||||
currentValue += step;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
currentState = State::Hold;
|
||||
currentValue = 1.0;
|
||||
[[fallthrough]];
|
||||
case State::Hold:
|
||||
if (hold-- > 0)
|
||||
return currentValue;
|
||||
|
||||
step = std::exp(std::log(sustain) / (decay > 0 ? decay : 1));
|
||||
currentState = State::Decay;
|
||||
[[fallthrough]];
|
||||
case State::Decay:
|
||||
if (decay-- > 0)
|
||||
{
|
||||
currentValue *= step;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
currentState = State::Sustain;
|
||||
currentValue = sustain;
|
||||
[[fallthrough]];
|
||||
case State::Sustain:
|
||||
return currentValue;
|
||||
case State::Release:
|
||||
if (release-- > 0)
|
||||
{
|
||||
currentValue *= step;
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
currentState = State::Done;
|
||||
currentValue = 0.0;
|
||||
[[fallthrough]];
|
||||
default:
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
||||
{
|
||||
auto originalSpan = output;
|
||||
auto remainingSamples = static_cast<int>(output.size());
|
||||
int length;
|
||||
switch(currentState)
|
||||
{
|
||||
case State::Delay:
|
||||
length = min(remainingSamples, delay);
|
||||
::fill<Type>(output, currentValue);
|
||||
output.remove_prefix(length);
|
||||
remainingSamples -= length;
|
||||
delay -= length;
|
||||
if (remainingSamples == 0)
|
||||
break;
|
||||
|
||||
currentState = State::Attack;
|
||||
step = (peak - start) / (attack > 0 ? attack : 1);
|
||||
[[fallthrough]];
|
||||
case State::Attack:
|
||||
length = min(remainingSamples, attack);
|
||||
currentValue = ::linearRamp<Type>(output, currentValue, step);
|
||||
output.remove_prefix(length);
|
||||
remainingSamples -= length;
|
||||
attack -= length;
|
||||
if (remainingSamples == 0)
|
||||
break;
|
||||
|
||||
currentValue = peak;
|
||||
currentState = State::Hold;
|
||||
[[fallthrough]];
|
||||
case State::Hold:
|
||||
length = min(remainingSamples, hold);
|
||||
::fill<Type>(output, currentValue);
|
||||
output.remove_prefix(length);
|
||||
remainingSamples -= length;
|
||||
hold -= length;
|
||||
if (remainingSamples == 0)
|
||||
break;
|
||||
|
||||
step = std::exp(std::log(sustain) / (decay > 0 ? decay : 1));
|
||||
currentState = State::Decay;
|
||||
[[fallthrough]];
|
||||
case State::Decay:
|
||||
length = min(remainingSamples, decay);
|
||||
currentValue = ::multiplicativeRamp<Type>(output, currentValue, step);
|
||||
output.remove_prefix(length);
|
||||
remainingSamples -= length;
|
||||
decay -= length;
|
||||
if (remainingSamples == 0)
|
||||
break;
|
||||
|
||||
currentValue = sustain;
|
||||
currentState = State::Sustain;
|
||||
[[fallthrough]];
|
||||
case State::Sustain:
|
||||
break;
|
||||
case State::Release:
|
||||
length = min(remainingSamples, release);
|
||||
currentValue = ::multiplicativeRamp<Type>(output, currentValue, step);
|
||||
output.remove_prefix(length);
|
||||
remainingSamples -= length;
|
||||
release -= length;
|
||||
if (remainingSamples == 0)
|
||||
break;
|
||||
|
||||
currentValue = 0.0;
|
||||
currentState = State::Done;
|
||||
[[fallthrough]];
|
||||
case State::Done:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
::fill<Type>(output, currentValue);
|
||||
|
||||
if (shouldRelease)
|
||||
{
|
||||
remainingSamples = static_cast<int>(originalSpan.size());
|
||||
if (releaseDelay > remainingSamples)
|
||||
{
|
||||
releaseDelay -= remainingSamples;
|
||||
return;
|
||||
}
|
||||
|
||||
originalSpan.remove_prefix(releaseDelay);
|
||||
currentValue = originalSpan.front();
|
||||
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
|
||||
remainingSamples -= releaseDelay;
|
||||
length = min(remainingSamples, release);
|
||||
currentState = State::Release;
|
||||
currentValue = ::multiplicativeRamp<Type>(originalSpan, currentValue, step);
|
||||
originalSpan.remove_prefix(length);
|
||||
release -= length;
|
||||
|
||||
if (release == 0)
|
||||
{
|
||||
currentValue = 0.0;
|
||||
currentState = State::Done;
|
||||
::fill<Type>(originalSpan, 0.0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void ADSREnvelope<Type>::startRelease(int releaseDelay) noexcept
|
||||
{
|
||||
shouldRelease = true;
|
||||
this->releaseDelay = releaseDelay;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
constexpr bool ADSREnvelope<Type>::isSmoothing() noexcept
|
||||
{
|
||||
return currentState != State::Done;
|
||||
}
|
||||
|
||||
template class ADSREnvelope<float>;
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
#include "Globals.h"
|
||||
|
||||
#include <absl/types/span.h>
|
||||
namespace sfz
|
||||
{
|
||||
|
||||
|
|
@ -7,34 +6,30 @@ template<class Type>
|
|||
class ADSREnvelope
|
||||
{
|
||||
public:
|
||||
struct Description
|
||||
{
|
||||
Description()
|
||||
: depth(1) {}
|
||||
Descrition(Type depth)
|
||||
: depth(depth) {}
|
||||
|
||||
int delay { 0 };
|
||||
int attack { 0 };
|
||||
int decay { 0 };
|
||||
int release { 0 };
|
||||
int hold { 0 };
|
||||
float start { 0 };
|
||||
float sustain { 0 };
|
||||
Type depth;
|
||||
};
|
||||
|
||||
ADSREnvelope() = default;
|
||||
void reset(Description desc)
|
||||
{
|
||||
|
||||
}
|
||||
void reset(int attack, int release, Type sustain=1.0, int delay = 0, int decay = 0, int hold = 0, Type start=0.0, Type depth=1) noexcept;
|
||||
Type getNextValue() noexcept;
|
||||
void getBlock(absl::Span<Type> output) noexcept;
|
||||
void startRelease(int releaseDelay) noexcept;
|
||||
constexpr bool isSmoothing() noexcept;
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
Delay, Attack, Hold, Sustain, Release, Done
|
||||
Delay, Attack, Hold, Decay, Sustain, Release, Done
|
||||
};
|
||||
State currentState { Done };
|
||||
State currentState { State::Done };
|
||||
Type currentValue { 0.0 };
|
||||
Type step { 0.0 };
|
||||
int delay { 0 };
|
||||
int attack { 0 };
|
||||
int decay { 0 };
|
||||
int release { 0 };
|
||||
int hold { 0 };
|
||||
Type start { 0 };
|
||||
Type peak { 0 };
|
||||
Type sustain { 0 };
|
||||
int releaseDelay { 0 };
|
||||
bool shouldRelease { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -68,13 +68,13 @@ void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<floa
|
|||
}
|
||||
|
||||
template<>
|
||||
void linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
|
||||
float linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
|
||||
{
|
||||
linearRamp<float, false>(output, start, step);
|
||||
return linearRamp<float, false>(output, start, step);
|
||||
}
|
||||
|
||||
template<>
|
||||
void multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
|
||||
float multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept
|
||||
{
|
||||
multiplicativeRamp<float, false>(output, start, step);
|
||||
return multiplicativeRamp<float, false>(output, start, step);
|
||||
}
|
||||
|
|
@ -206,11 +206,12 @@ inline void snippetRampLinear(T*& output, T& value, T step)
|
|||
}
|
||||
|
||||
template<class T, bool SIMD=SIMDConfig::linearRamp>
|
||||
void linearRamp(absl::Span<T> output, T start, T step) noexcept
|
||||
T linearRamp(absl::Span<T> output, T start, T step) noexcept
|
||||
{
|
||||
auto* out = output.begin();
|
||||
while(out < output.end())
|
||||
snippetRampLinear<T>(out, start, step);
|
||||
return start;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
|
@ -221,16 +222,17 @@ inline void snippetRampMultiplicative(T*& output, T& value, T step)
|
|||
}
|
||||
|
||||
template<class T, bool SIMD=SIMDConfig::multiplicativeRamp>
|
||||
void multiplicativeRamp(absl::Span<T> output, T start, T step) noexcept
|
||||
T multiplicativeRamp(absl::Span<T> output, T start, T step) noexcept
|
||||
{
|
||||
auto* out = output.begin();
|
||||
while(out < output.end())
|
||||
snippetRampMultiplicative<T>(out, start, step);
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
void linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
float linearRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
|
||||
template<>
|
||||
void multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
float multiplicativeRamp<float, true>(absl::Span<float> output, float start, float step) noexcept;
|
||||
|
|
@ -306,7 +306,7 @@ void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<floa
|
|||
}
|
||||
|
||||
template<>
|
||||
void linearRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
|
||||
float linearRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
|
||||
{
|
||||
auto* out = output.begin();
|
||||
const auto* lastAligned = prevAligned(output.end());
|
||||
|
|
@ -328,10 +328,11 @@ void linearRamp<float, true>(absl::Span<float> output, float value, float step)
|
|||
value = _mm_cvtss_f32(mmValue);
|
||||
while(out < output.end())
|
||||
snippetRampLinear<float>(out, value, step);
|
||||
return value;
|
||||
}
|
||||
|
||||
template<>
|
||||
void multiplicativeRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
|
||||
float multiplicativeRamp<float, true>(absl::Span<float> output, float value, float step) noexcept
|
||||
{
|
||||
auto* out = output.begin();
|
||||
const auto* lastAligned = prevAligned(output.end());
|
||||
|
|
@ -353,4 +354,5 @@ void multiplicativeRamp<float, true>(absl::Span<float> output, float value, floa
|
|||
value = _mm_cvtss_f32(mmValue);
|
||||
while(out < output.end())
|
||||
snippetRampMultiplicative<float>(out, value, step);
|
||||
return value;
|
||||
}
|
||||
189
tests/ADSREnvelopeT.cpp
Normal file
189
tests/ADSREnvelopeT.cpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
#include "catch2/catch.hpp"
|
||||
#include "../sources/ADSREnvelope.h"
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <absl/types/span.h>
|
||||
#include <absl/algorithm/container.h>
|
||||
using namespace Catch::literals;
|
||||
|
||||
template<class Type>
|
||||
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps=1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (rhs[i] != Approx(lhs[i]).epsilon(eps))
|
||||
{
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Basic state")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
std::array<float, 5> output;
|
||||
std::array<float, 5> expected { 0.0, 0.0, 0.0, 0.0, 0.0 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Attack")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 0);
|
||||
std::array<float, 5> output;
|
||||
std::array<float, 5> expected { 0.5, 1.0, 1.0, 1.0, 1.0 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 0);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Attack again")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(3, 0);
|
||||
std::array<float, 5> output;
|
||||
std::array<float, 5> expected { 0.33333, 0.66667, 1.0, 1.0, 1.0 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(3, 0);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Release")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4);
|
||||
envelope.startRelease(2);
|
||||
std::array<float, 8> output;
|
||||
std::array<float, 8> expected { 0.5, 1.0, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 4);
|
||||
envelope.startRelease(2);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Delay")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 1.0f, 2);
|
||||
std::array<float, 10> output;
|
||||
envelope.startRelease(4);
|
||||
std::array<float, 10> expected { 0.0, 0.0, 0.5, 1.0, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 4, 1.0f, 2);
|
||||
envelope.startRelease(4);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Lower sustain")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 0.5, 2);
|
||||
std::array<float, 10> output;
|
||||
std::array<float, 10> expected { 0.0, 0.0, 0.5, 1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 4, 0.5, 2);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Decay")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 0.5, 2, 2);
|
||||
std::array<float, 10> output;
|
||||
std::array<float, 10> expected { 0.0, 0.0, 0.5, 1.0, 0.707107, 0.5, 0.5, 0.5, 0.5, 0.5 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 4, 0.5, 2, 2);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Hold")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
std::array<float, 12> output;
|
||||
std::array<float, 12> expected { 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 0.707107, 0.5, 0.5, 0.5, 0.5, 0.5 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Hold with release")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
envelope.startRelease(8);
|
||||
std::array<float, 14> output;
|
||||
std::array<float, 14> expected { 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 0.707107, 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.0, 0.0 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
envelope.startRelease(8);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
|
||||
TEST_CASE("[ADSREnvelope] Hold with release 2")
|
||||
{
|
||||
sfz::ADSREnvelope<float> envelope;
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
envelope.startRelease(4);
|
||||
std::array<float, 14> output;
|
||||
std::array<float, 14> expected { 0.0, 0.0, 0.5, 1.0, 0.08409, 0.00707, 0.000594604, 0.00005, 0.0, 0.0, 0.0, 0.0 };
|
||||
for (auto& out: output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
envelope.reset(2, 4, 0.5, 2, 2, 2);
|
||||
envelope.startRelease(4);
|
||||
absl::c_fill(output, -1.0);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE( approxEqual<float>(output, expected) );
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue