diff --git a/src/sfizz/ADSREnvelope.cpp b/src/sfizz/ADSREnvelope.cpp index 21a76cf9..0acbdbe3 100644 --- a/src/sfizz/ADSREnvelope.cpp +++ b/src/sfizz/ADSREnvelope.cpp @@ -12,7 +12,7 @@ namespace sfz { template -void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, uint8_t velocity, float sampleRate) noexcept +void ADSREnvelope::reset(const Region& region, const MidiState& state, int delay, float velocity, float sampleRate) noexcept { auto secondsToSamples = [sampleRate](Type timeInSeconds) { return static_cast(timeInSeconds * sampleRate); diff --git a/src/sfizz/ADSREnvelope.h b/src/sfizz/ADSREnvelope.h index f2468a09..6a647d03 100644 --- a/src/sfizz/ADSREnvelope.h +++ b/src/sfizz/ADSREnvelope.h @@ -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 * diff --git a/src/sfizz/EGDescription.h b/src/sfizz/EGDescription.h index 3064b3c8..ee44f754 100644 --- a/src/sfizz/EGDescription.h +++ b/src/sfizz/EGDescription.h @@ -32,9 +32,7 @@ #include "MidiState.h" #include - -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 @@ -60,36 +58,35 @@ inline float ccSwitchedValue(const MidiState& state, const absl::optional> ccAttack; - absl::optional> ccDecay; - absl::optional> ccDelay; - absl::optional> ccHold; - absl::optional> ccRelease; - absl::optional> ccStart; - absl::optional> ccSustain; + absl::optional> ccAttack; + absl::optional> ccDecay; + absl::optional> ccDelay; + absl::optional> ccHold; + absl::optional> ccRelease; + absl::optional> ccStart; + absl::optional> ccSustain; /** * @brief Get the attack with possibly a CC modifier and a velocity modifier @@ -98,9 +95,10 @@ struct EGDescription * @param velocity * @return float */ - float getAttack(const MidiState &state, uint8_t velocity) const noexcept + float getAttack(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, 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 @@ -109,9 +107,10 @@ struct EGDescription * @param velocity * @return float */ - float getDecay(const MidiState &state, uint8_t velocity) const noexcept + float getDecay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, 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 @@ -120,9 +119,10 @@ struct EGDescription * @param velocity * @return float */ - float getDelay(const MidiState &state, uint8_t velocity) const noexcept + float getDelay(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, 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 @@ -131,9 +131,10 @@ struct EGDescription * @param velocity * @return float */ - float getHold(const MidiState &state, uint8_t velocity) const noexcept + float getHold(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, 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 @@ -142,9 +143,10 @@ struct EGDescription * @param velocity * @return float */ - float getRelease(const MidiState &state, uint8_t velocity) const noexcept + float getRelease(const MidiState& state, float velocity) const noexcept { - return Default::egTimeRange.clamp(ccSwitchedValue(state, 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 @@ -153,7 +155,7 @@ struct EGDescription * @param velocity * @return float */ - float getStart(const MidiState &state, uint8_t velocity) const noexcept + float getStart(const MidiState& state, float velocity) const noexcept { UNUSED(velocity); return Default::egPercentRange.clamp(ccSwitchedValue(state, ccStart, start)); @@ -165,9 +167,10 @@ struct EGDescription * @param velocity * @return float */ - float getSustain(const MidiState &state, uint8_t velocity) const noexcept + float getSustain(const MidiState& state, float velocity) const noexcept { - return Default::egPercentRange.clamp(ccSwitchedValue(state, 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); }; diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 1979c08e..79d7ccbe 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -140,7 +140,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value, initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = midiNoteFrequency(number); bendStepFactor = centsFactor(region->bendStep); - egEnvelope.reset(*region, resources.midiState, delay, denormalize7Bits(value), sampleRate); + egEnvelope.reset(*region, resources.midiState, delay, value, sampleRate); } bool sfz::Voice::isFree() const noexcept diff --git a/tests/ADSREnvelopeT.cpp b/tests/ADSREnvelopeT.cpp index f11945b4..22783f3d 100644 --- a/tests/ADSREnvelopeT.cpp +++ b/tests/ADSREnvelopeT.cpp @@ -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 output; std::array expected { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(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(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 output; std::array expected { 0.33333f, 0.66667f, 1.0f, 1.0f, 1.0f }; for (auto& out : output) out = envelope.getNextValue(); REQUIRE(approxEqual(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(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 output; std::array 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(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 output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); envelope.startRelease(4); std::array 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(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 output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array 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(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(output, expected)); @@ -161,13 +161,13 @@ TEST_CASE("[ADSREnvelope] Decay") region.amplitudeEG.sustain = 50.0f; region.amplitudeEG.decay = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array 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(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(output, expected)); @@ -185,13 +185,13 @@ TEST_CASE("[ADSREnvelope] Hold") region.amplitudeEG.decay = 0.02f; region.amplitudeEG.hold = 0.02f; std::array output; - envelope.reset(region, state, 0, 0, 100.0f); + envelope.reset(region, state, 0, 0.0f, 100.0f); std::array 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(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(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 output; std::array 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(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 output; std::array 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(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)); diff --git a/tests/EGDescriptionT.cpp b/tests/EGDescriptionT.cpp index 291e222d..305a60fe 100644 --- a/tests/EGDescriptionT.cpp +++ b/tests/EGDescriptionT.cpp @@ -18,13 +18,13 @@ TEST_CASE("[EGDescription] Attack range") eg.attack = 1; eg.vel2attack = -1.27f; eg.ccAttack = { 63, 1.27f }; - REQUIRE( eg.getAttack(state, 0) == 1.0f ); - REQUIRE( eg.getAttack(state, 127) == 0.0f ); + 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) == 1.0f ); - REQUIRE( eg.getAttack(state, 0) == 2.27f ); + REQUIRE( eg.getAttack(state, 127_norm) == 1.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 2.27f ); eg.ccAttack = { 63, 127.0f }; - REQUIRE( eg.getAttack(state, 0) == 100.0f ); + REQUIRE( eg.getAttack(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Delay range") @@ -34,13 +34,13 @@ TEST_CASE("[EGDescription] Delay range") eg.delay = 1; eg.vel2delay = -1.27f; eg.ccDelay = { 63, 1.27f }; - REQUIRE( eg.getDelay(state, 0) == 1.0f ); - REQUIRE( eg.getDelay(state, 127) == 0.0f ); + 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) == 1.0f ); - REQUIRE( eg.getDelay(state, 0) == 2.27f ); + REQUIRE( eg.getDelay(state, 127_norm) == 1.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 2.27f ); eg.ccDelay = { 63, 127.0f }; - REQUIRE( eg.getDelay(state, 0) == 100.0f ); + REQUIRE( eg.getDelay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Decay range") @@ -50,13 +50,13 @@ TEST_CASE("[EGDescription] Decay range") eg.decay = 1.0f; eg.vel2decay = -1.27f; eg.ccDecay = { 63, 1.27f }; - REQUIRE( eg.getDecay(state, 0) == 1.0f ); - REQUIRE( eg.getDecay(state, 127) == 0.0f ); + 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) == 1.0f ); - REQUIRE( eg.getDecay(state, 0) == 2.27f ); + REQUIRE( eg.getDecay(state, 127_norm) == 1.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 2.27f ); eg.ccDecay = { 63, 127.0f }; - REQUIRE( eg.getDecay(state, 0) == 100.0f ); + REQUIRE( eg.getDecay(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Release range") @@ -66,13 +66,13 @@ TEST_CASE("[EGDescription] Release range") eg.release = 1; eg.vel2release = -1.27f; eg.ccRelease = { 63, 1.27f }; - REQUIRE( eg.getRelease(state, 0) == 1.0f ); - REQUIRE( eg.getRelease(state, 127) == 0.0f ); + 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) == 1.0f ); - REQUIRE( eg.getRelease(state, 0) == 2.27f ); + REQUIRE( eg.getRelease(state, 127_norm) == 1.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 2.27f ); eg.ccRelease = { 63, 127.0f }; - REQUIRE( eg.getRelease(state, 0) == 100.0f ); + REQUIRE( eg.getRelease(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Hold range") @@ -82,13 +82,13 @@ TEST_CASE("[EGDescription] Hold range") eg.hold = 1; eg.vel2hold = -1.27f; eg.ccHold = { 63, 1.27f }; - REQUIRE( eg.getHold(state, 0) == 1.0f ); - REQUIRE( eg.getHold(state, 127) == 0.0f ); + 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) == 1.0f ); - REQUIRE( eg.getHold(state, 0) == 2.27f ); + REQUIRE( eg.getHold(state, 127_norm) == 1.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 2.27f ); eg.ccHold = { 63, 127.0f }; - REQUIRE( eg.getHold(state, 0) == 100.0f ); + REQUIRE( eg.getHold(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Sustain level") @@ -98,12 +98,12 @@ TEST_CASE("[EGDescription] Sustain level") eg.sustain = 50; eg.vel2sustain = -100; eg.ccSustain = { 63, 100.0f }; - REQUIRE( eg.getSustain(state, 0) == 50.0f ); - REQUIRE( eg.getSustain(state, 127) == 0.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) == 50.0f ); + REQUIRE( eg.getSustain(state, 127_norm) == 50.0f ); eg.ccSustain = { 63, 200.0f }; - REQUIRE( eg.getSustain(state, 0) == 100.0f ); + REQUIRE( eg.getSustain(state, 0_norm) == 100.0f ); } TEST_CASE("[EGDescription] Start level") @@ -112,10 +112,10 @@ TEST_CASE("[EGDescription] Start level") sfz::MidiState state; eg.start = 0; eg.ccStart = { 63, 127.0f }; - REQUIRE( eg.getStart(state, 0) == 0.0f ); - REQUIRE( eg.getStart(state, 127) == 0.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) == 100.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 100.0f ); eg.ccStart = { 63, -127.0f }; - REQUIRE( eg.getStart(state, 0) == 0.0f ); + REQUIRE( eg.getStart(state, 0_norm) == 0.0f ); }