Consolidate the envelopes

This commit is contained in:
Paul Ferrand 2019-12-12 19:24:09 +01:00
parent 055f233d88
commit 72f141aecb
5 changed files with 93 additions and 44 deletions

View file

@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "LinearEnvelope.h" #include "EventEnvelopes.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "MathHelpers.h" #include "MathHelpers.h"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
@ -29,59 +29,90 @@
namespace sfz { namespace sfz {
template <class Type> template <class Type>
LinearEnvelope<Type>::LinearEnvelope() EventEnvelope<Type>::EventEnvelope()
{ {
setMaxCapacity(maxCapacity); setMaxCapacity(maxCapacity);
} }
template <class Type> template <class Type>
LinearEnvelope<Type>::LinearEnvelope(int maxCapacity, std::function<Type(Type)> function) EventEnvelope<Type>::EventEnvelope(int maxCapacity, std::function<Type(Type)> function)
{ {
setMaxCapacity(maxCapacity); setMaxCapacity(maxCapacity);
setFunction(function); setFunction(function);
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::setMaxCapacity(int maxCapacity) void EventEnvelope<Type>::setMaxCapacity(int maxCapacity)
{ {
events.reserve(maxCapacity); events.reserve(maxCapacity);
this->maxCapacity = maxCapacity; this->maxCapacity = maxCapacity;
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::setFunction(std::function<Type(Type)> function) void EventEnvelope<Type>::setFunction(std::function<Type(Type)> function)
{ {
this->function = function; this->function = function;
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::registerEvent(int timestamp, Type inputValue) void EventEnvelope<Type>::registerEvent(int timestamp, Type inputValue)
{ {
if (static_cast<int>(events.size()) < maxCapacity) if (static_cast<int>(events.size()) < maxCapacity)
events.emplace_back(timestamp, function(inputValue)); events.emplace_back(timestamp, function(inputValue));
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::clear() void EventEnvelope<Type>::prepareEvents()
{ {
events.clear(); if (resetEvents) {
if (!events.empty())
currentValue = events.back().second;
clear();
} else {
absl::c_sort(events, [](const auto& lhs, const auto& rhs) {
return lhs.first < rhs.first;
});
resetEvents = true;
}
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::reset(Type value) void EventEnvelope<Type>::clear()
{
events.clear();
resetEvents = false;
}
template <class Type>
void EventEnvelope<Type>::reset(Type value)
{ {
clear(); clear();
currentValue = function(value); currentValue = function(value);
resetEvents = false;
}
template <class Type>
void EventEnvelope<Type>::getBlock(absl::Span<Type>)
{
prepareEvents();
}
template <class Type>
void EventEnvelope<Type>::getQuantizedBlock(absl::Span<Type>, Type)
{
prepareEvents();
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::getBlock(absl::Span<Type> output) void LinearEnvelope<Type>::getBlock(absl::Span<Type> output)
{ {
absl::c_sort(events, [](const auto& lhs, const auto& rhs) { EventEnvelope<Type>::getBlock(output);
return lhs.first < rhs.first; auto& events = EventEnvelope<Type>::events;
}); auto& currentValue = EventEnvelope<Type>::currentValue;
int index { 0 };
int index { 0 };
for (auto& event : events) { for (auto& event : events) {
const auto length = min(event.first, static_cast<int>(output.size())) - index; const auto length = min(event.first, static_cast<int>(output.size())) - index;
if (length == 0) { if (length == 0) {
@ -96,18 +127,16 @@ void LinearEnvelope<Type>::getBlock(absl::Span<Type> output)
if (index < static_cast<int>(output.size())) if (index < static_cast<int>(output.size()))
fill<Type>(output.subspan(index), currentValue); fill<Type>(output.subspan(index), currentValue);
clear();
} }
template <class Type> template <class Type>
void LinearEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quantizationStep) void LinearEnvelope<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); ASSERT(quantizationStep != 0.0);
absl::c_sort(events, [](const auto& lhs, const auto& rhs) {
return lhs.first < rhs.first;
});
int index { 0 }; int index { 0 };
const auto halfStep = quantizationStep / 2; const auto halfStep = quantizationStep / 2;
@ -118,10 +147,17 @@ void LinearEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quant
currentValue = quantize(currentValue); currentValue = quantize(currentValue);
const auto outputSize = static_cast<int>(output.size()); const auto outputSize = static_cast<int>(output.size());
for (auto& event : events) { for (auto& event : events) {
const auto length = min(event.first, outputSize) - index;
const auto newValue = quantize(event.second); const auto newValue = quantize(event.second);
if (length == 0) { 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; currentValue = newValue;
continue; continue;
} }
@ -137,8 +173,6 @@ void LinearEnvelope<Type>::getQuantizedBlock(absl::Span<Type> output, Type quant
if (index < outputSize) if (index < outputSize)
fill<Type>(output.subspan(index), currentValue); fill<Type>(output.subspan(index), currentValue);
clear();
} }
} }

View file

@ -31,11 +31,10 @@
namespace sfz { namespace sfz {
/** /**
* @brief Describes a simple linear envelope that can be polled in a blockwise * @brief Describes a simple envelope that can be polled in a blockwise
* manner. It works by storing "events" in the immediate future and linearly * manner. It works by storing "events" in the immediate future and linearly
* interpolating between these events. This envelope can also transform its * interpolating between these events. This envelope can also transform its
* incoming target points through a lambda, although the interpolation will * incoming target points through a lambda, although the lambda function is applied before the interpolation.
* always be linear (i.e. the lambda function is applied before the interpolation).
* *
* The way to use this class is by repeatedly calling `registerEvent` and then * The way to use this class is by repeatedly calling `registerEvent` and then
* `getBlock` to get a block of interpolated values in between the specified events. * `getBlock` to get a block of interpolated values in between the specified events.
@ -45,14 +44,14 @@ namespace sfz {
* @tparam Type * @tparam Type
*/ */
template <class Type> template <class Type>
class LinearEnvelope { class EventEnvelope {
public: public:
/** /**
* @brief Construct a new linear envelope with a default memory size for * @brief Construct a new linear envelope with a default memory size for
* incoming events. * incoming events.
* *
*/ */
LinearEnvelope(); EventEnvelope();
/** /**
* @brief Construct a new linear envelope with a specific memory size for * @brief Construct a new linear envelope with a specific memory size for
* incoming events as well as a transformation function for incoming events. * incoming events as well as a transformation function for incoming events.
@ -60,7 +59,7 @@ public:
* @param maxCapacity * @param maxCapacity
* @param function * @param function
*/ */
LinearEnvelope(int maxCapacity, std::function<Type(Type)> function); EventEnvelope(int maxCapacity, std::function<Type(Type)> function);
/** /**
* @brief Set the maximum memory size for incoming events * @brief Set the maximum memory size for incoming events
* *
@ -98,7 +97,7 @@ public:
* *
* @param output * @param output
*/ */
void getBlock(absl::Span<Type> output); virtual void getBlock(absl::Span<Type> output);
/** /**
* @brief Get a block of interpolated values with a forced quantization. The * @brief Get a block of interpolated values with a forced quantization. The
* values within the block will vary in quantization steps. * values within the block will vary in quantization steps.
@ -106,14 +105,29 @@ public:
* @param output * @param output
* @param quantizationStep * @param quantizationStep
*/ */
void getQuantizedBlock(absl::Span<Type> output, Type quantizationStep); virtual void getQuantizedBlock(absl::Span<Type> output, Type quantizationStep);
private: protected:
std::function<Type(Type)> function { [](Type input) { return input; } };
static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic");
std::vector<std::pair<int, Type>> events; std::vector<std::pair<int, Type>> events;
int maxCapacity { config::defaultSamplesPerBlock };
Type currentValue { 0.0 }; Type currentValue { 0.0 };
LEAK_DETECTOR(LinearEnvelope); private:
static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic");
std::function<Type(Type)> function { [](Type input) { return input; } };
int maxCapacity { config::defaultSamplesPerBlock };
void prepareEvents();
bool resetEvents { false };
LEAK_DETECTOR(EventEnvelope);
}; };
/**
* @brief Describes a simple linear envelope.
*
* @tparam Type
*/
template <class Type>
class LinearEnvelope: public EventEnvelope<Type> {
public:
void getBlock(absl::Span<Type> output) final;
void getQuantizedBlock(absl::Span<Type> output, Type quantizationStep) final;
};
} }

View file

@ -32,16 +32,17 @@
* *
*/ */
#include "LinearEnvelope.h" #include "EventEnvelopes.h"
#include "ADSREnvelope.h" #include "ADSREnvelope.h"
// Include the generic implementations // Include the generic implementations
#include "LinearEnvelope.cpp" #include "EventEnvelopes.cpp"
#include "ADSREnvelope.cpp" #include "ADSREnvelope.cpp"
// And explicitely instantiate the float version // And explicitely instantiate the float version
namespace sfz namespace sfz
{ {
template class EventEnvelope<float>;
template class LinearEnvelope<float>; template class LinearEnvelope<float>;
template class ADSREnvelope<float>; template class ADSREnvelope<float>;
} }

View file

@ -24,7 +24,7 @@
#pragma once #pragma once
#include "ADSREnvelope.h" #include "ADSREnvelope.h"
#include "Config.h" #include "Config.h"
#include "LinearEnvelope.h" #include "EventEnvelopes.h"
#include "HistoricalBuffer.h" #include "HistoricalBuffer.h"
#include "Region.h" #include "Region.h"
#include "AudioBuffer.h" #include "AudioBuffer.h"

View file

@ -21,7 +21,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "LinearEnvelope.h" #include "EventEnvelopes.h"
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
#include <absl/types/span.h> #include <absl/types/span.h>
@ -164,7 +164,7 @@ TEST_CASE("[LinearEnvelope] Get quantized")
envelope.registerEvent(2, 1.0); envelope.registerEvent(2, 1.0);
envelope.registerEvent(6, 2.0); envelope.registerEvent(6, 2.0);
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0 }; 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); envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -175,7 +175,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps")
envelope.registerEvent(2, 1.0); envelope.registerEvent(2, 1.0);
envelope.registerEvent(6, 3.0); envelope.registerEvent(6, 3.0);
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0 }; std::array<float, 8> expected { 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f); envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -186,7 +186,7 @@ TEST_CASE("[LinearEnvelope] Get quantized with unclean events")
envelope.registerEvent(2, 1.2); envelope.registerEvent(2, 1.2);
envelope.registerEvent(6, 2.5); envelope.registerEvent(6, 2.5);
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0 }; std::array<float, 8> expected { 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 3.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f); envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -198,7 +198,7 @@ TEST_CASE("[LinearEnvelope] Get quantized 3 events, one out of block")
envelope.registerEvent(6, 2.0); envelope.registerEvent(6, 2.0);
envelope.registerEvent(10, 3.0); envelope.registerEvent(10, 3.0);
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0 }; std::array<float, 8> expected { 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0 };
std::array<float, 8> expected2 { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 }; std::array<float, 8> expected2 { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 };
envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f); envelope.getQuantizedBlock(absl::MakeSpan(output), 1.0f);
REQUIRE(output == expected); REQUIRE(output == expected);