Use float internally in the voices
This commit is contained in:
parent
601126db0d
commit
e7686d233e
7 changed files with 47 additions and 44 deletions
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue