Remove CCArray, and store floats internally

This commit is contained in:
Paul Fd 2020-03-17 00:17:18 +01:00 committed by Paul Ferrand
parent f8801e78e4
commit 9373f31945
11 changed files with 209 additions and 151 deletions

View file

@ -18,15 +18,14 @@ void ADSREnvelope<Type>::reset(const Region& region, const MidiState& state, int
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;

View file

@ -29,6 +29,7 @@
#include "Macros.h"
#include "LeakDetector.h"
#include "SfzHelpers.h"
#include "MidiState.h"
#include <absl/types/optional.h>
@ -42,6 +43,23 @@ namespace sfz
* TODO: should be updated for SFZ v2
*
*/
/**
* @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 * normalizeCC(state.getCCValue(ccSwitch->cc));
else
return value;
}
struct EGDescription
{
EGDescription() = default;
@ -76,80 +94,80 @@ struct EGDescription
/**
* @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, uint8_t velocity) const noexcept
{
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccAttack, attack) + normalizeVelocity(velocity)*vel2attack);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccAttack, attack) + normalizeVelocity(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, uint8_t velocity) const noexcept
{
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDecay, decay) + normalizeVelocity(velocity)*vel2decay);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDecay, decay) + normalizeVelocity(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, uint8_t velocity) const noexcept
{
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccDelay, delay) + normalizeVelocity(velocity)*vel2delay);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccDelay, delay) + normalizeVelocity(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, uint8_t velocity) const noexcept
{
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccHold, hold) + normalizeVelocity(velocity)*vel2hold);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccHold, hold) + normalizeVelocity(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, uint8_t velocity) const noexcept
{
return Default::egTimeRange.clamp(ccSwitchedValue(ccValues, ccRelease, release) + normalizeVelocity(velocity)*vel2release);
return Default::egTimeRange.clamp(ccSwitchedValue(state, ccRelease, release) + normalizeVelocity(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, uint8_t 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, uint8_t velocity) const noexcept
{
return Default::egPercentRange.clamp(ccSwitchedValue(ccValues, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain);
return Default::egPercentRange.clamp(ccSwitchedValue(state, ccSustain, sustain) + normalizeVelocity(velocity)*vel2sustain);
}
LEAK_DETECTOR(EGDescription);
};

View file

@ -14,9 +14,19 @@ sfz::MidiState::MidiState()
}
void sfz::MidiState::noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept
{
noteOnEventNormalized(delay, noteNumber, normalizeVelocity(velocity));
}
void sfz::MidiState::noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept
{
noteOffEventNormalized(delay, noteNumber, normalizeVelocity(velocity));
}
void sfz::MidiState::noteOnEventNormalized(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 +36,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::noteOffEventNormalized(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)
@ -52,12 +62,18 @@ float sfz::MidiState::getNoteDuration(int noteNumber) const
}
uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
{
return denormalizeVelocity(getNoteVelocityNormalized(noteNumber));
}
float sfz::MidiState::getNoteVelocityNormalized(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);
@ -72,24 +88,28 @@ int sfz::MidiState::getPitchBend() const noexcept
void sfz::MidiState::ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept
{
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
ASSERT(ccValue >= 0 && ccValue <= 127);
cc[ccNumber] = ccValue;
ccEventNormalized(delay, ccNumber, normalizeCC(ccValue));
}
uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept
{
return denormalizeCC(getCCValueNormalized(ccNumber));
}
void sfz::MidiState::ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept
{
ASSERT(ccValue >= 0.0 && ccValue <= 1.0);
cc[ccNumber] = ccValue;
}
float sfz::MidiState::getCCValueNormalized(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)

View file

@ -7,7 +7,6 @@
#pragma once
#include <chrono>
#include <array>
#include "SfzHelpers.h"
#include "CCMap.h"
#include "Range.h"
@ -31,6 +30,14 @@ public:
*/
void noteOnEvent(int delay, int noteNumber, uint8_t velocity) noexcept;
/**
* @brief Update the state after a note on event
*
* @param noteNumber
* @param velocity
*/
void noteOnEventNormalized(int delay, int noteNumber, float velocity) noexcept;
/**
* @brief Update the state after a note off event
*
@ -39,6 +46,14 @@ public:
*/
void noteOffEvent(int delay, int noteNumber, uint8_t velocity) noexcept;
/**
* @brief Update the state after a note off event
*
* @param noteNumber
* @param velocity
*/
void noteOffEventNormalized(int delay, int noteNumber, float velocity) noexcept;
int getActiveNotes() const noexcept { return activeNotes; }
/**
@ -57,6 +72,14 @@ public:
*/
uint8_t getNoteVelocity(int noteNumber) const noexcept;
/**
* @brief Get the note on velocity for a given note
*
* @param noteNumber
* @return float
*/
float getNoteVelocityNormalized(int noteNumber) const noexcept;
/**
* @brief Register a pitch bend event
*
@ -79,6 +102,14 @@ public:
*/
void ccEvent(int delay, int ccNumber, uint8_t ccValue) noexcept;
/**
* @brief Register a CC event
*
* @param ccNumber
* @param ccValue
*/
void ccEventNormalized(int delay, int ccNumber, float ccValue) noexcept;
/**
* @brief Get the CC value for CC number
*
@ -88,11 +119,12 @@ public:
uint8_t getCCValue(int ccNumber) const noexcept;
/**
* @brief Get the full CC status
* @brief Get the CC value for CC number
*
* @return const SfzCCArray&
* @param ccNumber
* @return float
*/
const SfzCCArray& getCCArray() const noexcept;
float getCCValueNormalized(int ccNumber) const noexcept;
/**
* @brief Reset the midi state (does not impact the last note on time)
@ -140,12 +172,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
*/

View file

@ -1009,19 +1009,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);
}

View file

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

View file

@ -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,22 +150,6 @@ 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
{
if (ccSwitch)
return value + ccSwitch->value * normalizeCC(ccValues[ccSwitch->cc]);
else
return value;
}
/**
* @brief Convert a note in string to its equivalent midi note number
*

View file

@ -89,7 +89,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value
gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value);
amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain));
float crossfadeGain { region->getCrossfadeGain(resources.midiState.getCCArray()) };
float crossfadeGain { region->getCrossfadeGain() };
crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain));
basePan = normalizePercents(region->pan);
@ -225,7 +225,7 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept
}
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));
}
}

View file

@ -12,108 +12,108 @@ using namespace Catch::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) == 1.0f );
REQUIRE( eg.getAttack(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getAttack(state, 127) == 1.0f );
REQUIRE( eg.getAttack(state, 0) == 2.27f );
eg.ccAttack = { 63, 127.0f };
REQUIRE( eg.getAttack(ccArray, 0) == 100.0f );
REQUIRE( eg.getAttack(state, 0) == 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) == 1.0f );
REQUIRE( eg.getDelay(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getDelay(state, 127) == 1.0f );
REQUIRE( eg.getDelay(state, 0) == 2.27f );
eg.ccDelay = { 63, 127.0f };
REQUIRE( eg.getDelay(ccArray, 0) == 100.0f );
REQUIRE( eg.getDelay(state, 0) == 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) == 1.0f );
REQUIRE( eg.getDecay(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getDecay(state, 127) == 1.0f );
REQUIRE( eg.getDecay(state, 0) == 2.27f );
eg.ccDecay = { 63, 127.0f };
REQUIRE( eg.getDecay(ccArray, 0) == 100.0f );
REQUIRE( eg.getDecay(state, 0) == 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) == 1.0f );
REQUIRE( eg.getRelease(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getRelease(state, 127) == 1.0f );
REQUIRE( eg.getRelease(state, 0) == 2.27f );
eg.ccRelease = { 63, 127.0f };
REQUIRE( eg.getRelease(ccArray, 0) == 100.0f );
REQUIRE( eg.getRelease(state, 0) == 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) == 1.0f );
REQUIRE( eg.getHold(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getHold(state, 127) == 1.0f );
REQUIRE( eg.getHold(state, 0) == 2.27f );
eg.ccHold = { 63, 127.0f };
REQUIRE( eg.getHold(ccArray, 0) == 100.0f );
REQUIRE( eg.getHold(state, 0) == 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) == 50.0f );
REQUIRE( eg.getSustain(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getSustain(state, 127) == 50.0f );
eg.ccSustain = { 63, 200.0f };
REQUIRE( eg.getSustain(ccArray, 0) == 100.0f );
REQUIRE( eg.getSustain(state, 0) == 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) == 0.0f );
REQUIRE( eg.getStart(state, 127) == 0.0f );
state.ccEvent(0, 63, 127);
REQUIRE( eg.getStart(state, 0) == 100.0f );
eg.ccStart = { 63, -127.0f };
REQUIRE( eg.getStart(ccArray, 0) == 0.0f );
REQUIRE( eg.getStart(state, 0) == 0.0f );
}

View file

@ -18,21 +18,18 @@ using namespace Catch::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 );
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);
}
TEST_CASE("[MidiState] Set and get pitch bends")
@ -68,6 +65,5 @@ TEST_CASE("[MidiState] Set and get note velocities")
TEST_CASE("[MidiState] Extended CCs")
{
sfz::MidiState state;
REQUIRE(state.getCCArray().size() >= 142);
state.ccEvent(0, 142, 64); // should not trap
}

View file

@ -166,13 +166,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); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.5_a );
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a );
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.86603_a );
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a );
}
TEST_CASE("[Region] Crossfade in on CC - gain")
@ -184,13 +184,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); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.25_a );
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a );
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.75_a );
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 1.0_a );
}
TEST_CASE("[Region] Crossfade out on CC")
{
@ -200,13 +200,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); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.86603_a );
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.70711_a );
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.5_a );
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a );
}
TEST_CASE("[Region] Crossfade out on CC - gain")
@ -218,13 +218,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); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 20); REQUIRE( region.getCrossfadeGain() == 1.0_a );
midiState.ccEvent(0, 24, 21); REQUIRE( region.getCrossfadeGain() == 0.75_a );
midiState.ccEvent(0, 24, 22); REQUIRE( region.getCrossfadeGain() == 0.5_a );
midiState.ccEvent(0, 24, 23); REQUIRE( region.getCrossfadeGain() == 0.25_a );
midiState.ccEvent(0, 24, 24); REQUIRE( region.getCrossfadeGain() == 0.0_a );
midiState.ccEvent(0, 24, 25); REQUIRE( region.getCrossfadeGain() == 0.0_a );
}
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")