From e7686d233e9895a0fd54ea9788208c35776511cc Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Thu, 26 Mar 2020 00:12:30 +0100 Subject: [PATCH] Use float internally in the voices --- src/sfizz/Config.h | 2 +- src/sfizz/Region.cpp | 1 + src/sfizz/SIMDHelpers.h | 2 +- src/sfizz/Synth.cpp | 27 +++++++++++++------------- src/sfizz/Synth.h | 4 ++-- src/sfizz/Voice.cpp | 43 +++++++++++++++++++++-------------------- src/sfizz/Voice.h | 12 ++++++------ 7 files changed, 47 insertions(+), 44 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index eda45b81..0af2263f 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -43,7 +43,7 @@ namespace config { constexpr int allNotesOffCC { 123 }; constexpr int omniOffCC { 124 }; constexpr int omniOnCC { 125 }; - constexpr int halfCCThreshold { 64 }; + constexpr float halfCCThreshold { 0.5f }; constexpr int centPerSemitone { 100 }; constexpr float virtuallyZero { 0.00005f }; constexpr float fastReleaseDuration { 0.01f }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 61082200..3d1304a0 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -902,6 +902,7 @@ bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept { + ASSERT(ccValue >= 0.0f && ccValue <= 1.0f); if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); else diff --git a/src/sfizz/SIMDHelpers.h b/src/sfizz/SIMDHelpers.h index e8134809..2e217c3a 100644 --- a/src/sfizz/SIMDHelpers.h +++ b/src/sfizz/SIMDHelpers.h @@ -523,7 +523,7 @@ void multiplyAdd(absl::Span gain, absl::Span void multiplyAdd(const T gain, absl::Span input, absl::Span output) noexcept { - ASSERT(input.size() <= output.size()); + // ASSERT(input.size() <= output.size()); auto* in = input.begin(); auto* out = output.begin(); auto* sentinel = out + std::min(output.size(), input.size()); diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 7bec6cbf..9724ce20 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -600,15 +600,15 @@ void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept { ASSERT(noteNumber < 128); ASSERT(noteNumber >= 0); - + const auto normalizedVelocity = normalizeVelocity(velocity); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.noteOnEvent(delay, noteNumber, velocity); + resources.midiState.noteOnEventNormalized(delay, noteNumber, normalizedVelocity); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) return; - noteOnDispatch(delay, noteNumber, velocity); + noteOnDispatch(delay, noteNumber, normalizedVelocity); } void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept @@ -627,7 +627,7 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // way in sfz to specify that a release trigger should NOT use the note-on velocity? // auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity); - const auto replacedVelocity = resources.midiState.getNoteVelocity(noteNumber); + const auto replacedVelocity = resources.midiState.getNoteVelocityNormalized(noteNumber); for (auto& voice : voices) voice->registerNoteOff(delay, noteNumber, replacedVelocity); @@ -635,11 +635,11 @@ void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity) noexcept noteOffDispatch(delay, noteNumber, replacedVelocity); } -void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOff(noteNumber, velocity, randValue)) { + if (region->registerNoteOffNormalized(noteNumber, velocity, randValue)) { auto voice = findFreeVoice(); if (voice == nullptr) continue; @@ -649,11 +649,11 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, uint8_t velocity) no } } -void sfz::Synth::noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept { const auto randValue = randNoteDistribution(Random::randomGenerator); for (auto& region : noteActivationLists[noteNumber]) { - if (region->registerNoteOn(noteNumber, velocity, randValue)) { + if (region->registerNoteOnNormalized(noteNumber, velocity, randValue)) { for (auto& voice : voices) { if (voice->checkOffGroup(delay, region->group)) noteOffDispatch(delay, voice->getTriggerNumber(), voice->getTriggerValue()); @@ -672,9 +672,10 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept { ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber >= 0); + const auto normalizedCC = normalizeCC(ccValue); ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration }; - resources.midiState.ccEvent(delay, ccNumber, ccValue); + resources.midiState.ccEventNormalized(delay, ccNumber, normalizedCC); AtomicGuard callbackGuard { inCallback }; if (!canEnterCallback) @@ -686,7 +687,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept } for (auto& voice : voices) - voice->registerCC(delay, ccNumber, ccValue); + voice->registerCC(delay, ccNumber, normalizedCC); for (auto& region : ccActivationLists[ccNumber]) { if (region->registerCC(ccNumber, ccValue)) { @@ -694,7 +695,7 @@ void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept if (voice == nullptr) continue; - voice->startVoice(region, delay, ccNumber, ccValue, Voice::TriggerType::CC); + voice->startVoice(region, delay, ccNumber, normalizedCC, Voice::TriggerType::CC); } } } @@ -952,12 +953,12 @@ void sfz::Synth::resetAllControllers(int delay) noexcept for (auto& voice : voices) { voice->registerPitchWheel(delay, 0); for (unsigned cc = 0; cc < config::numCCs; ++cc) - voice->registerCC(delay, cc, 0); + voice->registerCC(delay, cc, 0.0f); } for (auto& region : regions) { for (unsigned cc = 0; cc < config::numCCs; ++cc) - region->registerCC(cc, 0); + region->registerCCNormalized(cc, 0.0f); } } diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index f42d67e3..b18a453b 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -463,8 +463,8 @@ private: fs::file_time_type checkModificationTime(); - void noteOnDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; - void noteOffDispatch(int delay, int noteNumber, uint8_t velocity) noexcept; + void noteOnDispatch(int delay, int noteNumber, float velocity) noexcept; + void noteOffDispatch(int delay, int noteNumber, float velocity) noexcept; // Opcode memory; these are used to build regions, as a new region // will integrate opcodes from the group, master and global block diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 71a06159..f0ffddfe 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -24,8 +24,10 @@ sfz::Voice::Voice(sfz::Resources& resources) waveOscillator.init(sampleRate); } -void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept +void sfz::Voice::startVoice(Region* region, int delay, int number, float value, sfz::Voice::TriggerType triggerType) noexcept { + ASSERT(value >= 0.0f && value <= 1.0f); + this->triggerType = triggerType; triggerNumber = number; triggerValue = value; @@ -71,22 +73,21 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value } speedRatio = static_cast(currentPromise->sampleRate / this->sampleRate); } - - pitchRatio = region->getBasePitchVariation(number, value); + pitchRatio = region->getBasePitchVariationNormalized(number, value); baseVolumedB = region->getBaseVolumedB(number); auto volumedB = baseVolumedB; if (region->volumeCC) - volumedB += normalizeCC(resources.midiState.getCCValue(region->volumeCC->cc)) * region->volumeCC->value; + volumedB += resources.midiState.getCCValueNormalized(region->volumeCC->cc) * region->volumeCC->value; volumeEnvelope.reset(db2mag(Default::volumeRange.clamp(volumedB))); baseGain = region->getBaseGain(); if (triggerType != TriggerType::CC) - baseGain *= region->getNoteGain(number, value); + baseGain *= region->getNoteGainNormalized(number, value); float gain { baseGain }; if (region->amplitudeCC) - gain += normalizeCC(resources.midiState.getCCValue(region->amplitudeCC->cc)) * normalizePercents(region->amplitudeCC->value); + gain += resources.midiState.getCCValueNormalized(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value); amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain)); float crossfadeGain { region->getCrossfadeGain() }; @@ -95,19 +96,19 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value basePan = normalizePercents(region->pan); auto pan = basePan; if (region->panCC) - pan += normalizeCC(resources.midiState.getCCValue(region->panCC->cc)) * normalizePercents(region->panCC->value); + pan += resources.midiState.getCCValueNormalized(region->panCC->cc) * normalizePercents(region->panCC->value); panEnvelope.reset(Default::symmetricNormalizedRange.clamp(pan)); basePosition = normalizePercents(region->position); auto position = basePosition; if (region->positionCC) - position += normalizeCC(resources.midiState.getCCValue(region->positionCC->cc)) * normalizePercents(region->positionCC->value); + position += resources.midiState.getCCValueNormalized(region->positionCC->cc) * normalizePercents(region->positionCC->value); positionEnvelope.reset(Default::symmetricNormalizedRange.clamp(position)); baseWidth = normalizePercents(region->width); auto width = baseWidth; if (region->widthCC) - width += normalizeCC(resources.midiState.getCCValue(region->widthCC->cc)) * normalizePercents(region->widthCC->value); + width += resources.midiState.getCCValueNormalized(region->widthCC->cc) * normalizePercents(region->widthCC->value); widthEnvelope.reset(Default::symmetricNormalizedRange.clamp(width)); pitchBendEnvelope.setFunction([region](float pitchValue){ @@ -123,13 +124,13 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value const unsigned numChannels = region->isStereo ? 2 : 1; for (auto& filter: region->filters) { - auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, value); + auto newFilter = resources.filterPool.getFilter(filter, numChannels, number, denormalize7Bits(value)); if (newFilter) filters.push_back(newFilter); } for (auto& eq: region->equalizers) { - auto newEQ = resources.eqPool.getEQ(eq, numChannels, value); + auto newEQ = resources.eqPool.getEQ(eq, numChannels, denormalize7Bits(value)); if (newEQ) equalizers.push_back(newEQ); } @@ -139,7 +140,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value initialDelay = delay + static_cast(region->getDelay() * sampleRate); baseFrequency = midiNoteFrequency(number); bendStepFactor = centsFactor(region->bendStep); - egEnvelope.reset(*region, resources.midiState, delay, value, sampleRate); + egEnvelope.reset(*region, resources.midiState, delay, denormalize7Bits(value), sampleRate); } bool sfz::Voice::isFree() const noexcept @@ -159,7 +160,7 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept } } -void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept +void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noexcept { UNUSED(velocity); if (region == nullptr) @@ -174,12 +175,12 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity) no if (region->loopMode == SfzLoopMode::one_shot) return; - if (!region->checkSustain || resources.midiState.getCCValue(config::sustainCC) < config::halfCCThreshold) + if (!region->checkSustain || resources.midiState.getCCValueNormalized(config::sustainCC) < config::halfCCThreshold) release(delay); } } -void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept +void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept { if (region == nullptr) return; @@ -200,27 +201,27 @@ void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept delay = max(delay, minEnvelopeDelay); if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) { - const float newGain { baseGain + normalizeCC(ccValue) * normalizePercents(region->amplitudeCC->value) }; + const float newGain { baseGain + ccValue * normalizePercents(region->amplitudeCC->value) }; amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain)); } if (region->volumeCC && ccNumber == region->volumeCC->cc) { - const float newVolumedB { baseVolumedB + normalizeCC(ccValue) * region->volumeCC->value }; + const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value }; volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB))); } if (region->panCC && ccNumber == region->panCC->cc) { - const float newPan { basePan + normalizeCC(ccValue) * normalizePercents(region->panCC->value) }; + const float newPan { basePan + ccValue * normalizePercents(region->panCC->value) }; panEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPan)); } if (region->positionCC && ccNumber == region->positionCC->cc) { - const float newPosition { basePosition + normalizeCC(ccValue) * normalizePercents(region->positionCC->value) }; + const float newPosition { basePosition + ccValue * normalizePercents(region->positionCC->value) }; positionEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newPosition)); } if (region->widthCC && ccNumber == region->widthCC->cc) { - const float newWidth { baseWidth + normalizeCC(ccValue) * normalizePercents(region->widthCC->value) }; + const float newWidth { baseWidth + ccValue * normalizePercents(region->widthCC->value) }; widthEnvelope.registerEvent(delay, Default::symmetricNormalizedRange.clamp(newWidth)); } @@ -552,7 +553,7 @@ int sfz::Voice::getTriggerNumber() const noexcept return triggerNumber; } -uint8_t sfz::Voice::getTriggerValue() const noexcept +float sfz::Voice::getTriggerValue() const noexcept { return triggerValue; } diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 291b1a38..1772bf45 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -81,7 +81,7 @@ public: * @param value * @param triggerType */ - void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept; + void startVoice(Region* region, int delay, int number, float value, TriggerType triggerType) noexcept; /** * @brief Register a note-off event; this may trigger a release. @@ -90,7 +90,7 @@ public: * @param noteNumber * @param velocity */ - void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept; + void registerNoteOff(int delay, int noteNumber, float velocity) noexcept; /** * @brief Register a CC event; this may trigger a release. If the voice is playing and its * region has CC modifiers, it will use this value to compute the CC envelope to apply to the @@ -100,7 +100,7 @@ public: * @param ccNumber * @param ccValue */ - void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept; + void registerCC(int delay, int ccNumber, float ccValue) noexcept; /** * @brief Register a pitch wheel event; for now this does nothing * @@ -163,9 +163,9 @@ public: /** * @brief Get the value that triggered the voice (note velocity or cc value) * - * @return uint8_t + * @return float */ - uint8_t getTriggerValue() const noexcept; + float getTriggerValue() const noexcept; /** * @brief Get the type of trigger * @@ -261,7 +261,7 @@ private: TriggerType triggerType; int triggerNumber; - uint8_t triggerValue; + float triggerValue; absl::optional triggerDelay; float speedRatio { 1.0 };