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:
Paul Ferrand 2020-03-08 00:29:20 +01:00 committed by GitHub
commit 0994212718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 505 additions and 122 deletions

350
benchmarks/BM_maps.cpp Normal file
View 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();

View file

@ -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)

View file

@ -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")

View file

@ -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

View file

@ -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);
};
}

View file

@ -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

View file

@ -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);
}

View file

@ -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 = {};
}

View file

@ -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
{

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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")

View file

@ -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")