Cosmetics

This commit is contained in:
paulfd 2019-08-25 14:01:03 +02:00
parent 8e8ea4f420
commit 5fd7cab909
37 changed files with 1428 additions and 1527 deletions

View file

@ -1,13 +1,12 @@
#include "Globals.h"
#include "SIMDHelpers.h"
#include "Helpers.h"
#include "ADSREnvelope.h" #include "ADSREnvelope.h"
#include "Globals.h"
#include "Helpers.h"
#include "SIMDHelpers.h"
#include <algorithm> #include <algorithm>
namespace sfz namespace sfz {
{
template<class Type> template <class Type>
void ADSREnvelope<Type>::reset(int attack, int release, Type sustain, int delay, int decay, int hold, Type start, Type depth) noexcept 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(start <= 1.0f);
@ -32,17 +31,15 @@ void ADSREnvelope<Type>::reset(int attack, int release, Type sustain, int delay,
currentState = State::Delay; currentState = State::Delay;
} }
template<class Type> template <class Type>
Type ADSREnvelope<Type>::getNextValue() noexcept Type ADSREnvelope<Type>::getNextValue() noexcept
{ {
if (shouldRelease && releaseDelay-- == 0) if (shouldRelease && releaseDelay-- == 0) {
{
currentState = State::Release; currentState = State::Release;
step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1)); step = std::exp((std::log(config::virtuallyZero) - std::log(currentValue)) / (release > 0 ? release : 1));
} }
switch(currentState) switch (currentState) {
{
case State::Delay: case State::Delay:
if (delay-- > 0) if (delay-- > 0)
return start; return start;
@ -51,8 +48,7 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
step = (1.0 - currentValue) / (attack > 0 ? attack : 1); step = (1.0 - currentValue) / (attack > 0 ? attack : 1);
[[fallthrough]]; [[fallthrough]];
case State::Attack: case State::Attack:
if (attack-- > 0) if (attack-- > 0) {
{
currentValue += step; currentValue += step;
return currentValue; return currentValue;
} }
@ -68,8 +64,7 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
currentState = State::Decay; currentState = State::Decay;
[[fallthrough]]; [[fallthrough]];
case State::Decay: case State::Decay:
if (decay-- > 0) if (decay-- > 0) {
{
currentValue *= step; currentValue *= step;
return currentValue; return currentValue;
} }
@ -80,8 +75,7 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
case State::Sustain: case State::Sustain:
return currentValue; return currentValue;
case State::Release: case State::Release:
if (release-- > 0) if (release-- > 0) {
{
currentValue *= step; currentValue *= step;
return currentValue; return currentValue;
} }
@ -94,14 +88,13 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
} }
} }
template<class Type> template <class Type>
void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
{ {
auto originalSpan = output; auto originalSpan = output;
auto remainingSamples = static_cast<int>(output.size()); auto remainingSamples = static_cast<int>(output.size());
int length; int length;
switch(currentState) switch (currentState) {
{
case State::Delay: case State::Delay:
length = min(remainingSamples, delay); length = min(remainingSamples, delay);
::fill<Type>(output, currentValue); ::fill<Type>(output, currentValue);
@ -171,11 +164,9 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
} }
::fill<Type>(output, currentValue); ::fill<Type>(output, currentValue);
if (shouldRelease) if (shouldRelease) {
{
remainingSamples = static_cast<int>(originalSpan.size()); remainingSamples = static_cast<int>(originalSpan.size());
if (releaseDelay > remainingSamples) if (releaseDelay > remainingSamples) {
{
releaseDelay -= remainingSamples; releaseDelay -= remainingSamples;
return; return;
} }
@ -191,22 +182,20 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
originalSpan.remove_prefix(length); originalSpan.remove_prefix(length);
release -= length; release -= length;
if (release == 0) if (release == 0) {
{
currentValue = 0.0; currentValue = 0.0;
currentState = State::Done; currentState = State::Done;
::fill<Type>(originalSpan, 0.0); ::fill<Type>(originalSpan, 0.0);
} }
} }
} }
template<class Type> template <class Type>
bool ADSREnvelope<Type>::isSmoothing() noexcept bool ADSREnvelope<Type>::isSmoothing() noexcept
{ {
return currentState != State::Done; return currentState != State::Done;
} }
template<class Type> template <class Type>
void ADSREnvelope<Type>::startRelease(int releaseDelay) noexcept void ADSREnvelope<Type>::startRelease(int releaseDelay) noexcept
{ {
shouldRelease = true; shouldRelease = true;

View file

@ -1,23 +1,27 @@
#pragma once #pragma once
#include <absl/types/span.h>
#include "Helpers.h" #include "Helpers.h"
namespace sfz #include <absl/types/span.h>
{ namespace sfz {
template<class Type> template <class Type>
class ADSREnvelope class ADSREnvelope {
{
public: public:
ADSREnvelope() = default; ADSREnvelope() = default;
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; 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; Type getNextValue() noexcept;
void getBlock(absl::Span<Type> output) noexcept; void getBlock(absl::Span<Type> output) noexcept;
void startRelease(int releaseDelay) noexcept; void startRelease(int releaseDelay) noexcept;
bool isSmoothing() noexcept; bool isSmoothing() noexcept;
private: private:
enum class State enum class State {
{ Delay,
Delay, Attack, Hold, Decay, Sustain, Release, Done Attack,
Hold,
Decay,
Sustain,
Release,
Done
}; };
State currentState { State::Done }; State currentState { State::Done };
Type currentValue { 0.0 }; Type currentValue { 0.0 };

View file

@ -7,14 +7,13 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment> template <class Type, unsigned int Alignment = SIMDConfig::defaultAlignment>
class Buffer class Buffer {
{
public: public:
using value_type = std::remove_cv_t<Type>; using value_type = std::remove_cv_t<Type>;
using pointer = value_type *; using pointer = value_type*;
using const_pointer = const value_type *; using const_pointer = const value_type*;
using reference = value_type &; using reference = value_type&;
using const_reference = const value_type &; using const_reference = const value_type&;
using iterator = pointer; using iterator = pointer;
using const_iterator = const_pointer; using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
@ -31,16 +30,14 @@ public:
} }
bool resize(size_t newSize) bool resize(size_t newSize)
{ {
if (newSize == 0) if (newSize == 0) {
{
clear(); clear();
return true; return true;
} }
auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end auto tempSize = newSize + 2 * AlignmentMask; // To ensure that we have leeway at the beginning and at the end
auto *newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type)); auto* newData = paddedData != nullptr ? std::realloc(paddedData, tempSize * sizeof(value_type)) : std::malloc(tempSize * sizeof(value_type));
if (newData == nullptr) if (newData == nullptr) {
{
return false; return false;
} }
@ -71,15 +68,14 @@ public:
std::free(paddedData); std::free(paddedData);
} }
Buffer(const Buffer<Type> &other) Buffer(const Buffer<Type>& other)
{ {
if (resize(other.size())) if (resize(other.size())) {
{
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
} }
} }
Buffer(Buffer<Type> &&other) Buffer(Buffer<Type>&& other)
{ {
largerSize = std::exchange(other.largerSize, 0); largerSize = std::exchange(other.largerSize, 0);
alignedSize = std::exchange(other.alignedSize, 0); alignedSize = std::exchange(other.alignedSize, 0);
@ -89,20 +85,18 @@ public:
_alignedEnd = std::exchange(other._alignedEnd, nullptr); _alignedEnd = std::exchange(other._alignedEnd, nullptr);
} }
Buffer<Type> &operator=(const Buffer<Type> &other) Buffer<Type>& operator=(const Buffer<Type>& other)
{ {
if (this != &other) if (this != &other) {
{
if (resize(other.size())) if (resize(other.size()))
std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type)); std::memcpy(this->data(), other.data(), other.size() * sizeof(value_type));
} }
return *this; return *this;
} }
Buffer<Type> &operator=(Buffer<Type> &&other) Buffer<Type>& operator=(Buffer<Type>&& other)
{ {
if (this != &other) if (this != &other) {
{
std::free(paddedData); std::free(paddedData);
largerSize = std::exchange(other.largerSize, 0); largerSize = std::exchange(other.largerSize, 0);
alignedSize = std::exchange(other.alignedSize, 0); alignedSize = std::exchange(other.alignedSize, 0);
@ -114,7 +108,7 @@ public:
return *this; return *this;
} }
Type &operator[](int idx) { return *(normalData + idx); } Type& operator[](int idx) { return *(normalData + idx); }
constexpr pointer data() const noexcept { return normalData; } constexpr pointer data() const noexcept { return normalData; }
constexpr size_type size() const noexcept { return alignedSize; } constexpr size_type size() const noexcept { return alignedSize; }
constexpr bool empty() const noexcept { return alignedSize == 0; } constexpr bool empty() const noexcept { return alignedSize == 0; }
@ -123,17 +117,17 @@ public:
constexpr pointer alignedEnd() noexcept { return _alignedEnd; } constexpr pointer alignedEnd() noexcept { return _alignedEnd; }
private: private:
static constexpr auto AlignmentMask{Alignment - 1}; static constexpr auto AlignmentMask { Alignment - 1 };
static constexpr auto TypeAlignment{Alignment / sizeof(value_type)}; static constexpr auto TypeAlignment { Alignment / sizeof(value_type) };
static constexpr auto TypeAlignmentMask{TypeAlignment - 1}; static constexpr auto TypeAlignmentMask { TypeAlignment - 1 };
static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic"); static_assert(std::is_arithmetic<value_type>::value, "Type should be arithmetic");
static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value"); static_assert(Alignment == 0 || Alignment == 4 || Alignment == 8 || Alignment == 16, "Bad alignment value");
static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type"); static_assert(TypeAlignment * sizeof(value_type) == Alignment, "The alignment does not appear to be divided by the size of the Type");
size_type largerSize{0}; size_type largerSize { 0 };
size_type alignedSize{0}; size_type alignedSize { 0 };
pointer normalData{nullptr}; pointer normalData { nullptr };
pointer paddedData{nullptr}; pointer paddedData { nullptr };
pointer normalEnd{nullptr}; pointer normalEnd { nullptr };
pointer _alignedEnd{nullptr}; pointer _alignedEnd { nullptr };
LEAK_DETECTOR(Buffer); LEAK_DETECTOR(Buffer);
}; };

View file

@ -1,33 +1,31 @@
#pragma once #pragma once
#include <map>
#include "Helpers.h" #include "Helpers.h"
#include <map>
namespace sfz namespace sfz {
{ template <class ValueType>
template<class ValueType> class CCMap {
class CCMap
{
public: public:
CCMap() = delete; CCMap() = delete;
CCMap(const ValueType& defaultValue) : defaultValue(defaultValue) { } CCMap(const ValueType& defaultValue)
: defaultValue(defaultValue)
{
}
CCMap(CCMap&&) = default; CCMap(CCMap&&) = default;
CCMap(const CCMap&) = default; CCMap(const CCMap&) = default;
~CCMap() = default; ~CCMap() = default;
const ValueType &getWithDefault(int index) const noexcept const ValueType& getWithDefault(int index) const noexcept
{ {
auto it = container.find(index); auto it = container.find(index);
if (it == end(container)) if (it == end(container)) {
{
return defaultValue; return defaultValue;
} } else {
else
{
return it->second; return it->second;
} }
} }
ValueType &operator[](const int &key) noexcept ValueType& operator[](const int& key) noexcept
{ {
if (!contains(key)) if (!contains(key))
container.emplace(key, defaultValue); container.emplace(key, defaultValue);
@ -35,7 +33,7 @@ public:
} }
inline bool empty() const { return container.empty(); } inline bool empty() const { return container.empty(); }
const ValueType &at(int index) const { return container.at(index); } const ValueType& at(int index) const { return container.at(index); }
bool contains(int index) const noexcept { return container.find(index) != end(container); } bool contains(int index) const noexcept { return container.find(index) != end(container); }
private: private:

View file

@ -1,25 +1,22 @@
#pragma once #pragma once
#include "StereoBuffer.h"
#include "Defaults.h" #include "Defaults.h"
#include "StereoBuffer.h"
#include "Voice.h" #include "Voice.h"
#include <sndfile.hh>
#include <filesystem>
#include <optional>
#include <string_view>
#include <absl/container/flat_hash_map.h>
#include <map>
#include "readerwriterqueue.h" #include "readerwriterqueue.h"
#include <absl/container/flat_hash_map.h>
#include <filesystem>
#include <map>
#include <optional>
#include <sndfile.hh>
#include <string_view>
#include <thread> #include <thread>
namespace sfz namespace sfz {
{ class FilePool {
class FilePool
{
public: public:
FilePool() FilePool()
: fileLoadingThread(std::thread(&FilePool::loadingThread, this)) : fileLoadingThread(std::thread(&FilePool::loadingThread, this))
{ {
} }
~FilePool() ~FilePool()
@ -30,8 +27,7 @@ public:
void setRootDirectory(const std::filesystem::path& directory) { rootDirectory = directory; } void setRootDirectory(const std::filesystem::path& directory) { rootDirectory = directory; }
size_t getNumPreloadedSamples() { return preloadedData.size(); } size_t getNumPreloadedSamples() { return preloadedData.size(); }
struct FileInformation struct FileInformation {
{
uint32_t end { Default::sampleEndRange.getEnd() }; uint32_t end { Default::sampleEndRange.getEnd() };
uint32_t loopBegin { Default::loopRange.getStart() }; uint32_t loopBegin { Default::loopRange.getStart() };
uint32_t loopEnd { Default::loopRange.getEnd() }; uint32_t loopEnd { Default::loopRange.getEnd() };
@ -40,7 +36,8 @@ public:
}; };
std::optional<FileInformation> getFileInformation(std::string_view filename); std::optional<FileInformation> getFileInformation(std::string_view filename);
void enqueueLoading(Voice* voice, std::string_view sample, int numFrames); void enqueueLoading(Voice* voice, std::string_view sample, int numFrames);
static void deleteAndTrackBuffers(StereoBuffer<float>* buffer) { static void deleteAndTrackBuffers(StereoBuffer<float>* buffer)
{
fileBuffers--; fileBuffers--;
delete buffer; delete buffer;
}; };
@ -48,10 +45,10 @@ public:
{ {
return fileBuffers.load(); return fileBuffers.load();
} }
private: private:
std::filesystem::path rootDirectory; std::filesystem::path rootDirectory;
struct FileLoadingInformation struct FileLoadingInformation {
{
Voice* voice; Voice* voice;
std::string_view sample; std::string_view sample;
int numFrames; int numFrames;

View file

@ -1,10 +1,8 @@
#pragma once #pragma once
namespace sfz namespace sfz {
{
namespace config namespace config {
{
constexpr float defaultSampleRate { 48000 }; constexpr float defaultSampleRate { 48000 };
constexpr int defaultSamplesPerBlock { 1024 }; constexpr int defaultSamplesPerBlock { 1024 };
constexpr int preloadSize { 8192 }; constexpr int preloadSize { 8192 };
@ -21,21 +19,20 @@ namespace config
} // namespace sfz } // namespace sfz
namespace SIMDConfig namespace SIMDConfig {
{ constexpr unsigned int defaultAlignment { 16 };
constexpr unsigned int defaultAlignment { 16 }; constexpr bool writeInterleaved { true };
constexpr bool writeInterleaved { true }; constexpr bool readInterleaved { true };
constexpr bool readInterleaved { true }; constexpr bool fill { true };
constexpr bool fill { true }; constexpr bool gain { false };
constexpr bool gain { false }; constexpr bool mathfuns { false };
constexpr bool mathfuns { false }; constexpr bool loopingSFZIndex { true };
constexpr bool loopingSFZIndex { true }; constexpr bool linearRamp { false };
constexpr bool linearRamp { false }; constexpr bool multiplicativeRamp { true };
constexpr bool multiplicativeRamp { true }; constexpr bool add { false };
constexpr bool add { false };
#if USE_SIMD #if USE_SIMD
constexpr bool useSIMD { true }; constexpr bool useSIMD { true };
#else #else
constexpr bool useSIMD { false }; constexpr bool useSIMD { false };
#endif #endif
} }

View file

@ -3,17 +3,14 @@
#include <signal.h> #include <signal.h>
#include <string_view> #include <string_view>
inline void trimInPlace(std::string_view &s) inline void trimInPlace(std::string_view& s)
{ {
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v"); const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
if (leftPosition != s.npos) if (leftPosition != s.npos) {
{
s.remove_prefix(leftPosition); s.remove_prefix(leftPosition);
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v"); const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
s.remove_suffix(s.size() - rightPosition - 1); s.remove_suffix(s.size() - rightPosition - 1);
} } else {
else
{
s.remove_suffix(s.size()); s.remove_suffix(s.size());
} }
} }
@ -21,14 +18,11 @@ inline void trimInPlace(std::string_view &s)
inline std::string_view trim(std::string_view s) inline std::string_view trim(std::string_view s)
{ {
const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v"); const auto leftPosition = s.find_first_not_of(" \r\t\n\f\v");
if (leftPosition != s.npos) if (leftPosition != s.npos) {
{
s.remove_prefix(leftPosition); s.remove_prefix(leftPosition);
const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v"); const auto rightPosition = s.find_last_not_of(" \r\t\n\f\v");
s.remove_suffix(s.size() - rightPosition - 1); s.remove_suffix(s.size() - rightPosition - 1);
} } else {
else
{
s.remove_suffix(s.size()); s.remove_suffix(s.size());
} }
return s; return s;
@ -36,7 +30,7 @@ inline std::string_view trim(std::string_view s)
inline constexpr unsigned int Fnv1aBasis = 0x811C9DC5; inline constexpr unsigned int Fnv1aBasis = 0x811C9DC5;
inline constexpr unsigned int Fnv1aPrime = 0x01000193; inline constexpr unsigned int Fnv1aPrime = 0x01000193;
inline constexpr unsigned int hash(const char *s, unsigned int h = Fnv1aBasis) inline constexpr unsigned int hash(const char* s, unsigned int h = Fnv1aBasis)
{ {
return !*s ? h : hash(s + 1, static_cast<unsigned int>((h ^ *s) * static_cast<unsigned long long>(Fnv1aPrime))); return !*s ? h : hash(s + 1, static_cast<unsigned int>((h ^ *s) * static_cast<unsigned long long>(Fnv1aPrime)));
} }
@ -112,10 +106,9 @@ inline constexpr Type mag2db(Type in)
return static_cast<Type>(20.0) * std::log10(in); return static_cast<Type>(20.0) * std::log10(in);
} }
namespace Random namespace Random {
{
static inline std::random_device randomDevice; static inline std::random_device randomDevice;
static inline std::mt19937 randomGenerator{randomDevice()}; static inline std::mt19937 randomGenerator { randomDevice() };
} // namespace Random } // namespace Random
inline float midiNoteFrequency(const int noteNumber) inline float midiNoteFrequency(const int noteNumber)
@ -124,30 +117,28 @@ inline float midiNoteFrequency(const int noteNumber)
} }
template <class Type> template <class Type>
constexpr Type pi{3.141592653589793238462643383279502884}; constexpr Type pi { 3.141592653589793238462643383279502884 };
template <class Type> template <class Type>
constexpr Type twoPi{2 * pi<Type>}; constexpr Type twoPi { 2 * pi<Type> };
template <class Type> template <class Type>
constexpr Type piTwo{pi<Type> / 2}; constexpr Type piTwo { pi<Type> / 2 };
#include <atomic> #include <atomic>
template <class Owner> template <class Owner>
class LeakDetector class LeakDetector {
{
public: public:
LeakDetector() LeakDetector()
{ {
objectCounter.count++; objectCounter.count++;
} }
LeakDetector(const LeakDetector &) LeakDetector(const LeakDetector&)
{ {
objectCounter.count++; objectCounter.count++;
} }
~LeakDetector() ~LeakDetector()
{ {
objectCounter.count--; objectCounter.count--;
if (objectCounter.count.load() < 0) if (objectCounter.count.load() < 0) {
{
DBG("Deleted a dangling pointer for class " << Owner::getClassName()); DBG("Deleted a dangling pointer for class " << Owner::getClassName());
// Deleted a dangling pointer! // Deleted a dangling pointer!
ASSERTFALSE; ASSERTFALSE;
@ -155,19 +146,17 @@ public:
} }
private: private:
struct ObjectCounter struct ObjectCounter {
{
ObjectCounter() = default; ObjectCounter() = default;
~ObjectCounter() ~ObjectCounter()
{ {
if (auto residualCount = count.load() > 0) if (auto residualCount = count.load() > 0) {
{
DBG("Leaked " << residualCount << " instance(s) of class " << Owner::getClassName()); DBG("Leaked " << residualCount << " instance(s) of class " << Owner::getClassName());
// Leaked ojects // Leaked ojects
ASSERTFALSE; ASSERTFALSE;
} }
}; };
std::atomic<int> count{0}; std::atomic<int> count { 0 };
}; };
static inline ObjectCounter objectCounter; static inline ObjectCounter objectCounter;
}; };
@ -175,7 +164,7 @@ private:
#ifndef NDEBUG #ifndef NDEBUG
#define LEAK_DETECTOR(Class) \ #define LEAK_DETECTOR(Class) \
friend class LeakDetector<Class>; \ friend class LeakDetector<Class>; \
static const char *getClassName() { return #Class; } \ static const char* getClassName() { return #Class; } \
LeakDetector<Class> leakDetector; LeakDetector<Class> leakDetector;
#else #else
#define LEAK_DETECTOR(Class) #define LEAK_DETECTOR(Class)

View file

@ -3,56 +3,55 @@
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
namespace sfz namespace sfz {
{
template<class Type> template <class Type>
LinearEnvelope<Type>::LinearEnvelope() LinearEnvelope<Type>::LinearEnvelope()
{ {
setMaxCapacity(maxCapacity); setMaxCapacity(maxCapacity);
} }
template<class Type> template <class Type>
LinearEnvelope<Type>::LinearEnvelope(int maxCapacity, std::function<Type(Type)> function) LinearEnvelope<Type>::LinearEnvelope(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 LinearEnvelope<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 LinearEnvelope<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 LinearEnvelope<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 LinearEnvelope<Type>::clear()
{ {
events.clear(); events.clear();
} }
template<class Type> template <class Type>
void LinearEnvelope<Type>::reset(Type value) void LinearEnvelope<Type>::reset(Type value)
{ {
clear(); clear();
currentValue = function(value); currentValue = function(value);
} }
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) { absl::c_sort(events, [](const auto& lhs, const auto& rhs) {
@ -60,11 +59,9 @@ void LinearEnvelope<Type>::getBlock(absl::Span<Type> output)
}); });
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) {
{
currentValue = event.second; currentValue = event.second;
continue; continue;
} }

View file

@ -1,16 +1,14 @@
#pragma once #pragma once
#include "Globals.h" #include "Globals.h"
#include "Helpers.h" #include "Helpers.h"
#include <type_traits>
#include <functional>
#include <absl/types/span.h> #include <absl/types/span.h>
#include <functional>
#include <type_traits>
namespace sfz namespace sfz {
{
template<class Type> template <class Type>
class LinearEnvelope class LinearEnvelope {
{
public: public:
LinearEnvelope(); LinearEnvelope();
LinearEnvelope(int maxCapacity, std::function<Type(Type)> function); LinearEnvelope(int maxCapacity, std::function<Type(Type)> function);
@ -18,8 +16,9 @@ public:
void setFunction(std::function<Type(Type)> function); void setFunction(std::function<Type(Type)> function);
void registerEvent(int timestamp, Type inputValue); void registerEvent(int timestamp, Type inputValue);
void clear(); void clear();
void reset(Type value=0.0); void reset(Type value = 0.0);
void getBlock(absl::Span<Type> output); void getBlock(absl::Span<Type> output);
private: private:
std::function<Type(Type)> function { [](Type input) { return input; } }; std::function<Type(Type)> function { [](Type input) { return input; } };
static_assert(std::is_arithmetic<Type>::value); static_assert(std::is_arithmetic<Type>::value);

View file

@ -1,18 +1,17 @@
#pragma once #pragma once
#include "Globals.h" #include "Globals.h"
#include <cmath>
#include <absl/types/span.h> #include <absl/types/span.h>
#include <cmath>
template<class Type=float> template <class Type = float>
class OnePoleFilter class OnePoleFilter {
{
public: public:
OnePoleFilter() = default; OnePoleFilter() = default;
// Normalized cutoff with respect to the sampling rate // Normalized cutoff with respect to the sampling rate
template<class C> template <class C>
static Type normalizedGain(Type cutoff, C sampleRate) static Type normalizedGain(Type cutoff, C sampleRate)
{ {
return std::tan( cutoff / static_cast<Type>(sampleRate) * M_PIf32 ); return std::tan(cutoff / static_cast<Type>(sampleRate) * M_PIf32);
} }
OnePoleFilter(Type gain) OnePoleFilter(Type gain)
@ -23,7 +22,7 @@ public:
void setGain(Type gain) void setGain(Type gain)
{ {
this->gain = gain; this->gain = gain;
G = gain / ( 1 + gain); G = gain / (1 + gain);
} }
Type getGain() const { return gain; } Type getGain() const { return gain; }
@ -31,8 +30,7 @@ public:
int processLowpass(absl::Span<const Type> input, absl::Span<Type> lowpass) int processLowpass(absl::Span<const Type> input, absl::Span<Type> lowpass)
{ {
for (auto [in, out] = std::pair(input.begin(), lowpass.begin()); for (auto [in, out] = std::pair(input.begin(), lowpass.begin());
in < input.end() && out < lowpass.end(); in++, out++) in < input.end() && out < lowpass.end(); in++, out++) {
{
oneLowpass(in, out); oneLowpass(in, out);
} }
return std::min(input.size(), lowpass.size()); return std::min(input.size(), lowpass.size());
@ -41,8 +39,7 @@ public:
int processHighpass(absl::Span<const Type> input, absl::Span<Type> highpass) int processHighpass(absl::Span<const Type> input, absl::Span<Type> highpass)
{ {
for (auto [in, out] = std::pair(input.begin(), highpass.begin()); for (auto [in, out] = std::pair(input.begin(), highpass.begin());
in < input.end() && out < highpass.end(); in++, out++) in < input.end() && out < highpass.end(); in++, out++) {
{
oneHighpass(in, out); oneHighpass(in, out);
} }
return std::min(input.size(), highpass.size()); return std::min(input.size(), highpass.size());
@ -51,8 +48,7 @@ public:
int processLowpassVariableGain(absl::Span<const Type> input, absl::Span<Type> lowpass, absl::Span<const Type> gain) int processLowpassVariableGain(absl::Span<const Type> input, absl::Span<Type> lowpass, absl::Span<const Type> gain)
{ {
for (auto [in, out, g] = std::tuple(input.begin(), lowpass.begin(), gain.begin()); for (auto [in, out, g] = std::tuple(input.begin(), lowpass.begin(), gain.begin());
in < input.end() && out < lowpass.end() && g < gain.end(); in++, out++, g++) in < input.end() && out < lowpass.end() && g < gain.end(); in++, out++, g++) {
{
setGain(*g); setGain(*g);
oneLowpass(in, out); oneLowpass(in, out);
} }
@ -63,8 +59,7 @@ public:
int processHighpassVariableGain(absl::Span<const Type> input, absl::Span<Type> highpass, absl::Span<const Type> gain) int processHighpassVariableGain(absl::Span<const Type> input, absl::Span<Type> highpass, absl::Span<const Type> gain)
{ {
for (auto [in, out, g] = std::tuple(input.begin(), highpass.begin(), gain.begin()); for (auto [in, out, g] = std::tuple(input.begin(), highpass.begin(), gain.begin());
in < input.end() && out < highpass.end() && g < gain.end(); in++, out++, g++) in < input.end() && out < highpass.end() && g < gain.end(); in++, out++, g++) {
{
setGain(*g); setGain(*g);
oneHighpass(in, out); oneHighpass(in, out);
} }
@ -73,6 +68,7 @@ public:
} }
void reset() { state = 0.0; } void reset() { state = 0.0; }
private: private:
Type state { 0.0 }; Type state { 0.0 };
Type gain { 0.25 }; Type gain { 0.25 };
@ -90,6 +86,6 @@ private:
{ {
intermediate = G * (*in - state); intermediate = G * (*in - state);
*out = *in - intermediate - state; *out = *in - intermediate - state;
state += 2*intermediate; state += 2 * intermediate;
} }
}; };

View file

@ -1,15 +1,14 @@
#include "Opcode.h" #include "Opcode.h"
sfz::Opcode::Opcode(std::string_view inputOpcode, std::string_view inputValue) sfz::Opcode::Opcode(std::string_view inputOpcode, std::string_view inputValue)
:opcode(inputOpcode), value(inputValue) : opcode(inputOpcode)
, value(inputValue)
{ {
if (const auto lastCharIndex = inputOpcode.find_last_not_of("1234567890"); lastCharIndex != inputOpcode.npos) if (const auto lastCharIndex = inputOpcode.find_last_not_of("1234567890"); lastCharIndex != inputOpcode.npos) {
{
int returnedValue; int returnedValue;
std::string_view parameterView = inputOpcode; std::string_view parameterView = inputOpcode;
parameterView.remove_prefix(lastCharIndex + 1); parameterView.remove_prefix(lastCharIndex + 1);
if (absl::SimpleAtoi(parameterView, &returnedValue)) if (absl::SimpleAtoi(parameterView, &returnedValue)) {
{
parameter = returnedValue; parameter = returnedValue;
opcode.remove_suffix(opcode.size() - lastCharIndex - 1); opcode.remove_suffix(opcode.size() - lastCharIndex - 1);
} }

View file

@ -1,32 +1,29 @@
#pragma once #pragma once
#include "Helpers.h"
#include "SfzHelpers.h"
#include "Defaults.h" #include "Defaults.h"
#include "Helpers.h"
#include "Range.h" #include "Range.h"
#include <string_view> #include "SfzHelpers.h"
#include <optional> #include <optional>
#include <string_view>
// charconv support is still sketchy with clang/gcc so we use abseil's numbers // charconv support is still sketchy with clang/gcc so we use abseil's numbers
#include "absl/strings/numbers.h" #include "absl/strings/numbers.h"
namespace sfz namespace sfz {
{ struct Opcode {
struct Opcode
{
Opcode() = delete; Opcode() = delete;
Opcode(std::string_view inputOpcode, std::string_view inputValue); Opcode(std::string_view inputOpcode, std::string_view inputValue);
std::string_view opcode{}; std::string_view opcode {};
std::string_view value{}; std::string_view value {};
// This is to handle the integer parameter of some opcodes // This is to handle the integer parameter of some opcodes
std::optional<uint8_t> parameter; std::optional<uint8_t> parameter;
LEAK_DETECTOR(Opcode); LEAK_DETECTOR(Opcode);
}; };
template<class ValueType> template <class ValueType>
inline std::optional<ValueType> readOpcode(std::string_view value, const Range<ValueType>& validRange) inline std::optional<ValueType> readOpcode(std::string_view value, const Range<ValueType>& validRange)
{ {
if constexpr(std::is_integral<ValueType>::value) if constexpr (std::is_integral<ValueType>::value) {
{
int64_t returnedValue; int64_t returnedValue;
if (!absl::SimpleAtoi(value, &returnedValue)) if (!absl::SimpleAtoi(value, &returnedValue))
return {}; return {};
@ -37,9 +34,7 @@ inline std::optional<ValueType> readOpcode(std::string_view value, const Range<V
returnedValue = std::numeric_limits<ValueType>::min(); returnedValue = std::numeric_limits<ValueType>::min();
return validRange.clamp(static_cast<ValueType>(returnedValue)); return validRange.clamp(static_cast<ValueType>(returnedValue));
} } else {
else
{
float returnedValue; float returnedValue;
if (!absl::SimpleAtof(value, &returnedValue)) if (!absl::SimpleAtof(value, &returnedValue))
return std::nullopt; return std::nullopt;
@ -48,7 +43,7 @@ inline std::optional<ValueType> readOpcode(std::string_view value, const Range<V
} }
} }
template<class ValueType> template <class ValueType>
inline void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range<ValueType>& validRange) inline void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range<ValueType>& validRange)
{ {
auto value = readOpcode(opcode.value, validRange); auto value = readOpcode(opcode.value, validRange);
@ -58,7 +53,7 @@ inline void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Ra
target = *value; target = *value;
} }
template<class ValueType> template <class ValueType>
inline void setValueFromOpcode(const Opcode& opcode, std::optional<ValueType>& target, const Range<ValueType>& validRange) inline void setValueFromOpcode(const Opcode& opcode, std::optional<ValueType>& target, const Range<ValueType>& validRange)
{ {
auto value = readOpcode(opcode.value, validRange); auto value = readOpcode(opcode.value, validRange);
@ -68,7 +63,7 @@ inline void setValueFromOpcode(const Opcode& opcode, std::optional<ValueType>& t
target = *value; target = *value;
} }
template<class ValueType> template <class ValueType>
inline void setRangeEndFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange) inline void setRangeEndFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange)
{ {
auto value = readOpcode(opcode.value, validRange); auto value = readOpcode(opcode.value, validRange);
@ -78,7 +73,7 @@ inline void setRangeEndFromOpcode(const Opcode& opcode, Range<ValueType>& target
target.setEnd(*value); target.setEnd(*value);
} }
template<class ValueType> template <class ValueType>
inline void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange) inline void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& target, const Range<ValueType>& validRange)
{ {
auto value = readOpcode(opcode.value, validRange); auto value = readOpcode(opcode.value, validRange);
@ -88,11 +83,11 @@ inline void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& targ
target.setStart(*value); target.setStart(*value);
} }
template<class ValueType> template <class ValueType>
inline void setCCPairFromOpcode(const Opcode& opcode, std::optional<CCValuePair>& target, const Range<ValueType>& validRange) inline void setCCPairFromOpcode(const Opcode& opcode, std::optional<CCValuePair>& target, const Range<ValueType>& validRange)
{ {
auto value = readOpcode(opcode.value, validRange); auto value = readOpcode(opcode.value, validRange);
if (value && opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) if (value && opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter))
target = std::make_pair(*opcode.parameter, *value); target = std::make_pair(*opcode.parameter, *value);
else else
target = {}; target = {};

View file

@ -1,143 +1,130 @@
#include "Parser.h" #include "Parser.h"
#include "Helpers.h"
#include "Globals.h" #include "Globals.h"
#include "Helpers.h"
#include "absl/strings/str_join.h" #include "absl/strings/str_join.h"
#include <fstream>
#include <algorithm> #include <algorithm>
#include <fstream>
using svregex_iterator = std::regex_iterator<std::string_view::const_iterator>; using svregex_iterator = std::regex_iterator<std::string_view::const_iterator>;
using svmatch_results = std::match_results<std::string_view::const_iterator>; using svmatch_results = std::match_results<std::string_view::const_iterator>;
void removeCommentOnLine(std::string_view &line) void removeCommentOnLine(std::string_view& line)
{ {
if (auto position = line.find("//"); position != line.npos) if (auto position = line.find("//"); position != line.npos)
line.remove_suffix(line.size() - position); line.remove_suffix(line.size() - position);
} }
bool sfz::Parser::loadSfzFile(const std::filesystem::path &file) bool sfz::Parser::loadSfzFile(const std::filesystem::path& file)
{ {
const auto sfzFile = file.is_absolute() ? file : rootDirectory / file; const auto sfzFile = file.is_absolute() ? file : rootDirectory / file;
if (!std::filesystem::exists(sfzFile)) if (!std::filesystem::exists(sfzFile))
return false; return false;
rootDirectory = file.parent_path(); rootDirectory = file.parent_path();
std::vector<std::string> lines; std::vector<std::string> lines;
readSfzFile(file, lines); readSfzFile(file, lines);
aggregatedContent = absl::StrJoin(lines, " "); aggregatedContent = absl::StrJoin(lines, " ");
const std::string_view aggregatedView{aggregatedContent}; const std::string_view aggregatedView { aggregatedContent };
svregex_iterator headerIterator(aggregatedView.cbegin(), aggregatedView.cend(), sfz::Regexes::headers); svregex_iterator headerIterator(aggregatedView.cbegin(), aggregatedView.cend(), sfz::Regexes::headers);
const auto regexEnd = svregex_iterator(); const auto regexEnd = svregex_iterator();
std::vector<Opcode> currentMembers; std::vector<Opcode> currentMembers;
for (; headerIterator != regexEnd; ++headerIterator) for (; headerIterator != regexEnd; ++headerIterator) {
{ svmatch_results headerMatch = *headerIterator;
svmatch_results headerMatch = *headerIterator;
// Can't use uniform initialization here because it generates narrowing conversions // Can't use uniform initialization here because it generates narrowing conversions
const std::string_view header(&*headerMatch[1].first, headerMatch[1].length()); const std::string_view header(&*headerMatch[1].first, headerMatch[1].length());
const std::string_view members(&*headerMatch[2].first, headerMatch[2].length()); const std::string_view members(&*headerMatch[2].first, headerMatch[2].length());
auto paramIterator = svregex_iterator(members.cbegin(), members.cend(), sfz::Regexes::members); auto paramIterator = svregex_iterator(members.cbegin(), members.cend(), sfz::Regexes::members);
// Store or handle members // Store or handle members
for (; paramIterator != regexEnd; ++paramIterator) for (; paramIterator != regexEnd; ++paramIterator) {
{ const svmatch_results paramMatch = *paramIterator;
const svmatch_results paramMatch = *paramIterator; const std::string_view opcode(&*paramMatch[1].first, paramMatch[1].length());
const std::string_view opcode(&*paramMatch[1].first, paramMatch[1].length()); const std::string_view value(&*paramMatch[2].first, paramMatch[2].length());
const std::string_view value(&*paramMatch[2].first, paramMatch[2].length()); currentMembers.emplace_back(opcode, value);
currentMembers.emplace_back(opcode, value); }
} callback(header, currentMembers);
callback(header, currentMembers); currentMembers.clear();
currentMembers.clear(); }
}
return true; return true;
} }
void sfz::Parser::readSfzFile(const std::filesystem::path &fileName, std::vector<std::string> &lines) noexcept void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector<std::string>& lines) noexcept
{ {
std::ifstream fileStream(fileName.c_str()); std::ifstream fileStream(fileName.c_str());
if (!fileStream) if (!fileStream)
return; return;
// spdlog::info("Including file {}", fileName.string()); // spdlog::info("Including file {}", fileName.string());
svmatch_results includeMatch; svmatch_results includeMatch;
svmatch_results defineMatch; svmatch_results defineMatch;
std::string tmpString; std::string tmpString;
while (std::getline(fileStream, tmpString)) while (std::getline(fileStream, tmpString)) {
{ std::string_view tmpView { tmpString };
std::string_view tmpView{tmpString};
removeCommentOnLine(tmpView); removeCommentOnLine(tmpView);
trimInPlace(tmpView); trimInPlace(tmpView);
if (tmpView.empty()) if (tmpView.empty())
continue; continue;
// New #include // New #include
if (std::regex_search(tmpView.begin(), tmpView.end(), includeMatch, sfz::Regexes::includes)) if (std::regex_search(tmpView.begin(), tmpView.end(), includeMatch, sfz::Regexes::includes)) {
{ auto includePath = includeMatch.str(1);
auto includePath = includeMatch.str(1); std::replace(includePath.begin(), includePath.end(), '\\', '/');
std::replace(includePath.begin(), includePath.end(), '\\', '/'); const auto newFile = rootDirectory / includePath;
const auto newFile = rootDirectory / includePath; auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile); if (std::filesystem::exists(newFile)) {
if (std::filesystem::exists(newFile)) if (alreadyIncluded == includedFiles.end()) {
{ includedFiles.push_back(newFile);
if (alreadyIncluded == includedFiles.end()) readSfzFile(newFile, lines);
{ } else if (!recursiveIncludeGuard) {
includedFiles.push_back(newFile); readSfzFile(newFile, lines);
readSfzFile(newFile, lines); }
} }
else if (!recursiveIncludeGuard) continue;
{ }
readSfzFile(newFile, lines);
}
}
continue;
}
// New #define // New #define
if (std::regex_search(tmpView.begin(), tmpView.end(), defineMatch, sfz::Regexes::defines)) if (std::regex_search(tmpView.begin(), tmpView.end(), defineMatch, sfz::Regexes::defines)) {
{ defines[defineMatch.str(1)] = defineMatch.str(2);
defines[defineMatch.str(1)] = defineMatch.str(2); continue;
continue; }
}
// Replace defined variables starting with $ // Replace defined variables starting with $
std::string newString; std::string newString;
newString.reserve(tmpView.length()); newString.reserve(tmpView.length());
std::string::size_type lastPos = 0; std::string::size_type lastPos = 0;
std::string::size_type findPos = tmpView.find(sfz::config::defineCharacter, lastPos); std::string::size_type findPos = tmpView.find(sfz::config::defineCharacter, lastPos);
while (findPos < tmpView.npos) while (findPos < tmpView.npos) {
{ newString.append(tmpView, lastPos, findPos - lastPos);
newString.append(tmpView, lastPos, findPos - lastPos);
for (auto &definePair : defines) for (auto& definePair : defines) {
{ std::string_view candidate = tmpView.substr(findPos, definePair.first.length());
std::string_view candidate = tmpView.substr(findPos, definePair.first.length()); if (candidate == definePair.first) {
if (candidate == definePair.first) newString += definePair.second;
{ lastPos = findPos + definePair.first.length();
newString += definePair.second; break;
lastPos = findPos + definePair.first.length(); }
break; }
}
}
if (lastPos <= findPos) if (lastPos <= findPos) {
{ newString += sfz::config::defineCharacter;
newString += sfz::config::defineCharacter; lastPos = findPos + 1;
lastPos = findPos + 1; }
}
findPos = tmpView.find(sfz::config::defineCharacter, lastPos); findPos = tmpView.find(sfz::config::defineCharacter, lastPos);
} }
// Copy the rest of the string // Copy the rest of the string
newString += tmpView.substr(lastPos); newString += tmpView.substr(lastPos);
lines.push_back(std::move(newString)); lines.push_back(std::move(newString));
} }
} }

View file

@ -1,39 +1,38 @@
#pragma once #pragma once
#include "Opcode.h" #include "Opcode.h"
#include <filesystem> #include <filesystem>
#include <regex>
#include <map> #include <map>
#include <regex>
#include <string> #include <string>
#include <vector>
#include <string_view> #include <string_view>
#include <vector>
namespace sfz namespace sfz {
{ namespace Regexes {
namespace Regexes
{
inline static std::regex includes { R"V(#include\s*"(.*?)".*$)V", std::regex::optimize }; inline static std::regex includes { R"V(#include\s*"(.*?)".*$)V", std::regex::optimize };
inline static std::regex defines { R"(#define\s*(\$[a-zA-Z0-9]+)\s+([a-zA-Z0-9]+)(?=\s|$))", std::regex::optimize }; inline static std::regex defines { R"(#define\s*(\$[a-zA-Z0-9]+)\s+([a-zA-Z0-9]+)(?=\s|$))", std::regex::optimize };
inline static std::regex headers { R"(<(.*?)>(.*?)(?=<|$))", std::regex::optimize }; inline static std::regex headers { R"(<(.*?)>(.*?)(?=<|$))", std::regex::optimize };
inline static std::regex members { R"(([a-zA-Z0-9_]+)=([a-zA-Z0-9-_#.\/\s\\\(\),\*]+)(?![a-zA-Z0-9_]*=))", std::regex::optimize }; inline static std::regex members { R"(([a-zA-Z0-9_]+)=([a-zA-Z0-9-_#.\/\s\\\(\),\*]+)(?![a-zA-Z0-9_]*=))", std::regex::optimize };
inline static std::regex opcodeParameters{ R"(([a-zA-Z0-9_]+?)([0-9]+)$)", std::regex::optimize }; inline static std::regex opcodeParameters { R"(([a-zA-Z0-9_]+?)([0-9]+)$)", std::regex::optimize };
} }
class Parser class Parser {
{
public: public:
virtual bool loadSfzFile(const std::filesystem::path& file); virtual bool loadSfzFile(const std::filesystem::path& file);
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; } const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; } const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; }
void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; } void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; }
void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; } void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; }
protected: protected:
virtual void callback(std::string_view header, const std::vector<Opcode>& members) = 0; virtual void callback(std::string_view header, const std::vector<Opcode>& members) = 0;
std::filesystem::path rootDirectory { std::filesystem::current_path() }; std::filesystem::path rootDirectory { std::filesystem::current_path() };
private: private:
bool recursiveIncludeGuard { false }; bool recursiveIncludeGuard { false };
std::map<std::string, std::string> defines; std::map<std::string, std::string> defines;
std::vector<std::filesystem::path> includedFiles; std::vector<std::filesystem::path> includedFiles;
std::string aggregatedContent { }; std::string aggregatedContent {};
void readSfzFile(const std::filesystem::path& fileName, std::vector<std::string>& lines) noexcept; void readSfzFile(const std::filesystem::path& fileName, std::vector<std::string>& lines) noexcept;
}; };

View file

@ -1,12 +1,12 @@
#pragma once #pragma once
#include <type_traits>
#include <initializer_list>
#include <algorithm> #include <algorithm>
#include <initializer_list>
#include <type_traits>
template<class Type> template <class Type>
class Range class Range {
{
static_assert(std::is_arithmetic<Type>::value, "The Type should be arithmetic"); static_assert(std::is_arithmetic<Type>::value, "The Type should be arithmetic");
public: public:
constexpr Range() = default; constexpr Range() = default;
// constexpr Range(std::initializer_list<Type> list) // constexpr Range(std::initializer_list<Type> list)
@ -25,7 +25,10 @@ public:
// } // }
// } // }
constexpr Range(Type start, Type end) noexcept constexpr Range(Type start, Type end) noexcept
: _start(start), _end(std::max(start, end)) {} : _start(start)
, _end(std::max(start, end))
{
}
~Range() = default; ~Range() = default;
Type getStart() const noexcept { return _start; } Type getStart() const noexcept { return _start; }
Type getEnd() const noexcept { return _end; } Type getEnd() const noexcept { return _end; }
@ -48,7 +51,7 @@ public:
Type clamp(Type value) const noexcept { return std::clamp(value, _start, _end); } Type clamp(Type value) const noexcept { return std::clamp(value, _start, _end); }
bool containsWithEnd(Type value) const noexcept { return (value >= _start && value <= _end); } bool containsWithEnd(Type value) const noexcept { return (value >= _start && value <= _end); }
bool contains(Type value) const noexcept { return (value >= _start && value < _end); } bool contains(Type value) const noexcept { return (value >= _start && value < _end); }
void shrinkIfSmaller( Type start, Type end) void shrinkIfSmaller(Type start, Type end)
{ {
if (start > end) if (start > end)
std::swap(start, end); std::swap(start, end);
@ -59,24 +62,25 @@ public:
if (end < _end) if (end < _end)
_end = end; _end = end;
} }
private: private:
Type _start { static_cast<Type>(0.0) }; Type _start { static_cast<Type>(0.0) };
Type _end { static_cast<Type>(0.0) }; Type _end { static_cast<Type>(0.0) };
}; };
template<class Type> template <class Type>
bool operator==(const Range<Type>& lhs, const Range<Type>& rhs) bool operator==(const Range<Type>& lhs, const Range<Type>& rhs)
{ {
return (lhs.getStart() == rhs.getStart()) && (lhs.getEnd() == rhs.getEnd()); return (lhs.getStart() == rhs.getStart()) && (lhs.getEnd() == rhs.getEnd());
} }
template<class Type> template <class Type>
bool operator==(const Range<Type>& lhs, const std::pair<Type, Type>& rhs) bool operator==(const Range<Type>& lhs, const std::pair<Type, Type>& rhs)
{ {
return (lhs.getStart() == rhs.first) && (lhs.getEnd() == rhs.second); return (lhs.getStart() == rhs.first) && (lhs.getEnd() == rhs.second);
} }
template<class Type> template <class Type>
bool operator==(const std::pair<Type, Type>& lhs, const Range<Type>& rhs) bool operator==(const std::pair<Type, Type>& lhs, const Range<Type>& rhs)
{ {
return rhs == lhs; return rhs == lhs;

View file

@ -4,13 +4,12 @@
#include <bits/stdint-uintn.h> #include <bits/stdint-uintn.h>
#include <random> #include <random>
bool sfz::Region::parseOpcode(const Opcode &opcode) bool sfz::Region::parseOpcode(const Opcode& opcode)
{ {
switch (hash(opcode.opcode)) switch (hash(opcode.opcode)) {
{
// Sound source: sample playback // Sound source: sample playback
case hash("sample"): case hash("sample"):
sample = absl::StrReplaceAll(trim(opcode.value), {{"\\", "/"}}); sample = absl::StrReplaceAll(trim(opcode.value), { { "\\", "/" } });
break; break;
case hash("delay"): case hash("delay"):
setValueFromOpcode(opcode, delay, Default::delayRange); setValueFromOpcode(opcode, delay, Default::delayRange);
@ -34,8 +33,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
break; break;
case hash("loopmode"): case hash("loopmode"):
case hash("loop_mode"): case hash("loop_mode"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("no_loop"): case hash("no_loop"):
loopMode = SfzLoopMode::no_loop; loopMode = SfzLoopMode::no_loop;
break; break;
@ -70,8 +68,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
setValueFromOpcode(opcode, offBy, Default::groupRange); setValueFromOpcode(opcode, offBy, Default::groupRange);
break; break;
case hash("off_mode"): case hash("off_mode"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("fast"): case hash("fast"):
offMode = SfzOffMode::fast; offMode = SfzOffMode::fast;
break; break;
@ -115,8 +112,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
setRangeEndFromOpcode(opcode, bendRange, Default::bendRange); setRangeEndFromOpcode(opcode, bendRange, Default::bendRange);
break; break;
case hash("locc"): case hash("locc"):
if (opcode.parameter) if (opcode.parameter) {
{
setRangeStartFromOpcode(opcode, ccConditions[*opcode.parameter], Default::ccRange); setRangeStartFromOpcode(opcode, ccConditions[*opcode.parameter], Default::ccRange);
} }
break; break;
@ -146,8 +142,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
previousKeySwitched = false; previousKeySwitched = false;
break; break;
case hash("sw_vel"): case hash("sw_vel"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("current"): case hash("current"):
velocityOverride = SfzVelocityOverride::current; velocityOverride = SfzVelocityOverride::current;
break; break;
@ -186,8 +181,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
break; break;
// Region logic: triggers // Region logic: triggers
case hash("trigger"): case hash("trigger"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("attack"): case hash("attack"):
trigger = SfzTrigger::attack; trigger = SfzTrigger::attack;
break; break;
@ -263,8 +257,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
gainDistribution.param(std::uniform_real_distribution<float>::param_type(-ampRandom, ampRandom)); gainDistribution.param(std::uniform_real_distribution<float>::param_type(-ampRandom, ampRandom));
break; break;
case hash("amp_velcurve_"): case hash("amp_velcurve_"):
if (opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) if (opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) {
{
if (auto value = readOpcode(opcode.value, Default::ampVelcurveRange); value) if (auto value = readOpcode(opcode.value, Default::ampVelcurveRange); value)
velocityPoints.emplace_back(*opcode.parameter, *value); velocityPoints.emplace_back(*opcode.parameter, *value);
} }
@ -294,8 +287,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange); setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
break; break;
case hash("xf_keycurve"): case hash("xf_keycurve"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("power"): case hash("power"):
crossfadeKeyCurve = SfzCrossfadeCurve::power; crossfadeKeyCurve = SfzCrossfadeCurve::power;
break; break;
@ -307,8 +299,7 @@ bool sfz::Region::parseOpcode(const Opcode &opcode)
} }
break; break;
case hash("xf_velcurve"): case hash("xf_velcurve"):
switch (hash(opcode.value)) switch (hash(opcode.value)) {
{
case hash("power"): case hash("power"):
crossfadeVelCurve = SfzCrossfadeCurve::power; crossfadeVelCurve = SfzCrossfadeCurve::power;
break; break;
@ -423,10 +414,8 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
if (!chanOk) if (!chanOk)
return false; return false;
if (keyswitchRange.containsWithEnd(noteNumber)) if (keyswitchRange.containsWithEnd(noteNumber)) {
{ if (keyswitch) {
if (keyswitch)
{
if (*keyswitch == noteNumber) if (*keyswitch == noteNumber)
keySwitched = true; keySwitched = true;
else else
@ -441,8 +430,7 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
} }
const bool keyOk = keyRange.containsWithEnd(noteNumber); const bool keyOk = keyRange.containsWithEnd(noteNumber);
if (keyOk) if (keyOk) {
{
// Update the number of notes playing for the region // Update the number of notes playing for the region
activeNotesInRange++; activeNotesInRange++;
@ -457,8 +445,7 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
if (trigger == SfzTrigger::release_key || velocityOverride == SfzVelocityOverride::previous) if (trigger == SfzTrigger::release_key || velocityOverride == SfzVelocityOverride::previous)
lastNoteVelocities[noteNumber] = velocity; lastNoteVelocities[noteNumber] = velocity;
if (previousNote) if (previousNote) {
{
if (*previousNote == noteNumber) if (*previousNote == noteNumber)
previousKeySwitched = true; previousKeySwitched = true;
else else
@ -481,14 +468,13 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
return keyOk && velOk && chanOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote); return keyOk && velOk && chanOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
} }
bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity[[maybe_unused]], float randValue) bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity [[maybe_unused]], float randValue)
{ {
const bool chanOk = channelRange.containsWithEnd(channel); const bool chanOk = channelRange.containsWithEnd(channel);
if (!chanOk) if (!chanOk)
return false; return false;
if (keyswitchRange.containsWithEnd(noteNumber)) if (keyswitchRange.containsWithEnd(noteNumber)) {
{
if (keyswitchDown && *keyswitchDown == noteNumber) if (keyswitchDown && *keyswitchDown == noteNumber)
keySwitched = false; keySwitched = false;

View file

@ -1,21 +1,19 @@
#pragma once #pragma once
#include "Helpers.h"
#include <bits/stdint-uintn.h>
#include <optional>
#include <vector>
#include <string>
#include "Opcode.h"
#include "EGDescription.h"
#include "StereoBuffer.h"
#include "Defaults.h"
#include "CCMap.h" #include "CCMap.h"
#include "Defaults.h"
#include "EGDescription.h"
#include "Helpers.h"
#include "Opcode.h"
#include "StereoBuffer.h"
#include <bits/stdint-uintn.h>
#include <bitset> #include <bitset>
#include <optional>
#include <random> #include <random>
#include <string>
#include <vector>
namespace sfz namespace sfz {
{ struct Region {
struct Region
{
Region() Region()
{ {
ccSwitched.set(); ccSwitched.set();
@ -72,14 +70,14 @@ struct Region
SfzOffMode offMode { Default::offMode }; // off_mode SfzOffMode offMode { Default::offMode }; // off_mode
// Region logic: key mapping // Region logic: key mapping
Range<uint8_t> keyRange{ Default::keyRange }; //lokey, hikey and key Range<uint8_t> keyRange { Default::keyRange }; //lokey, hikey and key
Range<uint8_t> velocityRange{ Default::velocityRange }; // hivel and lovel Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
// Region logic: MIDI conditions // Region logic: MIDI conditions
Range<uint8_t> channelRange{ Default::channelRange }; //lochan and hichan Range<uint8_t> channelRange { Default::channelRange }; //lochan and hichan
Range<int> bendRange{ Default::bendRange }; // hibend and lobend Range<int> bendRange { Default::bendRange }; // hibend and lobend
CCMap<Range<uint8_t>> ccConditions { Default::ccRange }; CCMap<Range<uint8_t>> ccConditions { Default::ccRange };
Range<uint8_t> keyswitchRange{ Default::keyRange }; // sw_hikey and sw_lokey Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
std::optional<uint8_t> keyswitch {}; // sw_last std::optional<uint8_t> keyswitch {}; // sw_last
std::optional<uint8_t> keyswitchUp {}; // sw_up std::optional<uint8_t> keyswitchUp {}; // sw_up
std::optional<uint8_t> keyswitchDown {}; // sw_down std::optional<uint8_t> keyswitchDown {}; // sw_down
@ -87,9 +85,9 @@ struct Region
SfzVelocityOverride velocityOverride { Default::velocityOverride }; // sw_vel SfzVelocityOverride velocityOverride { Default::velocityOverride }; // sw_vel
// Region logic: internal conditions // Region logic: internal conditions
Range<uint8_t> aftertouchRange{ Default::aftertouchRange }; // hichanaft and lochanaft Range<uint8_t> aftertouchRange { Default::aftertouchRange }; // hichanaft and lochanaft
Range<float> bpmRange{ Default::bpmRange }; // hibpm and lobpm Range<float> bpmRange { Default::bpmRange }; // hibpm and lobpm
Range<float> randRange{ Default::randRange }; // hirand and lorand Range<float> randRange { Default::randRange }; // hirand and lorand
uint8_t sequenceLength { Default::sequenceLength }; // seq_length uint8_t sequenceLength { Default::sequenceLength }; // seq_length
uint8_t sequencePosition { Default::sequencePosition }; // seq_position uint8_t sequencePosition { Default::sequencePosition }; // seq_position
@ -122,10 +120,10 @@ struct Region
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
// Performance parameters: pitch // Performance parameters: pitch
uint8_t pitchKeycenter{Default::pitchKeycenter}; // pitch_keycenter uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter
int pitchKeytrack{ Default::pitchKeytrack }; // pitch_keytrack int pitchKeytrack { Default::pitchKeytrack }; // pitch_keytrack
int pitchRandom{ Default::pitchRandom }; // pitch_random int pitchRandom { Default::pitchRandom }; // pitch_random
int pitchVeltrack{ Default::pitchVeltrack }; // pitch_veltrack int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack
int transpose { Default::transpose }; // transpose int transpose { Default::transpose }; // transpose
int tune { Default::tune }; // tune int tune { Default::tune }; // tune
@ -137,6 +135,7 @@ struct Region
double sampleRate { config::defaultSampleRate }; double sampleRate { config::defaultSampleRate };
int numChannels { 1 }; int numChannels { 1 };
std::shared_ptr<StereoBuffer<float>> preloadedData { nullptr }; std::shared_ptr<StereoBuffer<float>> preloadedData { nullptr };
private: private:
bool keySwitched { true }; bool keySwitched { true };
bool previousKeySwitched { true }; bool previousKeySwitched { true };

View file

@ -1,79 +1,79 @@
#include "SIMDHelpers.h"
#include "Helpers.h" #include "Helpers.h"
#include "SIMDHelpers.h"
template<> template <>
void readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept void readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept
{ {
readInterleaved<float, false>(input, outputLeft, outputRight); readInterleaved<float, false>(input, outputLeft, outputRight);
} }
template<> template <>
void writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept void writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept
{ {
writeInterleaved<float, false>(inputLeft, inputRight, output); writeInterleaved<float, false>(inputLeft, inputRight, output);
} }
template<> template <>
void fill<float, true>(absl::Span<float> output, float value) noexcept void fill<float, true>(absl::Span<float> output, float value) noexcept
{ {
fill<float, false>(output, value); fill<float, false>(output, value);
} }
template<> template <>
void exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept void exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
exp<float, false>(input, output); exp<float, false>(input, output);
} }
template<> template <>
void log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept void log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
log<float, false>(input, output); log<float, false>(input, output);
} }
template<> template <>
void sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept void sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
sin<float, false>(input, output); sin<float, false>(input, output);
} }
template<> template <>
void cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept void cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
cos<float, false>(input, output); cos<float, false>(input, output);
} }
template<> template <>
void applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept void applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
applyGain<float, false>(gain, input, output); applyGain<float, false>(gain, input, output);
} }
template<> template <>
void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
applyGain<float, false>(gain, input, output); applyGain<float, false>(gain, input, output);
} }
template<> template <>
void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept
{ {
loopingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart); loopingSFZIndex<float, false>(jumps, leftCoeff, rightCoeff, indices, floatIndex, loopEnd, loopStart);
} }
template<> template <>
float 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
{ {
return linearRamp<float, false>(output, start, step); return linearRamp<float, false>(output, start, step);
} }
template<> template <>
float 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
{ {
return multiplicativeRamp<float, false>(output, start, step); return multiplicativeRamp<float, false>(output, start, step);
} }
template<> template <>
void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept
{ {
add<float, false>(input, output); add<float, false>(input, output);

View file

@ -1,18 +1,18 @@
#pragma once #pragma once
#include "Globals.h" #include "Globals.h"
#include <absl/types/span.h>
#include <absl/algorithm/container.h>
#include "Helpers.h" #include "Helpers.h"
#include <absl/algorithm/container.h>
#include <absl/types/span.h>
#include <cmath> #include <cmath>
template<class T> template <class T>
inline void snippetRead(const T*& input, T*& outputLeft, T*& outputRight) inline void snippetRead(const T*& input, T*& outputLeft, T*& outputRight)
{ {
*outputLeft++ = *input++; *outputLeft++ = *input++;
*outputRight++ = *input++; *outputRight++ = *input++;
} }
template<class T, bool SIMD=SIMDConfig::readInterleaved> template <class T, bool SIMD = SIMDConfig::readInterleaved>
void readInterleaved(absl::Span<const T> input, absl::Span<T> outputLeft, absl::Span<T> outputRight) noexcept void readInterleaved(absl::Span<const T> input, absl::Span<T> outputLeft, absl::Span<T> outputRight) noexcept
{ {
// The size of the output is not big enough for the input... // The size of the output is not big enough for the input...
@ -26,14 +26,14 @@ void readInterleaved(absl::Span<const T> input, absl::Span<T> outputLeft, absl::
snippetRead<T>(in, lOut, rOut); snippetRead<T>(in, lOut, rOut);
} }
template<class T> template <class T>
inline void snippetWrite(T*& output, const T*& inputLeft, const T*& inputRight) inline void snippetWrite(T*& output, const T*& inputLeft, const T*& inputRight)
{ {
*output++ = *inputLeft++; *output++ = *inputLeft++;
*output++ = *inputRight++; *output++ = *inputRight++;
} }
template<class T, bool SIMD=SIMDConfig::writeInterleaved> template <class T, bool SIMD = SIMDConfig::writeInterleaved>
void writeInterleaved(absl::Span<const T> inputLeft, absl::Span<const T> inputRight, absl::Span<T> output) noexcept void writeInterleaved(absl::Span<const T> inputLeft, absl::Span<const T> inputRight, absl::Span<T> output) noexcept
{ {
ASSERT(inputLeft.size() <= output.size() / 2); ASSERT(inputLeft.size() <= output.size() / 2);
@ -47,21 +47,21 @@ void writeInterleaved(absl::Span<const T> inputLeft, absl::Span<const T> inputRi
} }
// Specializations // Specializations
template<> template <>
void writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept; void writeInterleaved<float, true>(absl::Span<const float> inputLeft, absl::Span<const float> inputRight, absl::Span<float> output) noexcept;
template<> template <>
void readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept; void readInterleaved<float, true>(absl::Span<const float> input, absl::Span<float> outputLeft, absl::Span<float> outputRight) noexcept;
template<class T, bool SIMD=SIMDConfig::fill> template <class T, bool SIMD = SIMDConfig::fill>
void fill(absl::Span<T> output, T value) noexcept void fill(absl::Span<T> output, T value) noexcept
{ {
absl::c_fill(output, value); absl::c_fill(output, value);
} }
template<> template <>
void fill<float, true>(absl::Span<float> output, float value) noexcept; void fill<float, true>(absl::Span<float> output, float value) noexcept;
template<class Type, bool SIMD=SIMDConfig::mathfuns> template <class Type, bool SIMD = SIMDConfig::mathfuns>
void exp(absl::Span<const Type> input, absl::Span<Type> output) noexcept void exp(absl::Span<const Type> input, absl::Span<Type> output) noexcept
{ {
ASSERT(output.size() >= input.size()); ASSERT(output.size() >= input.size());
@ -70,10 +70,10 @@ void exp(absl::Span<const Type> input, absl::Span<Type> output) noexcept
output[i] = std::exp(input[i]); output[i] = std::exp(input[i]);
} }
template<> template <>
void exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept; void exp<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;
template<class Type, bool SIMD=SIMDConfig::mathfuns> template <class Type, bool SIMD = SIMDConfig::mathfuns>
void log(absl::Span<const Type> input, absl::Span<Type> output) noexcept void log(absl::Span<const Type> input, absl::Span<Type> output) noexcept
{ {
ASSERT(output.size() >= input.size()); ASSERT(output.size() >= input.size());
@ -82,10 +82,10 @@ void log(absl::Span<const Type> input, absl::Span<Type> output) noexcept
output[i] = std::log(input[i]); output[i] = std::log(input[i]);
} }
template<> template <>
void log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept; void log<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;
template<class Type, bool SIMD=SIMDConfig::mathfuns> template <class Type, bool SIMD = SIMDConfig::mathfuns>
void sin(absl::Span<const Type> input, absl::Span<Type> output) noexcept void sin(absl::Span<const Type> input, absl::Span<Type> output) noexcept
{ {
ASSERT(output.size() >= input.size()); ASSERT(output.size() >= input.size());
@ -94,10 +94,10 @@ void sin(absl::Span<const Type> input, absl::Span<Type> output) noexcept
output[i] = std::sin(input[i]); output[i] = std::sin(input[i]);
} }
template<> template <>
void sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept; void sin<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;
template<class Type, bool SIMD=SIMDConfig::mathfuns> template <class Type, bool SIMD = SIMDConfig::mathfuns>
void cos(absl::Span<const Type> input, absl::Span<Type> output) noexcept void cos(absl::Span<const Type> input, absl::Span<Type> output) noexcept
{ {
ASSERT(output.size() >= input.size()); ASSERT(output.size() >= input.size());
@ -106,10 +106,10 @@ void cos(absl::Span<const Type> input, absl::Span<Type> output) noexcept
output[i] = std::cos(input[i]); output[i] = std::cos(input[i]);
} }
template<> template <>
void cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept; void cos<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;
template<class T> template <class T>
inline void snippetLoopingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff, int*& index, T& floatIndex, T loopEnd, T loopStart) inline void snippetLoopingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff, int*& index, T& floatIndex, T loopEnd, T loopStart)
{ {
floatIndex += *jump; floatIndex += *jump;
@ -124,7 +124,7 @@ inline void snippetLoopingIndex(const T*& jump, T*& leftCoeff, T*& rightCoeff, i
jump++; jump++;
} }
template<class T, bool SIMD=SIMDConfig::loopingSFZIndex> template <class T, bool SIMD = SIMDConfig::loopingSFZIndex>
void loopingSFZIndex(absl::Span<const T> jumps, absl::Span<T> leftCoeffs, absl::Span<T> rightCoeffs, absl::Span<int> indices, T floatIndex, T loopEnd, T loopStart) noexcept void loopingSFZIndex(absl::Span<const T> jumps, absl::Span<T> leftCoeffs, absl::Span<T> rightCoeffs, absl::Span<int> indices, T floatIndex, T loopEnd, T loopStart) noexcept
{ {
ASSERT(indices.size() >= jumps.size()); ASSERT(indices.size() >= jumps.size());
@ -142,16 +142,16 @@ void loopingSFZIndex(absl::Span<const T> jumps, absl::Span<T> leftCoeffs, absl::
snippetLoopingIndex<T>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart); snippetLoopingIndex<T>(jump, leftCoeff, rightCoeff, index, floatIndex, loopEnd, loopStart);
} }
template<> template <>
void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept; void loopingSFZIndex<float, true>(absl::Span<const float> jumps, absl::Span<float> leftCoeff, absl::Span<float> rightCoeff, absl::Span<int> indices, float floatIndex, float loopEnd, float loopStart) noexcept;
template<class T> template <class T>
inline void snippetGain(T gain, const T*& input, T*& output) inline void snippetGain(T gain, const T*& input, T*& output)
{ {
*output++ = gain * (*input++); *output++ = gain * (*input++);
} }
template<class T, bool SIMD=SIMDConfig::gain> template <class T, bool SIMD = SIMDConfig::gain>
void applyGain(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept void applyGain(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
{ {
ASSERT(input.size() <= output.size()); ASSERT(input.size() <= output.size());
@ -162,13 +162,13 @@ void applyGain(T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
snippetGain<T>(gain, in, out); snippetGain<T>(gain, in, out);
} }
template<class T> template <class T>
inline void snippetGainSpan(const T*& gain, const T*& input, T*& output) inline void snippetGainSpan(const T*& gain, const T*& input, T*& output)
{ {
*output++ = (*gain++) * (*input++); *output++ = (*gain++) * (*input++);
} }
template<class T, bool SIMD=SIMDConfig::gain> template <class T, bool SIMD = SIMDConfig::gain>
void applyGain(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T> output) noexcept void applyGain(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T> output) noexcept
{ {
ASSERT(gain.size() == input.size()); ASSERT(gain.size() == input.size());
@ -181,69 +181,69 @@ void applyGain(absl::Span<const T> gain, absl::Span<const T> input, absl::Span<T
snippetGainSpan<T>(g, in, out); snippetGainSpan<T>(g, in, out);
} }
template<class T, bool SIMD=SIMDConfig::gain> template <class T, bool SIMD = SIMDConfig::gain>
void applyGain(T gain, absl::Span<T> output) noexcept void applyGain(T gain, absl::Span<T> output) noexcept
{ {
applyGain<T, SIMD>(gain, output, output); applyGain<T, SIMD>(gain, output, output);
} }
template<class T, bool SIMD=SIMDConfig::gain> template <class T, bool SIMD = SIMDConfig::gain>
void applyGain(absl::Span<const T> gain, absl::Span<T> output) noexcept void applyGain(absl::Span<const T> gain, absl::Span<T> output) noexcept
{ {
applyGain<T, SIMD>(gain, output, output); applyGain<T, SIMD>(gain, output, output);
} }
template<> template <>
void applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept; void applyGain<float, true>(float gain, absl::Span<const float> input, absl::Span<float> output) noexcept;
template<> template <>
void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept; void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float> input, absl::Span<float> output) noexcept;
template<class T> template <class T>
inline void snippetRampLinear(T*& output, T& value, T step) inline void snippetRampLinear(T*& output, T& value, T step)
{ {
value += step; value += step;
*output++ = value; *output++ = value;
} }
template<class T, bool SIMD=SIMDConfig::linearRamp> template <class T, bool SIMD = SIMDConfig::linearRamp>
T 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(); auto* out = output.begin();
while(out < output.end()) while (out < output.end())
snippetRampLinear<T>(out, start, step); snippetRampLinear<T>(out, start, step);
return start; return start;
} }
template<class T> template <class T>
inline void snippetRampMultiplicative(T*& output, T& value, T step) inline void snippetRampMultiplicative(T*& output, T& value, T step)
{ {
value *= step; value *= step;
*output++ = value; *output++ = value;
} }
template<class T, bool SIMD=SIMDConfig::multiplicativeRamp> template <class T, bool SIMD = SIMDConfig::multiplicativeRamp>
T 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(); auto* out = output.begin();
while(out < output.end()) while (out < output.end())
snippetRampMultiplicative<T>(out, start, step); snippetRampMultiplicative<T>(out, start, step);
return start; return start;
} }
template<> template <>
float 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<> template <>
float 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;
template<class T> template <class T>
inline void snippetAdd(const T*& input, T*& output) inline void snippetAdd(const T*& input, T*& output)
{ {
*output++ += *input++; *output++ += *input++;
} }
template<class T, bool SIMD=SIMDConfig::add> template <class T, bool SIMD = SIMDConfig::add>
void add(absl::Span<const T> input, absl::Span<T> output) noexcept void add(absl::Span<const T> input, absl::Span<T> output) noexcept
{ {
ASSERT(output.size() >= input.size()); ASSERT(output.size() >= input.size());
@ -254,5 +254,5 @@ void add(absl::Span<const T> input, absl::Span<T> output) noexcept
snippetAdd(in, out); snippetAdd(in, out);
} }
template<> template <>
void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept; void add<float, true>(absl::Span<const float> input, absl::Span<float> output) noexcept;

View file

@ -283,13 +283,13 @@ void applyGain<float, true>(absl::Span<const float> gain, absl::Span<const float
} }
template <> template <>
void loopingSFZIndex<float, true>( absl::Span<const float> jumps, void loopingSFZIndex<float, true>(absl::Span<const float> jumps,
absl::Span<float> leftCoeffs, absl::Span<float> leftCoeffs,
absl::Span<float> rightCoeffs, absl::Span<float> rightCoeffs,
absl::Span<int> indices, absl::Span<int> indices,
float floatIndex, float floatIndex,
float loopEnd, float loopEnd,
float loopStart) noexcept float loopStart) noexcept
{ {
ASSERT(indices.size() >= jumps.size()); ASSERT(indices.size() >= jumps.size());
ASSERT(indices.size() == leftCoeffs.size()); ASSERT(indices.size() == leftCoeffs.size());

View file

@ -1,13 +1,12 @@
#include "Synth.h" #include "Synth.h"
#include "Helpers.h" #include "Helpers.h"
#include <algorithm>
#include <iostream> #include <iostream>
#include <utility> #include <utility>
#include <algorithm>
void sfz::Synth::callback(std::string_view header, const std::vector<Opcode>& members) void sfz::Synth::callback(std::string_view header, const std::vector<Opcode>& members)
{ {
switch (hash(header)) switch (hash(header)) {
{
case hash("global"): case hash("global"):
// We shouldn't have multiple global headers in file // We shouldn't have multiple global headers in file
ASSERT(!hasGlobal); ASSERT(!hasGlobal);
@ -35,7 +34,7 @@ void sfz::Synth::callback(std::string_view header, const std::vector<Opcode>& me
numCurves++; numCurves++;
break; break;
case hash("effect"): case hash("effect"):
// TODO: implement curves // TODO: implement effects
break; break;
default: default:
std::cerr << "Unknown header: " << header << '\n'; std::cerr << "Unknown header: " << header << '\n';
@ -47,7 +46,7 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
auto lastRegion = std::make_unique<Region>(); auto lastRegion = std::make_unique<Region>();
auto parseOpcodes = [&](const auto& opcodes) { auto parseOpcodes = [&](const auto& opcodes) {
for (auto& opcode: opcodes) for (auto& opcode : opcodes)
if (!lastRegion->parseOpcode(opcode)) if (!lastRegion->parseOpcode(opcode))
unknownOpcodes.insert(opcode.opcode); unknownOpcodes.insert(opcode.opcode);
}; };
@ -68,7 +67,7 @@ void sfz::Synth::clear()
numMasters = 0; numMasters = 0;
numCurves = 0; numCurves = 0;
defaultSwitch = std::nullopt; defaultSwitch = std::nullopt;
for (auto& state: ccState) for (auto& state : ccState)
state = 0; state = 0;
ccNames.clear(); ccNames.clear();
globalOpcodes.clear(); globalOpcodes.clear();
@ -79,8 +78,7 @@ void sfz::Synth::clear()
void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members) void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members)
{ {
for (auto& member: members) for (auto& member : members) {
{
if (member.opcode == "sw_default") if (member.opcode == "sw_default")
setValueFromOpcode(member, defaultSwitch, Default::keyRange); setValueFromOpcode(member, defaultSwitch, Default::keyRange);
} }
@ -88,10 +86,8 @@ void sfz::Synth::handleGlobalOpcodes(const std::vector<Opcode>& members)
void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members) void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
{ {
for (auto& member: members) for (auto& member : members) {
{ switch (hash(member.opcode)) {
switch (hash(member.opcode))
{
case hash("set_cc"): case hash("set_cc"):
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter)) if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
setValueFromOpcode(member, ccState[*member.parameter], Default::ccRange); setValueFromOpcode(member, ccState[*member.parameter], Default::ccRange);
@ -125,19 +121,16 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
auto lastRegion = regions.end() - 1; auto lastRegion = regions.end() - 1;
auto currentRegion = regions.begin(); auto currentRegion = regions.begin();
while (currentRegion <= lastRegion) while (currentRegion <= lastRegion) {
{
auto region = currentRegion->get(); auto region = currentRegion->get();
if (region->isGenerator()) if (region->isGenerator()) {
{
currentRegion++; currentRegion++;
continue; continue;
} }
auto fileInformation = filePool.getFileInformation(region->sample); auto fileInformation = filePool.getFileInformation(region->sample);
if (!fileInformation) if (!fileInformation) {
{
DBG("Removing the region with sample " << region->sample); DBG("Removing the region with sample " << region->sample);
std::iter_swap(currentRegion, lastRegion); std::iter_swap(currentRegion, lastRegion);
lastRegion--; lastRegion--;
@ -159,8 +152,7 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
for (int ccIndex = 1; ccIndex < 128; ccIndex++) for (int ccIndex = 1; ccIndex < 128; ccIndex++)
region->registerCC(region->channelRange.getStart(), ccIndex, ccState[ccIndex]); region->registerCC(region->channelRange.getStart(), ccIndex, ccState[ccIndex]);
if (defaultSwitch) if (defaultSwitch) {
{
region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0); region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0);
region->registerNoteOff(region->channelRange.getStart(), *defaultSwitch, 0, 1.0); region->registerNoteOff(region->channelRange.getStart(), *defaultSwitch, 0, 1.0);
} }

View file

@ -5,13 +5,13 @@
#include "SfzHelpers.h" #include "SfzHelpers.h"
#include "StereoSpan.h" #include "StereoSpan.h"
#include "absl/types/span.h" #include "absl/types/span.h"
#include <chrono>
#include <optional> #include <optional>
#include <random> #include <random>
#include <set> #include <set>
#include <string_view> #include <string_view>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <chrono>
using namespace std::literals; using namespace std::literals;
namespace sfz { namespace sfz {
@ -129,7 +129,7 @@ public:
void tempo(int delay, float secondsPerQuarter); void tempo(int delay, float secondsPerQuarter);
protected: protected:
void callback(std::string_view header, const std::vector<Opcode>& members) final; void callback(std::string_view header, const std::vector<Opcode>& members) final;
private: private:
Voice* findFreeVoice() Voice* findFreeVoice()
@ -177,13 +177,12 @@ private:
std::thread garbageCollectionThread { [&]() { std::thread garbageCollectionThread { [&]() {
while (!threadsShouldQuit) { while (!threadsShouldQuit) {
auto activeVoices { 0 }; auto activeVoices { 0 };
for (auto& voice : voices) for (auto& voice : voices) {
{
voice->garbageCollect(); voice->garbageCollect();
if (!voice->isFree()) if (!voice->isFree())
activeVoices++; activeVoices++;
} }
DBG("Active voices:" << activeVoices << " | Stray buffers: " << FilePool::getFileBuffers()); DBG("Active voices:" << activeVoices << " | Stray buffers: " << FilePool::getFileBuffers());
std::this_thread::sleep_for(1s); std::this_thread::sleep_for(1s);
} }
} }; } };

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "ADSREnvelope.h" #include "ADSREnvelope.h"
#include "Defaults.h"
#include "Globals.h" #include "Globals.h"
#include "Region.h" #include "Region.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
@ -7,7 +8,6 @@
#include "StereoBuffer.h" #include "StereoBuffer.h"
#include "StereoSpan.h" #include "StereoSpan.h"
#include "absl/types/span.h" #include "absl/types/span.h"
#include "Defaults.h"
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@ -92,7 +92,6 @@ public:
if (ccState[64] < 63) if (ccState[64] < 63)
egEnvelope.startRelease(delay); egEnvelope.startRelease(delay);
} }
} }
void registerCC(int delay [[maybe_unused]], int channel [[maybe_unused]], int ccNumber [[maybe_unused]], uint8_t ccValue [[maybe_unused]]) void registerCC(int delay [[maybe_unused]], int channel [[maybe_unused]], int ccNumber [[maybe_unused]], uint8_t ccValue [[maybe_unused]])

View file

@ -1,21 +1,20 @@
#include "catch2/catch.hpp"
#include "../sources/ADSREnvelope.h" #include "../sources/ADSREnvelope.h"
#include <array> #include "catch2/catch.hpp"
#include <algorithm>
#include <iostream>
#include <absl/types/span.h>
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
#include <absl/types/span.h>
#include <algorithm>
#include <array>
#include <iostream>
using namespace Catch::literals; using namespace Catch::literals;
template<class Type> template <class Type>
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps=1e-3) inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size())
return false; return false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
{
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
return false; return false;
} }
@ -28,13 +27,13 @@ TEST_CASE("[ADSREnvelope] Basic state")
sfz::ADSREnvelope<float> envelope; sfz::ADSREnvelope<float> envelope;
std::array<float, 5> output; std::array<float, 5> output;
std::array<float, 5> expected { 0.0, 0.0, 0.0, 0.0, 0.0 }; std::array<float, 5> expected { 0.0, 0.0, 0.0, 0.0, 0.0 };
for (auto& out: output) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Attack") TEST_CASE("[ADSREnvelope] Attack")
@ -43,14 +42,14 @@ TEST_CASE("[ADSREnvelope] Attack")
envelope.reset(2, 0); envelope.reset(2, 0);
std::array<float, 5> output; std::array<float, 5> output;
std::array<float, 5> expected { 0.5, 1.0, 1.0, 1.0, 1.0 }; std::array<float, 5> expected { 0.5, 1.0, 1.0, 1.0, 1.0 };
for (auto& out: output) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 0); envelope.reset(2, 0);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Attack again") TEST_CASE("[ADSREnvelope] Attack again")
@ -59,14 +58,14 @@ TEST_CASE("[ADSREnvelope] Attack again")
envelope.reset(3, 0); envelope.reset(3, 0);
std::array<float, 5> output; std::array<float, 5> output;
std::array<float, 5> expected { 0.33333, 0.66667, 1.0, 1.0, 1.0 }; std::array<float, 5> expected { 0.33333, 0.66667, 1.0, 1.0, 1.0 };
for (auto& out: output) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(3, 0); envelope.reset(3, 0);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Release") TEST_CASE("[ADSREnvelope] Release")
@ -76,15 +75,15 @@ TEST_CASE("[ADSREnvelope] Release")
envelope.startRelease(2); envelope.startRelease(2);
std::array<float, 8> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4); envelope.reset(2, 4);
envelope.startRelease(2); envelope.startRelease(2);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Delay") TEST_CASE("[ADSREnvelope] Delay")
@ -94,15 +93,15 @@ TEST_CASE("[ADSREnvelope] Delay")
std::array<float, 10> output; std::array<float, 10> output;
envelope.startRelease(4); 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 1.0f, 2); envelope.reset(2, 4, 1.0f, 2);
envelope.startRelease(4); envelope.startRelease(4);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Lower sustain") TEST_CASE("[ADSREnvelope] Lower sustain")
@ -111,14 +110,14 @@ TEST_CASE("[ADSREnvelope] Lower sustain")
envelope.reset(2, 4, 0.5, 2); envelope.reset(2, 4, 0.5, 2);
std::array<float, 10> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 0.5, 2); envelope.reset(2, 4, 0.5, 2);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Decay") TEST_CASE("[ADSREnvelope] Decay")
@ -127,14 +126,14 @@ TEST_CASE("[ADSREnvelope] Decay")
envelope.reset(2, 4, 0.5, 2, 2); envelope.reset(2, 4, 0.5, 2, 2);
std::array<float, 10> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 0.5, 2, 2); envelope.reset(2, 4, 0.5, 2, 2);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Hold") TEST_CASE("[ADSREnvelope] Hold")
@ -143,14 +142,14 @@ TEST_CASE("[ADSREnvelope] Hold")
envelope.reset(2, 4, 0.5, 2, 2, 2); envelope.reset(2, 4, 0.5, 2, 2, 2);
std::array<float, 12> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 0.5, 2, 2, 2); envelope.reset(2, 4, 0.5, 2, 2, 2);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Hold with release") TEST_CASE("[ADSREnvelope] Hold with release")
@ -159,16 +158,16 @@ TEST_CASE("[ADSREnvelope] Hold with release")
envelope.reset(2, 4, 0.5, 2, 2, 2); envelope.reset(2, 4, 0.5, 2, 2, 2);
envelope.startRelease(8); envelope.startRelease(8);
std::array<float, 14> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 0.5, 2, 2, 2); envelope.reset(2, 4, 0.5, 2, 2, 2);
envelope.startRelease(8); envelope.startRelease(8);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }
TEST_CASE("[ADSREnvelope] Hold with release 2") TEST_CASE("[ADSREnvelope] Hold with release 2")
@ -178,12 +177,12 @@ TEST_CASE("[ADSREnvelope] Hold with release 2")
envelope.startRelease(4); envelope.startRelease(4);
std::array<float, 14> output; 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 }; 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) for (auto& out : output)
out = envelope.getNextValue(); out = envelope.getNextValue();
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
envelope.reset(2, 4, 0.5, 2, 2, 2); envelope.reset(2, 4, 0.5, 2, 2, 2);
envelope.startRelease(4); envelope.startRelease(4);
absl::c_fill(output, -1.0); absl::c_fill(output, -1.0);
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( approxEqual<float>(output, expected) ); REQUIRE(approxEqual<float>(output, expected));
} }

View file

@ -1,5 +1,5 @@
#include "catch2/catch.hpp"
#include "../sources/Synth.h" #include "../sources/Synth.h"
#include "catch2/catch.hpp"
#include <filesystem> #include <filesystem>
using namespace Catch::literals; using namespace Catch::literals;
@ -7,76 +7,76 @@ TEST_CASE("[Files] Single region (regions_one.sfz)")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_one.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_one.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->sample == "dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
} }
TEST_CASE("[Files] Multiple regions (regions_many.sfz)") TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_many.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
REQUIRE( synth.getNumRegions() == 3 ); REQUIRE(synth.getNumRegions() == 3);
REQUIRE( synth.getRegionView(0)->sample == "dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
REQUIRE( synth.getRegionView(1)->sample == "dummy.1.wav" ); REQUIRE(synth.getRegionView(1)->sample == "dummy.1.wav");
REQUIRE( synth.getRegionView(2)->sample == "dummy.2.wav" ); REQUIRE(synth.getRegionView(2)->sample == "dummy.2.wav");
} }
TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)") TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->channelRange == Range<uint8_t>(2, 14) ); REQUIRE(synth.getRegionView(0)->channelRange == Range<uint8_t>(2, 14));
} }
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)") TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain ); REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain);
} }
TEST_CASE("[Files] Local include") TEST_CASE("[Files] Local include")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_local.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_local.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->sample == "dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
} }
TEST_CASE("[Files] Multiple includes") TEST_CASE("[Files] Multiple includes")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
REQUIRE( synth.getNumRegions() == 2 ); REQUIRE(synth.getNumRegions() == 2);
REQUIRE( synth.getRegionView(0)->sample == "dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
REQUIRE( synth.getRegionView(1)->sample == "dummy2.wav" ); REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
} }
TEST_CASE("[Files] Multiple includes with comments") TEST_CASE("[Files] Multiple includes with comments")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz");
REQUIRE( synth.getNumRegions() == 2 ); REQUIRE(synth.getNumRegions() == 2);
REQUIRE( synth.getRegionView(0)->sample == "dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
REQUIRE( synth.getRegionView(1)->sample == "dummy2.wav" ); REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
} }
TEST_CASE("[Files] Subdir include") TEST_CASE("[Files] Subdir include")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->sample == "dummy_subdir.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
} }
TEST_CASE("[Files] Subdir include Win") TEST_CASE("[Files] Subdir include Win")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
REQUIRE( synth.getNumRegions() == 1 ); REQUIRE(synth.getNumRegions() == 1);
REQUIRE( synth.getRegionView(0)->sample == "dummy_subdir.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
} }
TEST_CASE("[Files] Recursive include (with include guard)") TEST_CASE("[Files] Recursive include (with include guard)")
@ -84,9 +84,9 @@ TEST_CASE("[Files] Recursive include (with include guard)")
sfz::Synth synth; sfz::Synth synth;
synth.enableRecursiveIncludeGuard(); synth.enableRecursiveIncludeGuard();
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_recursive.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
REQUIRE( synth.getNumRegions() == 2 ); REQUIRE(synth.getNumRegions() == 2);
REQUIRE( synth.getRegionView(0)->sample == "dummy_recursive2.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy_recursive2.wav");
REQUIRE( synth.getRegionView(1)->sample == "dummy_recursive1.wav" ); REQUIRE(synth.getRegionView(1)->sample == "dummy_recursive1.wav");
} }
TEST_CASE("[Files] Include loops (with include guard)") TEST_CASE("[Files] Include loops (with include guard)")
@ -94,88 +94,85 @@ TEST_CASE("[Files] Include loops (with include guard)")
sfz::Synth synth; sfz::Synth synth;
synth.enableRecursiveIncludeGuard(); synth.enableRecursiveIncludeGuard();
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_loop.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
REQUIRE( synth.getNumRegions() == 2 ); REQUIRE(synth.getNumRegions() == 2);
REQUIRE( synth.getRegionView(0)->sample == "dummy_loop2.wav" ); REQUIRE(synth.getRegionView(0)->sample == "dummy_loop2.wav");
REQUIRE( synth.getRegionView(1)->sample == "dummy_loop1.wav" ); REQUIRE(synth.getRegionView(1)->sample == "dummy_loop1.wav");
} }
TEST_CASE("[Files] Define test") TEST_CASE("[Files] Define test")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/defines.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/defines.sfz");
REQUIRE( synth.getNumRegions() == 3 ); REQUIRE(synth.getNumRegions() == 3);
REQUIRE( synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36) ); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
REQUIRE( synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38) ); REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
REQUIRE( synth.getRegionView(2)->keyRange == Range<uint8_t>(42, 42) ); REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(42, 42));
} }
TEST_CASE("[Files] Group from AVL") TEST_CASE("[Files] Group from AVL")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/groups_avl.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/groups_avl.sfz");
REQUIRE( synth.getNumRegions() == 5 ); REQUIRE(synth.getNumRegions() == 5);
for (int i = 0; i < synth.getNumRegions(); ++i) for (int i = 0; i < synth.getNumRegions(); ++i) {
{ REQUIRE(synth.getRegionView(i)->volume == 6.0f);
REQUIRE( synth.getRegionView(i)->volume == 6.0f ); REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(36, 36));
REQUIRE( synth.getRegionView(i)->keyRange == Range<uint8_t>(36, 36) );
} }
REQUIRE( synth.getRegionView(0)->velocityRange == Range<uint8_t>(1, 26) ); REQUIRE(synth.getRegionView(0)->velocityRange == Range<uint8_t>(1, 26));
REQUIRE( synth.getRegionView(1)->velocityRange == Range<uint8_t>(27, 52) ); REQUIRE(synth.getRegionView(1)->velocityRange == Range<uint8_t>(27, 52));
REQUIRE( synth.getRegionView(2)->velocityRange == Range<uint8_t>(53, 77) ); REQUIRE(synth.getRegionView(2)->velocityRange == Range<uint8_t>(53, 77));
REQUIRE( synth.getRegionView(3)->velocityRange == Range<uint8_t>(78, 102) ); REQUIRE(synth.getRegionView(3)->velocityRange == Range<uint8_t>(78, 102));
REQUIRE( synth.getRegionView(4)->velocityRange == Range<uint8_t>(103, 127) ); REQUIRE(synth.getRegionView(4)->velocityRange == Range<uint8_t>(103, 127));
} }
TEST_CASE("[Files] Full hierarchy") TEST_CASE("[Files] Full hierarchy")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE( synth.getNumRegions() == 8 ); REQUIRE(synth.getNumRegions() == 8);
for (int i = 0; i < synth.getNumRegions(); ++i) for (int i = 0; i < synth.getNumRegions(); ++i) {
{ REQUIRE(synth.getRegionView(i)->width == 40.0f);
REQUIRE( synth.getRegionView(i)->width == 40.0f );
} }
REQUIRE( synth.getRegionView(0)->pan == 30.0f ); REQUIRE(synth.getRegionView(0)->pan == 30.0f);
REQUIRE( synth.getRegionView(0)->delay == 67 ); REQUIRE(synth.getRegionView(0)->delay == 67);
REQUIRE( synth.getRegionView(0)->keyRange == Range<uint8_t>(60, 60) ); REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(60, 60));
REQUIRE( synth.getRegionView(1)->pan == 30.0f ); REQUIRE(synth.getRegionView(1)->pan == 30.0f);
REQUIRE( synth.getRegionView(1)->delay == 67 ); REQUIRE(synth.getRegionView(1)->delay == 67);
REQUIRE( synth.getRegionView(1)->keyRange == Range<uint8_t>(61, 61) ); REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(61, 61));
REQUIRE( synth.getRegionView(2)->pan == 30.0f ); REQUIRE(synth.getRegionView(2)->pan == 30.0f);
REQUIRE( synth.getRegionView(2)->delay == 56 ); REQUIRE(synth.getRegionView(2)->delay == 56);
REQUIRE( synth.getRegionView(2)->keyRange == Range<uint8_t>(50, 50) ); REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(50, 50));
REQUIRE( synth.getRegionView(3)->pan == 30.0f ); REQUIRE(synth.getRegionView(3)->pan == 30.0f);
REQUIRE( synth.getRegionView(3)->delay == 56 ); REQUIRE(synth.getRegionView(3)->delay == 56);
REQUIRE( synth.getRegionView(3)->keyRange == Range<uint8_t>(51, 51) ); REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(51, 51));
REQUIRE( synth.getRegionView(4)->pan == -10.0f ); REQUIRE(synth.getRegionView(4)->pan == -10.0f);
REQUIRE( synth.getRegionView(4)->delay == 47 ); REQUIRE(synth.getRegionView(4)->delay == 47);
REQUIRE( synth.getRegionView(4)->keyRange == Range<uint8_t>(40, 40) ); REQUIRE(synth.getRegionView(4)->keyRange == Range<uint8_t>(40, 40));
REQUIRE( synth.getRegionView(5)->pan == -10.0f ); REQUIRE(synth.getRegionView(5)->pan == -10.0f);
REQUIRE( synth.getRegionView(5)->delay == 47 ); REQUIRE(synth.getRegionView(5)->delay == 47);
REQUIRE( synth.getRegionView(5)->keyRange == Range<uint8_t>(41, 41) ); REQUIRE(synth.getRegionView(5)->keyRange == Range<uint8_t>(41, 41));
REQUIRE( synth.getRegionView(6)->pan == -10.0f ); REQUIRE(synth.getRegionView(6)->pan == -10.0f);
REQUIRE( synth.getRegionView(6)->delay == 36 ); REQUIRE(synth.getRegionView(6)->delay == 36);
REQUIRE( synth.getRegionView(6)->keyRange == Range<uint8_t>(30, 30) ); REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(30, 30));
REQUIRE( synth.getRegionView(7)->pan == -10.0f ); REQUIRE(synth.getRegionView(7)->pan == -10.0f);
REQUIRE( synth.getRegionView(7)->delay == 36 ); REQUIRE(synth.getRegionView(7)->delay == 36);
REQUIRE( synth.getRegionView(7)->keyRange == Range<uint8_t>(31, 31) ); REQUIRE(synth.getRegionView(7)->keyRange == Range<uint8_t>(31, 31));
} }
TEST_CASE("[Files] Reloading files") TEST_CASE("[Files] Reloading files")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE( synth.getNumRegions() == 8 ); REQUIRE(synth.getNumRegions() == 8);
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE( synth.getNumRegions() == 8 ); REQUIRE(synth.getNumRegions() == 8);
} }
TEST_CASE("[Files] Full hierarchy with antislashes") TEST_CASE("[Files] Full hierarchy with antislashes")
@ -183,29 +180,29 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
REQUIRE( synth.getNumRegions() == 8 ); REQUIRE(synth.getNumRegions() == 8);
REQUIRE( synth.getRegionView(0)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(1)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(2)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(3)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(4)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(4)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(5)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(5)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(6)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(6)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(7)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(7)->sample == "Regions/dummy.1.wav");
} }
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz");
REQUIRE( synth.getNumRegions() == 8 ); REQUIRE(synth.getNumRegions() == 8);
REQUIRE( synth.getRegionView(0)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(1)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(2)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(2)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(3)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(3)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(4)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(4)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(5)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(5)->sample == "Regions/dummy.1.wav");
REQUIRE( synth.getRegionView(6)->sample == "Regions/dummy.wav" ); REQUIRE(synth.getRegionView(6)->sample == "Regions/dummy.wav");
REQUIRE( synth.getRegionView(7)->sample == "Regions/dummy.1.wav" ); REQUIRE(synth.getRegionView(7)->sample == "Regions/dummy.1.wav");
} }
} }
@ -213,22 +210,21 @@ TEST_CASE("[Files] Pizz basic")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz"); synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
REQUIRE( synth.getNumRegions() == 4 ); REQUIRE(synth.getNumRegions() == 4);
for (int i = 0; i < synth.getNumRegions(); ++i) for (int i = 0; i < synth.getNumRegions(); ++i) {
{ REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
REQUIRE( synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22) ); REQUIRE(synth.getRegionView(i)->velocityRange == Range<uint8_t>(97, 127));
REQUIRE( synth.getRegionView(i)->velocityRange == Range<uint8_t>(97, 127) ); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
REQUIRE( synth.getRegionView(i)->pitchKeycenter == 21 ); REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == Range<uint8_t>(0, 13));
REQUIRE( synth.getRegionView(i)->ccConditions.getWithDefault(107) == Range<uint8_t>(0, 13) );
} }
REQUIRE( synth.getRegionView(0)->randRange == Range<float>(0, 0.25) ); REQUIRE(synth.getRegionView(0)->randRange == Range<float>(0, 0.25));
REQUIRE( synth.getRegionView(1)->randRange == Range<float>(0.25, 0.5) ); REQUIRE(synth.getRegionView(1)->randRange == Range<float>(0.25, 0.5));
REQUIRE( synth.getRegionView(2)->randRange == Range<float>(0.5, 0.75) ); REQUIRE(synth.getRegionView(2)->randRange == Range<float>(0.5, 0.75));
REQUIRE( synth.getRegionView(3)->randRange == Range<float>(0.75, 1.0) ); REQUIRE(synth.getRegionView(3)->randRange == Range<float>(0.75, 1.0));
REQUIRE( synth.getRegionView(0)->sample == R"(../Samples/pizz/a0_vl4_rr1.wav)" ); REQUIRE(synth.getRegionView(0)->sample == R"(../Samples/pizz/a0_vl4_rr1.wav)");
REQUIRE( synth.getRegionView(1)->sample == R"(../Samples/pizz/a0_vl4_rr2.wav)" ); REQUIRE(synth.getRegionView(1)->sample == R"(../Samples/pizz/a0_vl4_rr2.wav)");
REQUIRE( synth.getRegionView(2)->sample == R"(../Samples/pizz/a0_vl4_rr3.wav)" ); REQUIRE(synth.getRegionView(2)->sample == R"(../Samples/pizz/a0_vl4_rr3.wav)");
REQUIRE( synth.getRegionView(3)->sample == R"(../Samples/pizz/a0_vl4_rr4.wav)" ); REQUIRE(synth.getRegionView(3)->sample == R"(../Samples/pizz/a0_vl4_rr4.wav)");
} }
// TEST_CASE("[Files] sw_default") // TEST_CASE("[Files] sw_default")

View file

@ -1,8 +1,8 @@
#include "catch2/catch.hpp"
#include "../sources/Helpers.h" #include "../sources/Helpers.h"
#include "catch2/catch.hpp"
#include <string_view> #include <string_view>
using namespace Catch::literals; using namespace Catch::literals;
using namespace std::literals::string_view_literals; using namespace std::literals::string_view_literals;
TEST_CASE("[Helpers] trimInPlace") TEST_CASE("[Helpers] trimInPlace")
{ {
@ -10,28 +10,28 @@ TEST_CASE("[Helpers] trimInPlace")
{ {
auto input { "view"sv }; auto input { "view"sv };
trimInPlace(input); trimInPlace(input);
REQUIRE( input == "view"sv ); REQUIRE(input == "view"sv);
} }
SECTION("Trim spaces") SECTION("Trim spaces")
{ {
auto input { " view "sv }; auto input { " view "sv };
trimInPlace(input); trimInPlace(input);
REQUIRE( input == "view"sv ); REQUIRE(input == "view"sv);
} }
SECTION("Trim other chars") SECTION("Trim other chars")
{ {
auto input { " \tview \t"sv }; auto input { " \tview \t"sv };
trimInPlace(input); trimInPlace(input);
REQUIRE( input == "view"sv ); REQUIRE(input == "view"sv);
} }
SECTION("Empty view") SECTION("Empty view")
{ {
auto input { " "sv }; auto input { " "sv };
trimInPlace(input); trimInPlace(input);
REQUIRE( input.empty() ); REQUIRE(input.empty());
} }
} }
@ -40,24 +40,24 @@ TEST_CASE("[Helpers] trim")
SECTION("Trim nothing") SECTION("Trim nothing")
{ {
auto input { "view"sv }; auto input { "view"sv };
REQUIRE( trim(input) == "view"sv ); REQUIRE(trim(input) == "view"sv);
} }
SECTION("Trim spaces") SECTION("Trim spaces")
{ {
auto input { " view "sv }; auto input { " view "sv };
REQUIRE( trim(input) == "view"sv ); REQUIRE(trim(input) == "view"sv);
} }
SECTION("Trim other chars") SECTION("Trim other chars")
{ {
auto input { " \tview \t"sv }; auto input { " \tview \t"sv };
REQUIRE( trim(input) == "view"sv ); REQUIRE(trim(input) == "view"sv);
} }
SECTION("Empty view") SECTION("Empty view")
{ {
auto input { " "sv }; auto input { " "sv };
REQUIRE( trim(input).empty() ); REQUIRE(trim(input).empty());
} }
} }

View file

@ -1,21 +1,20 @@
#include "catch2/catch.hpp"
#include "../sources/LinearEnvelope.h" #include "../sources/LinearEnvelope.h"
#include <array> #include "catch2/catch.hpp"
#include <algorithm>
#include <iostream>
#include <absl/types/span.h>
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
#include <absl/types/span.h>
#include <algorithm>
#include <array>
#include <iostream>
using namespace Catch::literals; using namespace Catch::literals;
template<class Type> template <class Type>
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps=1e-3) inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size())
return false; return false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
{
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
return false; return false;
} }
@ -29,7 +28,7 @@ TEST_CASE("[LinearEnvelope] Basic state")
std::array<float, 5> output; std::array<float, 5> output;
std::array<float, 5> expected { 0.0, 0.0, 0.0, 0.0, 0.0 }; std::array<float, 5> expected { 0.0, 0.0, 0.0, 0.0, 0.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] Basic event") TEST_CASE("[LinearEnvelope] Basic event")
@ -39,7 +38,7 @@ TEST_CASE("[LinearEnvelope] Basic event")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.25, 0.5, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0 }; std::array<float, 8> expected { 0.25, 0.5, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, close") TEST_CASE("[LinearEnvelope] 2 events, close")
@ -50,7 +49,7 @@ TEST_CASE("[LinearEnvelope] 2 events, close")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.25, 0.5, 0.75, 1.0, 2.0, 2.0, 2.0, 2.0 }; std::array<float, 8> expected { 0.25, 0.5, 0.75, 1.0, 2.0, 2.0, 2.0, 2.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, far") TEST_CASE("[LinearEnvelope] 2 events, far")
@ -61,7 +60,7 @@ TEST_CASE("[LinearEnvelope] 2 events, far")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.0, 2.0 }; std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.0, 2.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, reversed") TEST_CASE("[LinearEnvelope] 2 events, reversed")
@ -72,7 +71,7 @@ TEST_CASE("[LinearEnvelope] 2 events, reversed")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.0, 2.0 }; std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.0, 2.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 3 events, overlapping") TEST_CASE("[LinearEnvelope] 3 events, overlapping")
@ -84,7 +83,7 @@ TEST_CASE("[LinearEnvelope] 3 events, overlapping")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 3.0, 3.0 }; std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 3.0, 3.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 3 events, out of block") TEST_CASE("[LinearEnvelope] 3 events, out of block")
@ -96,7 +95,7 @@ TEST_CASE("[LinearEnvelope] 3 events, out of block")
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0 }; std::array<float, 8> expected { 0.5, 1, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 3 events, out of block, with another block call") TEST_CASE("[LinearEnvelope] 3 events, out of block, with another block call")
@ -109,7 +108,7 @@ TEST_CASE("[LinearEnvelope] 3 events, out of block, with another block call")
std::array<float, 8> expected { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 }; std::array<float, 8> expected { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, with another block call") TEST_CASE("[LinearEnvelope] 2 events, with another block call")
@ -121,17 +120,17 @@ TEST_CASE("[LinearEnvelope] 2 events, with another block call")
std::array<float, 8> expected { 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 }; std::array<float, 8> expected { 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, function") TEST_CASE("[LinearEnvelope] 2 events, function")
{ {
sfz::LinearEnvelope<float> envelope; sfz::LinearEnvelope<float> envelope;
envelope.setFunction([](auto x) { return 2*x; }); envelope.setFunction([](auto x) { return 2 * x; });
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 { 1, 2, 2.5, 3, 3.5, 4.0, 4.0, 4.0 }; std::array<float, 8> expected { 1, 2, 2.5, 3, 3.5, 4.0, 4.0, 4.0 };
envelope.getBlock(absl::MakeSpan(output)); envelope.getBlock(absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }

View file

@ -1,21 +1,20 @@
#include "../sources/OnePoleFilter.h" #include "../sources/OnePoleFilter.h"
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "cnpy.h" #include "cnpy.h"
#include <string>
#include <filesystem>
#include <algorithm>
#include <absl/types/span.h> #include <absl/types/span.h>
#include <algorithm>
#include <filesystem>
#include <string>
using namespace Catch::literals; using namespace Catch::literals;
template<class Type> template <class Type>
inline bool approxEqual(const std::vector<Type>& lhs, const std::vector<Type>& rhs) inline bool approxEqual(const std::vector<Type>& lhs, const std::vector<Type>& rhs)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size())
return false; return false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)
if (lhs[i] != Approx(rhs[i]).epsilon(1e-3)) if (lhs[i] != Approx(rhs[i]).epsilon(1e-3)) {
{
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
return false; return false;
} }
@ -23,74 +22,74 @@ inline bool approxEqual(const std::vector<Type>& lhs, const std::vector<Type>& r
return true; return true;
} }
template<class Type> template <class Type>
void testLowpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain) void testLowpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain)
{ {
const auto input = cnpy::npy_load(inputNumpyFile.string()); const auto input = cnpy::npy_load(inputNumpyFile.string());
REQUIRE( input.word_size == 8 ); REQUIRE(input.word_size == 8);
const auto inputSpan = absl::MakeSpan(input.data<double>(), input.shape[0]); const auto inputSpan = absl::MakeSpan(input.data<double>(), input.shape[0]);
const auto output = cnpy::npy_load(outputNumpyFile.string()); const auto output = cnpy::npy_load(outputNumpyFile.string());
REQUIRE( output.word_size == 8 ); REQUIRE(output.word_size == 8);
const auto outputSpan = absl::MakeSpan(output.data<double>(), output.shape[0]); const auto outputSpan = absl::MakeSpan(output.data<double>(), output.shape[0]);
auto size = std::min(outputSpan.size(), inputSpan.size()); auto size = std::min(outputSpan.size(), inputSpan.size());
REQUIRE( size > 0 ); REQUIRE(size > 0);
std::vector<Type> inputData; std::vector<Type> inputData;
std::vector<Type> expectedData; std::vector<Type> expectedData;
inputData.reserve(size); inputData.reserve(size);
expectedData.reserve(size); expectedData.reserve(size);
for (auto& data: inputSpan) for (auto& data : inputSpan)
inputData.push_back(static_cast<Type>(data)); inputData.push_back(static_cast<Type>(data));
for (auto& data: outputSpan) for (auto& data : outputSpan)
expectedData.push_back(static_cast<Type>(data)); expectedData.push_back(static_cast<Type>(data));
OnePoleFilter filter { gain }; OnePoleFilter filter { gain };
std::vector<Type> outputData (size); std::vector<Type> outputData(size);
filter.processLowpass(inputData, absl::MakeSpan(outputData)); filter.processLowpass(inputData, absl::MakeSpan(outputData));
REQUIRE( approxEqual(outputData, expectedData) ); REQUIRE(approxEqual(outputData, expectedData));
filter.reset(); filter.reset();
std::fill(outputData.begin(), outputData.end(), 0.0); std::fill(outputData.begin(), outputData.end(), 0.0);
std::vector<Type> gains(size); std::vector<Type> gains(size);
std::fill(gains.begin(), gains.end(), gain); std::fill(gains.begin(), gains.end(), gain);
filter.processLowpassVariableGain(inputData, absl::MakeSpan(outputData), gains); filter.processLowpassVariableGain(inputData, absl::MakeSpan(outputData), gains);
REQUIRE( approxEqual(outputData, expectedData) ); REQUIRE(approxEqual(outputData, expectedData));
} }
template<class Type> template <class Type>
void testHighpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain) void testHighpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain)
{ {
const auto input = cnpy::npy_load(inputNumpyFile.string()); const auto input = cnpy::npy_load(inputNumpyFile.string());
REQUIRE( input.word_size == 8 ); REQUIRE(input.word_size == 8);
const auto inputSpan = absl::MakeSpan(input.data<double>(), input.shape[0]); const auto inputSpan = absl::MakeSpan(input.data<double>(), input.shape[0]);
const auto output = cnpy::npy_load(outputNumpyFile.string()); const auto output = cnpy::npy_load(outputNumpyFile.string());
REQUIRE( output.word_size == 8 ); REQUIRE(output.word_size == 8);
const auto outputSpan = absl::MakeSpan(output.data<double>(), output.shape[0]); const auto outputSpan = absl::MakeSpan(output.data<double>(), output.shape[0]);
auto size = std::min(outputSpan.size(), inputSpan.size()); auto size = std::min(outputSpan.size(), inputSpan.size());
REQUIRE( size > 0 ); REQUIRE(size > 0);
std::vector<Type> inputData; std::vector<Type> inputData;
std::vector<Type> expectedData; std::vector<Type> expectedData;
inputData.reserve(size); inputData.reserve(size);
expectedData.reserve(size); expectedData.reserve(size);
for (auto& data: inputSpan) for (auto& data : inputSpan)
inputData.push_back(static_cast<Type>(data)); inputData.push_back(static_cast<Type>(data));
for (auto& data: outputSpan) for (auto& data : outputSpan)
expectedData.push_back(static_cast<Type>(data)); expectedData.push_back(static_cast<Type>(data));
OnePoleFilter filter { gain }; OnePoleFilter filter { gain };
std::vector<Type> outputData (size); std::vector<Type> outputData(size);
filter.processHighpass(inputData, absl::MakeSpan(outputData)); filter.processHighpass(inputData, absl::MakeSpan(outputData));
REQUIRE( approxEqual(outputData, expectedData) ); REQUIRE(approxEqual(outputData, expectedData));
filter.reset(); filter.reset();
std::fill(outputData.begin(), outputData.end(), 0.0); std::fill(outputData.begin(), outputData.end(), 0.0);
std::vector<Type> gains(size); std::vector<Type> gains(size);
std::fill(gains.begin(), gains.end(), gain); std::fill(gains.begin(), gains.end(), gain);
filter.processHighpassVariableGain(inputData, absl::MakeSpan(outputData), gains); filter.processHighpassVariableGain(inputData, absl::MakeSpan(outputData), gains);
REQUIRE( approxEqual(outputData, expectedData) ); REQUIRE(approxEqual(outputData, expectedData));
} }
TEST_CASE("[OnePoleFilter] Lowpass Float") TEST_CASE("[OnePoleFilter] Lowpass Float")
@ -98,28 +97,23 @@ TEST_CASE("[OnePoleFilter] Lowpass Float")
testLowpass<float>( testLowpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
0.1f 0.1f);
);
testLowpass<float>( testLowpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
0.3f 0.3f);
);
testLowpass<float>( testLowpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
0.5f 0.5f);
);
testLowpass<float>( testLowpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
0.7f 0.7f);
);
testLowpass<float>( testLowpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
0.9f 0.9f);
);
} }
TEST_CASE("[OnePoleFilter] Lowpass Double") TEST_CASE("[OnePoleFilter] Lowpass Double")
@ -127,28 +121,23 @@ TEST_CASE("[OnePoleFilter] Lowpass Double")
testLowpass<double>( testLowpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
0.1f 0.1f);
);
testLowpass<double>( testLowpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
0.3f 0.3f);
);
testLowpass<double>( testLowpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
0.5f 0.5f);
);
testLowpass<double>( testLowpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
0.7f 0.7f);
);
testLowpass<double>( testLowpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
0.9f 0.9f);
);
} }
TEST_CASE("[OnePoleFilter] Highpass Float") TEST_CASE("[OnePoleFilter] Highpass Float")
@ -156,28 +145,23 @@ TEST_CASE("[OnePoleFilter] Highpass Float")
testHighpass<float>( testHighpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
0.1f 0.1f);
);
testHighpass<float>( testHighpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
0.3f 0.3f);
);
testHighpass<float>( testHighpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
0.5f 0.5f);
);
testHighpass<float>( testHighpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
0.7f 0.7f);
);
testHighpass<float>( testHighpass<float>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
0.9f 0.9f);
);
} }
TEST_CASE("[OnePoleFilter] Highpass Double") TEST_CASE("[OnePoleFilter] Highpass Double")
@ -185,26 +169,21 @@ TEST_CASE("[OnePoleFilter] Highpass Double")
testHighpass<double>( testHighpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
0.1f 0.1f);
);
testHighpass<double>( testHighpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
0.3f 0.3f);
);
testHighpass<double>( testHighpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
0.5f 0.5f);
);
testHighpass<double>( testHighpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
0.7f 0.7f);
);
testHighpass<double>( testHighpass<double>(
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy", std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
0.9f 0.9f);
);
} }

View file

@ -1,62 +1,62 @@
#include "catch2/catch.hpp"
#include "../sources/Region.h" #include "../sources/Region.h"
#include "catch2/catch.hpp"
using namespace Catch::literals; using namespace Catch::literals;
TEST_CASE("[Opcode] Construction") TEST_CASE("[Opcode] Construction")
{ {
SECTION("Normal construction") SECTION("Normal construction")
{ {
sfz::Opcode opcode { "sample", "dummy"}; sfz::Opcode opcode { "sample", "dummy" };
REQUIRE( opcode.opcode == "sample" ); REQUIRE(opcode.opcode == "sample");
REQUIRE( opcode.value == "dummy" ); REQUIRE(opcode.value == "dummy");
REQUIRE( !opcode.parameter ); REQUIRE(!opcode.parameter);
} }
SECTION("Normal construction with underscore") SECTION("Normal construction with underscore")
{ {
sfz::Opcode opcode { "sample_underscore", "dummy"}; sfz::Opcode opcode { "sample_underscore", "dummy" };
REQUIRE( opcode.opcode == "sample_underscore" ); REQUIRE(opcode.opcode == "sample_underscore");
REQUIRE( opcode.value == "dummy" ); REQUIRE(opcode.value == "dummy");
REQUIRE( !opcode.parameter ); REQUIRE(!opcode.parameter);
} }
SECTION("Parameterized opcode") SECTION("Parameterized opcode")
{ {
sfz::Opcode opcode { "sample123", "dummy"}; sfz::Opcode opcode { "sample123", "dummy" };
REQUIRE( opcode.opcode == "sample" ); REQUIRE(opcode.opcode == "sample");
REQUIRE( opcode.value == "dummy" ); REQUIRE(opcode.value == "dummy");
REQUIRE( opcode.parameter ); REQUIRE(opcode.parameter);
REQUIRE( *opcode.parameter == 123 ); REQUIRE(*opcode.parameter == 123);
} }
SECTION("Parameterized opcode with underscore") SECTION("Parameterized opcode with underscore")
{ {
sfz::Opcode opcode { "sample_underscore123", "dummy"}; sfz::Opcode opcode { "sample_underscore123", "dummy" };
REQUIRE( opcode.opcode == "sample_underscore" ); REQUIRE(opcode.opcode == "sample_underscore");
REQUIRE( opcode.value == "dummy" ); REQUIRE(opcode.value == "dummy");
REQUIRE( opcode.parameter ); REQUIRE(opcode.parameter);
REQUIRE( *opcode.parameter == 123 ); REQUIRE(*opcode.parameter == 123);
} }
} }
TEST_CASE("[Opcode] Note values") TEST_CASE("[Opcode] Note values")
{ {
auto noteValue = sfz::readNoteValue("c-1"); auto noteValue = sfz::readNoteValue("c-1");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 0); REQUIRE(*noteValue == 0);
noteValue = sfz::readNoteValue("C-1"); noteValue = sfz::readNoteValue("C-1");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 0); REQUIRE(*noteValue == 0);
noteValue = sfz::readNoteValue("g9"); noteValue = sfz::readNoteValue("g9");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 127); REQUIRE(*noteValue == 127);
noteValue = sfz::readNoteValue("G9"); noteValue = sfz::readNoteValue("G9");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 127); REQUIRE(*noteValue == 127);
noteValue = sfz::readNoteValue("c#4"); noteValue = sfz::readNoteValue("c#4");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 61); REQUIRE(*noteValue == 61);
noteValue = sfz::readNoteValue("C#4"); noteValue = sfz::readNoteValue("C#4");
REQUIRE( noteValue ); REQUIRE(noteValue);
REQUIRE( *noteValue == 61); REQUIRE(*noteValue == 61);
} }

View file

@ -4,88 +4,88 @@ using namespace Catch::literals;
TEST_CASE("[Range] Equality operators") TEST_CASE("[Range] Equality operators")
{ {
Range<int> intRange {1, 1}; Range<int> intRange { 1, 1 };
REQUIRE( intRange == Range<int>(1, 1) ); REQUIRE(intRange == Range<int>(1, 1));
REQUIRE( intRange == std::pair<int, int>(1, 1) ); REQUIRE(intRange == std::pair<int, int>(1, 1));
REQUIRE( std::pair<int, int>(1, 1) == intRange ); REQUIRE(std::pair<int, int>(1, 1) == intRange);
Range<float> floatRange {1.0f, 1.0f}; Range<float> floatRange { 1.0f, 1.0f };
REQUIRE( floatRange == Range<float>(1.0f, 1.0f) ); REQUIRE(floatRange == Range<float>(1.0f, 1.0f));
REQUIRE( floatRange == std::pair<float, float>(1.0f, 1.0f) ); REQUIRE(floatRange == std::pair<float, float>(1.0f, 1.0f));
REQUIRE( std::pair<float, float>(1.0f, 1.0f) == floatRange); REQUIRE(std::pair<float, float>(1.0f, 1.0f) == floatRange);
} }
TEST_CASE("[Range] Default ranges for classical types") TEST_CASE("[Range] Default ranges for classical types")
{ {
Range<int> intRange; Range<int> intRange;
REQUIRE( intRange == Range<int>(0, 0) ); REQUIRE(intRange == Range<int>(0, 0));
Range<int32_t> int32_tRange; Range<int32_t> int32_tRange;
REQUIRE( int32_tRange == Range<int32_t>(0, 0) ); REQUIRE(int32_tRange == Range<int32_t>(0, 0));
Range<uint32_t> uint32_tRange; Range<uint32_t> uint32_tRange;
REQUIRE( uint32_tRange == Range<uint32_t>(0, 0) ); REQUIRE(uint32_tRange == Range<uint32_t>(0, 0));
Range<int64_t> int64_tRange; Range<int64_t> int64_tRange;
REQUIRE( int64_tRange == Range<int64_t>(0, 0) ); REQUIRE(int64_tRange == Range<int64_t>(0, 0));
Range<uint64_t> uint64_tRange; Range<uint64_t> uint64_tRange;
REQUIRE( uint64_tRange == Range<uint64_t>(0, 0) ); REQUIRE(uint64_tRange == Range<uint64_t>(0, 0));
Range<float> floatRange; Range<float> floatRange;
REQUIRE( floatRange == Range<float>(0, 0) ); REQUIRE(floatRange == Range<float>(0, 0));
Range<double> doubleRange; Range<double> doubleRange;
REQUIRE( doubleRange == Range<double>(0, 0) ); REQUIRE(doubleRange == Range<double>(0, 0));
} }
TEST_CASE("[Range] Contains") TEST_CASE("[Range] Contains")
{ {
Range<int> intRange {1, 10}; Range<int> intRange { 1, 10 };
REQUIRE( !intRange.contains(0) ); REQUIRE(!intRange.contains(0));
REQUIRE( intRange.contains(1) ); REQUIRE(intRange.contains(1));
REQUIRE( intRange.contains(5) ); REQUIRE(intRange.contains(5));
REQUIRE( !intRange.contains(10) ); REQUIRE(!intRange.contains(10));
REQUIRE( !intRange.containsWithEnd(0) ); REQUIRE(!intRange.containsWithEnd(0));
REQUIRE( intRange.containsWithEnd(1) ); REQUIRE(intRange.containsWithEnd(1));
REQUIRE( intRange.containsWithEnd(5) ); REQUIRE(intRange.containsWithEnd(5));
REQUIRE( intRange.containsWithEnd(10) ); REQUIRE(intRange.containsWithEnd(10));
Range<float> floatRange {1.0, 10.0}; Range<float> floatRange { 1.0, 10.0 };
REQUIRE( !floatRange.contains(0.0) ); REQUIRE(!floatRange.contains(0.0));
REQUIRE( floatRange.contains(1.0) ); REQUIRE(floatRange.contains(1.0));
REQUIRE( floatRange.contains(5.0) ); REQUIRE(floatRange.contains(5.0));
REQUIRE( !floatRange.contains(10.0) ); REQUIRE(!floatRange.contains(10.0));
REQUIRE( !floatRange.containsWithEnd(0.0) ); REQUIRE(!floatRange.containsWithEnd(0.0));
REQUIRE( floatRange.containsWithEnd(1.0) ); REQUIRE(floatRange.containsWithEnd(1.0));
REQUIRE( floatRange.containsWithEnd(5.0) ); REQUIRE(floatRange.containsWithEnd(5.0));
REQUIRE( floatRange.containsWithEnd(10.0) ); REQUIRE(floatRange.containsWithEnd(10.0));
} }
TEST_CASE("[Range] Clamp") TEST_CASE("[Range] Clamp")
{ {
Range<int> intRange {1, 10}; Range<int> intRange { 1, 10 };
REQUIRE( intRange.clamp(0) == 1 ); REQUIRE(intRange.clamp(0) == 1);
REQUIRE( intRange.clamp(1) == 1 ); REQUIRE(intRange.clamp(1) == 1);
REQUIRE( intRange.clamp(5) == 5 ); REQUIRE(intRange.clamp(5) == 5);
REQUIRE( intRange.clamp(10) == 10 ); REQUIRE(intRange.clamp(10) == 10);
REQUIRE( intRange.clamp(11) == 10 ); REQUIRE(intRange.clamp(11) == 10);
Range<float> floatRange {1.0, 10.0}; Range<float> floatRange { 1.0, 10.0 };
REQUIRE( floatRange.clamp(0.0) == 1.0_a ); REQUIRE(floatRange.clamp(0.0) == 1.0_a);
REQUIRE( floatRange.clamp(1.0) == 1.0_a ); REQUIRE(floatRange.clamp(1.0) == 1.0_a);
REQUIRE( floatRange.clamp(5.0) == 5.0_a ); REQUIRE(floatRange.clamp(5.0) == 5.0_a);
REQUIRE( floatRange.clamp(10.0) == 10.0_a ); REQUIRE(floatRange.clamp(10.0) == 10.0_a);
REQUIRE( floatRange.clamp(11.0) == 10.0_a ); REQUIRE(floatRange.clamp(11.0) == 10.0_a);
} }
TEST_CASE("[Range] shrinkIfSmaller") TEST_CASE("[Range] shrinkIfSmaller")
{ {
Range<int> intRange {2, 10}; Range<int> intRange { 2, 10 };
intRange.shrinkIfSmaller(0, 10); intRange.shrinkIfSmaller(0, 10);
REQUIRE( intRange == Range<int>(2, 10) ); REQUIRE(intRange == Range<int>(2, 10));
intRange.shrinkIfSmaller(2, 11); intRange.shrinkIfSmaller(2, 11);
REQUIRE( intRange == Range<int>(2, 10) ); REQUIRE(intRange == Range<int>(2, 10));
intRange.shrinkIfSmaller(2, 9); intRange.shrinkIfSmaller(2, 9);
REQUIRE( intRange == Range<int>(2, 9) ); REQUIRE(intRange == Range<int>(2, 9));
intRange.shrinkIfSmaller(3, 9); intRange.shrinkIfSmaller(3, 9);
REQUIRE( intRange == Range<int>(3, 9) ); REQUIRE(intRange == Range<int>(3, 9));
intRange.shrinkIfSmaller(4, 7); intRange.shrinkIfSmaller(4, 7);
REQUIRE( intRange == Range<int>(4, 7) ); REQUIRE(intRange == Range<int>(4, 7));
intRange.shrinkIfSmaller(6, 5); intRange.shrinkIfSmaller(6, 5);
REQUIRE( intRange == Range<int>(5, 6) ); REQUIRE(intRange == Range<int>(5, 6));
} }

View file

@ -1,5 +1,5 @@
#include "catch2/catch.hpp"
#include "../sources/Parser.h" #include "../sources/Parser.h"
#include "catch2/catch.hpp"
using namespace Catch::literals; using namespace Catch::literals;
void includeTest(const std::string& line, const std::string& fileName) void includeTest(const std::string& line, const std::string& fileName)
@ -57,7 +57,7 @@ TEST_CASE("[Regex] Header")
SECTION("Basic header match") SECTION("Basic header match")
{ {
std::smatch headerMatch; std::smatch headerMatch;
std::string line{"<header>param1=value1 param2=value2<next>"}; std::string line { "<header>param1=value1 param2=value2<next>" };
auto found = std::regex_search(line, headerMatch, sfz::Regexes::headers); auto found = std::regex_search(line, headerMatch, sfz::Regexes::headers);
REQUIRE(found); REQUIRE(found);
REQUIRE(headerMatch[1] == "header"); REQUIRE(headerMatch[1] == "header");
@ -66,7 +66,7 @@ TEST_CASE("[Regex] Header")
SECTION("EOL header match") SECTION("EOL header match")
{ {
std::smatch headerMatch; std::smatch headerMatch;
std::string line{"<header>param1=value1 param2=value2"}; std::string line { "<header>param1=value1 param2=value2" };
auto found = std::regex_search(line, headerMatch, sfz::Regexes::headers); auto found = std::regex_search(line, headerMatch, sfz::Regexes::headers);
REQUIRE(found); REQUIRE(found);
REQUIRE(headerMatch[1] == "header"); REQUIRE(headerMatch[1] == "header");
@ -74,7 +74,7 @@ TEST_CASE("[Regex] Header")
} }
} }
void memberTest(const std::string &line, const std::string &variable, const std::string &value) void memberTest(const std::string& line, const std::string& variable, const std::string& value)
{ {
std::smatch memberMatch; std::smatch memberMatch;
auto found = std::regex_search(line, memberMatch, sfz::Regexes::members); auto found = std::regex_search(line, memberMatch, sfz::Regexes::members);
@ -114,7 +114,7 @@ void parameterTest(const std::string& line, const std::string& opcode, const std
REQUIRE(parameterMatch[2] == parameter); REQUIRE(parameterMatch[2] == parameter);
} }
void parameterFail(const std::string &line) void parameterFail(const std::string& line)
{ {
std::smatch parameterMatch; std::smatch parameterMatch;
auto found = std::regex_search(line, parameterMatch, sfz::Regexes::opcodeParameters); auto found = std::regex_search(line, parameterMatch, sfz::Regexes::opcodeParameters);

View file

@ -1,15 +1,15 @@
#include "catch2/catch.hpp"
#include "../sources/Region.h" #include "../sources/Region.h"
#include "catch2/catch.hpp"
using namespace Catch::literals; using namespace Catch::literals;
TEST_CASE("Region activation", "Region tests") TEST_CASE("Region activation", "Region tests")
{ {
sfz::Region region { }; sfz::Region region {};
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
SECTION("Basic state") SECTION("Basic state")
{ {
region.registerCC(1, 4, 0); region.registerCC(1, 4, 0);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
} }
SECTION("Single CC range") SECTION("Single CC range")
@ -17,19 +17,19 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "locc4", "56" }); region.parseOpcode({ "locc4", "56" });
region.parseOpcode({ "hicc4", "59" }); region.parseOpcode({ "hicc4", "59" });
region.registerCC(1, 4, 0); region.registerCC(1, 4, 0);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 4, 57); region.registerCC(1, 4, 57);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 56); region.registerCC(1, 4, 56);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 59); region.registerCC(1, 4, 59);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 43); region.registerCC(1, 4, 43);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 4, 65); region.registerCC(1, 4, 65);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 6, 57); region.registerCC(1, 6, 57);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("Multiple CC ranges") SECTION("Multiple CC ranges")
@ -40,25 +40,25 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "hicc54", "27" }); region.parseOpcode({ "hicc54", "27" });
region.registerCC(1, 4, 0); region.registerCC(1, 4, 0);
region.registerCC(1, 54, 0); region.registerCC(1, 54, 0);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 4, 57); region.registerCC(1, 4, 57);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 54, 19); region.registerCC(1, 54, 19);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 54, 18); region.registerCC(1, 54, 18);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 54, 27); region.registerCC(1, 54, 27);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 56); region.registerCC(1, 4, 56);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 59); region.registerCC(1, 4, 59);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 54, 2); region.registerCC(1, 54, 2);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerCC(1, 54, 26); region.registerCC(1, 54, 26);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerCC(1, 4, 65); region.registerCC(1, 4, 65);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("Bend ranges") SECTION("Bend ranges")
@ -66,13 +66,13 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "lobend", "56" }); region.parseOpcode({ "lobend", "56" });
region.parseOpcode({ "hibend", "243" }); region.parseOpcode({ "hibend", "243" });
region.registerPitchWheel(1, 0); region.registerPitchWheel(1, 0);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerPitchWheel(1, 56); region.registerPitchWheel(1, 56);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerPitchWheel(1, 243); region.registerPitchWheel(1, 243);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerPitchWheel(1, 245); region.registerPitchWheel(1, 245);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("Aftertouch ranges") SECTION("Aftertouch ranges")
@ -80,13 +80,13 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "lochanaft", "56" }); region.parseOpcode({ "lochanaft", "56" });
region.parseOpcode({ "hichanaft", "68" }); region.parseOpcode({ "hichanaft", "68" });
region.registerAftertouch(1, 0); region.registerAftertouch(1, 0);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerAftertouch(1, 56); region.registerAftertouch(1, 56);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerAftertouch(1, 68); region.registerAftertouch(1, 68);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerAftertouch(1, 98); region.registerAftertouch(1, 98);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("BPM ranges") SECTION("BPM ranges")
@ -94,26 +94,26 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "lobpm", "56" }); region.parseOpcode({ "lobpm", "56" });
region.parseOpcode({ "hibpm", "68" }); region.parseOpcode({ "hibpm", "68" });
region.registerTempo(2.0f); region.registerTempo(2.0f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerTempo(0.90f); region.registerTempo(0.90f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerTempo(1.01f); region.registerTempo(1.01f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerTempo(1.1f); region.registerTempo(1.1f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
// TODO: add keyswitches // TODO: add keyswitches
SECTION("Keyswitches: sw_last") SECTION("Keyswitches: sw_last")
{ {
region.parseOpcode({ "sw_last", "40" }); region.parseOpcode({ "sw_last", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 64, 0.5f); region.registerNoteOff(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
} }
@ -122,20 +122,20 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "sw_lokey", "30" }); region.parseOpcode({ "sw_lokey", "30" });
region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_hikey", "50" });
region.parseOpcode({ "sw_last", "40" }); region.parseOpcode({ "sw_last", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 60, 64, 0.5f); region.registerNoteOn(1, 60, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 60, 0, 0.5f); region.registerNoteOff(1, 60, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 60, 64, 0.5f); region.registerNoteOn(1, 60, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 60, 0, 0.5f); region.registerNoteOff(1, 60, 0, 0.5f);
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
} }
@ -144,20 +144,20 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "sw_lokey", "30" }); region.parseOpcode({ "sw_lokey", "30" });
region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_hikey", "50" });
region.parseOpcode({ "sw_down", "40" }); region.parseOpcode({ "sw_down", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 60, 64, 0.5f); region.registerNoteOn(1, 60, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 60, 0, 0.5f); region.registerNoteOff(1, 60, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 60, 64, 0.5f); region.registerNoteOn(1, 60, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 60, 0, 0.5f); region.registerNoteOff(1, 60, 0, 0.5f);
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
} }
@ -166,39 +166,39 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "sw_lokey", "30" }); region.parseOpcode({ "sw_lokey", "30" });
region.parseOpcode({ "sw_hikey", "50" }); region.parseOpcode({ "sw_hikey", "50" });
region.parseOpcode({ "sw_up", "40" }); region.parseOpcode({ "sw_up", "40" });
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
} }
SECTION("Keyswitches: sw_previous") SECTION("Keyswitches: sw_previous")
{ {
region.parseOpcode({ "sw_previous", "40" }); region.parseOpcode({ "sw_previous", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 41, 64, 0.5f); region.registerNoteOn(1, 41, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("Sequences: length 2, default position") SECTION("Sequences: length 2, default position")
@ -206,63 +206,60 @@ TEST_CASE("Region activation", "Region tests")
region.parseOpcode({ "seq_length", "2" }); region.parseOpcode({ "seq_length", "2" });
region.parseOpcode({ "seq_position", "1" }); region.parseOpcode({ "seq_position", "1" });
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
} }
SECTION("Sequences: length 2, position 2") SECTION("Sequences: length 2, position 2")
{ {
region.parseOpcode({ "seq_length", "2" }); region.parseOpcode({ "seq_length", "2" });
region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "seq_position", "2" });
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
} }
SECTION("Sequences: length 3, position 2") SECTION("Sequences: length 3, position 2")
{ {
region.parseOpcode({ "seq_length", "3" }); region.parseOpcode({ "seq_length", "3" });
region.parseOpcode({ "seq_position", "2" }); region.parseOpcode({ "seq_position", "2" });
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( !region.isSwitchedOn() ); REQUIRE(!region.isSwitchedOn());
region.registerNoteOn(1, 40, 64, 0.5f); region.registerNoteOn(1, 40, 64, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
REQUIRE( region.isSwitchedOn() ); REQUIRE(region.isSwitchedOn());
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
#include "catch2/catch.hpp"
#include "../sources/Region.h" #include "../sources/Region.h"
#include "catch2/catch.hpp"
using namespace Catch::literals; using namespace Catch::literals;
TEST_CASE("Basic triggers", "Region triggers") TEST_CASE("Basic triggers", "Region triggers")
@ -9,44 +9,44 @@ TEST_CASE("Basic triggers", "Region triggers")
SECTION("key") SECTION("key")
{ {
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
REQUIRE( region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE( !region.registerCC(1, 63, 64) ); REQUIRE(!region.registerCC(1, 63, 64));
} }
SECTION("lokey and hikey") SECTION("lokey and hikey")
{ {
region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "42" }); region.parseOpcode({ "hikey", "42" });
REQUIRE( !region.registerNoteOn(1, 39, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 39, 64, 0.5f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE( region.registerNoteOn(1, 42, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 42, 64, 0.5f));
REQUIRE( !region.registerNoteOn(1, 43, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 43, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 42, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 42, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 42, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 42, 64, 0.5f));
REQUIRE( !region.registerCC(1, 63, 64) ); REQUIRE(!region.registerCC(1, 63, 64));
} }
SECTION("key and release trigger") SECTION("key and release trigger")
{ {
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "trigger", "release" }); region.parseOpcode({ "trigger", "release" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOff(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 41, 64, 0.5f));
REQUIRE( !region.registerCC(1, 63, 64) ); REQUIRE(!region.registerCC(1, 63, 64));
} }
SECTION("key and release_key trigger") SECTION("key and release_key trigger")
{ {
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "trigger", "release_key" }); region.parseOpcode({ "trigger", "release_key" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOff(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOff(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
REQUIRE( !region.registerNoteOff(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOff(1, 41, 64, 0.5f));
REQUIRE( !region.registerCC(1, 63, 64) ); REQUIRE(!region.registerCC(1, 63, 64));
} }
// TODO: first and legato triggers // TODO: first and legato triggers
SECTION("lovel and hivel") SECTION("lovel and hivel")
@ -54,22 +54,22 @@ TEST_CASE("Basic triggers", "Region triggers")
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lovel", "60" }); region.parseOpcode({ "lovel", "60" });
region.parseOpcode({ "hivel", "70" }); region.parseOpcode({ "hivel", "70" });
REQUIRE( region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(1, 40, 60, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 60, 0.5f));
REQUIRE( region.registerNoteOn(1, 40, 70, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 70, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 71, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 71, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 59, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 59, 0.5f));
} }
SECTION("lochan and hichan") SECTION("lochan and hichan")
{ {
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lochan", "2" }); region.parseOpcode({ "lochan", "2" });
region.parseOpcode({ "hichan", "4" }); region.parseOpcode({ "hichan", "4" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(2, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(2, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(3, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(3, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(4, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(4, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOn(5, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(5, 40, 64, 0.5f));
} }
SECTION("lorand and hirand") SECTION("lorand and hirand")
@ -77,37 +77,37 @@ TEST_CASE("Basic triggers", "Region triggers")
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lorand", "0.35" }); region.parseOpcode({ "lorand", "0.35" });
region.parseOpcode({ "hirand", "0.40" }); region.parseOpcode({ "hirand", "0.40" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.34f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.34f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.35f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.35f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.36f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.36f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.37f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.37f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.38f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.38f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.39f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.39f));
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.40f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.40f));
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.41f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.41f));
} }
SECTION("lorand and hirand on 1.0f") SECTION("lorand and hirand on 1.0f")
{ {
region.parseOpcode({ "key", "40" }); region.parseOpcode({ "key", "40" });
region.parseOpcode({ "lorand", "0.35" }); region.parseOpcode({ "lorand", "0.35" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.34f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.34f));
REQUIRE( region.registerNoteOn(1, 40, 64, 0.35f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.35f));
REQUIRE( region.registerNoteOn(1, 40, 64, 1.0f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 1.0f));
} }
SECTION("on_loccN, on_hiccN") SECTION("on_loccN, on_hiccN")
{ {
region.parseOpcode({ "on_locc47", "64" }); region.parseOpcode({ "on_locc47", "64" });
region.parseOpcode({ "on_hicc47", "68" }); region.parseOpcode({ "on_hicc47", "68" });
REQUIRE( !region.registerCC(1, 47, 63) ); REQUIRE(!region.registerCC(1, 47, 63));
REQUIRE( region.registerCC(1, 47, 64) ); REQUIRE(region.registerCC(1, 47, 64));
REQUIRE( region.registerCC(1, 47, 65) ); REQUIRE(region.registerCC(1, 47, 65));
REQUIRE( region.registerCC(1, 47, 66) ); REQUIRE(region.registerCC(1, 47, 66));
REQUIRE( region.registerCC(1, 47, 67) ); REQUIRE(region.registerCC(1, 47, 67));
REQUIRE( region.registerCC(1, 47, 68) ); REQUIRE(region.registerCC(1, 47, 68));
REQUIRE( !region.registerCC(1, 47, 69) ); REQUIRE(!region.registerCC(1, 47, 69));
REQUIRE( !region.registerCC(1, 40, 64) ); REQUIRE(!region.registerCC(1, 40, 64));
} }
} }
@ -120,11 +120,11 @@ TEST_CASE("Legato triggers", "Region triggers")
region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "hikey", "50" });
region.parseOpcode({ "trigger", "first" }); region.parseOpcode({ "trigger", "first" });
REQUIRE( region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( !region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 41, 64, 0.5f));
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE( region.registerNoteOn(1, 42, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 42, 64, 0.5f));
} }
SECTION("Second note playing") SECTION("Second note playing")
@ -132,10 +132,10 @@ TEST_CASE("Legato triggers", "Region triggers")
region.parseOpcode({ "lokey", "40" }); region.parseOpcode({ "lokey", "40" });
region.parseOpcode({ "hikey", "50" }); region.parseOpcode({ "hikey", "50" });
region.parseOpcode({ "trigger", "legato" }); region.parseOpcode({ "trigger", "legato" });
REQUIRE( !region.registerNoteOn(1, 40, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 40, 64, 0.5f));
REQUIRE( region.registerNoteOn(1, 41, 64, 0.5f) ); REQUIRE(region.registerNoteOn(1, 41, 64, 0.5f));
region.registerNoteOff(1, 40, 0, 0.5f); region.registerNoteOff(1, 40, 0, 0.5f);
region.registerNoteOff(1, 41, 0, 0.5f); region.registerNoteOff(1, 41, 0, 0.5f);
REQUIRE( !region.registerNoteOn(1, 42, 64, 0.5f) ); REQUIRE(!region.registerNoteOn(1, 42, 64, 0.5f));
} }
} }

View file

@ -1,10 +1,10 @@
#include "catch2/catch.hpp"
#include "../sources/SIMDHelpers.h" #include "../sources/SIMDHelpers.h"
#include <array> #include "catch2/catch.hpp"
#include <algorithm>
#include <iostream>
#include <absl/types/span.h>
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
#include <absl/types/span.h>
#include <algorithm>
#include <array>
#include <iostream>
using namespace Catch::literals; using namespace Catch::literals;
constexpr int smallBufferSize { 3 }; constexpr int smallBufferSize { 3 };
@ -12,15 +12,14 @@ constexpr int bigBufferSize { 4095 };
constexpr int medBufferSize { 127 }; constexpr int medBufferSize { 127 };
constexpr double fillValue { 1.3 }; constexpr double fillValue { 1.3 };
template<class Type> template <class Type>
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps=1e-3) inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size())
return false; return false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
{
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n'; std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
return false; return false;
} }
@ -30,7 +29,7 @@ inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs,
TEST_CASE("[Helpers] fill() - Manual buffer") TEST_CASE("[Helpers] fill() - Manual buffer")
{ {
std::vector<float> buffer (5); std::vector<float> buffer(5);
std::vector<float> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::vector<float> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
fill<float, false>(absl::MakeSpan(buffer), fillValue); fill<float, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
@ -38,8 +37,8 @@ TEST_CASE("[Helpers] fill() - Manual buffer")
TEST_CASE("[Helpers] fill() - Small buffer") TEST_CASE("[Helpers] fill() - Small buffer")
{ {
std::vector<float> buffer (smallBufferSize); std::vector<float> buffer(smallBufferSize);
std::vector<float> expected (smallBufferSize); std::vector<float> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<float, false>(absl::MakeSpan(buffer), fillValue); fill<float, false>(absl::MakeSpan(buffer), fillValue);
@ -48,8 +47,8 @@ TEST_CASE("[Helpers] fill() - Small buffer")
TEST_CASE("[Helpers] fill() - Big buffer") TEST_CASE("[Helpers] fill() - Big buffer")
{ {
std::vector<float> buffer (bigBufferSize); std::vector<float> buffer(bigBufferSize);
std::vector<float> expected (bigBufferSize); std::vector<float> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<float, false>(absl::MakeSpan(buffer), fillValue); fill<float, false>(absl::MakeSpan(buffer), fillValue);
@ -58,8 +57,8 @@ TEST_CASE("[Helpers] fill() - Big buffer")
TEST_CASE("[Helpers] fill() - Small buffer -- SIMD") TEST_CASE("[Helpers] fill() - Small buffer -- SIMD")
{ {
std::vector<float> buffer (smallBufferSize); std::vector<float> buffer(smallBufferSize);
std::vector<float> expected (smallBufferSize); std::vector<float> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<float, true>(absl::MakeSpan(buffer), fillValue); fill<float, true>(absl::MakeSpan(buffer), fillValue);
@ -68,8 +67,8 @@ TEST_CASE("[Helpers] fill() - Small buffer -- SIMD")
TEST_CASE("[Helpers] fill() - Big buffer -- SIMD") TEST_CASE("[Helpers] fill() - Big buffer -- SIMD")
{ {
std::vector<float> buffer (bigBufferSize); std::vector<float> buffer(bigBufferSize);
std::vector<float> expected (bigBufferSize); std::vector<float> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<float, true>(absl::MakeSpan(buffer), fillValue); fill<float, true>(absl::MakeSpan(buffer), fillValue);
@ -78,8 +77,8 @@ TEST_CASE("[Helpers] fill() - Big buffer -- SIMD")
TEST_CASE("[Helpers] fill() - Small buffer -- doubles") TEST_CASE("[Helpers] fill() - Small buffer -- doubles")
{ {
std::vector<double> buffer (smallBufferSize); std::vector<double> buffer(smallBufferSize);
std::vector<double> expected (smallBufferSize); std::vector<double> expected(smallBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<double, false>(absl::MakeSpan(buffer), fillValue); fill<double, false>(absl::MakeSpan(buffer), fillValue);
@ -88,18 +87,17 @@ TEST_CASE("[Helpers] fill() - Small buffer -- doubles")
TEST_CASE("[Helpers] fill() - Big buffer -- doubles") TEST_CASE("[Helpers] fill() - Big buffer -- doubles")
{ {
std::vector<double> buffer (bigBufferSize); std::vector<double> buffer(bigBufferSize);
std::vector<double> expected (bigBufferSize); std::vector<double> expected(bigBufferSize);
std::fill(expected.begin(), expected.end(), fillValue); std::fill(expected.begin(), expected.end(), fillValue);
fill<double, false>(absl::MakeSpan(buffer), fillValue); fill<double, false>(absl::MakeSpan(buffer), fillValue);
REQUIRE(buffer == expected); REQUIRE(buffer == expected);
} }
TEST_CASE("[Helpers] Interleaved read") TEST_CASE("[Helpers] Interleaved read")
{ {
std::array<float, 16> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; std::array<float, 16> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
std::array<float, 16> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; std::array<float, 16> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput; std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput; std::array<float, 8> rightOutput;
@ -107,50 +105,50 @@ TEST_CASE("[Helpers] Interleaved read")
std::array<float, 16> real; std::array<float, 16> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Interleaved read unaligned end") TEST_CASE("[Helpers] Interleaved read unaligned end")
{ {
std::array<float, 20> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array<float, 20> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
std::array<float, 20> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; std::array<float, 20> expected { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput; std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput; std::array<float, 10> rightOutput;
readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput)); readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real; std::array<float, 20> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Small interleaved read unaligned end") TEST_CASE("[Helpers] Small interleaved read unaligned end")
{ {
std::array<float, 6> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; std::array<float, 6> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f}; std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput; std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput; std::array<float, 3> rightOutput;
readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput)); readInterleaved<float, false>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real; std::array<float, 6> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Interleaved read -- SIMD") TEST_CASE("[Helpers] Interleaved read -- SIMD")
{ {
std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; std::array<float, 16> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; std::array<float, 16> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 8> leftOutput; std::array<float, 8> leftOutput;
std::array<float, 8> rightOutput; std::array<float, 8> rightOutput;
@ -158,45 +156,45 @@ TEST_CASE("[Helpers] Interleaved read -- SIMD")
std::array<float, 16> real; std::array<float, 16> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD") TEST_CASE("[Helpers] Interleaved read unaligned end -- SIMD")
{ {
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; std::array<float, 20> expected = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 10> leftOutput; std::array<float, 10> leftOutput;
std::array<float, 10> rightOutput; std::array<float, 10> rightOutput;
readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput)); readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 20> real; std::array<float, 20> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD") TEST_CASE("[Helpers] Small interleaved read unaligned end -- SIMD")
{ {
std::array<float, 6> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; std::array<float, 6> input { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f}; std::array<float, 6> expected { 0.0f, 1.0f, 2.0f, 10.0f, 11.0f, 12.0f };
std::array<float, 3> leftOutput; std::array<float, 3> leftOutput;
std::array<float, 3> rightOutput; std::array<float, 3> rightOutput;
readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput)); readInterleaved<float, true>(input, absl::MakeSpan(leftOutput), absl::MakeSpan(rightOutput));
std::array<float, 6> real; std::array<float, 6> real;
auto realIdx = 0; auto realIdx = 0;
for (auto value: leftOutput) for (auto value : leftOutput)
real[realIdx++] = value; real[realIdx++] = value;
for (auto value: rightOutput) for (auto value : rightOutput)
real[realIdx++] = value; real[realIdx++] = value;
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar") TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar")
@ -209,68 +207,86 @@ TEST_CASE("[Helpers] Interleaved read SIMD vs Scalar")
std::iota(input.begin(), input.end(), 0.0f); std::iota(input.begin(), input.end(), 0.0f);
readInterleaved<float, false>(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar)); readInterleaved<float, false>(input, absl::MakeSpan(leftOutputScalar), absl::MakeSpan(rightOutputScalar));
readInterleaved<float, true>(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD)); readInterleaved<float, true>(input, absl::MakeSpan(leftOutputSIMD), absl::MakeSpan(rightOutputSIMD));
REQUIRE( leftOutputScalar == leftOutputSIMD ); REQUIRE(leftOutputScalar == leftOutputSIMD);
REQUIRE( rightOutputScalar == rightOutputSIMD ); REQUIRE(rightOutputScalar == rightOutputSIMD);
} }
TEST_CASE("[Helpers] Interleaved write") TEST_CASE("[Helpers] Interleaved write")
{ {
std::array<float, 8> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, }; std::array<float, 8> leftInput {
0.0f,
1.0f,
2.0f,
3.0f,
4.0f,
5.0f,
6.0f,
7.0f,
};
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output; std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Interleaved write unaligned end") TEST_CASE("[Helpers] Interleaved write unaligned end")
{ {
std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f }; std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output; std::array<float, 20> output;
std::array<float, 20> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array<float, 20> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Small interleaved write unaligned end") TEST_CASE("[Helpers] Small interleaved write unaligned end")
{ {
std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f}; std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f };
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f }; std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Interleaved write -- SIMD") TEST_CASE("[Helpers] Interleaved write -- SIMD")
{ {
std::array<float, 8> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, }; std::array<float, 8> leftInput {
0.0f,
1.0f,
2.0f,
3.0f,
4.0f,
5.0f,
6.0f,
7.0f,
};
std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f }; std::array<float, 8> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f };
std::array<float, 16> output; std::array<float, 16> output;
std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f}; std::array<float, 16> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f };
writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD") TEST_CASE("[Helpers] Interleaved write unaligned end -- SIMD")
{ {
std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; std::array<float, 10> leftInput { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f };
std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f }; std::array<float, 10> rightInput { 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output; std::array<float, 20> output;
std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array<float, 20> expected = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD") TEST_CASE("[Helpers] Small interleaved write unaligned end -- SIMD")
{ {
std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f}; std::array<float, 3> leftInput { 0.0f, 1.0f, 2.0f };
std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f }; std::array<float, 3> rightInput { 10.0f, 11.0f, 12.0f };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; std::array<float, 6> expected { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output)); writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar") TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar")
@ -283,7 +299,7 @@ TEST_CASE("[Helpers] Interleaved write SIMD vs Scalar")
std::iota(rightInput.begin(), rightInput.end(), medBufferSize); std::iota(rightInput.begin(), rightInput.end(), medBufferSize);
writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(outputScalar)); writeInterleaved<float, false>(leftInput, rightInput, absl::MakeSpan(outputScalar));
writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(outputSIMD)); writeInterleaved<float, true>(leftInput, rightInput, absl::MakeSpan(outputSIMD));
REQUIRE( outputScalar == outputSIMD ); REQUIRE(outputScalar == outputSIMD);
} }
TEST_CASE("[Helpers] Gain, single") TEST_CASE("[Helpers] Gain, single")
@ -292,7 +308,7 @@ TEST_CASE("[Helpers] Gain, single")
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
applyGain<float, false>(fillValue, input, absl::MakeSpan(output)); applyGain<float, false>(fillValue, input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Gain, single and inplace") TEST_CASE("[Helpers] Gain, single and inplace")
@ -300,7 +316,7 @@ TEST_CASE("[Helpers] Gain, single and inplace")
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
applyGain<float, false>(fillValue, buffer, absl::MakeSpan(buffer)); applyGain<float, false>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE( buffer == expected ); REQUIRE(buffer == expected);
} }
TEST_CASE("[Helpers] Gain, spans") TEST_CASE("[Helpers] Gain, spans")
@ -310,7 +326,7 @@ TEST_CASE("[Helpers] Gain, spans")
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
applyGain<float, false>(gain, input, absl::MakeSpan(output)); applyGain<float, false>(gain, input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Gain, spans and inplace") TEST_CASE("[Helpers] Gain, spans and inplace")
@ -319,7 +335,7 @@ TEST_CASE("[Helpers] Gain, spans and inplace")
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
applyGain<float, false>(gain, buffer, absl::MakeSpan(buffer)); applyGain<float, false>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE( buffer == expected ); REQUIRE(buffer == expected);
} }
TEST_CASE("[Helpers] Gain, single (SIMD)") TEST_CASE("[Helpers] Gain, single (SIMD)")
@ -328,7 +344,7 @@ TEST_CASE("[Helpers] Gain, single (SIMD)")
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
applyGain<float, true>(fillValue, input, absl::MakeSpan(output)); applyGain<float, true>(fillValue, input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Gain, single and inplace (SIMD)") TEST_CASE("[Helpers] Gain, single and inplace (SIMD)")
@ -336,7 +352,7 @@ TEST_CASE("[Helpers] Gain, single and inplace (SIMD)")
std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> buffer { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue }; std::array<float, 5> expected { fillValue, fillValue, fillValue, fillValue, fillValue };
applyGain<float, true>(fillValue, buffer, absl::MakeSpan(buffer)); applyGain<float, true>(fillValue, buffer, absl::MakeSpan(buffer));
REQUIRE( buffer == expected ); REQUIRE(buffer == expected);
} }
TEST_CASE("[Helpers] Gain, spans (SIMD)") TEST_CASE("[Helpers] Gain, spans (SIMD)")
@ -346,7 +362,7 @@ TEST_CASE("[Helpers] Gain, spans (SIMD)")
std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> output { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
applyGain<float, true>(gain, input, absl::MakeSpan(output)); applyGain<float, true>(gain, input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)") TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)")
@ -355,12 +371,12 @@ TEST_CASE("[Helpers] Gain, spans and inplace (SIMD)")
std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> gain { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; std::array<float, 5> expected { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
applyGain<float, true>(gain, buffer, absl::MakeSpan(buffer)); applyGain<float, true>(gain, buffer, absl::MakeSpan(buffer));
REQUIRE( buffer == expected ); REQUIRE(buffer == expected);
} }
TEST_CASE("[Helpers] SFZ looping index") TEST_CASE("[Helpers] SFZ looping index")
{ {
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f}; // 1.1 2.3 3.6 5.0 6.5 8.1 std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0 6.5 8.1
std::array<int, 6> indices; std::array<int, 6> indices;
std::array<float, 6> leftCoeffs; std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs; std::array<float, 6> rightCoeffs;
@ -368,14 +384,14 @@ TEST_CASE("[Helpers] SFZ looping index")
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f }; std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f }; std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f };
loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1); loopingSFZIndex<float, false>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1);
REQUIRE( indices == expectedIndices ); REQUIRE(indices == expectedIndices);
REQUIRE( approxEqual<float>(leftCoeffs, expectedLeft) ); REQUIRE(approxEqual<float>(leftCoeffs, expectedLeft));
REQUIRE( approxEqual<float>(rightCoeffs, expectedRight) ); REQUIRE(approxEqual<float>(rightCoeffs, expectedRight));
} }
TEST_CASE("[Helpers] SFZ looping index (SIMD)") TEST_CASE("[Helpers] SFZ looping index (SIMD)")
{ {
std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f}; // 1.1 2.3 3.6 5.0 6.5 8.1 std::array<float, 6> jumps { 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f }; // 1.1 2.3 3.6 5.0 6.5 8.1
std::array<int, 6> indices; std::array<int, 6> indices;
std::array<float, 6> leftCoeffs; std::array<float, 6> leftCoeffs;
std::array<float, 6> rightCoeffs; std::array<float, 6> rightCoeffs;
@ -383,9 +399,9 @@ TEST_CASE("[Helpers] SFZ looping index (SIMD)")
std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f }; std::array<float, 6> expectedLeft { 0.9f, 0.7f, 0.4f, 1.0f, 0.5f, 0.9f };
std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f }; std::array<float, 6> expectedRight { 0.1f, 0.3f, 0.6f, 0.0f, 0.5f, 0.1f };
loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1); loopingSFZIndex<float, true>(jumps, absl::MakeSpan(leftCoeffs), absl::MakeSpan(rightCoeffs), absl::MakeSpan(indices), 1.0f, 6, 1);
REQUIRE( indices == expectedIndices ); REQUIRE(indices == expectedIndices);
REQUIRE( approxEqual<float>(leftCoeffs, expectedLeft) ); REQUIRE(approxEqual<float>(leftCoeffs, expectedLeft));
REQUIRE( approxEqual<float>(rightCoeffs, expectedRight) ); REQUIRE(approxEqual<float>(rightCoeffs, expectedRight));
} }
// TEST_CASE("[Helpers] SFZ looping index (SIMD vs Scalar)") // TEST_CASE("[Helpers] SFZ looping index (SIMD vs Scalar)")
@ -413,9 +429,9 @@ TEST_CASE("[Helpers] Linear Ramp")
const float start { 0.0f }; const float start { 0.0f };
const float v { fillValue }; const float v { fillValue };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected {v, v + v, v + v + v, v + v + v + v, v + v + v + v + v, v + v + v + v + v + v} ; std::array<float, 6> expected { v, v + v, v + v + v, v + v + v + v, v + v + v + v + v, v + v + v + v + v + v };
linearRamp<float, false>(absl::MakeSpan(output), start, v); linearRamp<float, false>(absl::MakeSpan(output), start, v);
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Linear Ramp (SIMD)") TEST_CASE("[Helpers] Linear Ramp (SIMD)")
@ -423,9 +439,9 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD)")
const float start { 0.0f }; const float start { 0.0f };
const float v { fillValue }; const float v { fillValue };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected {v, v + v, v + v + v, v + v + v + v, v + v + v + v + v, v + v + v + v + v + v} ; std::array<float, 6> expected { v, v + v, v + v + v, v + v + v + v, v + v + v + v + v, v + v + v + v + v + v };
linearRamp<float, true>(absl::MakeSpan(output), start, v); linearRamp<float, true>(absl::MakeSpan(output), start, v);
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)") TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)")
@ -435,7 +451,7 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)")
std::vector<float> outputSIMD(bigBufferSize); std::vector<float> outputSIMD(bigBufferSize);
linearRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue); linearRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue);
linearRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue); linearRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue);
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) ); REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
} }
TEST_CASE("[Helpers] Linear Ramp unaligned (SIMD vs scalar)") TEST_CASE("[Helpers] Linear Ramp unaligned (SIMD vs scalar)")
@ -445,7 +461,7 @@ TEST_CASE("[Helpers] Linear Ramp unaligned (SIMD vs scalar)")
std::vector<float> outputSIMD(bigBufferSize); std::vector<float> outputSIMD(bigBufferSize);
linearRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue); linearRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
linearRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue); linearRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) ); REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
} }
TEST_CASE("[Helpers] Multiplicative Ramp") TEST_CASE("[Helpers] Multiplicative Ramp")
@ -453,9 +469,9 @@ TEST_CASE("[Helpers] Multiplicative Ramp")
const float start { 1.0f }; const float start { 1.0f };
const float v { fillValue }; const float v { fillValue };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected {v, v * v, v * v * v, v * v * v * v, v * v * v * v * v, v * v * v * v * v * v} ; std::array<float, 6> expected { v, v * v, v * v * v, v * v * v * v, v * v * v * v * v, v * v * v * v * v * v };
multiplicativeRamp<float, false>(absl::MakeSpan(output), start, v); multiplicativeRamp<float, false>(absl::MakeSpan(output), start, v);
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Multiplicative Ramp (SIMD)") TEST_CASE("[Helpers] Multiplicative Ramp (SIMD)")
@ -463,9 +479,9 @@ TEST_CASE("[Helpers] Multiplicative Ramp (SIMD)")
const float start { 1.0f }; const float start { 1.0f };
const float v { fillValue }; const float v { fillValue };
std::array<float, 6> output; std::array<float, 6> output;
std::array<float, 6> expected {v, v * v, v * v * v, v * v * v * v, v * v * v * v * v, v * v * v * v * v * v} ; std::array<float, 6> expected { v, v * v, v * v * v, v * v * v * v, v * v * v * v * v, v * v * v * v * v * v };
multiplicativeRamp<float, true>(absl::MakeSpan(output), start, v); multiplicativeRamp<float, true>(absl::MakeSpan(output), start, v);
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Multiplicative Ramp (SIMD vs scalar)") TEST_CASE("[Helpers] Multiplicative Ramp (SIMD vs scalar)")
@ -475,7 +491,7 @@ TEST_CASE("[Helpers] Multiplicative Ramp (SIMD vs scalar)")
std::vector<float> outputSIMD(bigBufferSize); std::vector<float> outputSIMD(bigBufferSize);
multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue); multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar), start, fillValue);
multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue); multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD), start, fillValue);
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) ); REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
} }
TEST_CASE("[Helpers] Multiplicative Ramp unaligned (SIMD vs scalar)") TEST_CASE("[Helpers] Multiplicative Ramp unaligned (SIMD vs scalar)")
@ -485,7 +501,7 @@ TEST_CASE("[Helpers] Multiplicative Ramp unaligned (SIMD vs scalar)")
std::vector<float> outputSIMD(bigBufferSize); std::vector<float> outputSIMD(bigBufferSize);
multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue); multiplicativeRamp<float, false>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue); multiplicativeRamp<float, true>(absl::MakeSpan(outputSIMD).subspan(1), start, fillValue);
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) ); REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
} }
TEST_CASE("[Helpers] Add") TEST_CASE("[Helpers] Add")
@ -494,7 +510,7 @@ TEST_CASE("[Helpers] Add")
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 }; std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 };
add<float, false>(input, absl::MakeSpan(output)); add<float, false>(input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Add (SIMD)") TEST_CASE("[Helpers] Add (SIMD)")
@ -503,7 +519,7 @@ TEST_CASE("[Helpers] Add (SIMD)")
std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 5> output { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 }; std::array<float, 5> expected { 2.0, 3.0, 4.0, 5.0, 6.0 };
add<float, true>(input, absl::MakeSpan(output)); add<float, true>(input, absl::MakeSpan(output));
REQUIRE( output == expected ); REQUIRE(output == expected);
} }
TEST_CASE("[Helpers] Add (SIMD vs scalar)") TEST_CASE("[Helpers] Add (SIMD vs scalar)")
@ -517,5 +533,5 @@ TEST_CASE("[Helpers] Add (SIMD vs scalar)")
add<float, false>(input, absl::MakeSpan(outputScalar)); add<float, false>(input, absl::MakeSpan(outputScalar));
add<float, true>(input, absl::MakeSpan(outputSIMD)); add<float, true>(input, absl::MakeSpan(outputSIMD));
REQUIRE( approxEqual<float>(outputScalar, outputSIMD) ); REQUIRE(approxEqual<float>(outputScalar, outputSIMD));
} }

View file

@ -1,5 +1,5 @@
#include "catch2/catch.hpp"
#include "../sources/StereoBuffer.h" #include "../sources/StereoBuffer.h"
#include "catch2/catch.hpp"
#include <algorithm> #include <algorithm>
using namespace Catch::literals; using namespace Catch::literals;
@ -33,16 +33,14 @@ TEST_CASE("[StereoBuffer] Access")
{ {
const int size { 5 }; const int size { 5 };
StereoBuffer<double> doubleBuffer(size); StereoBuffer<double> doubleBuffer(size);
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx) for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx) {
{
doubleBuffer.getSample(Channel::left, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx; doubleBuffer.getSample(Channel::left, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx;
doubleBuffer.getSample(Channel::right, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx; doubleBuffer.getSample(Channel::right, frameIdx) = static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx;
} }
for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx) for (auto frameIdx = 0; frameIdx < doubleBuffer.getNumFrames(); ++frameIdx) {
{ REQUIRE(doubleBuffer.getSample(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx);
REQUIRE(doubleBuffer.getSample(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx); REQUIRE(doubleBuffer(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx);
REQUIRE(doubleBuffer(Channel::left, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) + frameIdx);
REQUIRE(doubleBuffer.getSample(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx); REQUIRE(doubleBuffer.getSample(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx);
REQUIRE(doubleBuffer(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx); REQUIRE(doubleBuffer(Channel::right, frameIdx) == static_cast<double>(doubleBuffer.getNumFrames()) - frameIdx);
} }
@ -56,17 +54,17 @@ TEST_CASE("[StereoBuffer] Iterators")
std::fill(buffer.begin(Channel::left), buffer.end(Channel::left), fillValue); std::fill(buffer.begin(Channel::left), buffer.end(Channel::left), fillValue);
std::fill(buffer.begin(Channel::right), buffer.end(Channel::right), fillValue); std::fill(buffer.begin(Channel::right), buffer.end(Channel::right), fillValue);
REQUIRE( std::all_of(buffer.begin(Channel::left), buffer.end(Channel::left), [fillValue](auto value) { return value == fillValue; }) ); REQUIRE(std::all_of(buffer.begin(Channel::left), buffer.end(Channel::left), [fillValue](auto value) { return value == fillValue; }));
REQUIRE( std::all_of(buffer.begin(Channel::right), buffer.end(Channel::right), [fillValue](auto value) { return value == fillValue; }) ); REQUIRE(std::all_of(buffer.begin(Channel::right), buffer.end(Channel::right), [fillValue](auto value) { return value == fillValue; }));
} }
template<class Type, unsigned int Alignment = 16> template <class Type, unsigned int Alignment = 16>
void channelAlignmentTest(int size) void channelAlignmentTest(int size)
{ {
static constexpr auto AlignmentMask { Alignment - 1 }; static constexpr auto AlignmentMask { Alignment - 1 };
StereoBuffer<Type, Alignment> buffer(size); StereoBuffer<Type, Alignment> buffer(size);
REQUIRE( ((size_t)buffer.getChannel(Channel::left) & AlignmentMask) == 0 ); REQUIRE(((size_t)buffer.getChannel(Channel::left) & AlignmentMask) == 0);
REQUIRE( ((size_t)buffer.getChannel(Channel::right) & AlignmentMask) == 0 ); REQUIRE(((size_t)buffer.getChannel(Channel::right) & AlignmentMask) == 0);
} }
TEST_CASE("[StereoBuffer] Channel alignments (floats)") TEST_CASE("[StereoBuffer] Channel alignments (floats)")
@ -140,19 +138,18 @@ TEST_CASE("[AudioBuffer] fills")
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::left, frameIdx); real[frameIdx] = buffer(Channel::left, frameIdx);
REQUIRE( real == expected ); REQUIRE(real == expected);
for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx) for (auto frameIdx = 0; frameIdx < buffer.getNumFrames(); ++frameIdx)
real[frameIdx] = buffer(Channel::right, frameIdx); real[frameIdx] = buffer(Channel::right, frameIdx);
REQUIRE( real == expected ); REQUIRE(real == expected);
} }
TEST_CASE("[AudioBuffer] Fill a big Audiobuffer") TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
{ {
constexpr int size { 2039247 }; constexpr int size { 2039247 };
StereoBuffer<float> buffer (size); StereoBuffer<float> buffer(size);
std::vector<float> input (2*size); std::vector<float> input(2 * size);
std::iota(input.begin(), input.end(), 1.0f); std::iota(input.begin(), input.end(), 1.0f);
buffer.readInterleaved(input); buffer.readInterleaved(input);
} }
@ -160,29 +157,29 @@ TEST_CASE("[AudioBuffer] Fill a big Audiobuffer")
TEST_CASE("[StereoBuffer] Interleaved write -- Scalar") TEST_CASE("[StereoBuffer] Interleaved write -- Scalar")
{ {
StereoBuffer<float> buffer(10); StereoBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f}; std::array<float, 20> input = { 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f, 17.0f, 18.0f, 19.0f };
std::array<float, 20> output { 0.0f }; std::array<float, 20> output { 0.0f };
buffer.readInterleaved(input); buffer.readInterleaved(input);
buffer.writeInterleaved(absl::MakeSpan(output)); buffer.writeInterleaved(absl::MakeSpan(output));
REQUIRE( output == input ); REQUIRE(output == input);
} }
TEST_CASE("[StereoBuffer] Interleaved write -- SIMD") TEST_CASE("[StereoBuffer] Interleaved write -- SIMD")
{ {
StereoBuffer<float> buffer(10); StereoBuffer<float> buffer(10);
std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f}; std::array<float, 20> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f, 3.0f, 13.0f, 4.0f, 14.0f, 5.0f, 15.0f, 6.0f, 16.0f, 7.0f, 17.0f, 8.0f, 18.0f, 9.0f, 19.0f };
std::array<float, 20> output { 0.0f }; std::array<float, 20> output { 0.0f };
buffer.readInterleaved(input); buffer.readInterleaved(input);
buffer.writeInterleaved(absl::MakeSpan(output)); buffer.writeInterleaved(absl::MakeSpan(output));
REQUIRE( output == input ); REQUIRE(output == input);
} }
TEST_CASE("[StereoBuffer] Small interleaved write -- SIMD") TEST_CASE("[StereoBuffer] Small interleaved write -- SIMD")
{ {
StereoBuffer<float> buffer(3); StereoBuffer<float> buffer(3);
std::array<float, 6> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f}; std::array<float, 6> input = { 0.0f, 10.0f, 1.0f, 11.0f, 2.0f, 12.0f };
std::array<float, 6> output { 0.0f }; std::array<float, 6> output { 0.0f };
buffer.readInterleaved(input); buffer.readInterleaved(input);
buffer.writeInterleaved(absl::MakeSpan(output)); buffer.writeInterleaved(absl::MakeSpan(output));
REQUIRE( output == input ); REQUIRE(output == input);
} }