Added a multiplicative envelope and associated tests
This commit is contained in:
parent
72f141aecb
commit
4e26852713
5 changed files with 267 additions and 5 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
namespace sfz
|
||||
{
|
||||
template class EventEnvelope<float>;
|
||||
template class MultiplicativeEnvelope<float>;
|
||||
template class LinearEnvelope<float>;
|
||||
template class ADSREnvelope<float>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ set(SFIZZ_TEST_SOURCES
|
|||
RegionActivationT.cpp
|
||||
RegionValueComputationsT.cpp
|
||||
ADSREnvelopeT.cpp
|
||||
LinearEnvelopeT.cpp
|
||||
EventEnvelopesT.cpp
|
||||
MainT.cpp
|
||||
SynthT.cpp
|
||||
RegionTriggersT.cpp
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue