Better handling of the source position. The integer part and the floating point part are separated, the solution before lost precision in the interpolation for longer samples.

This commit is contained in:
paulfd 2019-09-15 23:03:40 +02:00
parent 86b59b1b59
commit e6ffec5885
2 changed files with 55 additions and 36 deletions

View file

@ -94,8 +94,8 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
widthEnvelope.reset(width);
// DBG("Base width: " << baseWidth << " - with modifier: " << width);
floatPosition = static_cast<float>(region->getOffset());
// DBG("Offset: " << floatPosition);
sourcePosition = region->getOffset();
// DBG("Offset: " << sourcePosition);
initialDelay = delay + static_cast<uint32_t>(region->getDelay() / sampleRate);
baseFrequency = midiNoteFrequency(number) * pitchRatio;
prepareEGEnvelope(delay, value);
@ -177,17 +177,17 @@ void sfz::Voice::registerCC(int delay, int channel [[maybe_unused]], int ccNumbe
}
if (region->panCC && ccNumber == region->panCC->first) {
const float newPan { basePan + normalizeCC(ccValue) * normalizeNegativePercents(region->panCC->second)};
const float newPan { basePan + normalizeCC(ccValue) * normalizeNegativePercents(region->panCC->second) };
panEnvelope.registerEvent(delay, newPan);
}
if (region->positionCC && ccNumber == region->positionCC->first) {
const float newPosition { basePosition + normalizeCC(ccValue) * normalizeNegativePercents(region->positionCC->second)};
const float newPosition { basePosition + normalizeCC(ccValue) * normalizeNegativePercents(region->positionCC->second) };
positionEnvelope.registerEvent(delay, newPosition);
}
if (region->widthCC && ccNumber == region->widthCC->first) {
const float newWidth { baseWidth + normalizeCC(ccValue) * normalizeNegativePercents(region->widthCC->second)};
const float newWidth { baseWidth + normalizeCC(ccValue) * normalizeNegativePercents(region->widthCC->second) };
widthEnvelope.registerEvent(delay, newWidth);
}
}
@ -244,10 +244,10 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
processStereo(buffer);
else
processMono(buffer);
if (!egEnvelope.isSmoothing())
reset();
powerHistory.push(buffer.meanSquared());
}
@ -256,7 +256,7 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
const auto numSamples = buffer.getNumFrames();
auto leftBuffer = buffer.getSpan(0);
auto rightBuffer = buffer.getSpan(1);
auto span1 = tempSpan1.first(numSamples);
auto span2 = tempSpan2.first(numSamples);
@ -299,7 +299,7 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
// Amplitude envelope
amplitudeEnvelope.getBlock(span1);
buffer.applyGain(span1);
// AmpEG envelope
egEnvelope.getBlock(span1);
buffer.applyGain(span1);
@ -356,33 +356,41 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
auto rightCoeffs = tempSpan2.first(buffer.getNumFrames());
::fill<float>(jumps, pitchRatio * speedRatio);
jumps[0] += floatPositionOffset;
::cumsum<float>(jumps, jumps);
::sfzInterpolationCast<float>(jumps, indices, leftCoeffs, rightCoeffs);
::add<int>(sourcePosition, indices);
if (region->shouldLoop() && region->trueSampleEnd() <= source.getNumFrames()) {
floatPosition = ::loopingSFZIndex<float, false>(
jumps,
leftCoeffs,
rightCoeffs,
indices,
floatPosition,
region->trueSampleEnd() - 1,
region->loopRange.getStart());
//FIXME : all this casting is driving me crazy
const auto sampleEnd = static_cast<int>(region->trueSampleEnd()) - 1;
if (region->shouldLoop() && static_cast<size_t>(sampleEnd) <= source.getNumFrames()) {
const auto offset = sampleEnd - static_cast<int>(region->loopRange.getStart());
for (auto* index = indices.begin(); index < indices.end(); ++index) {
if (*index > sampleEnd) {
const auto remainingElements = static_cast<size_t>(std::distance(index, indices.end()));
::subtract<int>(offset, { index, remainingElements });
}
}
} else {
floatPosition = ::saturatingSFZIndex<float, false>(
jumps,
leftCoeffs,
rightCoeffs,
indices,
floatPosition,
source.getNumFrames() - 1);
for (auto* index = indices.begin(); index < indices.end(); ++index) {
if (*index > sampleEnd) {
const auto remainingElements = static_cast<size_t>(std::distance(index, indices.end()));
::fill<int>(indices.last(remainingElements), sampleEnd);
::fill<float>(leftCoeffs.last(remainingElements), 0.0f);
::fill<float>(rightCoeffs.last(remainingElements), 1.0f);
break;
}
}
}
auto ind = indices.data();
auto leftCoeff = leftCoeffs.data();
auto rightCoeff = rightCoeffs.data();
auto left = buffer.getChannel(0);
auto leftSource = source.getChannel(0);
if (source.getNumChannels() == 1) {
while (ind < indices.end()) {
*left = source.getChannel(0)[*ind] * (*leftCoeff) + source.getChannel(0)[*ind + 1] * (*rightCoeff);
*left = leftSource[*ind] * (*leftCoeff) + leftSource[*ind + 1] * (*rightCoeff);
left++;
ind++;
leftCoeff++;
@ -390,9 +398,10 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
}
} else {
auto right = buffer.getChannel(1);
auto rightSource = source.getChannel(1);
while (ind < indices.end()) {
*left = source.getChannel(0)[*ind] * (*leftCoeff) + source.getChannel(0)[*ind + 1] * (*rightCoeff);
*right = source.getChannel(1)[*ind] * (*leftCoeff) + source.getChannel(1)[*ind + 1] * (*rightCoeff);
*left = leftSource[*ind] * (*leftCoeff) + leftSource[*ind + 1] * (*rightCoeff);
*right = rightSource[*ind] * (*leftCoeff) + rightSource[*ind + 1] * (*rightCoeff);
left++;
right++;
ind++;
@ -401,10 +410,14 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
}
}
if (!region->shouldLoop() && (floatPosition + 1.01) > source.getNumFrames()) {
sourcePosition = indices.back();
floatPositionOffset = rightCoeffs.back();
if (state != State::release && !region->shouldLoop() && sourcePosition == sampleEnd) {
DBG("Releasing " << region->sample);
auto last = std::distance(indices.begin(), absl::c_find(indices, region->trueSampleEnd() - 1));
auto last = std::distance(indices.begin(), absl::c_find(indices, sampleEnd));
release(last);
buffer.subspan(last).fill(0.0f);
}
}
@ -418,6 +431,10 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
::sin<float>(tempSpan1.first(buffer.getNumFrames()), buffer.getSpan(0));
::copy<float>(buffer.getSpan(0), buffer.getSpan(1));
// Wrap the phase so we don't loose too much precision on longer notes
const auto numTwoPiWraps = static_cast<int>(phase / twoPi<float>);
phase -= twoPi<float> * static_cast<float>(numTwoPiWraps);
}
bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept
@ -458,7 +475,8 @@ void sfz::Voice::reset() noexcept
if (region != nullptr) {
DBG("Reset voice with sample " << region->sample);
}
floatPosition = 0.0f;
sourcePosition = 0;
floatPositionOffset = 0.0f;
region = nullptr;
noteIsOff = false;
}
@ -484,7 +502,7 @@ bool sfz::Voice::canBeStolen() const noexcept
return state == State::release;
}
float sfz::Voice::getSourcePosition() const noexcept
uint32_t sfz::Voice::getSourcePosition() const noexcept
{
return floatPosition;
return sourcePosition;
}

View file

@ -70,7 +70,7 @@ public:
void garbageCollect() noexcept;
float getMeanSquaredAverage() const noexcept;
float getSourcePosition() const noexcept;
uint32_t getSourcePosition() const noexcept;
private:
void fillWithData(AudioSpan<float> buffer) noexcept;
void fillWithGenerator(AudioSpan<float> buffer) noexcept;
@ -103,8 +103,9 @@ private:
float baseFrequency { 440.0 };
float phase { 0.0f };
float floatPosition { 0.0f };
uint32_t initialDelay { 0 };
float floatPositionOffset { 0.0f };
int sourcePosition { 0 };
int initialDelay { 0 };
std::atomic<bool> dataReady { false };
std::unique_ptr<AudioBuffer<float>> fileData { nullptr };