Formatting step

This commit is contained in:
Paul Fd 2020-03-31 10:46:12 +02:00
parent 7e8cdff407
commit 5088bed9b0
16 changed files with 323 additions and 294 deletions

View file

@ -39,8 +39,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Linear)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{static_cast<int>(state.range(0) - 1), dist(gen)} { static_cast<int>(state.range(0) - 1), dist(gen) }
}; };
linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; }); linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
} }
@ -50,10 +50,11 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, LinearQuantized)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{static_cast<int>(state.range(0) - 1), dist(gen)} { static_cast<int>(state.range(0) - 1), dist(gen) }
}; };
linearEnvelope(events, absl::MakeSpan(output), [](float x) { return x; }, 0.5); linearEnvelope(
events, absl::MakeSpan(output), [](float x) { return x; }, 0.5);
} }
} }
@ -61,8 +62,8 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, Multiplicative)(benchmark::State& state) {
for (auto _ : state) for (auto _ : state)
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{static_cast<int>(state.range(0) - 1), dist(gen)} { static_cast<int>(state.range(0) - 1), dist(gen) }
}; };
multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; }); multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; });
} }
@ -72,10 +73,11 @@ BENCHMARK_DEFINE_F(EnvelopeFixture, MultiplicativeQuantized)(benchmark::State& s
for (auto _ : state) for (auto _ : state)
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{static_cast<int>(state.range(0) - 1), dist(gen)} { static_cast<int>(state.range(0) - 1), dist(gen) }
}; };
multiplicativeEnvelope(events, absl::MakeSpan(output), [](float x) { return x; }, 2.0f); multiplicativeEnvelope(
events, absl::MakeSpan(output), [](float x) { return x; }, 2.0f);
} }
} }

View file

@ -24,7 +24,7 @@ void ADSREnvelope<Type>::reset(const Region& region, const MidiState& state, int
this->release = secondsToSamples(region.amplitudeEG.getRelease(state, velocity)); this->release = secondsToSamples(region.amplitudeEG.getRelease(state, velocity));
this->hold = secondsToSamples(region.amplitudeEG.getHold(state, velocity)); this->hold = secondsToSamples(region.amplitudeEG.getHold(state, velocity));
this->peak = 1.0; this->peak = 1.0;
this->sustain = normalizePercents(region.amplitudeEG.getSustain(state, velocity)); this->sustain = normalizePercents(region.amplitudeEG.getSustain(state, velocity));
this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(state, velocity)); this->start = this->peak * normalizePercents(region.amplitudeEG.getStart(state, velocity));
releaseDelay = 0; releaseDelay = 0;

View file

@ -14,15 +14,13 @@
#include <functional> #include <functional>
#include "absl/algorithm/container.h" #include "absl/algorithm/container.h"
#ifndef NDEBUG #ifndef NDEBUG
#include "MathHelpers.h" #include "MathHelpers.h"
#endif #endif
namespace sfz namespace sfz {
{
template<class T> template <class T>
class SpanHolder class SpanHolder {
{
public: public:
SpanHolder() {} SpanHolder() {}
SpanHolder(const SpanHolder<T>&) = delete; SpanHolder(const SpanHolder<T>&) = delete;
@ -40,7 +38,10 @@ public:
other.available = nullptr; other.available = nullptr;
} }
SpanHolder(T&& value, int* available) SpanHolder(T&& value, int* available)
: value(std::forward<T>(value)), available(available) {} : value(std::forward<T>(value))
, available(available)
{
}
T& operator*() { return value; } T& operator*() { return value; }
T* operator->() { return &value; } T* operator->() { return &value; }
explicit operator bool() const { return available != nullptr; } explicit operator bool() const { return available != nullptr; }
@ -49,13 +50,13 @@ public:
if (available) if (available)
*available += 1; *available += 1;
} }
private: private:
T value {}; T value {};
int* available { nullptr }; int* available { nullptr };
}; };
class BufferPool class BufferPool {
{
public: public:
BufferPool() BufferPool()
{ {
@ -148,7 +149,6 @@ public:
} }
#endif #endif
private: private:
void _setBufferSize(unsigned bufferSize) void _setBufferSize(unsigned bufferSize)
{ {

View file

@ -57,7 +57,7 @@ namespace Default
// common defaults // common defaults
constexpr Range<uint8_t> midi7Range { 0, 127 }; constexpr Range<uint8_t> midi7Range { 0, 127 };
constexpr Range<float> normalizedRange { 0.0f, 1.0f }; constexpr Range<float> normalizedRange { 0.0f, 1.0f };
constexpr Range<float> symmetricNormalizedRange { -1.0, 1.0 }; constexpr Range<float> symmetricNormalizedRange { -1.0, 1.0 };
constexpr float zeroModifier { 0.0f }; constexpr float zeroModifier { 0.0f };
// Wavetable oscillator // Wavetable oscillator
@ -68,21 +68,21 @@ namespace Default
constexpr uint32_t group { 0 }; constexpr uint32_t group { 0 };
constexpr Range<uint32_t> groupRange { 0, std::numeric_limits<uint32_t>::max() }; constexpr Range<uint32_t> groupRange { 0, std::numeric_limits<uint32_t>::max() };
constexpr SfzOffMode offMode { SfzOffMode::fast }; constexpr SfzOffMode offMode { SfzOffMode::fast };
constexpr Range<uint32_t> polyphonyRange { 0, config::maxVoices }; constexpr Range<uint32_t> polyphonyRange { 0, config::maxVoices };
constexpr SfzSelfMask selfMask { SfzSelfMask::mask }; constexpr SfzSelfMask selfMask { SfzSelfMask::mask };
// Region logic: key mapping // Region logic: key mapping
constexpr Range<uint8_t> keyRange { 0, 127 }; constexpr Range<uint8_t> keyRange { 0, 127 };
constexpr auto velocityRange = normalizedRange; constexpr auto velocityRange = normalizedRange;
// Region logic: MIDI conditions // Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 }; constexpr Range<uint8_t> channelRange { 1, 16 };
constexpr Range<uint8_t> midiChannelRange { 0, 15 }; constexpr Range<uint8_t> midiChannelRange { 0, 15 };
constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs }; constexpr Range<uint16_t> ccNumberRange { 0, config::numCCs };
constexpr auto ccValueRange = normalizedRange; constexpr auto ccValueRange = normalizedRange;
constexpr Range<int> bendRange = { -8192, 8192 }; constexpr Range<int> bendRange = { -8192, 8192 };
constexpr Range<float> bendValueRange = symmetricNormalizedRange; constexpr Range<float> bendValueRange = symmetricNormalizedRange;
constexpr int bend { 0 }; constexpr int bend { 0 };
constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current }; constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current };
// Region logic: internal conditions // Region logic: internal conditions
@ -97,9 +97,9 @@ namespace Default
// Region logic: Triggers // Region logic: Triggers
constexpr SfzTrigger trigger { SfzTrigger::attack }; constexpr SfzTrigger trigger { SfzTrigger::attack };
constexpr Range<float> ccTriggerValueRange = normalizedRange; constexpr Range<float> ccTriggerValueRange = normalizedRange;
// Performance parameters: amplifier // Performance parameters: amplifier
constexpr float globalVolume { -7.35f }; constexpr float globalVolume { -7.35f };
constexpr float volume { 0.0f }; constexpr float volume { 0.0f };
constexpr Range<float> volumeRange { -144.0, 6.0 }; constexpr Range<float> volumeRange { -144.0, 6.0 };
@ -125,11 +125,11 @@ namespace Default
constexpr Range<float> ampRandomRange { 0.0, 24.0 }; constexpr Range<float> ampRandomRange { 0.0, 24.0 };
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 }; constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 }; constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f }; constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f }; constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
constexpr Range<float> crossfadeCCInRange { 0.0f, 0.0f }; constexpr Range<float> crossfadeCCInRange { 0.0f, 0.0f };
constexpr Range<float> crossfadeCCOutRange { 1.0f, 1.0f }; constexpr Range<float> crossfadeCCOutRange { 1.0f, 1.0f };
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
constexpr float rtDecay { 0.0f }; constexpr float rtDecay { 0.0f };

View file

@ -51,7 +51,7 @@ void sfz::MidiState::setSampleRate(float sampleRate) noexcept
void sfz::MidiState::advanceTime(int numSamples) noexcept void sfz::MidiState::advanceTime(int numSamples) noexcept
{ {
internalClock += numSamples; internalClock += numSamples;
for (auto& ccEvents: cc) { for (auto& ccEvents : cc) {
ASSERT(!ccEvents.empty()); // CC event vectors should never be empty ASSERT(!ccEvents.empty()); // CC event vectors should never be empty
ccEvents.front().value = ccEvents.back().value; ccEvents.front().value = ccEvents.back().value;
ccEvents.front().delay = 0; ccEvents.front().delay = 0;
@ -66,7 +66,7 @@ void sfz::MidiState::advanceTime(int numSamples) noexcept
void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept
{ {
this->samplesPerBlock = samplesPerBlock; this->samplesPerBlock = samplesPerBlock;
for (auto& ccEvents: cc) { for (auto& ccEvents : cc) {
ccEvents.shrink_to_fit(); ccEvents.shrink_to_fit();
ccEvents.reserve(samplesPerBlock); ccEvents.reserve(samplesPerBlock);
} }
@ -92,12 +92,11 @@ float sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
return lastNoteVelocities[noteNumber]; return lastNoteVelocities[noteNumber];
} }
void sfz::MidiState::pitchBendEvent(int delay, float pitchBendValue) noexcept void sfz::MidiState::pitchBendEvent(int delay, float pitchBendValue) noexcept
{ {
ASSERT(pitchBendValue >= -1.0f && pitchBendValue <= 1.0f); ASSERT(pitchBendValue >= -1.0f && pitchBendValue <= 1.0f);
const auto insertionPoint = absl::c_upper_bound(pitchEvents, delay, MidiEventDelayComparator{}); const auto insertionPoint = absl::c_upper_bound(pitchEvents, delay, MidiEventDelayComparator {});
if (insertionPoint == pitchEvents.end() || insertionPoint->delay != delay) if (insertionPoint == pitchEvents.end() || insertionPoint->delay != delay)
pitchEvents.insert(insertionPoint, { delay, pitchBendValue }); pitchEvents.insert(insertionPoint, { delay, pitchBendValue });
else else
@ -113,7 +112,7 @@ float sfz::MidiState::getPitchBend() const noexcept
void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept
{ {
ASSERT(ccValue >= 0.0 && ccValue <= 1.0); ASSERT(ccValue >= 0.0 && ccValue <= 1.0);
const auto insertionPoint = absl::c_upper_bound(cc[ccNumber], delay, MidiEventDelayComparator{}); const auto insertionPoint = absl::c_upper_bound(cc[ccNumber], delay, MidiEventDelayComparator {});
if (insertionPoint == cc[ccNumber].end() || insertionPoint->delay != delay) if (insertionPoint == cc[ccNumber].end() || insertionPoint->delay != delay)
cc[ccNumber].insert(insertionPoint, { delay, ccValue }); cc[ccNumber].insert(insertionPoint, { delay, ccValue });
else else
@ -132,7 +131,7 @@ void sfz::MidiState::reset() noexcept
for (auto& velocity: lastNoteVelocities) for (auto& velocity: lastNoteVelocities)
velocity = 0; velocity = 0;
for (auto& ccEvents: cc) { for (auto& ccEvents : cc) {
ccEvents.clear(); ccEvents.clear();
ccEvents.push_back({ 0, 0.0f }); ccEvents.push_back({ 0, 0.0f });
} }

View file

@ -28,7 +28,7 @@ public:
* @param noteNumber * @param noteNumber
* @param velocity * @param velocity
*/ */
void noteOnEvent(int delay, int noteNumber, float velocity) noexcept; void noteOnEvent(int delay, int noteNumber, float velocity) noexcept;
/** /**
* @brief Update the state after a note off event * @brief Update the state after a note off event
@ -36,7 +36,7 @@ public:
* @param noteNumber * @param noteNumber
* @param velocity * @param velocity
*/ */
void noteOffEvent(int delay, int noteNumber, float velocity) noexcept; void noteOffEvent(int delay, int noteNumber, float velocity) noexcept;
int getActiveNotes() const noexcept { return activeNotes; } int getActiveNotes() const noexcept { return activeNotes; }
@ -47,7 +47,7 @@ public:
* @param delay * @param delay
* @return float * @return float
*/ */
float getNoteDuration(int noteNumber, int delay = 0) const; float getNoteDuration(int noteNumber, int delay = 0) const;
/** /**
* @brief Set the maximum size of the blocks for the callback. The actual * @brief Set the maximum size of the blocks for the callback. The actual
@ -70,7 +70,7 @@ public:
* @param noteNumber * @param noteNumber
* @return float * @return float
*/ */
float getNoteVelocity(int noteNumber) const noexcept; float getNoteVelocity(int noteNumber) const noexcept;
/** /**
* @brief Register a pitch bend event * @brief Register a pitch bend event
@ -125,35 +125,42 @@ public:
const EventVector& getPitchEvents() const noexcept; const EventVector& getPitchEvents() const noexcept;
private: private:
int activeNotes { 0 };
int activeNotes { 0 };
/** /**
* @brief Stores the note on times. * @brief Stores the note on times.
* *
*/ */
MidiNoteArray<unsigned> noteOnTimes {{}}; MidiNoteArray<unsigned> noteOnTimes { {} };
/** /**
* @brief Stores the note off times. * @brief Stores the note off times.
* *
*/ */
MidiNoteArray<unsigned> noteOffTimes {{}};
MidiNoteArray<unsigned> noteOffTimes { {} };
/** /**
* @brief Stores the velocity of the note ons for currently * @brief Stores the velocity of the note ons for currently
* depressed notes. * depressed notes.
* *
*/ */
MidiNoteArray<float> lastNoteVelocities; MidiNoteArray<float> lastNoteVelocities;
/** /**
* @brief Current known values for the CCs. * @brief Current known values for the CCs.
* *
*/ */
std::array<EventVector, config::numCCs> cc; std::array<EventVector, config::numCCs> cc;
const EventVector nullEvent {{0, 0.0f}};
/** /**
* Pitch bend status * @brief Null event
*
*/
const EventVector nullEvent { { 0, 0.0f } };
/**
* @brief Pitch bend status
*/ */
EventVector pitchEvents; EventVector pitchEvents;
float sampleRate { config::defaultSampleRate }; float sampleRate { config::defaultSampleRate };

View file

@ -21,7 +21,7 @@ namespace sfz
{ {
using CCNamePair = std::pair<uint16_t, std::string>; using CCNamePair = std::pair<uint16_t, std::string>;
template<class T> template <class T>
using MidiNoteArray = std::array<T, 128>; using MidiNoteArray = std::array<T, 128>;
template<class ValueType> template<class ValueType>
struct CCValuePair { struct CCValuePair {
@ -193,17 +193,15 @@ constexpr float normalizeBend(float bendValue)
return clamp(bendValue, -8191.0f, 8191.0f) / 8191.0f; return clamp(bendValue, -8191.0f, 8191.0f) / 8191.0f;
} }
namespace literals namespace literals {
{ inline float operator""_norm(unsigned long long int value)
inline float operator ""_norm(unsigned long long int value) {
{ if (value > 127)
if (value > 127) value = 127;
value = 127;
return normalize7Bits(value); return normalize7Bits(value);
}
} }
}
/** /**
* @brief Convert a note in string to its equivalent midi note number * @brief Convert a note in string to its equivalent midi note number
@ -275,7 +273,6 @@ bool findDefine(absl::string_view line, absl::string_view& variable, absl::strin
*/ */
bool findInclude(absl::string_view line, std::string& path); bool findInclude(absl::string_view line, std::string& path);
/** /**
* @brief multiply a value by a factor, in cents. To be used for pitch variations. * @brief multiply a value by a factor, in cents. To be used for pitch variations.
* *
@ -284,20 +281,19 @@ bool findInclude(absl::string_view line, std::string& path);
*/ */
inline CXX14_CONSTEXPR float multiplyByCentsModifier(int modifier, float base) inline CXX14_CONSTEXPR float multiplyByCentsModifier(int modifier, float base)
{ {
return base * centsFactor(modifier); return base * centsFactor(modifier);
} }
template<class T> template <class T>
inline CXX14_CONSTEXPR float gainModifier(T modifier, float value) inline CXX14_CONSTEXPR float gainModifier(T modifier, float value)
{ {
return value * modifier; return value * modifier;
} }
/** /**
* @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...) * @brief Compute a crossfade in value with respect to a crossfade range (note, velocity, cc, ...)
*/ */
template<class T, class U> template <class T, class U>
float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve) float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{ {
if (value < crossfadeRange.getStart()) if (value < crossfadeRange.getStart())
@ -318,11 +314,10 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
return 1.0f; return 1.0f;
} }
/** /**
* @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...) * @brief Compute a crossfade out value with respect to a crossfade range (note, velocity, cc, ...)
*/ */
template<class T, class U> template <class T, class U>
float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve) float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurve curve)
{ {
if (value > crossfadeRange.getEnd()) if (value > crossfadeRange.getEnd())
@ -343,7 +338,7 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
return 1.0f; return 1.0f;
} }
template<class F> template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda) void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{ {
ASSERT(events.size() > 0); ASSERT(events.size() > 0);
@ -355,16 +350,16 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
auto lastValue = lambda(events[0].value); auto lastValue = lambda(events[0].value);
auto lastDelay = events[0].delay; auto lastDelay = events[0].delay;
for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++ i) { for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) {
const auto length = min(events[i].delay, maxDelay) - lastDelay; const auto length = min(events[i].delay, maxDelay) - lastDelay;
const auto step = (lambda(events[i].value) - lastValue)/ length; const auto step = (lambda(events[i].value) - lastValue) / length;
lastValue = linearRamp<float>(envelope.subspan(lastDelay, length), lastValue, step); lastValue = linearRamp<float>(envelope.subspan(lastDelay, length), lastValue, step);
lastDelay += length; lastDelay += length;
} }
fill<float>(envelope.subspan(lastDelay), lastValue); fill<float>(envelope.subspan(lastDelay), lastValue);
} }
template<class F> template <class F>
void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda, float step) void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda, float step)
{ {
ASSERT(events.size() > 0); ASSERT(events.size() > 0);
@ -381,7 +376,7 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
auto lastValue = quantize(lambda(events[0].value)); auto lastValue = quantize(lambda(events[0].value));
auto lastDelay = events[0].delay; auto lastDelay = events[0].delay;
for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++ i) { for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) {
const auto nextValue = quantize(lambda(events[i].value)); const auto nextValue = quantize(lambda(events[i].value));
const auto difference = std::abs(nextValue - lastValue); const auto difference = std::abs(nextValue - lastValue);
const auto length = min(events[i].delay, maxDelay) - lastDelay; const auto length = min(events[i].delay, maxDelay) - lastDelay;
@ -404,7 +399,7 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
fill<float>(envelope.subspan(lastDelay), lastValue); fill<float>(envelope.subspan(lastDelay), lastValue);
} }
template<class F> template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda) void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda)
{ {
ASSERT(events.size() > 0); ASSERT(events.size() > 0);
@ -416,7 +411,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
auto lastValue = lambda(events[0].value); auto lastValue = lambda(events[0].value);
auto lastDelay = events[0].delay; auto lastDelay = events[0].delay;
for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++ i) { for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) {
const auto length = min(events[i].delay, maxDelay) - lastDelay; const auto length = min(events[i].delay, maxDelay) - lastDelay;
const auto nextValue = lambda(events[i].value); const auto nextValue = lambda(events[i].value);
const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length); const auto step = std::exp((std::log(nextValue) - std::log(lastValue)) / length);
@ -427,7 +422,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
fill<float>(envelope.subspan(lastDelay), lastValue); fill<float>(envelope.subspan(lastDelay), lastValue);
} }
template<class F> template <class F>
void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda, float step) void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelope, F&& lambda, float step)
{ {
ASSERT(events.size() > 0); ASSERT(events.size() > 0);
@ -445,12 +440,12 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop
// log q log q // log q log q
// and log(b)\log(q) is between 0 and 1. // and log(b)\log(q) is between 0 and 1.
auto quantize = [logStep](float value) -> float { auto quantize = [logStep](float value) -> float {
return std::exp(logStep * std::round(std::log(value)/logStep)); return std::exp(logStep * std::round(std::log(value) / logStep));
}; };
auto lastValue = quantize(lambda(events[0].value)); auto lastValue = quantize(lambda(events[0].value));
auto lastDelay = events[0].delay; auto lastDelay = events[0].delay;
for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++ i) { for (unsigned i = 1; i < events.size() && lastDelay < maxDelay; ++i) {
const auto length = min(events[i].delay, maxDelay) - lastDelay; const auto length = min(events[i].delay, maxDelay) - lastDelay;
const auto nextValue = quantize(lambda(events[i].value)); const auto nextValue = quantize(lambda(events[i].value));
const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue; const auto difference = nextValue > lastValue ? nextValue / lastValue : lastValue / nextValue;

View file

@ -705,16 +705,16 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc
if (voice->getTriggerNumber() == noteNumber && voice->getTriggerType() == Voice::TriggerType::NoteOn) { if (voice->getTriggerNumber() == noteNumber && voice->getTriggerType() == Voice::TriggerType::NoteOn) {
activeNotes += 1; activeNotes += 1;
switch (region->selfMask) { switch (region->selfMask) {
case SfzSelfMask::mask: case SfzSelfMask::mask:
if (voice->getTriggerValue() < velocity) { if (voice->getTriggerValue() < velocity) {
if (!selfMaskCandidate || selfMaskCandidate->getTriggerValue() > voice->getTriggerValue()) if (!selfMaskCandidate || selfMaskCandidate->getTriggerValue() > voice->getTriggerValue())
selfMaskCandidate = voice.get();
}
break;
case SfzSelfMask::dontMask:
if (!selfMaskCandidate || selfMaskCandidate->getSourcePosition() < voice->getSourcePosition())
selfMaskCandidate = voice.get(); selfMaskCandidate = voice.get();
break; }
break;
case SfzSelfMask::dontMask:
if (!selfMaskCandidate || selfMaskCandidate->getSourcePosition() < voice->getSourcePosition())
selfMaskCandidate = voice.get();
break;
} }
} }
} }

View file

@ -410,11 +410,11 @@ void sfz::Voice::filterStageMono(AudioSpan<float> buffer) noexcept
const auto leftBuffer = buffer.getSpan(0); const auto leftBuffer = buffer.getSpan(0);
const float* inputChannel[1] { leftBuffer.data() }; const float* inputChannel[1] { leftBuffer.data() };
float* outputChannel[1] { leftBuffer.data() }; float* outputChannel[1] { leftBuffer.data() };
for (auto& filter: filters) { for (auto& filter : filters) {
filter->process(inputChannel, outputChannel, numSamples); filter->process(inputChannel, outputChannel, numSamples);
} }
for (auto& eq: equalizers) { for (auto& eq : equalizers) {
eq->process(inputChannel, outputChannel, numSamples); eq->process(inputChannel, outputChannel, numSamples);
} }
} }
@ -429,11 +429,11 @@ void sfz::Voice::filterStageStereo(AudioSpan<float> buffer) noexcept
const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() }; const float* inputChannels[2] { leftBuffer.data(), rightBuffer.data() };
float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() }; float* outputChannels[2] { leftBuffer.data(), rightBuffer.data() };
for (auto& filter: filters) { for (auto& filter : filters) {
filter->process(inputChannels, outputChannels, numSamples); filter->process(inputChannels, outputChannels, numSamples);
} }
for (auto& eq: equalizers) { for (auto& eq : equalizers) {
eq->process(inputChannels, outputChannels, numSamples); eq->process(inputChannels, outputChannels, numSamples);
} }
} }
@ -462,7 +462,7 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
fill<float>(*jumps, pitchRatio * speedRatio); fill<float>(*jumps, pitchRatio * speedRatio);
const auto events = resources.midiState.getPitchEvents(); const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend){ const auto bendLambda = [this](float bend) {
const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown); const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown);
return centsFactor(bendInCents); return centsFactor(bendInCents);
}; };
@ -545,7 +545,6 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
const auto leftSpan = buffer.getSpan(0); const auto leftSpan = buffer.getSpan(0);
const auto rightSpan = buffer.getSpan(1); const auto rightSpan = buffer.getSpan(1);
if (region->sample == "*noise") { if (region->sample == "*noise") {
absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(leftSpan, [&](){ return noiseDist(Random::randomGenerator); });
absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); });
@ -560,7 +559,7 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
fill<float>(*frequencies, pitchRatio * keycenterFrequency); fill<float>(*frequencies, pitchRatio * keycenterFrequency);
const auto events = resources.midiState.getPitchEvents(); const auto events = resources.midiState.getPitchEvents();
const auto bendLambda = [this](float bend){ const auto bendLambda = [this](float bend) {
const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown); const auto bendInCents = bend > 0.0f ? bend * static_cast<float>(region->bendUp) : -bend * static_cast<float>(region->bendDown);
return centsFactor(bendInCents); return centsFactor(bendInCents);
}; };

View file

@ -18,13 +18,13 @@ TEST_CASE("[EGDescription] Attack range")
eg.attack = 1; eg.attack = 1;
eg.vel2attack = -1.27f; eg.vel2attack = -1.27f;
eg.ccAttack = { 63, 1.27f }; eg.ccAttack = { 63, 1.27f };
REQUIRE( eg.getAttack(state, 0_norm) == 1.0f ); REQUIRE(eg.getAttack(state, 0_norm) == 1.0f);
REQUIRE( eg.getAttack(state, 127_norm) == 0.0f ); REQUIRE(eg.getAttack(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getAttack(state, 127_norm) == 1.0f ); REQUIRE(eg.getAttack(state, 127_norm) == 1.0f);
REQUIRE( eg.getAttack(state, 0_norm) == 2.27f ); REQUIRE(eg.getAttack(state, 0_norm) == 2.27f);
eg.ccAttack = { 63, 127.0f }; eg.ccAttack = { 63, 127.0f };
REQUIRE( eg.getAttack(state, 0_norm) == 100.0f ); REQUIRE(eg.getAttack(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Delay range") TEST_CASE("[EGDescription] Delay range")
@ -34,13 +34,13 @@ TEST_CASE("[EGDescription] Delay range")
eg.delay = 1; eg.delay = 1;
eg.vel2delay = -1.27f; eg.vel2delay = -1.27f;
eg.ccDelay = { 63, 1.27f }; eg.ccDelay = { 63, 1.27f };
REQUIRE( eg.getDelay(state, 0_norm) == 1.0f ); REQUIRE(eg.getDelay(state, 0_norm) == 1.0f);
REQUIRE( eg.getDelay(state, 127_norm) == 0.0f ); REQUIRE(eg.getDelay(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getDelay(state, 127_norm) == 1.0f ); REQUIRE(eg.getDelay(state, 127_norm) == 1.0f);
REQUIRE( eg.getDelay(state, 0_norm) == 2.27f ); REQUIRE(eg.getDelay(state, 0_norm) == 2.27f);
eg.ccDelay = { 63, 127.0f }; eg.ccDelay = { 63, 127.0f };
REQUIRE( eg.getDelay(state, 0_norm) == 100.0f ); REQUIRE(eg.getDelay(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Decay range") TEST_CASE("[EGDescription] Decay range")
@ -50,13 +50,13 @@ TEST_CASE("[EGDescription] Decay range")
eg.decay = 1.0f; eg.decay = 1.0f;
eg.vel2decay = -1.27f; eg.vel2decay = -1.27f;
eg.ccDecay = { 63, 1.27f }; eg.ccDecay = { 63, 1.27f };
REQUIRE( eg.getDecay(state, 0_norm) == 1.0f ); REQUIRE(eg.getDecay(state, 0_norm) == 1.0f);
REQUIRE( eg.getDecay(state, 127_norm) == 0.0f ); REQUIRE(eg.getDecay(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getDecay(state, 127_norm) == 1.0f ); REQUIRE(eg.getDecay(state, 127_norm) == 1.0f);
REQUIRE( eg.getDecay(state, 0_norm) == 2.27f ); REQUIRE(eg.getDecay(state, 0_norm) == 2.27f);
eg.ccDecay = { 63, 127.0f }; eg.ccDecay = { 63, 127.0f };
REQUIRE( eg.getDecay(state, 0_norm) == 100.0f ); REQUIRE(eg.getDecay(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Release range") TEST_CASE("[EGDescription] Release range")
@ -66,13 +66,13 @@ TEST_CASE("[EGDescription] Release range")
eg.release = 1; eg.release = 1;
eg.vel2release = -1.27f; eg.vel2release = -1.27f;
eg.ccRelease = { 63, 1.27f }; eg.ccRelease = { 63, 1.27f };
REQUIRE( eg.getRelease(state, 0_norm) == 1.0f ); REQUIRE(eg.getRelease(state, 0_norm) == 1.0f);
REQUIRE( eg.getRelease(state, 127_norm) == 0.0f ); REQUIRE(eg.getRelease(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getRelease(state, 127_norm) == 1.0f ); REQUIRE(eg.getRelease(state, 127_norm) == 1.0f);
REQUIRE( eg.getRelease(state, 0_norm) == 2.27f ); REQUIRE(eg.getRelease(state, 0_norm) == 2.27f);
eg.ccRelease = { 63, 127.0f }; eg.ccRelease = { 63, 127.0f };
REQUIRE( eg.getRelease(state, 0_norm) == 100.0f ); REQUIRE(eg.getRelease(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Hold range") TEST_CASE("[EGDescription] Hold range")
@ -82,13 +82,13 @@ TEST_CASE("[EGDescription] Hold range")
eg.hold = 1; eg.hold = 1;
eg.vel2hold = -1.27f; eg.vel2hold = -1.27f;
eg.ccHold = { 63, 1.27f }; eg.ccHold = { 63, 1.27f };
REQUIRE( eg.getHold(state, 0_norm) == 1.0f ); REQUIRE(eg.getHold(state, 0_norm) == 1.0f);
REQUIRE( eg.getHold(state, 127_norm) == 0.0f ); REQUIRE(eg.getHold(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getHold(state, 127_norm) == 1.0f ); REQUIRE(eg.getHold(state, 127_norm) == 1.0f);
REQUIRE( eg.getHold(state, 0_norm) == 2.27f ); REQUIRE(eg.getHold(state, 0_norm) == 2.27f);
eg.ccHold = { 63, 127.0f }; eg.ccHold = { 63, 127.0f };
REQUIRE( eg.getHold(state, 0_norm) == 100.0f ); REQUIRE(eg.getHold(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Sustain level") TEST_CASE("[EGDescription] Sustain level")
@ -98,12 +98,12 @@ TEST_CASE("[EGDescription] Sustain level")
eg.sustain = 50; eg.sustain = 50;
eg.vel2sustain = -100; eg.vel2sustain = -100;
eg.ccSustain = { 63, 100.0f }; eg.ccSustain = { 63, 100.0f };
REQUIRE( eg.getSustain(state, 0_norm) == 50.0f ); REQUIRE(eg.getSustain(state, 0_norm) == 50.0f);
REQUIRE( eg.getSustain(state, 127_norm) == 0.0f ); REQUIRE(eg.getSustain(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getSustain(state, 127_norm) == 50.0f ); REQUIRE(eg.getSustain(state, 127_norm) == 50.0f);
eg.ccSustain = { 63, 200.0f }; eg.ccSustain = { 63, 200.0f };
REQUIRE( eg.getSustain(state, 0_norm) == 100.0f ); REQUIRE(eg.getSustain(state, 0_norm) == 100.0f);
} }
TEST_CASE("[EGDescription] Start level") TEST_CASE("[EGDescription] Start level")
@ -112,10 +112,10 @@ TEST_CASE("[EGDescription] Start level")
sfz::MidiState state; sfz::MidiState state;
eg.start = 0; eg.start = 0;
eg.ccStart = { 63, 127.0f }; eg.ccStart = { 63, 127.0f };
REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); REQUIRE(eg.getStart(state, 0_norm) == 0.0f);
REQUIRE( eg.getStart(state, 127_norm) == 0.0f ); REQUIRE(eg.getStart(state, 127_norm) == 0.0f);
state.ccEvent(0, 63, 127_norm); state.ccEvent(0, 63, 127_norm);
REQUIRE( eg.getStart(state, 0_norm) == 100.0f ); REQUIRE(eg.getStart(state, 0_norm) == 100.0f);
eg.ccStart = { 63, -127.0f }; eg.ccStart = { 63, -127.0f };
REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); REQUIRE(eg.getStart(state, 0_norm) == 0.0f);
} }

View file

@ -29,14 +29,14 @@ inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs,
return true; return true;
} }
const auto idModifier = [] (float x) { return x; }; const auto idModifier = [](float x) { return x; };
const auto twiceModifier = [] (float x) { return 2 * x; }; const auto twiceModifier = [](float x) { return 2 * x; };
const auto expModifier = [] (float x) { return std::exp(x); }; const auto expModifier = [](float x) { return std::exp(x); };
TEST_CASE("[Envelopes] Empty") TEST_CASE("[Envelopes] Empty")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f} { 0, 0.0f }
}; };
std::array<float, 5> output; std::array<float, 5> output;
std::array<float, 5> expected { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; std::array<float, 5> expected { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
@ -54,8 +54,8 @@ TEST_CASE("[Envelopes] Empty")
TEST_CASE("[Envelopes] Linear basic") TEST_CASE("[Envelopes] Linear basic")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{4, 1.0f} { 4, 1.0f }
}; };
std::array<float, 9> output; std::array<float, 9> output;
std::array<float, 9> expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; std::array<float, 9> expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
@ -66,9 +66,9 @@ TEST_CASE("[Envelopes] Linear basic")
TEST_CASE("[LinearEnvelope] 2 events, close") TEST_CASE("[LinearEnvelope] 2 events, close")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{4, 1.0f}, { 4, 1.0f },
{5, 2.0f} { 5, 2.0f }
}; };
std::array<float, 9> output; std::array<float, 9> output;
std::array<float, 9> expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f }; std::array<float, 9> expected { 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f };
@ -76,13 +76,12 @@ TEST_CASE("[LinearEnvelope] 2 events, close")
REQUIRE(output == expected); REQUIRE(output == expected);
} }
TEST_CASE("[LinearEnvelope] 2 events, far") TEST_CASE("[LinearEnvelope] 2 events, far")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 2.0f} { 6, 2.0f }
}; };
std::array<float, 9> output; std::array<float, 9> output;
std::array<float, 9> expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.0f, 2.0f }; std::array<float, 9> expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.0f, 2.0f };
@ -93,10 +92,10 @@ TEST_CASE("[LinearEnvelope] 2 events, far")
TEST_CASE("[LinearEnvelope] 3 events, out of block") TEST_CASE("[LinearEnvelope] 3 events, out of block")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 2.0f}, { 6, 2.0f },
{10, 3.0f} { 10, 3.0f }
}; };
std::array<float, 9> output; std::array<float, 9> output;
std::array<float, 9> expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f }; std::array<float, 9> expected { 0.0f, 0.5f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f };
@ -107,9 +106,9 @@ TEST_CASE("[LinearEnvelope] 3 events, out of block")
TEST_CASE("[LinearEnvelope] 2 events, function") TEST_CASE("[LinearEnvelope] 2 events, function")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 2.0f} { 6, 2.0f }
}; };
std::array<float, 9> output; std::array<float, 9> output;
std::array<float, 9> expected { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.0f, 4.0f }; std::array<float, 9> expected { 0.0f, 1.0f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.0f, 4.0f };
@ -120,9 +119,9 @@ TEST_CASE("[LinearEnvelope] 2 events, function")
TEST_CASE("[LinearEnvelope] Get quantized") TEST_CASE("[LinearEnvelope] Get quantized")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 2.0f} { 6, 2.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f };
@ -133,9 +132,9 @@ TEST_CASE("[LinearEnvelope] Get quantized")
TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets") TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.1f}, { 2, 1.1f },
{6, 1.9f} { 6, 1.9f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f };
@ -146,9 +145,9 @@ TEST_CASE("[LinearEnvelope] Get quantized with unquantized targets")
TEST_CASE("[LinearEnvelope] Get quantized with 2 steps") TEST_CASE("[LinearEnvelope] Get quantized with 2 steps")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 3.0f} { 6, 3.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f }; std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f };
@ -159,10 +158,10 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps")
TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of block step") TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of block step")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 0.0f}, { 0, 0.0f },
{2, 1.0f}, { 2, 1.0f },
{6, 3.0f}, { 6, 3.0f },
{10, 4.2f}, { 10, 4.2f },
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 4.0f }; std::array<float, 8> expected { 0.0f, 0.0f, 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 4.0f };
@ -174,9 +173,9 @@ TEST_CASE("[LinearEnvelope] Get quantized with 2 steps and an unquantized out of
TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps") TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 3.0f}, { 0, 3.0f },
{2, 2.0f}, { 2, 2.0f },
{6, 0.0f} { 6, 0.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 3.0f, 3.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f }; std::array<float, 8> expected { 3.0f, 3.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.0f, 0.0f };
@ -187,8 +186,8 @@ TEST_CASE("[LinearEnvelope] Going down quantized with 2 steps")
TEST_CASE("[MultiplicativeEnvelope] Basic event") TEST_CASE("[MultiplicativeEnvelope] Basic event")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{4, 2.0f} { 4, 2.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 2.0f, 2.0f, 2.0f }; std::array<float, 8> expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 2.0f, 2.0f, 2.0f };
@ -199,12 +198,12 @@ TEST_CASE("[MultiplicativeEnvelope] Basic event")
TEST_CASE("[MultiplicativeEnvelope] 2 events") TEST_CASE("[MultiplicativeEnvelope] 2 events")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{4, 2.0f}, { 4, 2.0f },
{5, 4.0f} { 5, 4.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 4.0f, 4.0f, 4.0f}; std::array<float, 8> expected { 1.0f, 1.1892f, 1.4142f, 1.68176f, 2.0f, 4.0f, 4.0f, 4.0f };
multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier); multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier);
REQUIRE(approxEqual<float>(output, expected)); REQUIRE(approxEqual<float>(output, expected));
} }
@ -212,12 +211,12 @@ TEST_CASE("[MultiplicativeEnvelope] 2 events")
TEST_CASE("[MultiplicativeEnvelope] 2 events, far") TEST_CASE("[MultiplicativeEnvelope] 2 events, far")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{2, 2.0f}, { 2, 2.0f },
{6, 4.0f} { 6, 4.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.4142f, 2.0f, 2.37841f, 2.82843f, 3.36358f, 4.0f, 4.0f}; std::array<float, 8> expected { 1.0f, 1.4142f, 2.0f, 2.37841f, 2.82843f, 3.36358f, 4.0f, 4.0f };
multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier); multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier);
REQUIRE(approxEqual<float>(output, expected)); REQUIRE(approxEqual<float>(output, expected));
} }
@ -225,12 +224,12 @@ TEST_CASE("[MultiplicativeEnvelope] 2 events, far")
TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps") TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{2, 2.0f}, { 2, 2.0f },
{6, 4.0f} { 6, 4.0f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f}; std::array<float, 8> expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 4.0f };
multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f); multiplicativeEnvelope(events, absl::MakeSpan(output), idModifier, 2.0f);
REQUIRE(output == expected); REQUIRE(output == expected);
} }
@ -238,10 +237,10 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with 2 steps")
TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step") TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of range step")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{2, 2.0f}, { 2, 2.0f },
{6, 4.0f}, { 6, 4.0f },
{10, 8.2f} { 10, 8.2f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 8.0f }; std::array<float, 8> expected { 1.0f, 1.0f, 2.0f, 2.0f, 2.0f, 2.0f, 4.0f, 8.0f };
@ -252,9 +251,9 @@ TEST_CASE("[MultiplicativeEnvelope] Get quantized with an unquantized out of ran
TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps") TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 4.0f}, { 0, 4.0f },
{2, 2.0f}, { 2, 2.0f },
{6, 0.5f} { 6, 0.5f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f }; std::array<float, 8> expected { 4.0f, 4.0f, 2.0f, 2.0f, 1.0f, 1.0f, 0.5f, 0.5f };
@ -265,9 +264,9 @@ TEST_CASE("[MultiplicativeEnvelope] Going down quantized with 2 steps")
TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events") TEST_CASE("[MultiplicativeEnvelope] Get quantized with unclean events")
{ {
sfz::EventVector events { sfz::EventVector events {
{0, 1.0f}, { 0, 1.0f },
{2, 1.2f}, { 2, 1.2f },
{6, 2.5f} { 6, 2.5f }
}; };
std::array<float, 8> output; std::array<float, 8> output;
std::array<float, 8> expected { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f }; std::array<float, 8> expected { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 2.0f };

View file

@ -309,9 +309,9 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 ); REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 );
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
REQUIRE( !synth.getRegionView(2)->amplitudeCC.empty() ); REQUIRE(!synth.getRegionView(2)->amplitudeCC.empty());
REQUIRE( synth.getRegionView(2)->amplitudeCC.contains(10) ); REQUIRE(synth.getRegionView(2)->amplitudeCC.contains(10));
REQUIRE( synth.getRegionView(2)->amplitudeCC.getWithDefault(10) == 0.34f ); REQUIRE(synth.getRegionView(2)->amplitudeCC.getWithDefault(10) == 0.34f);
} }
TEST_CASE("[Files] Specific bug: relative path with backslashes") TEST_CASE("[Files] Specific bug: relative path with backslashes")

View file

@ -21,7 +21,7 @@ TEST_CASE("[MidiState] Initial values")
{ {
sfz::MidiState state; sfz::MidiState state;
for (unsigned cc = 0; cc < sfz::config::numCCs; cc++) for (unsigned cc = 0; cc < sfz::config::numCCs; cc++)
REQUIRE( state.getCCValue(cc) == 0_norm ); REQUIRE(state.getCCValue(cc) == 0_norm);
REQUIRE( state.getPitchBend() == 0 ); REQUIRE( state.getPitchBend() == 0 );
} }

View file

@ -1434,13 +1434,13 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("amplitude") SECTION("amplitude")
{ {
REQUIRE(region.amplitude == 1.0_a ); REQUIRE(region.amplitude == 1.0_a);
region.parseOpcode({ "amplitude", "40" }); region.parseOpcode({ "amplitude", "40" });
REQUIRE(region.amplitude == 0.4_a ); REQUIRE(region.amplitude == 0.4_a);
region.parseOpcode({ "amplitude", "-40" }); region.parseOpcode({ "amplitude", "-40" });
REQUIRE(region.amplitude == 0_a ); REQUIRE(region.amplitude == 0_a);
region.parseOpcode({ "amplitude", "140" }); region.parseOpcode({ "amplitude", "140" });
REQUIRE(region.amplitude == 1.0_a ); REQUIRE(region.amplitude == 1.0_a);
} }
SECTION("amplitude_cc") SECTION("amplitude_cc")

View file

@ -21,9 +21,9 @@ TEST_CASE("[Region] Crossfade in on key")
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "3" }); region.parseOpcode({ "xfin_hikey", "3" });
REQUIRE( region.getNoteGain(2, 127_norm) == 0.70711_a ); REQUIRE(region.getNoteGain(2, 127_norm) == 0.70711_a);
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a);
REQUIRE( region.getNoteGain(3, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(3, 127_norm) == 1.0_a);
} }
TEST_CASE("[Region] Crossfade in on key - 2") TEST_CASE("[Region] Crossfade in on key - 2")
@ -33,12 +33,12 @@ TEST_CASE("[Region] Crossfade in on key - 2")
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xfin_hikey", "5" });
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a);
REQUIRE( region.getNoteGain(2, 127_norm) == 0.5_a ); REQUIRE(region.getNoteGain(2, 127_norm) == 0.5_a);
REQUIRE( region.getNoteGain(3, 127_norm) == 0.70711_a ); REQUIRE(region.getNoteGain(3, 127_norm) == 0.70711_a);
REQUIRE( region.getNoteGain(4, 127_norm) == 0.86603_a ); REQUIRE(region.getNoteGain(4, 127_norm) == 0.86603_a);
REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a);
REQUIRE( region.getNoteGain(6, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(6, 127_norm) == 1.0_a);
} }
TEST_CASE("[Region] Crossfade in on key - gain") TEST_CASE("[Region] Crossfade in on key - gain")
@ -49,11 +49,11 @@ TEST_CASE("[Region] Crossfade in on key - gain")
region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xfin_hikey", "5" });
region.parseOpcode({ "xf_keycurve", "gain" }); region.parseOpcode({ "xf_keycurve", "gain" });
REQUIRE( region.getNoteGain(1, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a);
REQUIRE( region.getNoteGain(2, 127_norm) == 0.25_a ); REQUIRE(region.getNoteGain(2, 127_norm) == 0.25_a);
REQUIRE( region.getNoteGain(3, 127_norm) == 0.5_a ); REQUIRE(region.getNoteGain(3, 127_norm) == 0.5_a);
REQUIRE( region.getNoteGain(4, 127_norm) == 0.75_a ); REQUIRE(region.getNoteGain(4, 127_norm) == 0.75_a);
REQUIRE( region.getNoteGain(5, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a);
} }
TEST_CASE("[Region] Crossfade out on key") TEST_CASE("[Region] Crossfade out on key")
@ -63,13 +63,13 @@ TEST_CASE("[Region] Crossfade out on key")
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_lokey", "51" });
region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xfout_hikey", "55" });
REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a);
REQUIRE( region.getNoteGain(51, 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(52, 127_norm) == 0.86603_a);
REQUIRE( region.getNoteGain(53, 127_norm) == 0.70711_a ); REQUIRE(region.getNoteGain(53, 127_norm) == 0.70711_a);
REQUIRE( region.getNoteGain(54, 127_norm) == 0.5_a ); REQUIRE(region.getNoteGain(54, 127_norm) == 0.5_a);
REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a);
REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a);
} }
TEST_CASE("[Region] Crossfade out on key - gain") TEST_CASE("[Region] Crossfade out on key - gain")
@ -80,13 +80,13 @@ TEST_CASE("[Region] Crossfade out on key - gain")
region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_lokey", "51" });
region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xfout_hikey", "55" });
region.parseOpcode({ "xf_keycurve", "gain" }); region.parseOpcode({ "xf_keycurve", "gain" });
REQUIRE( region.getNoteGain(50, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a);
REQUIRE( region.getNoteGain(51, 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(52, 127_norm) == 0.75_a);
REQUIRE( region.getNoteGain(53, 127_norm) == 0.5_a ); REQUIRE(region.getNoteGain(53, 127_norm) == 0.5_a);
REQUIRE( region.getNoteGain(54, 127_norm) == 0.25_a ); REQUIRE(region.getNoteGain(54, 127_norm) == 0.25_a);
REQUIRE( region.getNoteGain(55, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a);
REQUIRE( region.getNoteGain(56, 127_norm) == 0.0_a ); REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a);
} }
TEST_CASE("[Region] Crossfade in on velocity") TEST_CASE("[Region] Crossfade in on velocity")
@ -97,13 +97,13 @@ TEST_CASE("[Region] Crossfade in on velocity")
region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_lovel", "20" });
region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xfin_hivel", "24" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a);
REQUIRE( region.getNoteGain(1, 20_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(2, 21_norm) == 0.5_a);
REQUIRE( region.getNoteGain(3, 22_norm) == 0.70711_a ); REQUIRE(region.getNoteGain(3, 22_norm) == 0.70711_a);
REQUIRE( region.getNoteGain(4, 23_norm) == 0.86603_a ); REQUIRE(region.getNoteGain(4, 23_norm) == 0.86603_a);
REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a);
REQUIRE( region.getNoteGain(6, 25_norm) == 1.0_a ); REQUIRE(region.getNoteGain(6, 25_norm) == 1.0_a);
} }
TEST_CASE("[Region] Crossfade in on vel - gain") TEST_CASE("[Region] Crossfade in on vel - gain")
@ -115,13 +115,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain")
region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xfin_hivel", "24" });
region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "xf_velcurve", "gain" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE( region.getNoteGain(1, 19_norm) == 0.0_a ); REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a);
REQUIRE( region.getNoteGain(1, 20_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(2, 21_norm) == 0.25_a);
REQUIRE( region.getNoteGain(3, 22_norm) == 0.5_a ); REQUIRE(region.getNoteGain(3, 22_norm) == 0.5_a);
REQUIRE( region.getNoteGain(4, 23_norm) == 0.75_a ); REQUIRE(region.getNoteGain(4, 23_norm) == 0.75_a);
REQUIRE( region.getNoteGain(5, 24_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a);
REQUIRE( region.getNoteGain(5, 25_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 25_norm) == 1.0_a);
} }
TEST_CASE("[Region] Crossfade out on vel") TEST_CASE("[Region] Crossfade out on vel")
@ -132,13 +132,13 @@ TEST_CASE("[Region] Crossfade out on vel")
region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_lovel", "51" });
region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xfout_hivel", "55" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE( region.getNoteGain(5, 50_norm) == 1.0_a ); REQUIRE(region.getNoteGain(5, 50_norm) == 1.0_a);
REQUIRE( region.getNoteGain(5, 51_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, 52_norm) == 0.86603_a);
REQUIRE( region.getNoteGain(5, 53_norm) == 0.70711_a ); REQUIRE(region.getNoteGain(5, 53_norm) == 0.70711_a);
REQUIRE( region.getNoteGain(5, 54_norm) == 0.5_a ); REQUIRE(region.getNoteGain(5, 54_norm) == 0.5_a);
REQUIRE( region.getNoteGain(5, 55_norm) == 0.0_a ); REQUIRE(region.getNoteGain(5, 55_norm) == 0.0_a);
REQUIRE( region.getNoteGain(5, 56_norm) == 0.0_a ); REQUIRE(region.getNoteGain(5, 56_norm) == 0.0_a);
} }
TEST_CASE("[Region] Crossfade out on vel - gain") TEST_CASE("[Region] Crossfade out on vel - gain")
@ -150,13 +150,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain")
region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xfout_hivel", "55" });
region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "xf_velcurve", "gain" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE( region.getNoteGain(56, 50_norm) == 1.0_a ); REQUIRE(region.getNoteGain(56, 50_norm) == 1.0_a);
REQUIRE( region.getNoteGain(56, 51_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, 52_norm) == 0.75_a);
REQUIRE( region.getNoteGain(56, 53_norm) == 0.5_a ); REQUIRE(region.getNoteGain(56, 53_norm) == 0.5_a);
REQUIRE( region.getNoteGain(56, 54_norm) == 0.25_a ); REQUIRE(region.getNoteGain(56, 54_norm) == 0.25_a);
REQUIRE( region.getNoteGain(56, 55_norm) == 0.0_a ); REQUIRE(region.getNoteGain(56, 55_norm) == 0.0_a);
REQUIRE( region.getNoteGain(56, 56_norm) == 0.0_a ); REQUIRE(region.getNoteGain(56, 56_norm) == 0.0_a);
} }
TEST_CASE("[Region] Crossfade in on CC") TEST_CASE("[Region] Crossfade in on CC")
@ -167,13 +167,20 @@ TEST_CASE("[Region] Crossfade in on CC")
region.parseOpcode({ "xfin_locc24", "20" }); region.parseOpcode({ "xfin_locc24", "20" });
region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "xfin_hicc24", "24" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); midiState.ccEvent(0, 24, 19_norm);
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); REQUIRE(region.getCrossfadeGain() == 0.0_a);
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); midiState.ccEvent(0, 24, 20_norm);
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); REQUIRE(region.getCrossfadeGain() == 0.0_a);
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); midiState.ccEvent(0, 24, 21_norm);
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); REQUIRE(region.getCrossfadeGain() == 0.5_a);
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_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") TEST_CASE("[Region] Crossfade in on CC - gain")
@ -185,13 +192,20 @@ TEST_CASE("[Region] Crossfade in on CC - gain")
region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "xfin_hicc24", "24" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
region.parseOpcode({ "xf_cccurve", "gain" }); region.parseOpcode({ "xf_cccurve", "gain" });
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); midiState.ccEvent(0, 24, 19_norm);
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); REQUIRE(region.getCrossfadeGain() == 0.0_a);
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); midiState.ccEvent(0, 24, 20_norm);
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); REQUIRE(region.getCrossfadeGain() == 0.0_a);
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); midiState.ccEvent(0, 24, 21_norm);
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); REQUIRE(region.getCrossfadeGain() == 0.25_a);
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 1.0_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") TEST_CASE("[Region] Crossfade out on CC")
{ {
@ -201,13 +215,20 @@ TEST_CASE("[Region] Crossfade out on CC")
region.parseOpcode({ "xfout_locc24", "20" }); region.parseOpcode({ "xfout_locc24", "20" });
region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "xfout_hicc24", "24" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); midiState.ccEvent(0, 24, 19_norm);
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); REQUIRE(region.getCrossfadeGain() == 1.0_a);
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.86603_a ); midiState.ccEvent(0, 24, 20_norm);
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.70711_a ); REQUIRE(region.getCrossfadeGain() == 1.0_a);
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); midiState.ccEvent(0, 24, 21_norm);
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); REQUIRE(region.getCrossfadeGain() == 0.86603_a);
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_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") TEST_CASE("[Region] Crossfade out on CC - gain")
@ -219,13 +240,20 @@ TEST_CASE("[Region] Crossfade out on CC - gain")
region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "xfout_hicc24", "24" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
region.parseOpcode({ "xf_cccurve", "gain" }); region.parseOpcode({ "xf_cccurve", "gain" });
midiState.ccEvent(0, 24, 19_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); midiState.ccEvent(0, 24, 19_norm);
midiState.ccEvent(0, 24, 20_norm); REQUIRE( region.getCrossfadeGain() == 1.0_a ); REQUIRE(region.getCrossfadeGain() == 1.0_a);
midiState.ccEvent(0, 24, 21_norm); REQUIRE( region.getCrossfadeGain() == 0.75_a ); midiState.ccEvent(0, 24, 20_norm);
midiState.ccEvent(0, 24, 22_norm); REQUIRE( region.getCrossfadeGain() == 0.5_a ); REQUIRE(region.getCrossfadeGain() == 1.0_a);
midiState.ccEvent(0, 24, 23_norm); REQUIRE( region.getCrossfadeGain() == 0.25_a ); midiState.ccEvent(0, 24, 21_norm);
midiState.ccEvent(0, 24, 24_norm); REQUIRE( region.getCrossfadeGain() == 0.0_a ); REQUIRE(region.getCrossfadeGain() == 0.75_a);
midiState.ccEvent(0, 24, 25_norm); REQUIRE( region.getCrossfadeGain() == 0.0_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") TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
@ -234,8 +262,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
sfz::Region region { midiState }; sfz::Region region { midiState };
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a);
REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a);
} }
@ -245,8 +273,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
sfz::Region region { midiState }; sfz::Region region { midiState };
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "100" }); region.parseOpcode({ "amp_veltrack", "100" });
REQUIRE( region.getNoteGain(64, 127_norm) == 1.0_a ); REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a);
REQUIRE( region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001) ); REQUIRE(region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001));
} }
TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
@ -255,8 +283,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
sfz::Region region { midiState }; sfz::Region region { midiState };
region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "-100" }); region.parseOpcode({ "amp_veltrack", "-100" });
REQUIRE( region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001) ); REQUIRE(region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001));
REQUIRE( region.getNoteGain(64, 0_norm) == 1.0_a ); REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a);
} }
TEST_CASE("[Region] rt_decay") TEST_CASE("[Region] rt_decay")

View file

@ -141,9 +141,9 @@ TEST_CASE("[Synth] Reset all controllers")
{ {
sfz::Synth synth; sfz::Synth synth;
synth.cc(0, 12, 64); synth.cc(0, 12, 64);
REQUIRE( synth.getMidiState().getCCValue(12) == 64_norm ); REQUIRE(synth.getMidiState().getCCValue(12) == 64_norm);
synth.cc(0, 121, 64); synth.cc(0, 121, 64);
REQUIRE( synth.getMidiState().getCCValue(12) == 0_norm ); REQUIRE(synth.getMidiState().getCCValue(12) == 0_norm);
} }
TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice") TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice")