Merge pull request #100 from paulfd/ccmap-not-a-map
Replace the underlying structure of CCMap with a smallish vector
This commit is contained in:
commit
0994212718
15 changed files with 505 additions and 122 deletions
350
benchmarks/BM_maps.cpp
Normal file
350
benchmarks/BM_maps.cpp
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include "../src/sfizz/Range.h"
|
||||
#include <absl/container/flat_hash_map.h>
|
||||
#include <absl/algorithm/container.h>
|
||||
|
||||
constexpr int maxCC { 256 };
|
||||
|
||||
class MyFixture : public benchmark::Fixture {
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& state)
|
||||
{
|
||||
std::random_device rd {};
|
||||
std::mt19937 gen { rd() };
|
||||
std::uniform_real_distribution<float> distFloat { 0.1f, 1.0f };
|
||||
std::uniform_int_distribution<int> distInt { 1, maxCC };
|
||||
floats = std::vector<float>(state.range(0));
|
||||
ccs = std::vector<int>(state.range(0));
|
||||
ranges = std::vector<sfz::Range<int>>(state.range(0));
|
||||
absl::c_generate(floats, [&]() {
|
||||
return distFloat(gen);
|
||||
});
|
||||
absl::c_generate(ccs, [&]() {
|
||||
return distInt(gen);
|
||||
});
|
||||
absl::c_generate(ranges, [&]() {
|
||||
return sfz::Range<int>(distInt(gen), distInt(gen));
|
||||
});
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& state [[maybe_unused]])
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<int> ccs;
|
||||
std::vector<sfz::Range<int>> ranges;
|
||||
std::vector<float> floats;
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
struct CCValuePair {
|
||||
int cc;
|
||||
ValueType value;
|
||||
};
|
||||
|
||||
template<class ValueType, bool CompareValue = false>
|
||||
struct CCValuePairComparator {
|
||||
bool operator()(const CCValuePair<ValueType>& valuePair, const int& cc)
|
||||
{
|
||||
return (valuePair.cc < cc);
|
||||
}
|
||||
|
||||
bool operator()(const int& cc, const CCValuePair<ValueType>& valuePair)
|
||||
{
|
||||
return (cc < valuePair.cc);
|
||||
}
|
||||
|
||||
bool operator()(const CCValuePair<ValueType>& lhs, const CCValuePair<ValueType>& rhs)
|
||||
{
|
||||
return (lhs.cc < rhs.cc);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
struct CCValuePairComparator<ValueType, true> {
|
||||
bool operator()(const CCValuePair<ValueType>& valuePair, const ValueType& value)
|
||||
{
|
||||
return (valuePair.value < value);
|
||||
}
|
||||
|
||||
bool operator()(const ValueType& value, const CCValuePair<ValueType>& valuePair)
|
||||
{
|
||||
return (value < valuePair.value);
|
||||
}
|
||||
|
||||
bool operator()(const CCValuePair<ValueType>& lhs, const CCValuePair<ValueType>& rhs)
|
||||
{
|
||||
return (lhs.value < rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
template <class ValueType>
|
||||
class CCMap {
|
||||
public:
|
||||
CCMap() = delete;
|
||||
/**
|
||||
* @brief Construct a new CCMap object with the specified default value.
|
||||
*
|
||||
* @param defaultValue
|
||||
*/
|
||||
CCMap(const ValueType& defaultValue)
|
||||
: defaultValue(defaultValue)
|
||||
{
|
||||
}
|
||||
CCMap(CCMap&&) = default;
|
||||
CCMap(const CCMap&) = default;
|
||||
~CCMap() = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the held object at the index, or a default value if not present
|
||||
*
|
||||
* @param index
|
||||
* @return const ValueType&
|
||||
*/
|
||||
const ValueType& getWithDefault(int index) const noexcept
|
||||
{
|
||||
auto it = absl::c_lower_bound(container, index, CCValuePairComparator<ValueType>{});
|
||||
if (it == container.end() || it->cc != index) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value at index or emplace a new one if not present
|
||||
*
|
||||
* @param index the index of the element
|
||||
* @return ValueType&
|
||||
*/
|
||||
ValueType& operator[](const int& index) noexcept
|
||||
{
|
||||
auto it = absl::c_lower_bound(container, index, CCValuePairComparator<ValueType>{});
|
||||
if (it == container.end() || it->cc != index) {
|
||||
auto inserted = container.insert(it, { index, defaultValue });
|
||||
return inserted->value;
|
||||
} else {
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is the container empty
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
inline bool empty() const { return container.empty(); }
|
||||
/**
|
||||
* @brief Returns true if the container containers an element at index
|
||||
*
|
||||
* @param index
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool contains(int index) const noexcept
|
||||
{
|
||||
return absl::c_binary_search(container, index, CCValuePairComparator<ValueType>{});
|
||||
}
|
||||
typename std::vector<CCValuePair<ValueType>>::const_iterator begin() const { return container.cbegin(); }
|
||||
typename std::vector<CCValuePair<ValueType>>::const_iterator end() const { return container.cend(); }
|
||||
private:
|
||||
// typename std::vector<std::pair<int, ValueType>>::iterator begin() { return container.begin(); }
|
||||
// typename std::vector<std::pair<int, ValueType>>::iterator end() { return container.end(); }
|
||||
|
||||
const ValueType defaultValue;
|
||||
std::vector<CCValuePair<ValueType>> container;
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, FillVector_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
CCMap<float> map { 0 };
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, FillVector_Range)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
CCMap<sfz::Range<int>> map { sfz::Range<int>(0, 127) };
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = ranges[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, FillAbseilFlatHM_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
absl::flat_hash_map<int, float> map;
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, FillAbseilFlatHM_Range)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
absl::flat_hash_map<int, sfz::Range<int>> map;
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = ranges[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupBaseline_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<float> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
std::vector<float> map;
|
||||
output.reserve(state.range(0));
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map.push_back(floats[i]);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupBaseline_Range)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<sfz::Range<int>> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
std::vector<sfz::Range<int>> map;
|
||||
output.reserve(state.range(0));
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map.push_back(ranges[i]);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[i];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupVector_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<float> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
CCMap<float> map { 0 };
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[ccs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupVector_Range)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<sfz::Range<int>> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
CCMap<sfz::Range<int>> map { sfz::Range<int>(0, 127) };
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = ranges[i];
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[ccs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupAbseilFlatHM_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<float> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
absl::flat_hash_map<int, float> map;
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[ccs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, LookupAbseilFlatHM_Range)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<sfz::Range<int>> output;
|
||||
output.resize(state.range(0));
|
||||
|
||||
absl::flat_hash_map<int, sfz::Range<int>> map;
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = ranges[i];
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
output[i] = map[ccs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, IterateVector_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<float> output;
|
||||
output.reserve(maxCC);
|
||||
|
||||
CCMap<float> map { 0 };
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
|
||||
for (auto _ : state) {
|
||||
for (auto& pair: map)
|
||||
output.push_back(pair.value);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, IterateAbseilFlatHM_Float)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
std::vector<float> output;
|
||||
output.reserve(maxCC);
|
||||
|
||||
absl::flat_hash_map<int, float> map;
|
||||
for (int i = 0; i < state.range(0); ++i)
|
||||
map[ccs[i]] = floats[i];
|
||||
for (auto _ : state) {
|
||||
for (auto& pair: map)
|
||||
output.push_back(pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BENCHMARK_REGISTER_F(MyFixture, FillVector_Float)->RangeMultiplier(2)->Range(16, 512);
|
||||
// BENCHMARK_REGISTER_F(MyFixture, FillVector_Range)->RangeMultiplier(2)->Range(16, 512);
|
||||
BENCHMARK_REGISTER_F(MyFixture, FillAbseilFlatHM_Float)->RangeMultiplier(2)->Range(16, 512);
|
||||
// BENCHMARK_REGISTER_F(MyFixture, FillAbseilFlatHM_Range)->RangeMultiplier(2)->Range(16, 512);
|
||||
BENCHMARK_REGISTER_F(MyFixture, LookupBaseline_Float)->RangeMultiplier(2)->Range(16, 512);
|
||||
// BENCHMARK_REGISTER_F(MyFixture, LookupBaseline_Range)->RangeMultiplier(2)->Range(16, 512);
|
||||
BENCHMARK_REGISTER_F(MyFixture, LookupVector_Float)->RangeMultiplier(2)->Range(16, 512);
|
||||
// BENCHMARK_REGISTER_F(MyFixture, LookupVector_Range)->RangeMultiplier(2)->Range(16, 512);
|
||||
BENCHMARK_REGISTER_F(MyFixture, LookupAbseilFlatHM_Float)->RangeMultiplier(2)->Range(16, 512);
|
||||
// BENCHMARK_REGISTER_F(MyFixture, LookupAbseilFlatHM_Range)->RangeMultiplier(2)->Range(16, 512);
|
||||
BENCHMARK_REGISTER_F(MyFixture, IterateVector_Float)->Range(maxCC, maxCC);
|
||||
BENCHMARK_REGISTER_F(MyFixture, IterateAbseilFlatHM_Float)->Range(maxCC, maxCC);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -27,7 +27,10 @@ macro(sfizz_add_benchmark TARGET)
|
|||
target_link_libraries("${TARGET}"
|
||||
PRIVATE absl::span absl::algorithm
|
||||
PRIVATE benchmark::benchmark benchmark::benchmark_main
|
||||
PRIVATE bm_simd bm_ftz)
|
||||
PRIVATE bm_simd bm_ftz)
|
||||
if (LIBATOMIC_FOUND)
|
||||
target_link_libraries ("${TARGET}" PRIVATE atomic)
|
||||
endif()
|
||||
target_include_directories("${TARGET}" PRIVATE ../src/sfizz ../src/external)
|
||||
endmacro()
|
||||
|
||||
|
|
@ -58,6 +61,8 @@ sfizz_add_benchmark(bm_diff BM_diff.cpp)
|
|||
sfizz_add_benchmark(bm_widthPos BM_widthPos.cpp)
|
||||
sfizz_add_benchmark(bm_interpolationCast BM_interpolationCast.cpp)
|
||||
sfizz_add_benchmark(bm_pointerIterationOrOffsets BM_pointerIterationOrOffsets.cpp)
|
||||
sfizz_add_benchmark(bm_maps BM_maps.cpp)
|
||||
target_link_libraries(bm_maps PRIVATE absl::flat_hash_map)
|
||||
|
||||
sfizz_add_benchmark(bm_logger BM_logger.cpp)
|
||||
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
include (CheckLibraryExists)
|
||||
if (UNIX AND NOT APPLE)
|
||||
check_library_exists(atomic __atomic_load "" LIBATOMIC_FOUND)
|
||||
endif()
|
||||
|
||||
# Don't show build information when building a different project
|
||||
function (show_build_info_if_needed)
|
||||
if (CMAKE_PROJECT_NAME STREQUAL "sfizz")
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
include (GNUInstallDirs)
|
||||
include (CheckLibraryExists)
|
||||
|
||||
set (SFIZZ_SOURCES
|
||||
sfizz/Synth.cpp
|
||||
|
|
@ -55,11 +54,8 @@ endif()
|
|||
|
||||
add_library (sfizz::parser ALIAS sfizz_parser)
|
||||
add_library (sfizz::sfizz ALIAS sfizz_static)
|
||||
if (UNIX AND NOT APPLE)
|
||||
check_library_exists(atomic __atomic_load "" LIBATOMIC_FOUND)
|
||||
if (LIBATOMIC_FOUND)
|
||||
target_link_libraries (sfizz_static PRIVATE atomic)
|
||||
endif()
|
||||
if (LIBATOMIC_FOUND)
|
||||
target_link_libraries (sfizz_static PRIVATE atomic)
|
||||
endif()
|
||||
|
||||
# Shared library and installation target
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
#pragma once
|
||||
#include "LeakDetector.h"
|
||||
#include <map>
|
||||
#include "SfzHelpers.h"
|
||||
#include <vector>
|
||||
#include <absl/algorithm/container.h>
|
||||
|
||||
namespace sfz {
|
||||
/**
|
||||
|
|
@ -42,25 +44,29 @@ public:
|
|||
*/
|
||||
const ValueType& getWithDefault(int index) const noexcept
|
||||
{
|
||||
auto it = container.find(index);
|
||||
if (it == container.end()) {
|
||||
auto it = absl::c_lower_bound(container, index, CCValuePairComparator<ValueType>{});
|
||||
if (it == container.end() || it->cc != index) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return it->second;
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the value at index key or emplace a new one if not present
|
||||
* @brief Get the value at index or emplace a new one if not present
|
||||
*
|
||||
* @param key the index of the element
|
||||
* @param index the index of the element
|
||||
* @return ValueType&
|
||||
*/
|
||||
ValueType& operator[](const int& key) noexcept
|
||||
ValueType& operator[](const int& index) noexcept
|
||||
{
|
||||
if (!contains(key))
|
||||
container.emplace(key, defaultValue);
|
||||
return container.operator[](key);
|
||||
auto it = absl::c_lower_bound(container, index, CCValuePairComparator<ValueType>{});
|
||||
if (it == container.end() || it->cc != index) {
|
||||
auto inserted = container.insert(it, { index, defaultValue });
|
||||
return inserted->value;
|
||||
} else {
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,13 +76,6 @@ public:
|
|||
* @return false
|
||||
*/
|
||||
inline bool empty() const { return container.empty(); }
|
||||
/**
|
||||
* @brief Returns the value at index with bounds checking (and possibly exceptions)
|
||||
*
|
||||
* @param index
|
||||
* @return const ValueType&
|
||||
*/
|
||||
const ValueType& at(int index) const { return container.at(index); }
|
||||
/**
|
||||
* @brief Returns true if the container containers an element at index
|
||||
*
|
||||
|
|
@ -84,14 +83,18 @@ public:
|
|||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool contains(int index) const noexcept { return container.find(index) != container.end(); }
|
||||
typename std::map<int, ValueType>::iterator begin() { return container.begin(); }
|
||||
typename std::map<int, ValueType>::const_iterator begin() const { return container.cbegin(); }
|
||||
typename std::map<int, ValueType>::iterator end() { return container.end(); }
|
||||
typename std::map<int, ValueType>::const_iterator end() const { return container.cend(); }
|
||||
bool contains(int index) const noexcept
|
||||
{
|
||||
return absl::c_binary_search(container, index, CCValuePairComparator<ValueType>{});
|
||||
}
|
||||
typename std::vector<CCValuePair<ValueType>>::const_iterator begin() const { return container.cbegin(); }
|
||||
typename std::vector<CCValuePair<ValueType>>::const_iterator end() const { return container.cend(); }
|
||||
private:
|
||||
// typename std::vector<std::pair<int, ValueType>>::iterator begin() { return container.begin(); }
|
||||
// typename std::vector<std::pair<int, ValueType>>::iterator end() { return container.end(); }
|
||||
|
||||
const ValueType defaultValue;
|
||||
std::map<int, ValueType> container;
|
||||
std::vector<CCValuePair<ValueType>> container;
|
||||
LEAK_DETECTOR(CCMap);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@ struct EGDescription
|
|||
float vel2sustain { Default::vel2sustain };
|
||||
int vel2depth { Default::depth };
|
||||
|
||||
absl::optional<CCValuePair> ccAttack;
|
||||
absl::optional<CCValuePair> ccDecay;
|
||||
absl::optional<CCValuePair> ccDelay;
|
||||
absl::optional<CCValuePair> ccHold;
|
||||
absl::optional<CCValuePair> ccRelease;
|
||||
absl::optional<CCValuePair> ccStart;
|
||||
absl::optional<CCValuePair> ccSustain;
|
||||
absl::optional<CCValuePair<float>> ccAttack;
|
||||
absl::optional<CCValuePair<float>> ccDecay;
|
||||
absl::optional<CCValuePair<float>> ccDelay;
|
||||
absl::optional<CCValuePair<float>> ccHold;
|
||||
absl::optional<CCValuePair<float>> ccRelease;
|
||||
absl::optional<CCValuePair<float>> ccStart;
|
||||
absl::optional<CCValuePair<float>> ccSustain;
|
||||
|
||||
/**
|
||||
* @brief Get the attack with possibly a CC modifier and a velocity modifier
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ public:
|
|||
T modulate(T value, const CCMap<U>& modifiers, const Range<T>& validRange, const modFunction<T, U>& lambda = addToBase<T>) const noexcept
|
||||
{
|
||||
for (auto& mod: modifiers) {
|
||||
lambda(value, normalizeCC(getCCValue(mod.first)) * mod.second);
|
||||
lambda(value, normalizeCC(getCCValue(mod.cc)) * mod.value);
|
||||
}
|
||||
return validRange.clamp(value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,11 +184,11 @@ inline void setRangeStartFromOpcode(const Opcode& opcode, Range<ValueType>& targ
|
|||
* @param validRange the range of admitted values used to clamp the opcode
|
||||
*/
|
||||
template <class ValueType>
|
||||
inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional<CCValuePair>& target, const Range<ValueType>& validRange)
|
||||
inline void setCCPairFromOpcode(const Opcode& opcode, absl::optional<CCValuePair<ValueType>>& target, const Range<ValueType>& validRange)
|
||||
{
|
||||
auto value = readOpcode(opcode.value, validRange);
|
||||
if (value && Default::ccNumberRange.containsWithEnd(opcode.parameters.back()))
|
||||
target = std::make_pair(opcode.parameters.back(), *value);
|
||||
target = { opcode.parameters.back(), *value };
|
||||
else
|
||||
target = {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,27 +22,12 @@ class Range {
|
|||
|
||||
public:
|
||||
constexpr Range() = default;
|
||||
// constexpr Range(std::initializer_list<Type> list)
|
||||
// {
|
||||
// switch(list.size())
|
||||
// {
|
||||
// case 0:
|
||||
// break;
|
||||
// case 1:
|
||||
// _start = *list.begin();
|
||||
// _end = _start;
|
||||
// break;
|
||||
// default:
|
||||
// _start = *list.begin();
|
||||
// _end = *(list.begin() + 1);
|
||||
// }
|
||||
// }
|
||||
constexpr Range(Type start, Type end) noexcept
|
||||
: _start(start)
|
||||
, _end(std::max(start, end))
|
||||
{
|
||||
|
||||
}
|
||||
~Range() = default;
|
||||
Type getStart() const noexcept { return _start; }
|
||||
Type getEnd() const noexcept { return _end; }
|
||||
/**
|
||||
|
|
@ -51,8 +36,6 @@ public:
|
|||
* @return std::pair<Type, Type>
|
||||
*/
|
||||
std::pair<Type, Type> getPair() const noexcept { return std::make_pair<Type, Type>(_start, _end); }
|
||||
Range(const Range<Type>& range) = default;
|
||||
Range(Range<Type>&& range) = default;
|
||||
constexpr Type length() const { return _end - _start; }
|
||||
void setStart(Type start) noexcept
|
||||
{
|
||||
|
|
|
|||
|
|
@ -854,7 +854,7 @@ bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept
|
|||
if (!triggerOnCC)
|
||||
return false;
|
||||
|
||||
if (ccTriggers.contains(ccNumber) && ccTriggers.at(ccNumber).containsWithEnd(ccValue))
|
||||
if (ccTriggers.contains(ccNumber) && ccTriggers[ccNumber].containsWithEnd(ccValue))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
|
@ -992,14 +992,14 @@ float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) noexcept
|
|||
|
||||
// Crossfades due to CC states
|
||||
for (const auto& valuePair : crossfadeCCInRange) {
|
||||
const auto ccValue = ccState[valuePair.first];
|
||||
const auto crossfadeRange = valuePair.second;
|
||||
const auto ccValue = ccState[valuePair.cc];
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
||||
for (const auto& valuePair : crossfadeCCOutRange) {
|
||||
const auto ccValue = ccState[valuePair.first];
|
||||
const auto crossfadeRange = valuePair.second;
|
||||
const auto ccValue = ccState[valuePair.cc];
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,11 +264,11 @@ struct Region {
|
|||
float pan { Default::pan }; // pan
|
||||
float width { Default::width }; // width
|
||||
float position { Default::position }; // position
|
||||
absl::optional<CCValuePair> volumeCC; // volume_oncc
|
||||
absl::optional<CCValuePair> amplitudeCC; // amplitude_oncc
|
||||
absl::optional<CCValuePair> panCC; // pan_oncc
|
||||
absl::optional<CCValuePair> widthCC; // width_oncc
|
||||
absl::optional<CCValuePair> positionCC; // position_oncc
|
||||
absl::optional<CCValuePair<float>> volumeCC; // volume_oncc
|
||||
absl::optional<CCValuePair<float>> amplitudeCC; // amplitude_oncc
|
||||
absl::optional<CCValuePair<float>> panCC; // pan_oncc
|
||||
absl::optional<CCValuePair<float>> widthCC; // width_oncc
|
||||
absl::optional<CCValuePair<float>> positionCC; // position_oncc
|
||||
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
|
||||
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
|
||||
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
|
||||
|
|
|
|||
|
|
@ -16,9 +16,50 @@ namespace sfz
|
|||
{
|
||||
|
||||
using SfzCCArray = std::array<uint8_t, config::numCCs>;
|
||||
using CCValuePair = std::pair<uint8_t, float> ;
|
||||
using CCNamePair = std::pair<uint8_t, std::string>;
|
||||
|
||||
template<class ValueType>
|
||||
struct CCValuePair {
|
||||
int cc;
|
||||
ValueType value;
|
||||
};
|
||||
|
||||
template<class ValueType, bool CompareValue = false>
|
||||
struct CCValuePairComparator {
|
||||
bool operator()(const CCValuePair<ValueType>& valuePair, const int& cc)
|
||||
{
|
||||
return (valuePair.cc < cc);
|
||||
}
|
||||
|
||||
bool operator()(const int& cc, const CCValuePair<ValueType>& valuePair)
|
||||
{
|
||||
return (cc < valuePair.cc);
|
||||
}
|
||||
|
||||
bool operator()(const CCValuePair<ValueType>& lhs, const CCValuePair<ValueType>& rhs)
|
||||
{
|
||||
return (lhs.cc < rhs.cc);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ValueType>
|
||||
struct CCValuePairComparator<ValueType, true> {
|
||||
bool operator()(const CCValuePair<ValueType>& valuePair, const ValueType& value)
|
||||
{
|
||||
return (valuePair.value < value);
|
||||
}
|
||||
|
||||
bool operator()(const ValueType& value, const CCValuePair<ValueType>& valuePair)
|
||||
{
|
||||
return (value < valuePair.value);
|
||||
}
|
||||
|
||||
bool operator()(const CCValuePair<ValueType>& lhs, const CCValuePair<ValueType>& rhs)
|
||||
{
|
||||
return (lhs.value < rhs.value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Converts cents to a pitch ratio
|
||||
*
|
||||
|
|
@ -94,10 +135,10 @@ constexpr float normalizeBend(float bendValue)
|
|||
* @param value
|
||||
* @return float
|
||||
*/
|
||||
inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional<CCValuePair>& ccSwitch, float value) noexcept
|
||||
inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional<CCValuePair<float>>& ccSwitch, float value) noexcept
|
||||
{
|
||||
if (ccSwitch)
|
||||
return value + ccSwitch->second * normalizeCC(ccValues[ccSwitch->first]);
|
||||
return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
|
|||
baseVolumedB = region->getBaseVolumedB(number);
|
||||
auto volumedB { baseVolumedB };
|
||||
if (region->volumeCC)
|
||||
volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->first)) * region->volumeCC->second;
|
||||
volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value;
|
||||
volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB)));
|
||||
|
||||
baseGain = region->getBaseGain();
|
||||
|
|
@ -56,7 +56,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
|
|||
|
||||
float gain { baseGain };
|
||||
if (region->amplitudeCC)
|
||||
gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->first)) * normalizePercents(region->amplitudeCC->second);
|
||||
gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value);
|
||||
amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain));
|
||||
|
||||
float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) };
|
||||
|
|
@ -65,19 +65,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
|
|||
basePan = normalizePercents(region->pan);
|
||||
auto pan { basePan };
|
||||
if (region->panCC)
|
||||
pan += normalizeCC(resources.midiState.getCCValue(region->panCC->first)) * normalizePercents(region->panCC->second);
|
||||
pan += normalizeCC(resources.midiState.getCCValue(region->panCC->cc)) * normalizePercents(region->panCC->value);
|
||||
panEnvelope.reset(Default::symmetricNormalizedRange.clamp(pan));
|
||||
|
||||
basePosition = normalizePercents(region->position);
|
||||
auto position { basePosition };
|
||||
if (region->positionCC)
|
||||
position += normalizeCC(resources.midiState.getCCValue(region->positionCC->first)) * normalizePercents(region->positionCC->second);
|
||||
position += normalizeCC(resources.midiState.getCCValue(region->positionCC->cc)) * normalizePercents(region->positionCC->value);
|
||||
positionEnvelope.reset(Default::symmetricNormalizedRange.clamp(position));
|
||||
|
||||
baseWidth = normalizePercents(region->width);
|
||||
auto width { baseWidth };
|
||||
if (region->widthCC)
|
||||
width += normalizeCC(resources.midiState.getCCValue(region->widthCC->first)) * normalizePercents(region->widthCC->second);
|
||||
width += normalizeCC(resources.midiState.getCCValue(region->widthCC->cc)) * normalizePercents(region->widthCC->value);
|
||||
widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width));
|
||||
|
||||
pitchBendEnvelope.setFunction([region](float pitchValue){
|
||||
|
|
@ -168,28 +168,28 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept
|
|||
// TODO: this feels like a hack, revisit this along with the smoothed envelopes...
|
||||
delay = max(delay, minEnvelopeDelay);
|
||||
|
||||
if (region->amplitudeCC && ccNumber == region->amplitudeCC->first) {
|
||||
const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->second) };
|
||||
if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) {
|
||||
const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) };
|
||||
amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain));
|
||||
}
|
||||
|
||||
if (region->volumeCC && ccNumber == region->volumeCC->first) {
|
||||
const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->second };
|
||||
if (region->volumeCC && ccNumber == region->volumeCC->cc) {
|
||||
const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value };
|
||||
volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB)));
|
||||
}
|
||||
|
||||
if (region->panCC && ccNumber == region->panCC->first) {
|
||||
const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->second) };
|
||||
if (region->panCC && ccNumber == region->panCC->cc) {
|
||||
const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) };
|
||||
panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan));
|
||||
}
|
||||
|
||||
if (region->positionCC && ccNumber == region->positionCC->first) {
|
||||
const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->second) };
|
||||
if (region->positionCC && ccNumber == region->positionCC->cc) {
|
||||
const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) };
|
||||
positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition));
|
||||
}
|
||||
|
||||
if (region->widthCC && ccNumber == region->widthCC->first) {
|
||||
const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->second) };
|
||||
if (region->widthCC && ccNumber == region->widthCC->cc) {
|
||||
const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) };
|
||||
widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -306,8 +306,8 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
|||
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
|
||||
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->first == 10 );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->second == 34.0f );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->cc == 10 );
|
||||
REQUIRE( synth.getRegionView(2)->amplitudeCC->value == 34.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Specific bug: relative path with backslashes")
|
||||
|
|
|
|||
|
|
@ -464,8 +464,8 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
REQUIRE(!region.panCC);
|
||||
region.parseOpcode({ "pan_oncc45", "4.2" });
|
||||
REQUIRE(region.panCC);
|
||||
REQUIRE(region.panCC->first == 45);
|
||||
REQUIRE(region.panCC->second == 4.2f);
|
||||
REQUIRE(region.panCC->cc == 45);
|
||||
REQUIRE(region.panCC->value == 4.2f);
|
||||
}
|
||||
|
||||
SECTION("width")
|
||||
|
|
@ -486,8 +486,8 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
REQUIRE(!region.widthCC);
|
||||
region.parseOpcode({ "width_oncc45", "4.2" });
|
||||
REQUIRE(region.widthCC);
|
||||
REQUIRE(region.widthCC->first == 45);
|
||||
REQUIRE(region.widthCC->second == 4.2f);
|
||||
REQUIRE(region.widthCC->cc == 45);
|
||||
REQUIRE(region.widthCC->value == 4.2f);
|
||||
}
|
||||
|
||||
SECTION("position")
|
||||
|
|
@ -508,8 +508,8 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
REQUIRE(!region.positionCC);
|
||||
region.parseOpcode({ "position_oncc45", "4.2" });
|
||||
REQUIRE(region.positionCC);
|
||||
REQUIRE(region.positionCC->first == 45);
|
||||
REQUIRE(region.positionCC->second == 4.2f);
|
||||
REQUIRE(region.positionCC->cc == 45);
|
||||
REQUIRE(region.positionCC->value == 4.2f);
|
||||
}
|
||||
|
||||
SECTION("amp_keycenter")
|
||||
|
|
@ -964,20 +964,20 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
REQUIRE(region.amplitudeEG.ccRelease);
|
||||
REQUIRE(region.amplitudeEG.ccStart);
|
||||
REQUIRE(region.amplitudeEG.ccSustain);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->first == 1);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->first == 2);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->first == 3);
|
||||
REQUIRE(region.amplitudeEG.ccHold->first == 4);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->first == 5);
|
||||
REQUIRE(region.amplitudeEG.ccStart->first == 6);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->first == 7);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->second == 1.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->second == 2.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->second == 3.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->second == 4.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->second == 5.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->second == 6.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->second == 7.0f);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->cc == 1);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->cc == 2);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->cc == 3);
|
||||
REQUIRE(region.amplitudeEG.ccHold->cc == 4);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->cc == 5);
|
||||
REQUIRE(region.amplitudeEG.ccStart->cc == 6);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->cc == 7);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->value == 1.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->value == 2.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->value == 3.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->value == 4.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->value == 5.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->value == 6.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->value == 7.0f);
|
||||
//
|
||||
region.parseOpcode({ "ampeg_attack_oncc1", "101" });
|
||||
region.parseOpcode({ "ampeg_decay_oncc2", "101" });
|
||||
|
|
@ -986,13 +986,13 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
region.parseOpcode({ "ampeg_release_oncc5", "101" });
|
||||
region.parseOpcode({ "ampeg_start_oncc6", "101" });
|
||||
region.parseOpcode({ "ampeg_sustain_oncc7", "101" });
|
||||
REQUIRE(region.amplitudeEG.ccAttack->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->second == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->value == 100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->value == 100.0f);
|
||||
//
|
||||
region.parseOpcode({ "ampeg_attack_oncc1", "-101" });
|
||||
region.parseOpcode({ "ampeg_decay_oncc2", "-101" });
|
||||
|
|
@ -1001,13 +1001,13 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
region.parseOpcode({ "ampeg_release_oncc5", "-101" });
|
||||
region.parseOpcode({ "ampeg_start_oncc6", "-101" });
|
||||
region.parseOpcode({ "ampeg_sustain_oncc7", "-101" });
|
||||
REQUIRE(region.amplitudeEG.ccAttack->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->second == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccAttack->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDecay->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccDelay->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccHold->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccRelease->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccStart->value == -100.0f);
|
||||
REQUIRE(region.amplitudeEG.ccSustain->value == -100.0f);
|
||||
}
|
||||
|
||||
SECTION("sustain_sw and sostenuto_sw")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue