Consolidate the envelopes
This commit is contained in:
parent
055f233d88
commit
72f141aecb
5 changed files with 93 additions and 44 deletions
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue