Use float internally in the voices

This commit is contained in:
Paul Fd 2020-03-26 00:12:30 +01:00 committed by Paul Ferrand
parent 601126db0d
commit e7686d233e
7 changed files with 47 additions and 44 deletions

View file

@ -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 };

View file

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

View file

@ -523,7 +523,7 @@ void multiplyAdd<float, true>(absl::Span<const float> gain, absl::Span<const flo
template <class T, bool SIMD = SIMDConfig::multiplyAdd>
void multiplyAdd(const T gain, absl::Span<const T> input, absl::Span<T> output) noexcept
{
ASSERT(input.size() <= output.size());
// ASSERT(input.size() <= output.size());
auto* in = input.begin();
auto* out = output.begin();
auto* sentinel = out + std::min(output.size(), input.size());

View file

@ -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);
}
}

View file

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

View file

@ -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<float>(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<uint8_t>(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<uint8_t>(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<int>(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<uint8_t>(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;
}

View file

@ -81,7 +81,7 @@ public:
* @param value
* @param triggerType
*/
void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept;
void startVoice(Region* region, int delay, int number, float value, TriggerType triggerType) noexcept;
/**
* @brief Register a note-off event; this may trigger a release.
@ -90,7 +90,7 @@ public:
* @param noteNumber
* @param velocity
*/
void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
void registerNoteOff(int delay, int noteNumber, float velocity) noexcept;
/**
* @brief Register a CC event; this may trigger a release. If the voice is playing and its
* region has CC modifiers, it will use this value to compute the CC envelope to apply to the
@ -100,7 +100,7 @@ public:
* @param ccNumber
* @param ccValue
*/
void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept;
void registerCC(int delay, int ccNumber, float ccValue) noexcept;
/**
* @brief Register a pitch wheel event; for now this does nothing
*
@ -163,9 +163,9 @@ public:
/**
* @brief Get the value that triggered the voice (note velocity or cc value)
*
* @return uint8_t
* @return float
*/
uint8_t getTriggerValue() const noexcept;
float getTriggerValue() const noexcept;
/**
* @brief Get the type of trigger
*
@ -261,7 +261,7 @@ private:
TriggerType triggerType;
int triggerNumber;
uint8_t triggerValue;
float triggerValue;
absl::optional<int> triggerDelay;
float speedRatio { 1.0 };