Added a multiplicative envelope and associated tests

This commit is contained in:
Paul Ferrand 2019-12-13 01:55:24 +01:00
parent 72f141aecb
commit 4e26852713
5 changed files with 267 additions and 5 deletions

View file

@ -144,7 +144,6 @@ void LinearEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quant
return static_cast<int>((value + halfStep) / quantizationStep) * quantizationStep;
};
currentValue = quantize(currentValue);
const auto outputSize = static_cast<int>(output.size());
for (auto& event : events) {
const auto newValue = quantize(event.second);
@ -162,11 +161,111 @@ void LinearEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quant
continue;
}
const int numSteps = abs(newValue - currentValue) / quantizationStep;
const auto difference = abs(newValue - currentValue);
if (difference < quantizationStep) {
fill<Type>(output.subspan(index, length), currentValue);
currentValue = newValue;
index += length;
continue;
}
const int numSteps = difference / quantizationStep;
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<Type>(output.subspan(index, stepLength), currentValue);
currentValue += currentValue <= newValue ? quantizationStep : -quantizationStep;
const auto delta = quantizationStep + currentValue - quantize(currentValue);
currentValue += currentValue <= newValue ? delta : -delta;
index += stepLength;
}
}
if (index < outputSize)
fill<Type>(output.subspan(index), currentValue);
}
template <class Type>
MultiplicativeEnvelope<Type>::MultiplicativeEnvelope()
{
EventEnvelope<Type>::reset(1.0);
}
template <class Type>
void MultiplicativeEnvelope<Type>::getBlock(absl::Span<Type> output)
{
EventEnvelope<Type>::getBlock(output);
auto& events = EventEnvelope<Type>::events;
auto& currentValue = EventEnvelope<Type>::currentValue;
int index { 0 };
for (auto& event : events) {
const auto length = min(event.first, static_cast<int>(output.size())) - index;
if (length == 0) {
currentValue = event.second;
continue;
}
const auto step = std::exp((std::log(event.second) - std::log(currentValue)) / length);
currentValue = multiplicativeRamp<Type>(output.subspan(index, length), currentValue, step);
index += length;
}
if (index < static_cast<int>(output.size()))
fill<Type>(output.subspan(index), currentValue);
}
template <class Type>
void MultiplicativeEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quantizationStep)
{
EventEnvelope<Type>::getQuantizedBlock(output, quantizationStep);
auto& events = EventEnvelope<Type>::events;
auto& currentValue = EventEnvelope<Type>::currentValue;
ASSERT(quantizationStep != 0.0);
int index { 0 };
const auto logStep = std::log(quantizationStep);
// If we assume that a = b.q^r for b in (1, q) then
// log a log b
// ----- = ----- + r
// log q log q
// and log(b)\log(q) is between 0 and 1.
auto quantize = [logStep](Type value) -> Type {
return std::exp(logStep * static_cast<int>(std::log(value)/logStep));
};
const auto outputSize = static_cast<int>(output.size());
for (auto& event : events) {
const auto newValue = quantize(event.second);
if (event.first > outputSize) {
fill<Type>(output.subspan(index), currentValue);
currentValue = newValue;
index = outputSize;
break;
}
const auto length = event.first - index - 1;
if (length <= 0) {
currentValue = newValue;
continue;
}
const auto difference = newValue > currentValue ? newValue / currentValue : currentValue / newValue;
if (difference < quantizationStep) {
fill<Type>(output.subspan(index, length), currentValue);
currentValue = newValue;
index += length;
continue;
}
const int numSteps = std::log(difference) / logStep;
const auto stepLength = static_cast<int>(length / numSteps);
for (int i = 0; i < numSteps; ++i) {
fill<Type>(output.subspan(index, stepLength), currentValue);
const auto delta = newValue > currentValue ?
quantize(currentValue) / currentValue * quantizationStep :
quantize(currentValue) / currentValue / quantizationStep ;
currentValue *= delta;
index += stepLength;
}
}

View file

@ -130,4 +130,17 @@ public:
void getBlock(absl::Span<Type> output) final;
void getQuantizedBlock(absl::Span<Type> output, Type quantizationStep) final;
};
/**
* @brief Describes a simple linear envelope.
*
* @tparam Type
*/
template <class Type>
class MultiplicativeEnvelope: public EventEnvelope<Type> {
public:
MultiplicativeEnvelope();
void getBlock(absl::Span<Type> output) final;
void getQuantizedBlock(absl::Span<Type> output, Type quantizationStep) final;
};
}

View file

@ -43,6 +43,7 @@
namespace sfz
{
template class EventEnvelope<float>;
template class MultiplicativeEnvelope<float>;
template class LinearEnvelope<float>;
template class ADSREnvelope<float>;
}

View file

@ -18,7 +18,7 @@ set(SFIZZ_TEST_SOURCES
RegionActivationT.cpp
RegionValueComputationsT.cpp
ADSREnvelopeT.cpp
LinearEnvelopeT.cpp
EventEnvelopesT.cpp
MainT.cpp
SynthT.cpp
RegionTriggersT.cpp

View file

@ -169,6 +169,17 @@ TEST_CASE("[LinearEnvelope] Get quantized")
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets")
{
sfz::LinearEnvelope<float> envelope;
envelope.registerEvent(2, 1.1);
envelope.registerEvent(6, 1.9);
std::array<float, 8> output;
std::array<float, 8> expected { 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Get quantized with 2 steps")
{
sfz::LinearEnvelope<float> envelope;
@ -180,6 +191,43 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps")
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps")
{
sfz::LinearEnvelope<float> envelope;
envelope.reset(3.0);
envelope.registerEvent(2, 2.0);
envelope.registerEvent(6, 0.0);
std::array<float, 8> output;
std::array<float, 8> expected { 3.0, 2.0, 2.0, 1.0, 1.0, 0.0, 0.0, 0.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and starting unquantized")
{
sfz::LinearEnvelope<float> envelope;
envelope.reset(0.1);
envelope.registerEvent(3, 1.0);
envelope.registerEvent(7, 3.0);
std::array<float, 8> output;
std::array<float, 8> expected { 0.1, 0.1, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps and starting unquantized")
{
sfz::LinearEnvelope<float> envelope;
envelope.reset(3.6);
envelope.registerEvent(4, 1.0);
envelope.registerEvent(7, 0.0);
std::array<float, 8> output;
std::array<float, 8> expected { 3.6, 3.0, 2.0, 2.0, 1.0, 1.0, 0.0, 0.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected);
}
TEST_CASE("[LinearEnvelope] Get quantized with unclean events")
{
sfz::LinearEnvelope<float> envelope;
@ -204,4 +252,105 @@ TEST_CASE("[LinearEnvelope] Get quantized 3 events, one out of block")
REQUIRE(output == expected);
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected2);
}
}
//
TEST_CASE("[MultiplicativeEnvelope] Basic state")
{
sfz::MultiplicativeEnvelope<float> envelope;
std::array<float, 5> output;
std::array<float, 5> expected { 1.0, 1.0, 1.0, 1.0, 1.0 };
envelope.getBlock(absl::MakeSpan(output));
REQUIRE(output == expected);
}
TEST_CASE("[MultiplicativeEnvelope] Basic event")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.registerEvent(4, 2.0);
std::array<float, 8> output;
std::array<float, 8> expected { 1.1892, 1.4142, 1.68176, 2.0, 2.0, 2.0, 2.0, 2.0 };
envelope.getBlock(absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
TEST_CASE("[MultiplicativeEnvelope] 2 events")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.registerEvent(4, 2.0);
envelope.registerEvent(5, 4.0);
std::array<float, 8> output;
std::array<float, 8> expected { 1.1892, 1.4142, 1.68176, 2.0, 4.0, 4.0, 4.0, 4.0 };
envelope.getBlock(absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
TEST_CASE("[MultiplicativeEnvelope] 2 events, far")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.registerEvent(2, 2.0);
envelope.registerEvent(6, 4.0);
std::array<float, 8> output;
std::array<float, 8> expected { 1.4142, 2.0, 2.37841, 2.82843, 3.36358, 4.0, 4.0, 4.0 };
envelope.getBlock(absl::MakeSpan(output));
REQUIRE(approxEqual<float>(output, expected));
}
TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.registerEvent(2, 2.0);
envelope.registerEvent(6, 4.0);
std::array<float, 8> output;
std::array<float, 8> expected { 1.0, 2.0, 2.0, 2.0, 2.0, 4.0, 4.0, 4.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 2.0f);
REQUIRE(output == expected);
}
TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.reset(4.0);
envelope.registerEvent(2, 2.0);
envelope.registerEvent(6, 0.5);
std::array<float, 8> output;
std::array<float, 8> expected { 4.0, 2.0, 2.0, 1.0, 1.0, 0.5, 0.5, 0.5 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 2.0f);
REQUIRE(output == expected);
}
TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.registerEvent(2, 1.2);
envelope.registerEvent(6, 2.5);
std::array<float, 8> output;
std::array<float, 8> expected { 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 2.0f);
REQUIRE(output == expected);
}
TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps and starting unquantized")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.reset(0.9);
envelope.registerEvent(3, 1.0);
envelope.registerEvent(7, 4.0);
std::array<float, 8> output;
std::array<float, 8> expected { 0.9, 0.9, 1.0, 1.0, 2.0, 2.0, 4.0, 4.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 2.0f);
REQUIRE(output == expected);
}
TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps and starting unquantized")
{
sfz::MultiplicativeEnvelope<float> envelope;
envelope.reset(4.6);
envelope.registerEvent(4, 1.0);
envelope.registerEvent(7, 0.25);
std::array<float, 8> output;
std::array<float, 8> expected { 4.6, 2.0, 1.0, 1.0, 0.5, 0.5, 0.25, 0.25 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 2.0f);
REQUIRE(output == expected);
}