Merge pull request #137 from paulfd/floating-midi-values
Floating midi values
This commit is contained in:
commit
45124cce53
30 changed files with 814 additions and 654 deletions
74
benchmarks/BM_mapVsArray.cpp
Normal file
74
benchmarks/BM_mapVsArray.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// 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/CCMap.h"
|
||||
#include <absl/algorithm/container.h>
|
||||
#include <absl/types/span.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::array<int, maxCC> sourceCC;
|
||||
absl::c_iota(sourceCC, 0);
|
||||
absl::c_shuffle(sourceCC, gen);
|
||||
ccs.resize(state.range(0));
|
||||
std::copy(sourceCC.begin(), sourceCC.begin() + state.range(0), ccs.begin());
|
||||
std::uniform_real_distribution<float> distFloat { 0.1f, 1.0f };
|
||||
vector.resize(maxCC);
|
||||
absl::c_generate(vector, [&]() {
|
||||
return distFloat(gen);
|
||||
});
|
||||
for (unsigned i = 0; i < state.range(0); ++i) {
|
||||
map[ccs[i]] = vector[ccs[i]];
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& /* state */)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<int> ccs;
|
||||
std::vector<float> vector;
|
||||
sfz::CCMap<float> map { 1 };
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, ArraySearch)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
float value { 1 };
|
||||
for (auto _ : state) {
|
||||
for (unsigned i = 0; i < state.range(0); ++i) {
|
||||
value *= vector[ccs[i]];
|
||||
}
|
||||
benchmark::DoNotOptimize(value);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_DEFINE_F(MyFixture, MapSearch)
|
||||
(benchmark::State& state)
|
||||
{
|
||||
float value { 0 };
|
||||
for (auto _ : state) {
|
||||
for (unsigned i = 0; i < state.range(0); ++i) {
|
||||
value *= map[ccs[i]];
|
||||
}
|
||||
benchmark::DoNotOptimize(value);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_F(MyFixture, ArraySearch)->Range(4, maxCC);
|
||||
BENCHMARK_REGISTER_F(MyFixture, MapSearch)->Range(4, maxCC);
|
||||
BENCHMARK_MAIN();
|
||||
|
|
@ -63,6 +63,7 @@ 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_mapVsArray BM_mapVsArray.cpp)
|
||||
|
||||
sfizz_add_benchmark(bm_logger BM_logger.cpp)
|
||||
target_link_libraries(bm_logger PRIVATE sfizz::sfizz)
|
||||
|
|
|
|||
|
|
@ -12,21 +12,20 @@
|
|||
namespace sfz {
|
||||
|
||||
template <class Type>
|
||||
void ADSREnvelope<Type>::reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept
|
||||
void ADSREnvelope<Type>::reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept
|
||||
{
|
||||
auto secondsToSamples = [sampleRate](Type timeInSeconds) {
|
||||
return static_cast<int>(timeInSeconds * sampleRate);
|
||||
};
|
||||
|
||||
const auto ccArray = state.getCCArray();
|
||||
this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(ccArray, velocity));
|
||||
this->attack = secondsToSamples(region.amplitudeEG.getAttack(ccArray, velocity));
|
||||
this->decay = secondsToSamples(region.amplitudeEG.getDecay(ccArray, velocity));
|
||||
this->release = secondsToSamples(region.amplitudeEG.getRelease(ccArray, velocity));
|
||||
this->hold = secondsToSamples(region.amplitudeEG.getHold(ccArray, velocity));
|
||||
this->delay = delay + secondsToSamples(region.amplitudeEG.getDelay(state, velocity));
|
||||
this->attack = secondsToSamples(region.amplitudeEG.getAttack(state, velocity));
|
||||
this->decay = secondsToSamples(region.amplitudeEG.getDecay(state, velocity));
|
||||
this->release = secondsToSamples(region.amplitudeEG.getRelease(state, velocity));
|
||||
this->hold = secondsToSamples(region.amplitudeEG.getHold(state, velocity));
|
||||
this->peak = 1.0;
|
||||
this->sustain = normalizePercents(region.amplitudeEG.getSustain(ccArray, velocity));
|
||||
this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(ccArray, velocity));
|
||||
this->sustain = normalizePercents(region.amplitudeEG.getSustain(state, velocity));
|
||||
this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(state, velocity));
|
||||
|
||||
releaseDelay = 0;
|
||||
shouldRelease = false;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public:
|
|||
* @param delay
|
||||
* @param velocity
|
||||
*/
|
||||
void reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept;
|
||||
void reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept;
|
||||
/**
|
||||
* @brief Get the next value for the envelope
|
||||
*
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace config {
|
|||
constexpr int allNotesOffCC { 123 };
|
||||
constexpr int omniOffCC { 124 };
|
||||
constexpr int omniOnCC { 125 };
|
||||
constexpr int halfCCThreshold { 64 };
|
||||
constexpr float halfCCThreshold { 0.5f };
|
||||
constexpr int centPerSemitone { 100 };
|
||||
constexpr float virtuallyZero { 0.00005f };
|
||||
constexpr float fastReleaseDuration { 0.01f };
|
||||
|
|
@ -52,7 +52,7 @@ namespace config {
|
|||
constexpr float A440 { 440.0 };
|
||||
constexpr size_t powerHistoryLength { 16 };
|
||||
constexpr float voiceStealingThreshold { 0.00001f };
|
||||
constexpr uint8_t numCCs { 143 };
|
||||
constexpr uint16_t numCCs { 512 };
|
||||
constexpr int chunkSize { 1024 };
|
||||
constexpr float defaultAmpEGRelease { 0.02f };
|
||||
constexpr int filtersInPool { maxVoices * 2 };
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ namespace Default
|
|||
constexpr SfzLoopMode loopMode { SfzLoopMode::no_loop };
|
||||
constexpr Range<uint32_t> loopRange { 0, std::numeric_limits<uint32_t>::max() };
|
||||
|
||||
// Global ranges
|
||||
constexpr Range<uint8_t> midi7Range { 0, 127 };
|
||||
constexpr Range<float> normalizedRange { 0.0f, 1.0f };
|
||||
|
||||
// Wavetable oscillator
|
||||
constexpr float oscillatorPhase { 0.0 };
|
||||
constexpr Range<float> oscillatorPhaseRange { -1.0, 360.0 };
|
||||
|
|
@ -64,14 +68,13 @@ namespace Default
|
|||
|
||||
// Region logic: key mapping
|
||||
constexpr Range<uint8_t> keyRange { 0, 127 };
|
||||
constexpr Range<uint8_t> velocityRange { 0, 127 };
|
||||
constexpr auto velocityRange = normalizedRange;
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
constexpr Range<uint8_t> channelRange { 1, 16 };
|
||||
constexpr Range<uint8_t> midiChannelRange { 0, 15 };
|
||||
constexpr Range<uint8_t> ccNumberRange { 0, config::numCCs };
|
||||
constexpr Range<uint8_t> ccValueRange { 0, 127 };
|
||||
constexpr uint8_t cc { 0 };
|
||||
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
|
||||
constexpr auto ccValueRange = normalizedRange;
|
||||
constexpr Range<int> bendRange { -8192, 8192 };
|
||||
constexpr int bend { 0 };
|
||||
constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current };
|
||||
|
|
@ -88,7 +91,7 @@ namespace Default
|
|||
|
||||
// Region logic: Triggers
|
||||
constexpr SfzTrigger trigger { SfzTrigger::attack };
|
||||
constexpr Range<uint8_t> ccTriggerValueRange{ 0, 127 };
|
||||
constexpr Range<float> ccTriggerValueRange = normalizedRange;
|
||||
|
||||
// Performance parameters: amplifier
|
||||
constexpr float globalVolume { -7.35f };
|
||||
|
|
@ -97,7 +100,6 @@ namespace Default
|
|||
constexpr Range<float> volumeCCRange { -144.0, 48.0 };
|
||||
constexpr float amplitude { 100.0 };
|
||||
constexpr Range<float> amplitudeRange { 0.0, 100.0 };
|
||||
constexpr Range<float> normalizedRange { 0.0, 1.0 };
|
||||
constexpr float pan { 0.0 };
|
||||
constexpr Range<float> panRange { -100.0, 100.0 };
|
||||
constexpr Range<float> panCCRange { -200.0, 200.0 };
|
||||
|
|
@ -118,10 +120,10 @@ namespace Default
|
|||
constexpr Range<float> ampRandomRange { 0.0, 24.0 };
|
||||
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
|
||||
constexpr Range<uint8_t> crossfadeVelInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeVelOutRange { 127, 127 };
|
||||
constexpr Range<uint8_t> crossfadeCCInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeCCOutRange { 127, 127 };
|
||||
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
|
||||
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
|
||||
constexpr Range<float> crossfadeCCInRange { 0.0f, 0.0f };
|
||||
constexpr Range<float> crossfadeCCOutRange { 1.0f, 1.0f };
|
||||
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
|
||||
constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
|
||||
constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
|
||||
|
|
|
|||
|
|
@ -29,11 +29,10 @@
|
|||
#include "Macros.h"
|
||||
#include "LeakDetector.h"
|
||||
#include "SfzHelpers.h"
|
||||
#include "MidiState.h"
|
||||
#include <absl/types/optional.h>
|
||||
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
namespace sfz {
|
||||
/**
|
||||
* @brief A description for an SFZ envelope generator, with its envelope parameters
|
||||
* and possible CC modulation. This is a structure to be integrated directly in a
|
||||
|
|
@ -42,114 +41,136 @@ namespace sfz
|
|||
* TODO: should be updated for SFZ v2
|
||||
*
|
||||
*/
|
||||
struct EGDescription
|
||||
|
||||
/**
|
||||
* @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone.
|
||||
*
|
||||
* @param ccValues
|
||||
* @param ccSwitch
|
||||
* @param value
|
||||
* @return float
|
||||
*/
|
||||
inline float ccSwitchedValue(const MidiState& state, const absl::optional<CCValuePair<float>>& ccSwitch, float value) noexcept
|
||||
{
|
||||
if (ccSwitch)
|
||||
return value + ccSwitch->value * state.getCCValue(ccSwitch->cc);
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
struct EGDescription {
|
||||
EGDescription() = default;
|
||||
EGDescription(const EGDescription&) = default;
|
||||
EGDescription(EGDescription&&) = default;
|
||||
~EGDescription() = default;
|
||||
|
||||
float attack { Default::attack };
|
||||
float decay { Default::decay };
|
||||
float delay { Default::delayEG };
|
||||
float hold { Default::hold };
|
||||
float release { Default::release };
|
||||
float start { Default::start };
|
||||
float sustain { Default::sustain };
|
||||
int depth { Default::depth };
|
||||
float vel2attack { Default::attack };
|
||||
float vel2decay { Default::decay };
|
||||
float vel2delay { Default::delayEG };
|
||||
float vel2hold { Default::hold };
|
||||
float vel2release { Default::vel2release };
|
||||
float vel2sustain { Default::vel2sustain };
|
||||
int vel2depth { Default::depth };
|
||||
float attack { Default::attack };
|
||||
float decay { Default::decay };
|
||||
float delay { Default::delayEG };
|
||||
float hold { Default::hold };
|
||||
float release { Default::release };
|
||||
float start { Default::start };
|
||||
float sustain { Default::sustain };
|
||||
int depth { Default::depth };
|
||||
float vel2attack { Default::attack };
|
||||
float vel2decay { Default::decay };
|
||||
float vel2delay { Default::delayEG };
|
||||
float vel2hold { Default::hold };
|
||||
float vel2release { Default::vel2release };
|
||||
float vel2sustain { Default::vel2sustain };
|
||||
int vel2depth { Default::depth };
|
||||
|
||||
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;
|
||||
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
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getAttack(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getAttack(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + velocity * vel2attack);
|
||||
}
|
||||
/**
|
||||
* @brief Get the decay with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getDecay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getDecay(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + velocity * vel2decay);
|
||||
}
|
||||
/**
|
||||
* @brief Get the delay with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getDelay(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getDelay(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + velocity * vel2delay);
|
||||
}
|
||||
/**
|
||||
* @brief Get the holding duration with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getHold(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getHold(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccHold, hold) + normalizeVelocity(velocity)*vel2hold);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + velocity * vel2hold);
|
||||
}
|
||||
/**
|
||||
* @brief Get the release duration with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getRelease(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getRelease(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccRelease, release) + normalizeVelocity(velocity)*vel2release);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + velocity * vel2release);
|
||||
}
|
||||
/**
|
||||
* @brief Get the starting level with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getStart(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getStart(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
UNUSED(velocity);
|
||||
return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccStart, start));
|
||||
return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start));
|
||||
}
|
||||
/**
|
||||
* @brief Get the sustain level with possibly a CC modifier and a velocity modifier
|
||||
*
|
||||
* @param ccValues
|
||||
* @param state
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getSustain(const SfzCCArray &ccValues, uint8_t velocity) const noexcept
|
||||
float getSustain(const MidiState& state, float velocity) const noexcept
|
||||
{
|
||||
return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain);
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + velocity * vel2sustain);
|
||||
}
|
||||
LEAK_DETECTOR(EGDescription);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,17 +15,17 @@ void sfz::EQHolder::reset()
|
|||
eq.clear();
|
||||
}
|
||||
|
||||
void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, uint8_t velocity)
|
||||
void sfz::EQHolder::setup(const EQDescription& description, unsigned numChannels, float velocity)
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
eq.setType(description.type);
|
||||
eq.setChannels(numChannels);
|
||||
this->description = &description;
|
||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||
|
||||
// Setup the base values
|
||||
baseFrequency = description.frequency + normalizedVelocity * description.vel2frequency;
|
||||
baseFrequency = description.frequency + velocity * description.vel2frequency;
|
||||
baseBandwidth = description.bandwidth;
|
||||
baseGain = description.gain + normalizedVelocity * description.vel2gain;
|
||||
baseGain = description.gain + velocity * description.vel2gain;
|
||||
|
||||
// Setup the modulated values
|
||||
lastFrequency = midiState.modulate(baseFrequency, description.frequencyCC, Default::eqFrequencyRange);
|
||||
|
|
@ -84,7 +84,7 @@ sfz::EQPool::EQPool(const MidiState& state, int numEQs)
|
|||
setnumEQs(numEQs);
|
||||
}
|
||||
|
||||
sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity)
|
||||
sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, unsigned numChannels, float velocity)
|
||||
{
|
||||
AtomicGuard guard { givingOutEQs };
|
||||
if (!canGiveOutEQs)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public:
|
|||
* @param numChannels the number of channels for the EQ
|
||||
* @param description the triggering velocity/value
|
||||
*/
|
||||
void setup(const EQDescription& description, unsigned numChannels, uint8_t velocity);
|
||||
void setup(const EQDescription& description, unsigned numChannels, float velocity);
|
||||
/**
|
||||
* @brief Process a block of stereo inputs
|
||||
*
|
||||
|
|
@ -90,7 +90,7 @@ public:
|
|||
* @param velocity the triggering note velocity/value
|
||||
* @return EQHolderPtr release this when done with the filter; no deallocation will be done
|
||||
*/
|
||||
EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, uint8_t velocity);
|
||||
EQHolderPtr getEQ(const EQDescription& description, unsigned numChannels, float velocity);
|
||||
/**
|
||||
* @brief Get the number of active EQs
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ void sfz::FilterHolder::reset()
|
|||
filter.clear();
|
||||
}
|
||||
|
||||
void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity)
|
||||
void sfz::FilterHolder::setup(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity)
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
this->description = &description;
|
||||
filter.setType(description.type);
|
||||
filter.setChannels(numChannels);
|
||||
|
|
@ -30,7 +32,7 @@ void sfz::FilterHolder::setup(const FilterDescription& description, unsigned num
|
|||
}
|
||||
const auto keytrack = description.keytrack * (noteNumber - description.keycenter);
|
||||
baseCutoff *= centsFactor(keytrack);
|
||||
const auto veltrack = static_cast<float>(description.veltrack) * normalizeVelocity(velocity);
|
||||
const auto veltrack = static_cast<float>(description.veltrack) * velocity;
|
||||
baseCutoff *= centsFactor(veltrack);
|
||||
baseCutoff = Default::filterCutoffRange.clamp(baseCutoff);
|
||||
|
||||
|
|
@ -83,7 +85,7 @@ sfz::FilterPool::FilterPool(const MidiState& state, int numFilters)
|
|||
setNumFilters(numFilters);
|
||||
}
|
||||
|
||||
sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, uint8_t velocity)
|
||||
sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber, float velocity)
|
||||
{
|
||||
AtomicGuard guard { givingOutFilters };
|
||||
if (!canGiveOutFilters)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public:
|
|||
* @param noteNumber the triggering note number
|
||||
* @param velocity the triggering note velocity/value
|
||||
*/
|
||||
void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast<int>(Default::filterKeycenter), uint8_t velocity = 0);
|
||||
void setup(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast<int>(Default::filterKeycenter), float velocity = 0);
|
||||
/**
|
||||
* @brief Process a block of stereo inputs
|
||||
*
|
||||
|
|
@ -94,7 +94,7 @@ public:
|
|||
* @param velocity the triggering note velocity
|
||||
* @return FilterHolderPtr release this when done with the filter; no deallocation will be done
|
||||
*/
|
||||
FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast<int>(Default::filterKeycenter), uint8_t velocity = 0);
|
||||
FilterHolderPtr getFilter(const FilterDescription& description, unsigned numChannels, int noteNumber = static_cast<int>(Default::filterKeycenter), float velocity = 0);
|
||||
/**
|
||||
* @brief Get the number of active filters
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ sfz::MidiState::MidiState()
|
|||
reset(0);
|
||||
}
|
||||
|
||||
void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noexcept
|
||||
{
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
ASSERT(velocity >= 0 && velocity <= 127);
|
||||
ASSERT(velocity >= 0 && velocity <= 1.0);
|
||||
|
||||
if (noteNumber >= 0 && noteNumber < 128) {
|
||||
lastNoteVelocities[noteNumber] = velocity;
|
||||
|
|
@ -26,10 +26,10 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) no
|
|||
|
||||
}
|
||||
|
||||
void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noexcept
|
||||
{
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
ASSERT(velocity >= 0 && velocity <= 127);
|
||||
ASSERT(velocity >= 0.0 && velocity <= 1.0);
|
||||
UNUSED(velocity);
|
||||
if (noteNumber >= 0 && noteNumber < 128) {
|
||||
if (activeNotes > 0)
|
||||
|
|
@ -51,13 +51,14 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const
|
|||
return 0.0f;
|
||||
}
|
||||
|
||||
uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
|
||||
float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
|
||||
{
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
|
||||
return lastNoteVelocities[noteNumber];
|
||||
}
|
||||
|
||||
|
||||
void sfz::MidiState::pitchBendEvent(int delay, int pitchBendValue) noexcept
|
||||
{
|
||||
ASSERT(pitchBendValue >= -8192 && pitchBendValue <= 8192);
|
||||
|
|
@ -70,26 +71,20 @@ int sfz::MidiState::getPitchBend() const noexcept
|
|||
return pitchBend;
|
||||
}
|
||||
|
||||
void sfz::MidiState::ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept
|
||||
void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept
|
||||
{
|
||||
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
|
||||
ASSERT(ccValue >= 0 && ccValue <= 127);
|
||||
ASSERT(ccValue >= 0.0 && ccValue <= 1.0);
|
||||
|
||||
cc[ccNumber] = ccValue;
|
||||
}
|
||||
|
||||
uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept
|
||||
float sfz::MidiState::getCCValue(int ccNumber) const noexcept
|
||||
{
|
||||
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
|
||||
|
||||
return cc[ccNumber];
|
||||
}
|
||||
|
||||
const sfz::SfzCCArray& sfz::MidiState::getCCArray() const noexcept
|
||||
{
|
||||
return cc;
|
||||
}
|
||||
|
||||
void sfz::MidiState::reset(int delay) noexcept
|
||||
{
|
||||
for (auto& velocity: lastNoteVelocities)
|
||||
|
|
@ -104,7 +99,7 @@ void sfz::MidiState::reset(int delay) noexcept
|
|||
|
||||
void sfz::MidiState::resetAllControllers(int delay) noexcept
|
||||
{
|
||||
for (int idx = 0; idx < config::numCCs; idx++)
|
||||
for (unsigned idx = 0; idx < config::numCCs; idx++)
|
||||
cc[idx] = 0;
|
||||
|
||||
pitchBend = 0;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#pragma once
|
||||
#include <chrono>
|
||||
#include <array>
|
||||
#include "SfzHelpers.h"
|
||||
#include "CCMap.h"
|
||||
#include "Range.h"
|
||||
|
||||
|
|
@ -23,13 +22,14 @@ class MidiState
|
|||
{
|
||||
public:
|
||||
MidiState();
|
||||
|
||||
/**
|
||||
* @brief Update the state after a note on event
|
||||
*
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
*/
|
||||
void noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOnEvent(int delay, int noteNumber, float velocity) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Update the state after a note off event
|
||||
|
|
@ -37,7 +37,7 @@ public:
|
|||
* @param noteNumber
|
||||
* @param velocity
|
||||
*/
|
||||
void noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOffEvent(int delay, int noteNumber, float velocity) noexcept;
|
||||
|
||||
int getActiveNotes() const noexcept { return activeNotes; }
|
||||
|
||||
|
|
@ -53,9 +53,9 @@ public:
|
|||
* @brief Get the note on velocity for a given note
|
||||
*
|
||||
* @param noteNumber
|
||||
* @return uint8_t
|
||||
* @return float
|
||||
*/
|
||||
uint8_t getNoteVelocity(int noteNumber) const noexcept;
|
||||
float getNoteVelocity(int noteNumber) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a pitch bend event
|
||||
|
|
@ -77,22 +77,15 @@ public:
|
|||
* @param ccNumber
|
||||
* @param ccValue
|
||||
*/
|
||||
void ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void ccEvent(int delay, int ccNumber, float ccValue) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the CC value for CC number
|
||||
*
|
||||
* @param ccNumber
|
||||
* @return uint8_t
|
||||
* @return float
|
||||
*/
|
||||
uint8_t getCCValue(int ccNumber) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the full CC status
|
||||
*
|
||||
* @return const SfzCCArray&
|
||||
*/
|
||||
const SfzCCArray& getCCArray() const noexcept;
|
||||
float getCCValue(int ccNumber) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Reset the midi state (does not impact the last note on time)
|
||||
|
|
@ -120,7 +113,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.cc)) * mod.value);
|
||||
lambda(value, getCCValue(mod.cc) * mod.value);
|
||||
}
|
||||
return validRange.clamp(value);
|
||||
}
|
||||
|
|
@ -140,12 +133,12 @@ private:
|
|||
* depressed notes.
|
||||
*
|
||||
*/
|
||||
MidiNoteArray<uint8_t> lastNoteVelocities;
|
||||
MidiNoteArray<float> lastNoteVelocities;
|
||||
/**
|
||||
* @brief Current known values for the CCs.
|
||||
*
|
||||
*/
|
||||
SfzCCArray cc;
|
||||
std::array<float, config::numCCs> cc;
|
||||
/**
|
||||
* Pitch bend status
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "MidiState.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/algorithm/container.h"
|
||||
#include <random>
|
||||
|
||||
template<class T>
|
||||
|
|
@ -138,10 +139,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);
|
||||
break;
|
||||
case hash("lovel"):
|
||||
setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
velocityRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("hivel"):
|
||||
setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
velocityRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
|
|
@ -152,10 +155,16 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setRangeEndFromOpcode(opcode, bendRange, Default::bendRange);
|
||||
break;
|
||||
case hash("locc&"):
|
||||
setRangeStartFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("hicc&"):
|
||||
setRangeEndFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("sw_lokey"):
|
||||
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
||||
|
|
@ -247,11 +256,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
break;
|
||||
case hash("on_locc&"): // fallthrough
|
||||
case hash("start_locc&"):
|
||||
setRangeStartFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("on_hicc&"): // fallthrough
|
||||
case hash("start_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
|
||||
// Performance parameters: amplifier
|
||||
|
|
@ -303,8 +318,11 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
case hash("amp_velcurve_&"):
|
||||
{
|
||||
auto value = readOpcode(opcode.value, Default::ampVelcurveRange);
|
||||
if (opcode.parameters.back() > 127)
|
||||
return false;
|
||||
|
||||
if (value)
|
||||
velocityPoints.emplace_back(opcode.parameters.back(), *value);
|
||||
velocityPoints.emplace_back(normalizeVelocity(opcode.parameters.back()), *value);
|
||||
}
|
||||
break;
|
||||
case hash("xfin_lokey"):
|
||||
|
|
@ -320,16 +338,20 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange);
|
||||
break;
|
||||
case hash("xfin_lovel"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelInRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfin_hivel"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelInRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfout_lovel"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelOutRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfout_hivel"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelOutRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xf_keycurve"):
|
||||
switch (hash(opcode.value)) {
|
||||
|
|
@ -356,16 +378,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
}
|
||||
break;
|
||||
case hash("xfin_locc&"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfin_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfout_locc&"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xfout_hicc&"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange);
|
||||
if (opcode.parameters.back() > config::numCCs)
|
||||
return false;
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value));
|
||||
break;
|
||||
case hash("xf_cccurve"):
|
||||
switch (hash(opcode.value)) {
|
||||
|
|
@ -771,8 +805,10 @@ bool sfz::Region::isSwitchedOn() const noexcept
|
|||
return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all();
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue) noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitch) {
|
||||
if (*keyswitch == noteNumber)
|
||||
|
|
@ -823,8 +859,10 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal
|
|||
return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValue) noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitchDown && *keyswitchDown == noteNumber)
|
||||
keySwitched = false;
|
||||
|
|
@ -847,8 +885,9 @@ bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randVa
|
|||
return keyOk && velOk && randOk && releaseTrigger;
|
||||
}
|
||||
|
||||
bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept
|
||||
bool sfz::Region::registerCC(int ccNumber, float ccValue) noexcept
|
||||
{
|
||||
ASSERT(ccValue >= 0.0f && ccValue <= 1.0f);
|
||||
if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue))
|
||||
ccSwitched.set(ccNumber, true);
|
||||
else
|
||||
|
|
@ -891,13 +930,15 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept
|
|||
bpmSwitched = false;
|
||||
}
|
||||
|
||||
float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept
|
||||
float sfz::Region::getBasePitchVariation(int noteNumber, float velocity) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
std::uniform_int_distribution<int> pitchDistribution { -pitchRandom, pitchRandom };
|
||||
auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center
|
||||
pitchVariationInCents += tune; // sample tuning
|
||||
pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose
|
||||
pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity
|
||||
pitchVariationInCents += static_cast<int>(velocity * pitchVeltrack); // track velocity
|
||||
pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes
|
||||
return centsFactor(pitchVariationInCents);
|
||||
}
|
||||
|
|
@ -961,8 +1002,13 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
|
|||
{
|
||||
if (value < crossfadeRange.getStart())
|
||||
return 0.0f;
|
||||
|
||||
const auto length = static_cast<float>(crossfadeRange.length());
|
||||
if (length == 0.0f)
|
||||
return 1.0f;
|
||||
|
||||
else if (value < crossfadeRange.getEnd()) {
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
|
||||
if (curve == SfzCrossfadeCurve::power)
|
||||
return sqrt(crossfadePosition);
|
||||
if (curve == SfzCrossfadeCurve::gain)
|
||||
|
|
@ -977,8 +1023,13 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
|
|||
{
|
||||
if (value > crossfadeRange.getEnd())
|
||||
return 0.0f;
|
||||
|
||||
const auto length = static_cast<float>(crossfadeRange.length());
|
||||
if (length == 0.0f)
|
||||
return 1.0f;
|
||||
|
||||
else if (value > crossfadeRange.getStart()) {
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
|
||||
if (curve == SfzCrossfadeCurve::power)
|
||||
return std::sqrt(1 - crossfadePosition);
|
||||
if (curve == SfzCrossfadeCurve::gain)
|
||||
|
|
@ -988,8 +1039,10 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
|
|||
return 1.0f;
|
||||
}
|
||||
|
||||
float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
|
||||
float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
float baseGain { 1.0f };
|
||||
|
||||
// Amplitude key tracking
|
||||
|
|
@ -1009,19 +1062,19 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
|
|||
return baseGain;
|
||||
}
|
||||
|
||||
float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) const noexcept
|
||||
float sfz::Region::getCrossfadeGain() const noexcept
|
||||
{
|
||||
float gain { 1.0f };
|
||||
|
||||
// Crossfades due to CC states
|
||||
for (const auto& valuePair : crossfadeCCInRange) {
|
||||
const auto ccValue = ccState[valuePair.cc];
|
||||
const auto ccValue = midiState.getCCValue(valuePair.cc);
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
||||
for (const auto& valuePair : crossfadeCCOutRange) {
|
||||
const auto ccValue = ccState[valuePair.cc];
|
||||
const auto ccValue = midiState.getCCValue(valuePair.cc);
|
||||
const auto crossfadeRange = valuePair.value;
|
||||
gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve);
|
||||
}
|
||||
|
|
@ -1029,27 +1082,27 @@ float sfz::Region::getCrossfadeGain(const sfz::SfzCCArray& ccState) const noexce
|
|||
return gain;
|
||||
}
|
||||
|
||||
float sfz::Region::velocityCurve(uint8_t velocity) const noexcept
|
||||
float sfz::Region::velocityCurve(float velocity) const noexcept
|
||||
{
|
||||
float gain { 1.0f };
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
float gain { 1.0f };
|
||||
if (velocityPoints.size() > 0) { // Custom velocity curve
|
||||
auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](const std::pair<int, float>& val) { return val.first >= velocity; });
|
||||
auto after = absl::c_find_if(velocityPoints, [velocity](const std::pair<float, float>& val) { return val.first >= velocity; });
|
||||
auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1;
|
||||
// Linear interpolation
|
||||
float relativePositionInSegment {
|
||||
static_cast<float>(velocity - before->first) / static_cast<float>(after->first - before->first)
|
||||
(velocity - before->first) / (after->first - before->first)
|
||||
};
|
||||
float segmentEndpoints { after->second - before->second };
|
||||
gain *= relativePositionInSegment * segmentEndpoints;
|
||||
} else { // Standard velocity curve
|
||||
const float floatVelocity { static_cast<float>(velocity) / 127.0f };
|
||||
// FIXME: Maybe there's a prettier way to check the boundaries?
|
||||
const float gaindB = [&]() {
|
||||
if (ampVeltrack >= 0)
|
||||
return floatVelocity == 0.0f ? -90.0f : 40 * std::log(floatVelocity) / std::log(10.0f);
|
||||
return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f);
|
||||
else
|
||||
return floatVelocity == 1.0f ? -90.0f : 40 * std::log(1 - floatVelocity) / std::log(10.0f);
|
||||
return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f);
|
||||
}();
|
||||
gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ struct Region {
|
|||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept;
|
||||
bool registerNoteOn(int noteNumber, float velocity, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new note off event. The region may be switched on or off using keys so
|
||||
* this function updates the keyswitches state.
|
||||
|
|
@ -97,7 +97,7 @@ struct Region {
|
|||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept;
|
||||
bool registerNoteOff(int noteNumber, float velocity, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new CC event. The region may be switched on or off using CCs so
|
||||
* this function checks if it indeeds need to activate or not.
|
||||
|
|
@ -107,7 +107,7 @@ struct Region {
|
|||
* @return true if the region should trigger on this event
|
||||
* @return false
|
||||
*/
|
||||
bool registerCC(int ccNumber, uint8_t ccValue) noexcept;
|
||||
bool registerCC(int ccNumber, float ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new pitch wheel event.
|
||||
*
|
||||
|
|
@ -135,7 +135,7 @@ struct Region {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept;
|
||||
float getBasePitchVariation(int noteNumber, float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the note-related gain of the region depending on which note has been
|
||||
* pressed and at which velocity.
|
||||
|
|
@ -144,7 +144,7 @@ struct Region {
|
|||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getNoteGain(int noteNumber, uint8_t velocity) const noexcept;
|
||||
float getNoteGain(int noteNumber, float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the additional crossfade gain of the region depending on the
|
||||
* CC values
|
||||
|
|
@ -152,7 +152,7 @@ struct Region {
|
|||
* @param ccState
|
||||
* @return float
|
||||
*/
|
||||
float getCrossfadeGain(const SfzCCArray& ccState) const noexcept;
|
||||
float getCrossfadeGain() const noexcept;
|
||||
/**
|
||||
* @brief Get the base volume of the region depending on which note has been
|
||||
* pressed to trigger the region.
|
||||
|
|
@ -178,7 +178,7 @@ struct Region {
|
|||
*
|
||||
* @return float
|
||||
*/
|
||||
float velocityCurve(uint8_t velocity) const noexcept;
|
||||
float velocityCurve(float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the region offset in samples
|
||||
*
|
||||
|
|
@ -243,11 +243,11 @@ struct Region {
|
|||
|
||||
// Region logic: key mapping
|
||||
Range<uint8_t> keyRange { Default::keyRange }; //lokey, hikey and key
|
||||
Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
|
||||
Range<float> velocityRange { Default::velocityRange }; // hivel and lovel
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
Range<int> bendRange { Default::bendRange }; // hibend and lobend
|
||||
CCMap<Range<uint8_t>> ccConditions { Default::ccValueRange };
|
||||
CCMap<Range<float>> ccConditions { Default::ccValueRange };
|
||||
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
|
||||
absl::optional<uint8_t> keyswitch {}; // sw_last
|
||||
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
||||
|
|
@ -266,7 +266,7 @@ struct Region {
|
|||
|
||||
// Region logic: triggers
|
||||
SfzTrigger trigger { Default::trigger }; // trigger
|
||||
CCMap<Range<uint8_t>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
|
||||
CCMap<Range<float>> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN
|
||||
|
||||
// Performance parameters: amplifier
|
||||
float volume { Default::volume }; // volume
|
||||
|
|
@ -282,17 +282,17 @@ struct Region {
|
|||
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
|
||||
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
|
||||
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
|
||||
std::vector<std::pair<int, float>> velocityPoints; // amp_velcurve_N
|
||||
std::vector<std::pair<float, float>> velocityPoints; // amp_velcurve_N
|
||||
float ampRandom { Default::ampRandom }; // amp_random
|
||||
Range<uint8_t> crossfadeKeyInRange { Default::crossfadeKeyInRange };
|
||||
Range<uint8_t> crossfadeKeyOutRange { Default::crossfadeKeyOutRange };
|
||||
Range<uint8_t> crossfadeVelInRange { Default::crossfadeVelInRange };
|
||||
Range<uint8_t> crossfadeVelOutRange { Default::crossfadeVelOutRange };
|
||||
Range<float> crossfadeVelInRange { Default::crossfadeVelInRange };
|
||||
Range<float> crossfadeVelOutRange { Default::crossfadeVelOutRange };
|
||||
SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve };
|
||||
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
|
||||
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
|
||||
CCMap<Range<uint8_t>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
|
||||
CCMap<Range<uint8_t>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
|
||||
CCMap<Range<float>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
|
||||
CCMap<Range<float>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
|
||||
float rtDecay { Default::rtDecay }; // rt_decay
|
||||
|
||||
// Filters and EQs
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ void multiplyAdd<float, true>(absl::Span<const float> gain, absl::Span<const flo
|
|||
template <class T, bool SIMD = SIMDConfig::multiplyAdd>
|
||||
void multiplyAdd(const T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
|
||||
{
|
||||
ASSERT(input.size() <= output.size());
|
||||
// ASSERT(input.size() <= output.size());
|
||||
auto* in = input.begin();
|
||||
auto* out = output.begin();
|
||||
auto* sentinel = out + std::min(output.size(), input.size());
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
namespace sfz
|
||||
{
|
||||
|
||||
using SfzCCArray = std::array<uint8_t, config::numCCs>;
|
||||
using CCNamePair = std::pair<uint16_t, std::string>;
|
||||
|
||||
template<class ValueType>
|
||||
|
|
@ -83,6 +82,16 @@ constexpr T denormalize7Bits(float value)
|
|||
return static_cast<T>(value * 127.0f);
|
||||
}
|
||||
|
||||
constexpr uint8_t denormalizeCC(float value)
|
||||
{
|
||||
return denormalize7Bits<uint8_t>(value);
|
||||
}
|
||||
|
||||
constexpr uint8_t denormalizeVelocity(float value)
|
||||
{
|
||||
return denormalize7Bits<uint8_t>(value);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr float normalize7Bits(T value)
|
||||
{
|
||||
|
|
@ -141,21 +150,17 @@ constexpr float normalizeBend(float bendValue)
|
|||
return min(max(bendValue, -8191.0f), 8191.0f) / 8191.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone.
|
||||
*
|
||||
* @param ccValues
|
||||
* @param ccSwitch
|
||||
* @param value
|
||||
* @return float
|
||||
*/
|
||||
inline float ccSwitchedValue(const SfzCCArray& ccValues, const absl::optional<CCValuePair<float>>& ccSwitch, float value) noexcept
|
||||
namespace literals
|
||||
{
|
||||
if (ccSwitch)
|
||||
return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]);
|
||||
else
|
||||
return value;
|
||||
inline float operator ""_norm(unsigned long long int value)
|
||||
{
|
||||
if (value > 127)
|
||||
value = 127;
|
||||
|
||||
return normalize7Bits(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a note in string to its equivalent midi note number
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ sfz::Synth::~Synth()
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto& voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
||||
resources.filePool.emptyFileLoadingQueues();
|
||||
|
|
@ -129,11 +129,11 @@ void sfz::Synth::clear()
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto &voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
for (auto& list: noteActivationLists)
|
||||
for (auto& list : noteActivationLists)
|
||||
list.clear();
|
||||
for (auto& list: ccActivationLists)
|
||||
for (auto& list : ccActivationLists)
|
||||
list.clear();
|
||||
regions.clear();
|
||||
effectBuses.clear();
|
||||
|
|
@ -181,8 +181,9 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
|||
case hash("Set_cc&"): // fallthrough
|
||||
case hash("set_cc&"):
|
||||
if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) {
|
||||
const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0);
|
||||
resources.midiState.ccEvent(0, member.parameters.back(), ccValue);
|
||||
const auto ccValue = readOpcode(member.value, Default::midi7Range);
|
||||
if (ccValue)
|
||||
resources.midiState.ccEvent(0, member.parameters.back(), normalizeCC(*ccValue));
|
||||
}
|
||||
break;
|
||||
case hash("Label_cc&"): // fallthrough
|
||||
|
|
@ -275,17 +276,19 @@ void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
|
|||
void addEndpointsToVelocityCurve(sfz::Region& region)
|
||||
{
|
||||
if (region.velocityPoints.size() > 0) {
|
||||
absl::c_sort(region.velocityPoints, [](const std::pair<int, float>& lhs, const std::pair<int, float>& rhs) { return lhs.first < rhs.first; });
|
||||
const auto velocityStart = sfz::Default::velocityRange.getStart();
|
||||
const auto velocityEnd = sfz::Default::velocityRange.getEnd();
|
||||
absl::c_sort(region.velocityPoints, [](const std::pair<float, float>& lhs, const std::pair<float, float>& rhs) { return lhs.first < rhs.first; });
|
||||
if (region.ampVeltrack > 0) {
|
||||
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getEnd())
|
||||
region.velocityPoints.push_back(std::make_pair<int, float>(127, 1.0f));
|
||||
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getStart())
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(0, 0.0f));
|
||||
if (region.velocityPoints.front().first != velocityStart)
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityStart, velocityStart));
|
||||
if (region.velocityPoints.back().first != velocityEnd)
|
||||
region.velocityPoints.push_back(std::make_pair(velocityEnd, velocityEnd));
|
||||
} else {
|
||||
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getEnd())
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(127, 0.0f));
|
||||
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getStart())
|
||||
region.velocityPoints.push_back(std::make_pair<int, float>(0, 1.0f));
|
||||
if (region.velocityPoints.front().first != velocityEnd)
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityEnd, velocityStart));
|
||||
if (region.velocityPoints.back().first != velocityStart)
|
||||
region.velocityPoints.push_back(std::make_pair(velocityStart, velocityEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -341,8 +344,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
if (region->loopRange.getEnd() == Default::loopRange.getEnd())
|
||||
region->loopRange.setEnd(region->sampleEnd);
|
||||
|
||||
if (fileInformation->loopBegin != Default::loopRange.getStart() &&
|
||||
fileInformation->loopEnd != Default::loopRange.getEnd()) {
|
||||
if (fileInformation->loopBegin != Default::loopRange.getStart() && fileInformation->loopEnd != Default::loopRange.getEnd()) {
|
||||
if (region->loopRange.getStart() == Default::loopRange.getStart())
|
||||
region->loopRange.setStart(fileInformation->loopBegin);
|
||||
|
||||
|
|
@ -374,38 +376,34 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
}
|
||||
|
||||
for (auto note = 0; note < 128; note++) {
|
||||
if (region->keyRange.containsWithEnd(note) ||
|
||||
(region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
|
||||
if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
|
||||
noteActivationLists[note].push_back(region);
|
||||
}
|
||||
|
||||
for (auto cc = 0; cc < config::numCCs; cc++) {
|
||||
for (unsigned cc = 0; cc < config::numCCs; cc++) {
|
||||
if (region->ccTriggers.contains(cc) || region->ccConditions.contains(cc))
|
||||
ccActivationLists[cc].push_back(region);
|
||||
}
|
||||
|
||||
// Defaults
|
||||
for (int ccIndex = 0; ccIndex < config::numCCs; ccIndex++) {
|
||||
region->registerCC(ccIndex, resources.midiState.getCCValue(ccIndex));
|
||||
for (unsigned cc = 0; cc < config::numCCs; cc++) {
|
||||
region->registerCC(cc, resources.midiState.getCCValue(cc));
|
||||
}
|
||||
|
||||
if (defaultSwitch) {
|
||||
region->registerNoteOn(*defaultSwitch, 127, 1.0);
|
||||
region->registerNoteOff(*defaultSwitch, 0, 1.0);
|
||||
region->registerNoteOn(*defaultSwitch, 1.0f, 1.0f);
|
||||
region->registerNoteOff(*defaultSwitch, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
// Set the default frequencies on equalizers if needed
|
||||
if (region->equalizers.size() > 0
|
||||
&& region->equalizers[0].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[0].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[0].frequency = Default::eqFrequency1;
|
||||
if (region->equalizers.size() > 1
|
||||
&& region->equalizers[1].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[1].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[1].frequency = Default::eqFrequency2;
|
||||
if (region->equalizers.size() > 2
|
||||
&& region->equalizers[2].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[2].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[2].frequency = Default::eqFrequency3;
|
||||
}
|
||||
}
|
||||
|
|
@ -425,7 +423,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
regions.resize(remainingRegions);
|
||||
modificationTime = checkModificationTime();
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->setMaxFiltersPerVoice(maxFilters);
|
||||
voice->setMaxEQsPerVoice(maxEQs);
|
||||
}
|
||||
|
|
@ -468,7 +466,6 @@ int sfz::Synth::getNumActiveVoices() const noexcept
|
|||
|
||||
void sfz::Synth::garbageCollect() noexcept
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||
|
|
@ -487,7 +484,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
|||
for (auto& voice : voices)
|
||||
voice->setSamplesPerBlock(samplesPerBlock);
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->setSamplesPerBlock(samplesPerBlock);
|
||||
}
|
||||
|
|
@ -507,7 +504,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
|||
resources.filterPool.setSampleRate(sampleRate);
|
||||
resources.eqPool.setSampleRate(sampleRate);
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->setSampleRate(sampleRate);
|
||||
}
|
||||
|
|
@ -532,7 +529,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
|
||||
{ // Prepare the effect inputs. They are mixes of per-region outputs.
|
||||
ScopedTiming logger { callbackBreakdown.effects };
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->clearInputs(numFrames);
|
||||
}
|
||||
|
|
@ -576,7 +573,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
// without any <effect>, the signal is just going to flow through it.
|
||||
ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration };
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus) {
|
||||
bus->process(numFrames);
|
||||
bus->mixOutputsTo(buffer, tempMixNode, numFrames);
|
||||
|
|
@ -604,15 +601,15 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
|
|||
{
|
||||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
resources.midiState.noteOnEvent(delay, noteNumber, velocity);
|
||||
resources.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
return;
|
||||
|
||||
noteOnDispatch(delay, noteNumber, velocity);
|
||||
noteOnDispatch(delay, noteNumber, normalizedVelocity);
|
||||
}
|
||||
|
||||
void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
|
|
@ -620,9 +617,9 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
|||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
UNUSED(velocity);
|
||||
|
||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
resources.midiState.noteOffEvent(delay, noteNumber, velocity);
|
||||
resources.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
|
|
@ -639,7 +636,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
|||
noteOffDispatch(delay, noteNumber, replacedVelocity);
|
||||
}
|
||||
|
||||
void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noexcept
|
||||
{
|
||||
const auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
for (auto& region : noteActivationLists[noteNumber]) {
|
||||
|
|
@ -653,7 +650,7 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) no
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept
|
||||
{
|
||||
const auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
for (auto& region : noteActivationLists[noteNumber]) {
|
||||
|
|
@ -676,9 +673,10 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
|
|||
{
|
||||
ASSERT(ccNumber < config::numCCs);
|
||||
ASSERT(ccNumber >= 0);
|
||||
const auto normalizedCC = normalizeCC(ccValue);
|
||||
|
||||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
resources.midiState.ccEvent(delay, ccNumber, ccValue);
|
||||
resources.midiState.ccEvent(delay, ccNumber, normalizedCC);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
|
|
@ -690,15 +688,15 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
|
|||
}
|
||||
|
||||
for (auto& voice : voices)
|
||||
voice->registerCC(delay, ccNumber, ccValue);
|
||||
voice->registerCC(delay, ccNumber, normalizedCC);
|
||||
|
||||
for (auto& region : ccActivationLists[ccNumber]) {
|
||||
if (region->registerCC(ccNumber, ccValue)) {
|
||||
if (region->registerCC(ccNumber, normalizedCC)) {
|
||||
auto voice = findFreeVoice();
|
||||
if (voice == nullptr)
|
||||
continue;
|
||||
|
||||
voice->startVoice(region, delay, ccNumber, ccValue, Voice::TriggerType::CC);
|
||||
voice->startVoice(region, delay, ccNumber, normalizedCC, Voice::TriggerType::CC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -711,11 +709,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
|
|||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
resources.midiState.pitchBendEvent(delay, pitch);
|
||||
|
||||
for (auto& region: regions) {
|
||||
for (auto& region : regions) {
|
||||
region->registerPitchWheel(pitch);
|
||||
}
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->registerPitchWheel(delay, pitch);
|
||||
}
|
||||
}
|
||||
|
|
@ -753,10 +751,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
if (model.empty())
|
||||
model = config::midnamModel;
|
||||
|
||||
doc.append_child(pugi::node_doctype).set_value(
|
||||
"MIDINameDocument PUBLIC"
|
||||
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
|
||||
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
|
||||
doc.append_child(pugi::node_doctype).set_value("MIDINameDocument PUBLIC"
|
||||
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
|
||||
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
|
||||
|
||||
pugi::xml_node root = doc.append_child("MIDINameDocument");
|
||||
|
||||
|
|
@ -767,9 +764,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
|
||||
pugi::xml_node device = root.append_child("MasterDeviceNames");
|
||||
device.append_child("Manufacturer")
|
||||
.append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str());
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value(std::string(manufacturer).c_str());
|
||||
device.append_child("Model")
|
||||
.append_child(pugi::node_pcdata).set_value(std::string(model).c_str());
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value(std::string(model).c_str());
|
||||
|
||||
{
|
||||
pugi::xml_node devmode = device.append_child("CustomDeviceMode");
|
||||
|
|
@ -795,7 +794,8 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
}
|
||||
|
||||
chns.append_child("UsesControlNameList")
|
||||
.append_attribute("Name").set_value("Controls");
|
||||
.append_attribute("Name")
|
||||
.set_value("Controls");
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -876,7 +876,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
|||
|
||||
void sfz::Synth::resetVoices(int numVoices)
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
|
@ -885,7 +885,7 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
for (int i = 0; i < numVoices; ++i)
|
||||
voices.push_back(absl::make_unique<Voice>(resources));
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->setSampleRate(this->sampleRate);
|
||||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||
}
|
||||
|
|
@ -896,12 +896,12 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
|
||||
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto& voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
||||
resources.filePool.emptyFileLoadingQueues();
|
||||
|
|
@ -916,7 +916,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept
|
|||
|
||||
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
|
@ -951,15 +951,15 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
|
|||
return;
|
||||
|
||||
resources.midiState.resetAllControllers(delay);
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->registerPitchWheel(delay, 0);
|
||||
for (int cc = 0; cc < config::numCCs; ++cc)
|
||||
voice->registerCC(delay, cc, 0);
|
||||
for (unsigned cc = 0; cc < config::numCCs; ++cc)
|
||||
voice->registerCC(delay, cc, 0.0f);
|
||||
}
|
||||
|
||||
for (auto& region: regions) {
|
||||
for (int cc = 0; cc < config::numCCs; ++cc)
|
||||
region->registerCC(cc, 0);
|
||||
for (auto& region : regions) {
|
||||
for (unsigned cc = 0; cc < config::numCCs; ++cc)
|
||||
region->registerCC(cc, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -996,13 +996,13 @@ void sfz::Synth::disableLogging() noexcept
|
|||
|
||||
void sfz::Synth::allSoundOff() noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto &voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
for (auto& effectBus: effectBuses)
|
||||
for (auto& effectBus : effectBuses)
|
||||
effectBus->clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -463,8 +463,8 @@ private:
|
|||
|
||||
fs::file_time_type checkModificationTime();
|
||||
|
||||
void noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept;
|
||||
void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept;
|
||||
|
||||
// Opcode memory; these are used to build regions, as a new region
|
||||
// will integrate opcodes from the group, master and global block
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ sfz::Voice::Voice(sfz::Resources& resources)
|
|||
waveOscillator.init(sampleRate);
|
||||
}
|
||||
|
||||
void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept
|
||||
void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept
|
||||
{
|
||||
ASSERT(value >= 0.0f && value <= 1.0f);
|
||||
|
||||
this->triggerType = triggerType;
|
||||
triggerNumber = number;
|
||||
triggerValue = value;
|
||||
|
|
@ -71,13 +73,12 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
|
|||
}
|
||||
speedRatio = static_cast<float>(currentPromise->sampleRate / this->sampleRate);
|
||||
}
|
||||
|
||||
pitchRatio = region->getBasePitchVariation(number, value);
|
||||
|
||||
baseVolumedB = region->getBaseVolumedB(number);
|
||||
auto volumedB = baseVolumedB;
|
||||
if (region->volumeCC)
|
||||
volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value;
|
||||
volumedB += resources.midiState.getCCValue(region->volumeCC->cc) * region->volumeCC->value;
|
||||
volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB)));
|
||||
|
||||
baseGain = region->getBaseGain();
|
||||
|
|
@ -86,28 +87,28 @@ 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->cc)) * normalizePercents(region->amplitudeCC->value);
|
||||
gain += resources.midiState.getCCValue(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value);
|
||||
amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain));
|
||||
|
||||
float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) };
|
||||
float crossfadeGain { region->getCrossfadeGain() };
|
||||
crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain));
|
||||
|
||||
basePan = normalizePercents(region->pan);
|
||||
auto pan = basePan;
|
||||
if (region->panCC)
|
||||
pan += normalizeCC(resources.midiState.getCCValue(region->panCC->cc)) * normalizePercents(region->panCC->value);
|
||||
pan += 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->cc)) * normalizePercents(region->positionCC->value);
|
||||
position += 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->cc)) * normalizePercents(region->widthCC->value);
|
||||
width += resources.midiState.getCCValue(region->widthCC->cc) * normalizePercents(region->widthCC->value);
|
||||
widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width));
|
||||
|
||||
pitchBendEnvelope.setFunction([region](float pitchValue){
|
||||
|
|
@ -159,9 +160,11 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0 && velocity <= 1.0);
|
||||
UNUSED(velocity);
|
||||
|
||||
if (region == nullptr)
|
||||
return;
|
||||
|
||||
|
|
@ -179,8 +182,9 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) no
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept
|
||||
void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept
|
||||
{
|
||||
ASSERT(ccValue >= 0.0 && ccValue <= 1.0);
|
||||
if (region == nullptr)
|
||||
return;
|
||||
|
||||
|
|
@ -200,32 +204,32 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept
|
|||
delay = max(delay, minEnvelopeDelay);
|
||||
|
||||
if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) {
|
||||
const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) };
|
||||
const float newGain { baseGain + ccValue * normalizePercents(region->amplitudeCC->value) };
|
||||
amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain));
|
||||
}
|
||||
|
||||
if (region->volumeCC && ccNumber == region->volumeCC->cc) {
|
||||
const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value };
|
||||
const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value };
|
||||
volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB)));
|
||||
}
|
||||
|
||||
if (region->panCC && ccNumber == region->panCC->cc) {
|
||||
const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) };
|
||||
const float newPan { basePan + ccValue * normalizePercents(region->panCC->value) };
|
||||
panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan));
|
||||
}
|
||||
|
||||
if (region->positionCC && ccNumber == region->positionCC->cc) {
|
||||
const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) };
|
||||
const float newPosition { basePosition + ccValue * normalizePercents(region->positionCC->value) };
|
||||
positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition));
|
||||
}
|
||||
|
||||
if (region->widthCC && ccNumber == region->widthCC->cc) {
|
||||
const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) };
|
||||
const float newWidth { baseWidth + ccValue * normalizePercents(region->widthCC->value) };
|
||||
widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth));
|
||||
}
|
||||
|
||||
if (region->crossfadeCCInRange.contains(ccNumber) || region->crossfadeCCOutRange.contains(ccNumber)) {
|
||||
const float crossfadeGain = region->getCrossfadeGain(resources.midiState.getCCArray());
|
||||
const float crossfadeGain = region->getCrossfadeGain();
|
||||
crossfadeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(crossfadeGain));
|
||||
}
|
||||
}
|
||||
|
|
@ -552,7 +556,7 @@ int sfz::Voice::getTriggerNumber() const noexcept
|
|||
return triggerNumber;
|
||||
}
|
||||
|
||||
uint8_t sfz::Voice::getTriggerValue() const noexcept
|
||||
float sfz::Voice::getTriggerValue() const noexcept
|
||||
{
|
||||
return triggerValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public:
|
|||
* @param value
|
||||
* @param triggerType
|
||||
*/
|
||||
void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept;
|
||||
void startVoice(Region* region, int delay, int number, float value, TriggerType triggerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a note-off event; this may trigger a release.
|
||||
|
|
@ -90,7 +90,7 @@ public:
|
|||
* @param noteNumber
|
||||
* @param velocity
|
||||
*/
|
||||
void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
void registerNoteOff(int delay, int noteNumber, float velocity) noexcept;
|
||||
/**
|
||||
* @brief Register a CC event; this may trigger a release. If the voice is playing and its
|
||||
* region has CC modifiers, it will use this value to compute the CC envelope to apply to the
|
||||
|
|
@ -100,7 +100,7 @@ public:
|
|||
* @param ccNumber
|
||||
* @param ccValue
|
||||
*/
|
||||
void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void registerCC(int delay, int ccNumber, float ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a pitch wheel event; for now this does nothing
|
||||
*
|
||||
|
|
@ -163,9 +163,9 @@ public:
|
|||
/**
|
||||
* @brief Get the value that triggered the voice (note velocity or cc value)
|
||||
*
|
||||
* @return uint8_t
|
||||
* @return float
|
||||
*/
|
||||
uint8_t getTriggerValue() const noexcept;
|
||||
float getTriggerValue() const noexcept;
|
||||
/**
|
||||
* @brief Get the type of trigger
|
||||
*
|
||||
|
|
@ -261,7 +261,7 @@ private:
|
|||
|
||||
TriggerType triggerType;
|
||||
int triggerNumber;
|
||||
uint8_t triggerValue;
|
||||
float triggerValue;
|
||||
absl::optional<int> triggerDelay;
|
||||
|
||||
float speedRatio { 1.0 };
|
||||
|
|
|
|||
|
|
@ -49,14 +49,14 @@ TEST_CASE("[ADSREnvelope] Attack")
|
|||
sfz::Region region { state };
|
||||
region.amplitudeEG.attack = 0.02f;
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
std::array<float, 5> output;
|
||||
std::array<float, 5> expected { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
|
@ -69,14 +69,14 @@ TEST_CASE("[ADSREnvelope] Attack again")
|
|||
sfz::Region region { state };
|
||||
region.amplitudeEG.attack = 0.03f;
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
std::array<float, 5> output;
|
||||
std::array<float, 5> expected { 0.33333f, 0.66667f, 1.0f, 1.0f, 1.0f };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
|
@ -90,7 +90,7 @@ TEST_CASE("[ADSREnvelope] Release")
|
|||
region.amplitudeEG.attack = 0.02f;
|
||||
region.amplitudeEG.release = 0.04f;
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(2);
|
||||
std::array<float, 8> output;
|
||||
std::array<float, 8> expected { 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f };
|
||||
|
|
@ -98,7 +98,7 @@ TEST_CASE("[ADSREnvelope] Release")
|
|||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(2);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
|
|
@ -114,14 +114,14 @@ TEST_CASE("[ADSREnvelope] Delay")
|
|||
region.amplitudeEG.release = 0.04f;
|
||||
region.amplitudeEG.delay = 0.02f;
|
||||
std::array<float, 10> output;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(4);
|
||||
std::array<float, 10> expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(4);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
|
|
@ -138,13 +138,13 @@ TEST_CASE("[ADSREnvelope] Lower sustain")
|
|||
region.amplitudeEG.delay = 0.02f;
|
||||
region.amplitudeEG.sustain = 50.0f;
|
||||
std::array<float, 10> output;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
std::array<float, 10> expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
|
@ -161,13 +161,13 @@ TEST_CASE("[ADSREnvelope] Decay")
|
|||
region.amplitudeEG.sustain = 50.0f;
|
||||
region.amplitudeEG.decay = 0.02f;
|
||||
std::array<float, 10> output;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
std::array<float, 10> expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5 };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
|
@ -185,13 +185,13 @@ TEST_CASE("[ADSREnvelope] Hold")
|
|||
region.amplitudeEG.decay = 0.02f;
|
||||
region.amplitudeEG.hold = 0.02f;
|
||||
std::array<float, 12> output;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
std::array<float, 12> expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
|
|
@ -208,7 +208,7 @@ TEST_CASE("[ADSREnvelope] Hold with release")
|
|||
region.amplitudeEG.sustain = 50.0f;
|
||||
region.amplitudeEG.decay = 0.02f;
|
||||
region.amplitudeEG.hold = 0.02f;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(8);
|
||||
std::array<float, 14> output;
|
||||
std::array<float, 14> expected { 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.707107f, 0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f, 0.0f, 0.0f };
|
||||
|
|
@ -216,7 +216,7 @@ TEST_CASE("[ADSREnvelope] Hold with release")
|
|||
out = envelope.getNextValue();
|
||||
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(8);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
|
|
@ -234,14 +234,14 @@ TEST_CASE("[ADSREnvelope] Hold with release 2")
|
|||
region.amplitudeEG.sustain = 50.0f;
|
||||
region.amplitudeEG.decay = 0.02f;
|
||||
region.amplitudeEG.hold = 0.02f;
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(4);
|
||||
std::array<float, 14> output;
|
||||
std::array<float, 14> expected { 0.0f, 0.0f, 0.5f, 1.0f, 0.08409f, 0.00707f, 0.000594604f, 0.00005f, 0.0f, 0.0f, 0.0f, 0.0 };
|
||||
for (auto& out : output)
|
||||
out = envelope.getNextValue();
|
||||
REQUIRE(approxEqual<float>(output, expected));
|
||||
envelope.reset(region, state, 0, 0, 100.0f);
|
||||
envelope.reset(region, state, 0, 0.0f, 100.0f);
|
||||
envelope.startRelease(4);
|
||||
absl::c_fill(output, -1.0f);
|
||||
envelope.getBlock(absl::MakeSpan(output));
|
||||
|
|
|
|||
|
|
@ -6,114 +6,116 @@
|
|||
|
||||
|
||||
#include "sfizz/EGDescription.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[EGDescription] Attack range")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.attack = 1;
|
||||
eg.vel2attack = -1.27f;
|
||||
eg.ccAttack = { 63, 1.27f };
|
||||
REQUIRE( eg.getAttack(ccArray, 0) == 1.0f );
|
||||
REQUIRE( eg.getAttack(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getAttack(ccArray, 127) == 1.0f );
|
||||
REQUIRE( eg.getAttack(ccArray, 0) == 2.27f );
|
||||
REQUIRE( eg.getAttack(state, 0_norm) == 1.0f );
|
||||
REQUIRE( eg.getAttack(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getAttack(state, 127_norm) == 1.0f );
|
||||
REQUIRE( eg.getAttack(state, 0_norm) == 2.27f );
|
||||
eg.ccAttack = { 63, 127.0f };
|
||||
REQUIRE( eg.getAttack(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getAttack(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Delay range")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.delay = 1;
|
||||
eg.vel2delay = -1.27f;
|
||||
eg.ccDelay = { 63, 1.27f };
|
||||
REQUIRE( eg.getDelay(ccArray, 0) == 1.0f );
|
||||
REQUIRE( eg.getDelay(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getDelay(ccArray, 127) == 1.0f );
|
||||
REQUIRE( eg.getDelay(ccArray, 0) == 2.27f );
|
||||
REQUIRE( eg.getDelay(state, 0_norm) == 1.0f );
|
||||
REQUIRE( eg.getDelay(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getDelay(state, 127_norm) == 1.0f );
|
||||
REQUIRE( eg.getDelay(state, 0_norm) == 2.27f );
|
||||
eg.ccDelay = { 63, 127.0f };
|
||||
REQUIRE( eg.getDelay(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getDelay(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Decay range")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.decay = 1.0f;
|
||||
eg.vel2decay = -1.27f;
|
||||
eg.ccDecay = { 63, 1.27f };
|
||||
REQUIRE( eg.getDecay(ccArray, 0) == 1.0f );
|
||||
REQUIRE( eg.getDecay(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getDecay(ccArray, 127) == 1.0f );
|
||||
REQUIRE( eg.getDecay(ccArray, 0) == 2.27f );
|
||||
REQUIRE( eg.getDecay(state, 0_norm) == 1.0f );
|
||||
REQUIRE( eg.getDecay(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getDecay(state, 127_norm) == 1.0f );
|
||||
REQUIRE( eg.getDecay(state, 0_norm) == 2.27f );
|
||||
eg.ccDecay = { 63, 127.0f };
|
||||
REQUIRE( eg.getDecay(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getDecay(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Release range")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.release = 1;
|
||||
eg.vel2release = -1.27f;
|
||||
eg.ccRelease = { 63, 1.27f };
|
||||
REQUIRE( eg.getRelease(ccArray, 0) == 1.0f );
|
||||
REQUIRE( eg.getRelease(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getRelease(ccArray, 127) == 1.0f );
|
||||
REQUIRE( eg.getRelease(ccArray, 0) == 2.27f );
|
||||
REQUIRE( eg.getRelease(state, 0_norm) == 1.0f );
|
||||
REQUIRE( eg.getRelease(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getRelease(state, 127_norm) == 1.0f );
|
||||
REQUIRE( eg.getRelease(state, 0_norm) == 2.27f );
|
||||
eg.ccRelease = { 63, 127.0f };
|
||||
REQUIRE( eg.getRelease(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getRelease(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Hold range")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.hold = 1;
|
||||
eg.vel2hold = -1.27f;
|
||||
eg.ccHold = { 63, 1.27f };
|
||||
REQUIRE( eg.getHold(ccArray, 0) == 1.0f );
|
||||
REQUIRE( eg.getHold(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getHold(ccArray, 127) == 1.0f );
|
||||
REQUIRE( eg.getHold(ccArray, 0) == 2.27f );
|
||||
REQUIRE( eg.getHold(state, 0_norm) == 1.0f );
|
||||
REQUIRE( eg.getHold(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getHold(state, 127_norm) == 1.0f );
|
||||
REQUIRE( eg.getHold(state, 0_norm) == 2.27f );
|
||||
eg.ccHold = { 63, 127.0f };
|
||||
REQUIRE( eg.getHold(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getHold(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Sustain level")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.sustain = 50;
|
||||
eg.vel2sustain = -100;
|
||||
eg.ccSustain = { 63, 100.0f };
|
||||
REQUIRE( eg.getSustain(ccArray, 0) == 50.0f );
|
||||
REQUIRE( eg.getSustain(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getSustain(ccArray, 127) == 50.0f );
|
||||
REQUIRE( eg.getSustain(state, 0_norm) == 50.0f );
|
||||
REQUIRE( eg.getSustain(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getSustain(state, 127_norm) == 50.0f );
|
||||
eg.ccSustain = { 63, 200.0f };
|
||||
REQUIRE( eg.getSustain(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getSustain(state, 0_norm) == 100.0f );
|
||||
}
|
||||
|
||||
TEST_CASE("[EGDescription] Start level")
|
||||
{
|
||||
sfz::EGDescription eg;
|
||||
sfz::SfzCCArray ccArray { 0 };
|
||||
sfz::MidiState state;
|
||||
eg.start = 0;
|
||||
eg.ccStart = { 63, 127.0f };
|
||||
REQUIRE( eg.getStart(ccArray, 0) == 0.0f );
|
||||
REQUIRE( eg.getStart(ccArray, 127) == 0.0f );
|
||||
ccArray[63] = 127;
|
||||
REQUIRE( eg.getStart(ccArray, 0) == 100.0f );
|
||||
REQUIRE( eg.getStart(state, 0_norm) == 0.0f );
|
||||
REQUIRE( eg.getStart(state, 127_norm) == 0.0f );
|
||||
state.ccEvent(0, 63, 127_norm);
|
||||
REQUIRE( eg.getStart(state, 0_norm) == 100.0f );
|
||||
eg.ccStart = { 63, -127.0f };
|
||||
REQUIRE( eg.getStart(ccArray, 0) == 0.0f );
|
||||
REQUIRE( eg.getStart(state, 0_norm) == 0.0f );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "sfizz/Synth.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "ghc/fs_std.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[Files] Single region (regions_one.sfz)")
|
||||
{
|
||||
|
|
@ -137,11 +139,11 @@ TEST_CASE("[Files] Group from AVL")
|
|||
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
|
||||
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(36, 36));
|
||||
}
|
||||
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<uint8_t>(1, 26));
|
||||
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<uint8_t>(27, 52));
|
||||
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<uint8_t>(53, 77));
|
||||
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<uint8_t>(78, 102));
|
||||
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<uint8_t>(103, 127));
|
||||
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<float>(1_norm, 26_norm));
|
||||
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<float>(27_norm, 52_norm));
|
||||
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<float>(53_norm, 77_norm));
|
||||
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<float>(78_norm, 102_norm));
|
||||
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<float>(103_norm, 127_norm));
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Full hierarchy")
|
||||
|
|
@ -232,9 +234,9 @@ TEST_CASE("[Files] Pizz basic")
|
|||
REQUIRE(synth.getNumRegions() == 4);
|
||||
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
||||
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22));
|
||||
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<uint8_t>(97, 127));
|
||||
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm));
|
||||
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
|
||||
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<uint8_t>(0, 13));
|
||||
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<float>(0_norm, 13_norm));
|
||||
}
|
||||
REQUIRE(synth.getRegionView(0)->randRange == sfz::Range<float>(0, 0.25));
|
||||
REQUIRE(synth.getRegionView(1)->randRange == sfz::Range<float>(0.25, 0.5));
|
||||
|
|
@ -353,8 +355,8 @@ TEST_CASE("[Files] Set CC applies properly")
|
|||
{
|
||||
sfz::Synth synth;
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
|
||||
REQUIRE(synth.getMidiState().getCCValue(142) == 63);
|
||||
REQUIRE(synth.getMidiState().getCCValue(61) == 122);
|
||||
REQUIRE(synth.getMidiState().getCCValue(142) == 63_norm);
|
||||
REQUIRE(synth.getMidiState().getCCValue(61) == 122_norm);
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Note and octave offsets")
|
||||
|
|
|
|||
|
|
@ -11,28 +11,27 @@
|
|||
*/
|
||||
|
||||
#include "sfizz/MidiState.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "absl/strings/string_view.h"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[MidiState] Initial values")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
for (auto& cc: state.getCCArray())
|
||||
REQUIRE( cc == 0 );
|
||||
for (unsigned cc = 0; cc < sfz::config::numCCs; cc++)
|
||||
REQUIRE( state.getCCValue(cc) == 0_norm );
|
||||
REQUIRE( state.getPitchBend() == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get CCs")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
const auto& cc = state.getCCArray();
|
||||
state.ccEvent(0, 24, 23);
|
||||
state.ccEvent(0, 123, 124);
|
||||
REQUIRE(state.getCCValue(24) == 23);
|
||||
REQUIRE(cc[24] == 23);
|
||||
REQUIRE(state.getCCValue(123) == 124);
|
||||
REQUIRE(cc[123] == 124);
|
||||
state.ccEvent(0, 24, 23_norm);
|
||||
state.ccEvent(0, 123, 124_norm);
|
||||
REQUIRE(state.getCCValue(24) == 23_norm);
|
||||
REQUIRE(state.getCCValue(123) == 124_norm);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get pitch bends")
|
||||
|
|
@ -48,26 +47,25 @@ TEST_CASE("[MidiState] Reset")
|
|||
{
|
||||
sfz::MidiState state;
|
||||
state.pitchBendEvent(0, 894);
|
||||
state.noteOnEvent(0, 64, 24);
|
||||
state.ccEvent(0, 123, 124);
|
||||
state.noteOnEvent(0, 64, 24_norm);
|
||||
state.ccEvent(0, 123, 124_norm);
|
||||
state.reset(0);
|
||||
REQUIRE(state.getPitchBend() == 0);
|
||||
REQUIRE(state.getNoteVelocity(64) == 0);
|
||||
REQUIRE(state.getCCValue(123) == 0);
|
||||
REQUIRE(state.getNoteVelocity(64) == 0_norm);
|
||||
REQUIRE(state.getCCValue(123) == 0_norm);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get note velocities")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
state.noteOnEvent(0, 64, 24);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 24);
|
||||
state.noteOnEvent(0, 64, 123);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 123);
|
||||
state.noteOnEvent(0, 64, 24_norm);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 24_norm);
|
||||
state.noteOnEvent(0, 64, 123_norm);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 123_norm);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Extended CCs")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
REQUIRE(state.getCCArray().size() >= 142);
|
||||
state.ccEvent(0, 142, 64); // should not trap
|
||||
state.ccEvent(0, 142, 64_norm); // should not trap
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "sfizz/Region.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("Region activation", "Region tests")
|
||||
{
|
||||
|
|
@ -16,7 +18,7 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
SECTION("Basic state")
|
||||
{
|
||||
region.registerCC(4, 0);
|
||||
region.registerCC(4, 0_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -24,19 +26,19 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "locc4", "56" });
|
||||
region.parseOpcode({ "hicc4", "59" });
|
||||
region.registerCC(4, 0);
|
||||
region.registerCC(4, 0_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(4, 57);
|
||||
region.registerCC(4, 57_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 56);
|
||||
region.registerCC(4, 56_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 59);
|
||||
region.registerCC(4, 59_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 43);
|
||||
region.registerCC(4, 43_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(4, 65);
|
||||
region.registerCC(4, 65_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(6, 57);
|
||||
region.registerCC(6, 57_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -46,26 +48,26 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "hicc4", "59" });
|
||||
region.parseOpcode({ "locc54", "18" });
|
||||
region.parseOpcode({ "hicc54", "27" });
|
||||
region.registerCC(4, 0);
|
||||
region.registerCC(54, 0);
|
||||
region.registerCC(4, 0_norm);
|
||||
region.registerCC(54, 0_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(4, 57);
|
||||
region.registerCC(4, 57_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(54, 19);
|
||||
region.registerCC(54, 19_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(54, 18);
|
||||
region.registerCC(54, 18_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(54, 27);
|
||||
region.registerCC(54, 27_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 56);
|
||||
region.registerCC(4, 56_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 59);
|
||||
region.registerCC(4, 59_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(54, 2);
|
||||
region.registerCC(54, 2_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(54, 26);
|
||||
region.registerCC(54, 26_norm);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(4, 65);
|
||||
region.registerCC(4, 65_norm);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -116,13 +118,13 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "sw_last", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 64, 0.5f);
|
||||
region.registerNoteOff(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_last with non-default keyswitch range")
|
||||
|
|
@ -131,20 +133,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_last", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_down with non-default keyswitch range")
|
||||
|
|
@ -153,20 +155,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_down", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOff(60, 0_norm, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_up with non-default keyswitch range")
|
||||
|
|
@ -175,16 +177,16 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_up", "40" });
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -192,20 +194,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "sw_previous", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -215,17 +217,17 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "1" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
SECTION("Sequences: length 2, position 2")
|
||||
|
|
@ -234,17 +236,17 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "2" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
SECTION("Sequences: length 3, position 2")
|
||||
|
|
@ -253,21 +255,21 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "2" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
#include "sfizz/MidiState.h"
|
||||
#include "sfizz/Region.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[Region] Parsing opcodes")
|
||||
{
|
||||
|
|
@ -204,19 +206,19 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("lovel, hivel")
|
||||
{
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "37" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(37, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(37_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "128" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "-3" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "hivel", "65" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 65));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 65_norm));
|
||||
region.parseOpcode({ "hivel", "-1" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "hivel", "128" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
}
|
||||
|
||||
SECTION("lobend, hibend")
|
||||
|
|
@ -238,16 +240,16 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("locc, hicc")
|
||||
{
|
||||
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range<float>(0_norm, 127_norm));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "locc6", "4" });
|
||||
REQUIRE(region.ccConditions[6] == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.ccConditions[6] == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "locc12", "-128" });
|
||||
REQUIRE(region.ccConditions[12] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[12] == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "hicc65", "39" });
|
||||
REQUIRE(region.ccConditions[65] == sfz::Range<uint8_t>(0, 39));
|
||||
REQUIRE(region.ccConditions[65] == sfz::Range<float>(0_norm, 39_norm));
|
||||
region.parseOpcode({ "hicc127", "135" });
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.ccConditions[127] == sfz::Range<float>(0_norm, 127_norm));
|
||||
}
|
||||
|
||||
SECTION("sw_lokey, sw_hikey")
|
||||
|
|
@ -425,10 +427,10 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
}
|
||||
region.parseOpcode({ "on_locc45", "15" });
|
||||
REQUIRE(region.ccTriggers.contains(45));
|
||||
REQUIRE(region.ccTriggers[45] == sfz::Range<uint8_t>(15, 127));
|
||||
REQUIRE(region.ccTriggers[45] == sfz::Range<float>(15_norm, 127_norm));
|
||||
region.parseOpcode({ "on_hicc4", "47" });
|
||||
REQUIRE(region.ccTriggers.contains(45));
|
||||
REQUIRE(region.ccTriggers[4] == sfz::Range<uint8_t>(0, 47));
|
||||
REQUIRE(region.ccTriggers[4] == sfz::Range<float>(0_norm, 47_norm));
|
||||
}
|
||||
|
||||
SECTION("volume")
|
||||
|
|
@ -563,9 +565,9 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
SECTION("amp_velcurve")
|
||||
{
|
||||
region.parseOpcode({ "amp_velcurve_6", "0.4" });
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(6, 0.4f));
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(6_norm, 0.4f));
|
||||
region.parseOpcode({ "amp_velcurve_127", "-1.0" });
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(127, 0.0f));
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(127_norm, 0.0f));
|
||||
}
|
||||
|
||||
SECTION("xfin_lokey, xfin_hikey")
|
||||
|
|
@ -589,21 +591,21 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("xfin_lovel, xfin_hivel")
|
||||
{
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "4" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(4, 4));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(4_norm, 4_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "128" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "59" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "59" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "128" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "0" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "-1" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfout_lokey, xfout_hikey")
|
||||
|
|
@ -627,59 +629,59 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("xfout_lovel, xfout_hivel")
|
||||
{
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "4" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "128" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "59" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "59" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "128" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "0" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "-1" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfin_locc, xfin_hicc")
|
||||
{
|
||||
REQUIRE(!region.crossfadeCCInRange.contains(4));
|
||||
region.parseOpcode({ "xfin_locc4", "4" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(4, 4));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(4_norm, 4_norm));
|
||||
region.parseOpcode({ "xfin_locc4", "128" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_locc4", "59" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "59" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "128" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "0" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_hicc4", "-1" });
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCInRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfout_locc, xfout_hicc")
|
||||
{
|
||||
REQUIRE(!region.crossfadeCCOutRange.contains(4));
|
||||
region.parseOpcode({ "xfout_locc4", "4" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_locc4", "128" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_locc4", "59" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "59" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "128" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "0" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfout_hicc4", "-1" });
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xf_keycurve")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "sfizz/Region.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("Basic triggers", "Region triggers")
|
||||
{
|
||||
|
|
@ -17,44 +19,44 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
SECTION("key")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64_norm));
|
||||
}
|
||||
SECTION("lokey and hikey")
|
||||
{
|
||||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "42" });
|
||||
REQUIRE(!region.registerNoteOn(39, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(43, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
REQUIRE(!region.registerNoteOn(39, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(43, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64_norm));
|
||||
}
|
||||
SECTION("key and release trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "trigger", "release" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64_norm));
|
||||
}
|
||||
SECTION("key and release_key trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "trigger", "release_key" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64_norm, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64_norm));
|
||||
}
|
||||
// TODO: first and legato triggers
|
||||
SECTION("lovel and hivel")
|
||||
|
|
@ -62,11 +64,11 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lovel", "60" });
|
||||
region.parseOpcode({ "hivel", "70" });
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 60, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 70, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 71, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 59, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 60_norm, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 70_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 71_norm, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 59_norm, 0.5f));
|
||||
}
|
||||
|
||||
SECTION("lorand and hirand")
|
||||
|
|
@ -74,54 +76,54 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lorand", "0.35" });
|
||||
region.parseOpcode({ "hirand", "0.40" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.36f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.37f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.38f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.39f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.40f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.41f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.36f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.37f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.38f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.39f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.40f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.41f));
|
||||
}
|
||||
|
||||
SECTION("lorand and hirand on 1.0f")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lorand", "0.35" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
}
|
||||
|
||||
SECTION("Disable key trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
region.parseOpcode({ "hikey", "40" });
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
region.parseOpcode({ "key", "-1" });
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 1.0f));
|
||||
}
|
||||
|
||||
SECTION("on_loccN, on_hiccN")
|
||||
{
|
||||
region.parseOpcode({ "on_locc47", "64" });
|
||||
region.parseOpcode({ "on_hicc47", "68" });
|
||||
REQUIRE(!region.registerCC(47, 63));
|
||||
REQUIRE(!region.registerCC(47, 64));
|
||||
REQUIRE(!region.registerCC(47, 65));
|
||||
REQUIRE(!region.registerCC(47, 63_norm));
|
||||
REQUIRE(!region.registerCC(47, 64_norm));
|
||||
REQUIRE(!region.registerCC(47, 65_norm));
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
REQUIRE(region.registerCC(47, 64));
|
||||
REQUIRE(region.registerCC(47, 65));
|
||||
REQUIRE(region.registerCC(47, 66));
|
||||
REQUIRE(region.registerCC(47, 67));
|
||||
REQUIRE(region.registerCC(47, 68));
|
||||
REQUIRE(!region.registerCC(47, 69));
|
||||
REQUIRE(!region.registerCC(40, 64));
|
||||
REQUIRE(region.registerCC(47, 64_norm));
|
||||
REQUIRE(region.registerCC(47, 65_norm));
|
||||
REQUIRE(region.registerCC(47, 66_norm));
|
||||
REQUIRE(region.registerCC(47, 67_norm));
|
||||
REQUIRE(region.registerCC(47, 68_norm));
|
||||
REQUIRE(!region.registerCC(47, 69_norm));
|
||||
REQUIRE(!region.registerCC(40, 64_norm));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,16 +137,16 @@ TEST_CASE("Legato triggers", "Region triggers")
|
|||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "50" });
|
||||
region.parseOpcode({ "trigger", "first" });
|
||||
midiState.noteOnEvent(0, 40, 64);
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
midiState.noteOnEvent(0, 41, 64);
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
midiState.noteOffEvent(0, 40, 0);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
midiState.noteOffEvent(0, 41, 0);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
midiState.noteOnEvent(0, 42, 64);
|
||||
REQUIRE(region.registerNoteOn(42, 64, 0.5f));
|
||||
midiState.noteOnEvent(0, 40, 64_norm);
|
||||
REQUIRE(region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
midiState.noteOnEvent(0, 41, 64_norm);
|
||||
REQUIRE(!region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
midiState.noteOffEvent(0, 40, 0_norm);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
midiState.noteOffEvent(0, 41, 0_norm);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
midiState.noteOnEvent(0, 42, 64_norm);
|
||||
REQUIRE(region.registerNoteOn(42, 64_norm, 0.5f));
|
||||
}
|
||||
|
||||
SECTION("Second note playing")
|
||||
|
|
@ -152,15 +154,15 @@ TEST_CASE("Legato triggers", "Region triggers")
|
|||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "50" });
|
||||
region.parseOpcode({ "trigger", "legato" });
|
||||
midiState.noteOnEvent(0, 40, 64);
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
midiState.noteOnEvent(0, 41, 64);
|
||||
REQUIRE(region.registerNoteOn(41, 64, 0.5f));
|
||||
midiState.noteOffEvent(0, 40, 64);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
midiState.noteOffEvent(0, 41, 64);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
midiState.noteOnEvent(0, 42, 64);
|
||||
REQUIRE(!region.registerNoteOn(42, 64, 0.5f));
|
||||
midiState.noteOnEvent(0, 40, 64_norm);
|
||||
REQUIRE(!region.registerNoteOn(40, 64_norm, 0.5f));
|
||||
midiState.noteOnEvent(0, 41, 64_norm);
|
||||
REQUIRE(region.registerNoteOn(41, 64_norm, 0.5f));
|
||||
midiState.noteOffEvent(0, 40, 64_norm);
|
||||
region.registerNoteOff(40, 0_norm, 0.5f);
|
||||
midiState.noteOffEvent(0, 41, 64_norm);
|
||||
region.registerNoteOff(41, 0_norm, 0.5f);
|
||||
midiState.noteOnEvent(0, 42, 64_norm);
|
||||
REQUIRE(!region.registerNoteOn(42, 64_norm, 0.5f));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <chrono>
|
||||
#include <thread>
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
constexpr int numRandomTests { 64 };
|
||||
TEST_CASE("[Region] Crossfade in on key")
|
||||
{
|
||||
|
|
@ -20,9 +21,9 @@ TEST_CASE("[Region] Crossfade in on key")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "xfin_lokey", "1" });
|
||||
region.parseOpcode({ "xfin_hikey", "3" });
|
||||
REQUIRE( region.getNoteGain(2, 127) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(1, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(3, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 127_norm) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(3, 127_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on key - 2")
|
||||
|
|
@ -32,12 +33,12 @@ TEST_CASE("[Region] Crossfade in on key - 2")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "xfin_lokey", "1" });
|
||||
region.parseOpcode({ "xfin_hikey", "5" });
|
||||
REQUIRE( region.getNoteGain(1, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 127) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(3, 127) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(4, 127) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(6, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 127_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(3, 127_norm) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(4, 127_norm) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(6, 127_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on key - gain")
|
||||
|
|
@ -48,11 +49,11 @@ TEST_CASE("[Region] Crossfade in on key - gain")
|
|||
region.parseOpcode({ "xfin_lokey", "1" });
|
||||
region.parseOpcode({ "xfin_hikey", "5" });
|
||||
region.parseOpcode({ "xf_keycurve", "gain" });
|
||||
REQUIRE( region.getNoteGain(1, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 127) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(3, 127) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(4, 127) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(5, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 127_norm) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(3, 127_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(4, 127_norm) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on key")
|
||||
|
|
@ -62,13 +63,13 @@ TEST_CASE("[Region] Crossfade out on key")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "xfout_lokey", "51" });
|
||||
region.parseOpcode({ "xfout_hikey", "55" });
|
||||
REQUIRE( region.getNoteGain(50, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(51, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(52, 127) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(53, 127) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(54, 127) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(55, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(52, 127_norm) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(53, 127_norm) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(54, 127_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on key - gain")
|
||||
|
|
@ -79,13 +80,13 @@ TEST_CASE("[Region] Crossfade out on key - gain")
|
|||
region.parseOpcode({ "xfout_lokey", "51" });
|
||||
region.parseOpcode({ "xfout_hikey", "55" });
|
||||
region.parseOpcode({ "xf_keycurve", "gain" });
|
||||
REQUIRE( region.getNoteGain(50, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(51, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(52, 127) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(53, 127) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(54, 127) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(55, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 127) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(51, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(52, 127_norm) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(53, 127_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(54, 127_norm) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on velocity")
|
||||
|
|
@ -96,13 +97,13 @@ TEST_CASE("[Region] Crossfade in on velocity")
|
|||
region.parseOpcode({ "xfin_lovel", "20" });
|
||||
region.parseOpcode({ "xfin_hivel", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
REQUIRE( region.getNoteGain(1, 19) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 20) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 21) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(3, 22) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(4, 23) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 24) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(6, 25) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 21_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(3, 22_norm) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(4, 23_norm) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(6, 25_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on vel - gain")
|
||||
|
|
@ -114,13 +115,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain")
|
|||
region.parseOpcode({ "xfin_hivel", "24" });
|
||||
region.parseOpcode({ "xf_velcurve", "gain" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
REQUIRE( region.getNoteGain(1, 19) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 20) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 21) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(3, 22) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(4, 23) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(5, 24) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 25) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(1, 20_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(2, 21_norm) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(3, 22_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(4, 23_norm) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 25_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on vel")
|
||||
|
|
@ -131,13 +132,13 @@ TEST_CASE("[Region] Crossfade out on vel")
|
|||
region.parseOpcode({ "xfout_lovel", "51" });
|
||||
region.parseOpcode({ "xfout_hivel", "55" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
REQUIRE( region.getNoteGain(5, 50) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 51) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 52) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 53) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(5, 54) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(5, 55) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 56) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 50_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 51_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 52_norm) == 0.86603_a );
|
||||
REQUIRE( region.getNoteGain(5, 53_norm) == 0.70711_a );
|
||||
REQUIRE( region.getNoteGain(5, 54_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(5, 55_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(5, 56_norm) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on vel - gain")
|
||||
|
|
@ -149,13 +150,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain")
|
|||
region.parseOpcode({ "xfout_hivel", "55" });
|
||||
region.parseOpcode({ "xf_velcurve", "gain" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
REQUIRE( region.getNoteGain(56, 50) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 51) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 52) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(56, 53) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(56, 54) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(56, 55) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 56) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 50_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 51_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 52_norm) == 0.75_a );
|
||||
REQUIRE( region.getNoteGain(56, 53_norm) == 0.5_a );
|
||||
REQUIRE( region.getNoteGain(56, 54_norm) == 0.25_a );
|
||||
REQUIRE( region.getNoteGain(56, 55_norm) == 0.0_a );
|
||||
REQUIRE( region.getNoteGain(56, 56_norm) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on CC")
|
||||
|
|
@ -166,13 +167,13 @@ TEST_CASE("[Region] Crossfade in on CC")
|
|||
region.parseOpcode({ "xfin_locc24", "20" });
|
||||
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a );
|
||||
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a );
|
||||
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a );
|
||||
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a );
|
||||
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on CC - gain")
|
||||
|
|
@ -184,13 +185,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain")
|
|||
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||
midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a );
|
||||
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a );
|
||||
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a );
|
||||
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a );
|
||||
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
}
|
||||
TEST_CASE("[Region] Crossfade out on CC")
|
||||
{
|
||||
|
|
@ -200,13 +201,13 @@ TEST_CASE("[Region] Crossfade out on CC")
|
|||
region.parseOpcode({ "xfout_locc24", "20" });
|
||||
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a );
|
||||
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a );
|
||||
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a );
|
||||
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a );
|
||||
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on CC - gain")
|
||||
|
|
@ -218,13 +219,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain")
|
|||
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||
midiState.ccEvent(0, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a );
|
||||
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a );
|
||||
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a );
|
||||
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a );
|
||||
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a );
|
||||
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a );
|
||||
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
||||
|
|
@ -233,8 +234,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
|||
sfz::Region region { midiState };
|
||||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 0) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -244,8 +245,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
|
|||
sfz::Region region { midiState };
|
||||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "amp_veltrack", "100" });
|
||||
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 0) == Approx(0.0).margin(0.0001) );
|
||||
REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001) );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
||||
|
|
@ -254,8 +255,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
|||
sfz::Region region { midiState };
|
||||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "amp_veltrack", "-100" });
|
||||
REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) );
|
||||
REQUIRE( region.getNoteGain(64, 0) == 1.0_a );
|
||||
REQUIRE( region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001) );
|
||||
REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] rt_decay")
|
||||
|
|
@ -265,15 +266,15 @@ TEST_CASE("[Region] rt_decay")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "trigger", "release" });
|
||||
region.parseOpcode({ "rt_decay", "10" });
|
||||
midiState.noteOnEvent(0, 64, 64);
|
||||
midiState.noteOnEvent(0, 64, 64_norm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||
region.parseOpcode({ "rt_decay", "20" });
|
||||
midiState.noteOnEvent(0, 64, 64);
|
||||
midiState.noteOnEvent(0, 64, 64_norm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||
region.parseOpcode({ "trigger", "attack" });
|
||||
midiState.noteOnEvent(0, 64, 64);
|
||||
midiState.noteOnEvent(0, 64, 64_norm);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "sfizz/Synth.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
constexpr int blockSize { 256 };
|
||||
|
||||
|
|
@ -139,9 +141,9 @@ TEST_CASE("[Synth] Reset all controllers")
|
|||
{
|
||||
sfz::Synth synth;
|
||||
synth.cc(0, 12, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 64 );
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 64_norm );
|
||||
synth.cc(0, 121, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 0 );
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 0_norm );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue