Avoid shared pointers and get a span "pointer" directly from the buffer pool

This commit is contained in:
Paul Ferrand 2020-03-30 15:14:33 +02:00
parent e54c27b1f2
commit 9c17201c63
5 changed files with 188 additions and 195 deletions

View file

@ -11,141 +11,122 @@
#include "AudioBuffer.h" #include "AudioBuffer.h"
#include <array> #include <array>
#include <memory> #include <memory>
#ifndef NDEBUG #include <functional>
#include "absl/algorithm/container.h" #include "absl/algorithm/container.h"
#ifndef NDEBUG
#include "MathHelpers.h" #include "MathHelpers.h"
#endif #endif
namespace sfz namespace sfz
{ {
template<class T>
class SpanHolder
{
public:
SpanHolder() {}
SpanHolder(const SpanHolder<T>&) = delete;
SpanHolder<T>& operator=(const SpanHolder<T>&) = delete;
SpanHolder(SpanHolder<T>&&) = delete;
SpanHolder<T>& operator=(SpanHolder<T>&&) = delete;
SpanHolder(T value, int* available)
: value(std::move(value)), available(available) {}
T& operator*() { return value; }
T* operator->() { return &value; }
operator bool() const { return available != nullptr; }
~SpanHolder()
{
if (available)
*available += 1;
}
private:
T value {};
int* available { nullptr };
};
class BufferPool class BufferPool
{ {
public: public:
BufferPool() BufferPool()
{ {
for (auto& buffer : buffers) {
buffer = std::make_shared<sfz::Buffer<float>>(config::defaultSamplesPerBlock);
}
for (auto& buffer : indexBuffers) {
buffer = std::make_shared<sfz::Buffer<int>>(config::defaultSamplesPerBlock);
}
for (auto& buffer : stereoBuffers) { for (auto& buffer : stereoBuffers) {
buffer = std::make_shared<sfz::AudioBuffer<float>>(2, config::defaultSamplesPerBlock); buffer.addChannels(2);
} }
monoAvailable.resize(config::bufferPoolSize);
stereoAvailable.resize(config::stereoBufferPoolSize);
indexAvailable.resize(config::indexBufferPoolSize);
_setBufferSize(config::defaultSamplesPerBlock);
} }
void setBufferSize(unsigned bufferSize) void setBufferSize(unsigned bufferSize)
{ {
for (auto& buffer: buffers) { ASSERT(absl::c_all_of(monoAvailable, [](int value) { return value == 1; }));
// Trying to resize a buffer in use ASSERT(absl::c_all_of(indexAvailable, [](int value) { return value == 1; }));
ASSERT(buffer.use_count() == 1); ASSERT(absl::c_all_of(stereoAvailable, [](int value) { return value == 1; }));
buffer->resize(bufferSize); _setBufferSize(bufferSize);
} }
for (auto& buffer: indexBuffers) { SpanHolder<absl::Span<float>> getBuffer(size_t numFrames)
// Trying to resize a buffer in use
ASSERT(buffer.use_count() == 1);
buffer->resize(bufferSize);
}
for (auto& buffer: stereoBuffers) {
// Trying to resize a buffer in use
ASSERT(buffer.use_count() == 1);
buffer->resize(bufferSize);
}
}
std::shared_ptr<sfz::Buffer<float>> getBuffer(size_t numFrames) const
{ {
auto bufferIt = buffers.begin(); const auto availableIt = absl::c_find(monoAvailable, 1);
if (availableIt == monoAvailable.end()) {
if (buffers.empty()) { DBG("[sfizz] No free buffers available...");
DBG("[sfizz] No available buffers in the pool");
return {}; return {};
} }
const auto freeIndex = std::distance(monoAvailable.begin(), availableIt);
if (buffers[0]->size() < numFrames) { if (monoBuffers[freeIndex].size() < numFrames) {
DBG("[sfizz] Someone asked for a buffer of size " << numFrames << "; only " << buffers[0]->size() << " available..."); DBG("[sfizz] Someone asked for a buffer of size " << numFrames << "; only " << monoBuffers[freeIndex].size() << " available...");
return {}; return {};
} }
#ifndef NDEBUG #ifndef NDEBUG
maxBuffersUsed = max<int>(1 + absl::c_count_if(buffers, [&](const std::shared_ptr<sfz::Buffer<float>>& buffer) { maxBuffersUsed = 1 + absl::c_count_if(monoAvailable, [](int value) { return value == 0; });
return (buffer.use_count() > 1);
}), maxBuffersUsed);
#endif #endif
*availableIt -= 1;
while (bufferIt < buffers.end()) { return { absl::MakeSpan(monoBuffers[freeIndex]).first(numFrames), &*availableIt };
if (bufferIt->use_count() == 1)
return *bufferIt;
++bufferIt;
} }
// No buffer found; debug message SpanHolder<absl::Span<int>> getIndexBuffer(size_t numFrames)
DBG("[sfizz] No free buffer available!");
return {};
}
std::shared_ptr<sfz::Buffer<int>> getIndexBuffer(size_t numFrames) const
{ {
auto bufferIt = indexBuffers.begin(); const auto availableIt = absl::c_find(indexAvailable, 1);
if (availableIt == indexAvailable.end()) {
if (indexBuffers.empty()) {
DBG("[sfizz] No available index buffers in the pool"); DBG("[sfizz] No available index buffers in the pool");
return {}; return {};
} }
const auto freeIndex = std::distance(indexAvailable.begin(), availableIt);
if (indexBuffers[0]->size() < numFrames) { if (indexBuffers[freeIndex].size() < numFrames) {
DBG("[sfizz] Someone asked for a index buffer of size " << numFrames << "; only " << indexBuffers[0]->size() << " available..."); DBG("[sfizz] Someone asked for a index buffer of size " << numFrames << "; only " << indexBuffers[freeIndex].size() << " available...");
return {}; return {};
} }
#ifndef NDEBUG #ifndef NDEBUG
maxIndexBuffersUsed = max<int>(1 + absl::c_count_if(indexBuffers, [&](const std::shared_ptr<sfz::Buffer<int>>& buffer) { maxIndexBuffersUsed = 1 + absl::c_count_if(indexAvailable, [](int value) { return value == 0; });
return (buffer.use_count() > 1);
}), maxIndexBuffersUsed);
#endif #endif
*availableIt -= 1;
while (bufferIt < indexBuffers.end()) { return { absl::MakeSpan(indexBuffers[freeIndex]).first(numFrames), &*availableIt };
if (bufferIt->use_count() == 1)
return *bufferIt;
++bufferIt;
} }
// No buffer found; debug message SpanHolder<AudioSpan<float>> getStereoBuffer(size_t numFrames)
DBG("[sfizz] No free index buffer available!");
return {};
}
std::shared_ptr<sfz::AudioBuffer<float>> getStereoBuffer(size_t numFrames) const
{ {
if (stereoBuffers.empty()) { const auto availableIt = absl::c_find(stereoAvailable, 1);
if (availableIt == stereoAvailable.end()) {
DBG("[sfizz] No available stereo buffers in the pool"); DBG("[sfizz] No available stereo buffers in the pool");
return {}; return {};
} }
const auto freeIndex = std::distance(stereoAvailable.begin(), availableIt);
if (stereoBuffers[0]->getNumFrames() < numFrames) { if (stereoBuffers[freeIndex].getNumFrames() < numFrames) {
DBG("[sfizz] Someone asked for a stereo buffer of size " << numFrames << "; only " << stereoBuffers[0]->getNumFrames() << " available..."); DBG("[sfizz] Someone asked for a stereo buffer of size " << numFrames << "; only " << stereoBuffers[freeIndex].getNumFrames() << " available...");
return {}; return {};
} }
#ifndef NDEBUG #ifndef NDEBUG
maxStereoBuffersUsed = max<int>(1 + absl::c_count_if(stereoBuffers, [&](const std::shared_ptr<sfz::AudioBuffer<float>>& buffer) { maxStereoBuffersUsed = 1 + absl::c_count_if(stereoAvailable, [](int value) { return value == 0; });
return (buffer.use_count() > 1);
}), maxStereoBuffersUsed);
#endif #endif
auto bufferIt = stereoBuffers.begin(); *availableIt -= 1;
while (bufferIt < stereoBuffers.end()) { return { sfz::AudioSpan<float>(stereoBuffers[freeIndex]).first(numFrames), &*availableIt };
if (bufferIt->use_count() == 1)
return *bufferIt;
++bufferIt;
}
// No buffer found; debug message
DBG("[sfizz] No free stereo buffer available!");
return {};
} }
#ifndef NDEBUG #ifndef NDEBUG
@ -156,10 +137,34 @@ public:
DBG("Max stereo buffers used: " << maxStereoBuffersUsed); DBG("Max stereo buffers used: " << maxStereoBuffersUsed);
} }
#endif #endif
private: private:
std::array<std::shared_ptr<sfz::Buffer<float>>, config::bufferPoolSize> buffers; void _setBufferSize(unsigned bufferSize)
std::array<std::shared_ptr<sfz::Buffer<int>>, config::bufferPoolSize> indexBuffers; {
std::array<std::shared_ptr<sfz::AudioBuffer<float>>, config::stereoBufferPoolSize> stereoBuffers; for (auto& buffer : monoBuffers) {
buffer.resize(bufferSize);
}
for (auto& buffer : indexBuffers) {
buffer.resize(bufferSize);
}
for (auto& buffer : stereoBuffers) {
buffer.resize(bufferSize);
}
absl::c_fill(monoAvailable, 1);
absl::c_fill(stereoAvailable, 1);
absl::c_fill(indexAvailable, 1);
}
std::array<sfz::Buffer<float>, config::bufferPoolSize> monoBuffers;
std::vector<int> monoAvailable;
std::array<sfz::Buffer<int>, config::bufferPoolSize> indexBuffers;
std::vector<int> indexAvailable;
std::array<sfz::AudioBuffer<float>, config::stereoBufferPoolSize> stereoBuffers;
std::vector<int> stereoAvailable;
#ifndef NDEBUG #ifndef NDEBUG
mutable int maxBuffersUsed { 0 }; mutable int maxBuffersUsed { 0 };
mutable int maxIndexBuffersUsed { 0 }; mutable int maxIndexBuffersUsed { 0 };

View file

@ -28,7 +28,7 @@ namespace config {
constexpr float defaultSampleRate { 48000 }; constexpr float defaultSampleRate { 48000 };
constexpr int defaultSamplesPerBlock { 1024 }; constexpr int defaultSamplesPerBlock { 1024 };
constexpr int maxBlockSize { 8192 }; constexpr int maxBlockSize { 8192 };
constexpr int bufferPoolSize { 8 }; constexpr int bufferPoolSize { 4 };
constexpr int stereoBufferPoolSize { 4 }; constexpr int stereoBufferPoolSize { 4 };
constexpr int indexBufferPoolSize { 2 }; constexpr int indexBufferPoolSize { 2 };
constexpr int preloadSize { 8192 }; constexpr int preloadSize { 8192 };

View file

@ -176,8 +176,6 @@ public:
const EventVector& getEvents(int ccIdx) const noexcept; const EventVector& getEvents(int ccIdx) const noexcept;
private:
template<class T, class F> template<class T, class F>
void linearEnvelope(T&& modifier, absl::Span<float> envelope, F&& lambda) const void linearEnvelope(T&& modifier, absl::Span<float> envelope, F&& lambda) const
{ {
@ -196,6 +194,10 @@ private:
} }
fill<float>(envelope.subspan(lastDelay), lastValue); fill<float>(envelope.subspan(lastDelay), lastValue);
} }
private:
int activeNotes { 0 }; int activeNotes { 0 };
/** /**

View file

@ -548,16 +548,12 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
return; return;
size_t numFrames = buffer.getNumFrames(); size_t numFrames = buffer.getNumFrames();
auto tempBuffer = resources.bufferPool.getStereoBuffer(numFrames); auto tempSpan = resources.bufferPool.getStereoBuffer(numFrames);
auto tempMixNodeBuffer = resources.bufferPool.getStereoBuffer(numFrames); auto tempMixSpan = resources.bufferPool.getStereoBuffer(numFrames);
if (!tempBuffer || !tempMixNodeBuffer) { if (!tempSpan || !tempMixSpan) {
DBG("[sfizz] Could not get a temporary buffer; exiting callback... "); DBG("[sfizz] Could not get a temporary buffer; exiting callback... ");
return; return;
} }
auto temp = AudioSpan<float>(*tempBuffer).first(numFrames);
auto tempMixNode = AudioSpan<float>(*tempMixNodeBuffer).first(numFrames);
CallbackBreakdown callbackBreakdown; CallbackBreakdown callbackBreakdown;
{ // Prepare the effect inputs. They are mixes of per-region outputs. { // Prepare the effect inputs. They are mixes of per-region outputs.
@ -572,7 +568,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{ // Main render block { // Main render block
ScopedTiming logger { callbackBreakdown.renderMethod }; ScopedTiming logger { callbackBreakdown.renderMethod };
buffer.fill(0.0f); buffer.fill(0.0f);
tempMixNode.fill(0.0f); tempSpan->fill(0.0f);
resources.filePool.cleanupPromises(); resources.filePool.cleanupPromises();
for (auto& voice : voices) { for (auto& voice : voices) {
@ -582,14 +578,14 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
const Region* region = voice->getRegion(); const Region* region = voice->getRegion();
numActiveVoices++; numActiveVoices++;
voice->renderBlock(temp); voice->renderBlock(*tempSpan);
{ // Add the output into the effects linked to this region { // Add the output into the effects linked to this region
ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration };
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) { for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
if (auto& bus = effectBuses[i]) { if (auto& bus = effectBuses[i]) {
float addGain = region->getGainToEffectBus(i); float addGain = region->getGainToEffectBus(i);
bus->addToInputs(temp, addGain, numFrames); bus->addToInputs(*tempSpan, addGain, numFrames);
} }
} }
} }
@ -609,7 +605,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
for (auto& bus : effectBuses) { for (auto& bus : effectBuses) {
if (bus) { if (bus) {
bus->process(numFrames); bus->process(numFrames);
bus->mixOutputsTo(buffer, tempMixNode, numFrames); bus->mixOutputsTo(buffer, *tempMixSpan, numFrames);
} }
} }
} }
@ -618,7 +614,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
// -- note(jpc) the purpose of the Mix output is not known. // -- note(jpc) the purpose of the Mix output is not known.
// perhaps it's designed as extension point for custom processing? // perhaps it's designed as extension point for custom processing?
// as default behavior, it adds itself to the Main signal. // as default behavior, it adds itself to the Main signal.
buffer.add(tempMixNode); buffer.add(*tempMixSpan);
// Apply the master volume // Apply the master volume
buffer.applyGain(db2mag(volume)); buffer.applyGain(db2mag(volume));

View file

@ -258,12 +258,11 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
auto leftBuffer = buffer.getSpan(0); auto leftBuffer = buffer.getSpan(0);
auto rightBuffer = buffer.getSpan(1); auto rightBuffer = buffer.getSpan(1);
auto modulationBuffer = resources.bufferPool.getBuffer(numSamples); auto modulationSpan = resources.bufferPool.getBuffer(numSamples);
auto tempBuffer = resources.bufferPool.getBuffer(numSamples); auto tempSpan = resources.bufferPool.getBuffer(numSamples);
if (!modulationBuffer || !tempBuffer) if (!modulationSpan || !tempSpan)
return; return;
auto modulationSpan = absl::MakeSpan(*modulationBuffer).first(numSamples);
auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples);
using namespace std::placeholders; using namespace std::placeholders;
const auto xfinBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve); const auto xfinBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve);
const auto xfoutBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve); const auto xfoutBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve);
@ -272,26 +271,26 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
ScopedTiming logger { amplitudeDuration }; ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
fill<float>(modulationSpan, baseGain); fill<float>(*modulationSpan, baseGain);
resources.midiState.multiplicativeModifiers(region->amplitudeCC, modulationSpan, tempSpan); resources.midiState.multiplicativeModifiers(region->amplitudeCC, *modulationSpan, *tempSpan);
DBG("Final gain: " << modulationSpan.back()); DBG("Final gain: " << modulationSpan->back());
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// Crossfade envelopes // Crossfade envelopes
// crossfadeEnvelope.getBlock(modulationSpan); // crossfadeEnvelope.getBlock(modulationSpan);
fill<float>(modulationSpan, 1.0f); fill<float>(*modulationSpan, 1.0f);
resources.midiState.multiplicativeModifiers(region->crossfadeCCInRange, modulationSpan, tempSpan, xfinBind); resources.midiState.multiplicativeModifiers(region->crossfadeCCInRange, *modulationSpan, *tempSpan, xfinBind);
resources.midiState.multiplicativeModifiers(region->crossfadeCCOutRange, modulationSpan, tempSpan, xfoutBind); resources.midiState.multiplicativeModifiers(region->crossfadeCCOutRange, *modulationSpan, *tempSpan, xfoutBind);
DBG("XF: " << modulationSpan.back()); DBG("XF: " << modulationSpan->back());
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// Volume envelope // Volume envelope
volumeEnvelope.getBlock(modulationSpan); volumeEnvelope.getBlock(*modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(*modulationSpan, leftBuffer);
// AmpEG envelope // AmpEG envelope
egEnvelope.getBlock(modulationSpan); egEnvelope.getBlock(*modulationSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(*modulationSpan, leftBuffer);
} }
{ // Filtering and EQ { // Filtering and EQ
@ -315,10 +314,10 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
copy<float>(leftBuffer, rightBuffer); copy<float>(leftBuffer, rightBuffer);
// Apply panning // Apply panning
fill<float>(modulationSpan, region->pan); fill<float>(*modulationSpan, region->pan);
resources.midiState.additiveModifiers(region->panCC, modulationSpan, tempSpan); resources.midiState.additiveModifiers(region->panCC, *modulationSpan, *tempSpan);
DBG("Pan: " << modulationSpan.back()); DBG("Pan: " << modulationSpan->back());
pan<float>(modulationSpan, leftBuffer, rightBuffer); pan<float>(*modulationSpan, leftBuffer, rightBuffer);
} }
} }
@ -328,12 +327,10 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
auto leftBuffer = buffer.getSpan(0); auto leftBuffer = buffer.getSpan(0);
auto rightBuffer = buffer.getSpan(1); auto rightBuffer = buffer.getSpan(1);
auto modulationBuffer = resources.bufferPool.getBuffer(numSamples); auto modulationSpan = resources.bufferPool.getBuffer(numSamples);
auto tempBuffer = resources.bufferPool.getBuffer(numSamples); auto tempSpan = resources.bufferPool.getBuffer(numSamples);
if (!modulationBuffer || !tempBuffer) if (!modulationSpan || !tempSpan)
return; return;
auto modulationSpan = absl::MakeSpan(*modulationBuffer).first(numSamples);
auto tempSpan = absl::MakeSpan(*tempBuffer).first(numSamples);
using namespace std::placeholders; using namespace std::placeholders;
const auto xfinBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve); const auto xfinBind = std::bind(crossfadeIn<float, float>, _1, _2, region->crossfadeCCCurve);
@ -343,45 +340,44 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
ScopedTiming logger { amplitudeDuration }; ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
fill<float>(modulationSpan, baseGain); fill<float>(*modulationSpan, baseGain);
resources.midiState.multiplicativeModifiers(region->amplitudeCC, modulationSpan, tempSpan); resources.midiState.multiplicativeModifiers(region->amplitudeCC, *modulationSpan, *tempSpan);
DBG("Final gain: " << modulationSpan.back()); buffer.applyGain(*modulationSpan);
buffer.applyGain(modulationSpan);
// Crossfade envelopes // Crossfade envelopes
fill<float>(modulationSpan, 1.0f); fill<float>(*modulationSpan, 1.0f);
resources.midiState.multiplicativeModifiers(region->crossfadeCCInRange, modulationSpan, tempSpan, xfinBind); resources.midiState.multiplicativeModifiers(region->crossfadeCCInRange, *modulationSpan, *tempSpan, xfinBind);
resources.midiState.multiplicativeModifiers(region->crossfadeCCOutRange, modulationSpan, tempSpan, xfoutBind); resources.midiState.multiplicativeModifiers(region->crossfadeCCOutRange, *modulationSpan, *tempSpan, xfoutBind);
buffer.applyGain(modulationSpan); buffer.applyGain(*modulationSpan);
// Volume envelope // Volume envelope
volumeEnvelope.getBlock(modulationSpan); volumeEnvelope.getBlock(*modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(*modulationSpan);
// AmpEG envelope // AmpEG envelope
egEnvelope.getBlock(modulationSpan); egEnvelope.getBlock(*modulationSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(*modulationSpan);
} }
{ // Panning and stereo processing { // Panning and stereo processing
ScopedTiming logger { panningDuration }; ScopedTiming logger { panningDuration };
// Apply panning // Apply panning
// panningModulation(modulationSpan); // panningModulation(*modulationSpan);
fill<float>(modulationSpan, region->pan); fill<float>(*modulationSpan, region->pan);
resources.midiState.additiveModifiers(region->panCC, modulationSpan, tempSpan); resources.midiState.additiveModifiers(region->panCC, *modulationSpan, *tempSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer); pan<float>(*modulationSpan, leftBuffer, rightBuffer);
// Apply the width/position process // Apply the width/position process
// widthModulation(modulationSpan); // widthModulation(*modulationSpan);
fill<float>(modulationSpan, region->width); fill<float>(*modulationSpan, region->width);
resources.midiState.additiveModifiers(region->widthCC, modulationSpan, tempSpan); resources.midiState.additiveModifiers(region->widthCC, *modulationSpan, *tempSpan);
width<float>(modulationSpan, leftBuffer, rightBuffer); width<float>(*modulationSpan, leftBuffer, rightBuffer);
// positionModulation(modulationSpan); // positionModulation(*modulationSpan);
fill<float>(modulationSpan, region->position); fill<float>(*modulationSpan, region->position);
resources.midiState.additiveModifiers(region->positionCC, modulationSpan, tempSpan); resources.midiState.additiveModifiers(region->positionCC, *modulationSpan, *tempSpan);
pan<float>(modulationSpan, leftBuffer, rightBuffer); pan<float>(*modulationSpan, leftBuffer, rightBuffer);
} }
{ // Filtering and EQ { // Filtering and EQ
@ -412,37 +408,33 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
} }
auto source = currentPromise->getData(); auto source = currentPromise->getData();
auto jumpBuffer = resources.bufferPool.getBuffer(numSamples);
auto bendBuffer = resources.bufferPool.getBuffer(numSamples); auto jumps = resources.bufferPool.getBuffer(numSamples);
auto leftCoeffBuffer = resources.bufferPool.getBuffer(numSamples); auto bends = resources.bufferPool.getBuffer(numSamples);
auto rightCoeffBuffer = resources.bufferPool.getBuffer(numSamples); auto leftCoeffs = resources.bufferPool.getBuffer(numSamples);
auto indexBuffer = resources.bufferPool.getIndexBuffer(numSamples); auto rightCoeffs = resources.bufferPool.getBuffer(numSamples);
if (!jumpBuffer || !bendBuffer || !indexBuffer || !rightCoeffBuffer || !leftCoeffBuffer) auto indices = resources.bufferPool.getIndexBuffer(numSamples);
if (!jumps || !bends || !indices || !rightCoeffs || !leftCoeffs)
return; return;
auto jumps = absl::MakeSpan(*jumpBuffer).first(numSamples);
auto bends = absl::MakeSpan(*bendBuffer).first(numSamples);
auto indices = absl::MakeSpan(*indexBuffer).first(numSamples);
auto leftCoeffs = absl::MakeSpan(*leftCoeffBuffer).first(numSamples);
auto rightCoeffs = absl::MakeSpan(*rightCoeffBuffer).first(numSamples);
fill<float>(jumps, pitchRatio * speedRatio); fill<float>(*jumps, pitchRatio * speedRatio);
if (region->bendStep > 1) if (region->bendStep > 1)
pitchBendEnvelope.getQuantizedBlock(bends, bendStepFactor); pitchBendEnvelope.getQuantizedBlock(*bends, bendStepFactor);
else else
pitchBendEnvelope.getBlock(bends); pitchBendEnvelope.getBlock(*bends);
applyGain<float>(bends, jumps); applyGain<float>(*bends, *jumps);
jumps[0] += floatPositionOffset; jumps->front() += floatPositionOffset;
cumsum<float>(jumps, jumps); cumsum<float>(*jumps, *jumps);
sfzInterpolationCast<float>(jumps, indices, leftCoeffs, rightCoeffs); sfzInterpolationCast<float>(*jumps, *indices, *leftCoeffs, *rightCoeffs);
add<int>(sourcePosition, indices); add<int>(sourcePosition, *indices);
if (region->shouldLoop() && region->loopEnd(currentPromise->oversamplingFactor) <= source.getNumFrames()) { if (region->shouldLoop() && region->loopEnd(currentPromise->oversamplingFactor) <= source.getNumFrames()) {
const auto loopEnd = static_cast<int>(region->loopEnd(currentPromise->oversamplingFactor)); const auto loopEnd = static_cast<int>(region->loopEnd(currentPromise->oversamplingFactor));
const auto offset = loopEnd - static_cast<int>(region->loopStart(currentPromise->oversamplingFactor)) + 1; const auto offset = loopEnd - static_cast<int>(region->loopStart(currentPromise->oversamplingFactor)) + 1;
for (auto* index = indices.begin(); index < indices.end(); ++index) { for (auto* index = indices->begin(); index < indices->end(); ++index) {
if (*index > loopEnd) { if (*index > loopEnd) {
const auto remainingElements = static_cast<size_t>(std::distance(index, indices.end())); const auto remainingElements = static_cast<size_t>(std::distance(index, indices->end()));
subtract<int>(offset, { index, remainingElements }); subtract<int>(offset, { index, remainingElements });
} }
} }
@ -451,46 +443,46 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
static_cast<int>(region->trueSampleEnd(currentPromise->oversamplingFactor)), static_cast<int>(region->trueSampleEnd(currentPromise->oversamplingFactor)),
static_cast<int>(source.getNumFrames()) static_cast<int>(source.getNumFrames())
) - 2; ) - 2;
for (auto* index = indices.begin(); index < indices.end(); ++index) { for (auto* index = indices->begin(); index < indices->end(); ++index) {
if (*index >= sampleEnd) { if (*index >= sampleEnd) {
release(static_cast<int>(std::distance(indices.begin(), index))); release(static_cast<int>(std::distance(indices->begin(), index)));
const auto remainingElements = static_cast<size_t>(std::distance(index, indices.end())); const auto remainingElements = static_cast<size_t>(std::distance(index, indices->end()));
if (source.getNumFrames() - 1 < region->trueSampleEnd(currentPromise->oversamplingFactor)) { if (source.getNumFrames() - 1 < region->trueSampleEnd(currentPromise->oversamplingFactor)) {
DBG("[sfizz] Underflow: source available samples " DBG("[sfizz] Underflow: source available samples "
<< source.getNumFrames() << "/" << source.getNumFrames() << "/"
<< region->trueSampleEnd(currentPromise->oversamplingFactor) << region->trueSampleEnd(currentPromise->oversamplingFactor)
<< " for sample " << region->sample); << " for sample " << region->sample);
} }
fill<int>(indices.last(remainingElements), sampleEnd); fill<int>(indices->last(remainingElements), sampleEnd);
fill<float>(leftCoeffs.last(remainingElements), 0.0f); fill<float>(leftCoeffs->last(remainingElements), 0.0f);
fill<float>(rightCoeffs.last(remainingElements), 1.0f); fill<float>(rightCoeffs->last(remainingElements), 1.0f);
break; break;
} }
} }
} }
auto ind = indices.data(); auto ind = indices->data();
auto leftCoeff = leftCoeffs.data(); auto leftCoeff = leftCoeffs->data();
auto rightCoeff = rightCoeffs.data(); auto rightCoeff = rightCoeffs->data();
auto leftSource = source.getConstSpan(0); auto leftSource = source.getConstSpan(0);
auto left = buffer.getChannel(0); auto left = buffer.getChannel(0);
if (source.getNumChannels() == 1) { if (source.getNumChannels() == 1) {
while (ind < indices.end()) { while (ind < indices->end()) {
*left = linearInterpolation(leftSource[*ind], leftSource[*ind + 1], *leftCoeff, *rightCoeff); *left = linearInterpolation(leftSource[*ind], leftSource[*ind + 1], *leftCoeff, *rightCoeff);
incrementAll(ind, left, leftCoeff, rightCoeff); incrementAll(ind, left, leftCoeff, rightCoeff);
} }
} else { } else {
auto right = buffer.getChannel(1); auto right = buffer.getChannel(1);
auto rightSource = source.getConstSpan(1); auto rightSource = source.getConstSpan(1);
while (ind < indices.end()) { while (ind < indices->end()) {
*left = linearInterpolation(leftSource[*ind], leftSource[*ind + 1], *leftCoeff, *rightCoeff); *left = linearInterpolation(leftSource[*ind], leftSource[*ind + 1], *leftCoeff, *rightCoeff);
*right = linearInterpolation(rightSource[*ind], rightSource[*ind + 1], *leftCoeff, *rightCoeff); *right = linearInterpolation(rightSource[*ind], rightSource[*ind + 1], *leftCoeff, *rightCoeff);
incrementAll(ind, left, right, leftCoeff, rightCoeff); incrementAll(ind, left, right, leftCoeff, rightCoeff);
} }
} }
sourcePosition = indices.back(); sourcePosition = indices->back();
floatPositionOffset = rightCoeffs.back(); floatPositionOffset = rightCoeffs->back();
} }
void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
@ -504,24 +496,22 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); }); absl::c_generate(rightSpan, [&](){ return noiseDist(Random::randomGenerator); });
} else { } else {
const auto numSamples = buffer.getNumFrames(); const auto numSamples = buffer.getNumFrames();
auto frequencyBuffer = resources.bufferPool.getBuffer(numSamples); auto frequencies = resources.bufferPool.getBuffer(numSamples);
auto bendBuffer = resources.bufferPool.getBuffer(numSamples); auto bends = resources.bufferPool.getBuffer(numSamples);
if (!frequencyBuffer || !bendBuffer) if (!frequencies || !bends)
return; return;
auto frequencies = absl::MakeSpan(*frequencyBuffer).first(numSamples);
auto bends = absl::MakeSpan(*bendBuffer).first(numSamples);
float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter); float keycenterFrequency = midiNoteFrequency(region->pitchKeycenter);
fill<float>(frequencies, pitchRatio * keycenterFrequency); fill<float>(*frequencies, pitchRatio * keycenterFrequency);
if (region->bendStep > 1) if (region->bendStep > 1)
pitchBendEnvelope.getQuantizedBlock(bends, bendStepFactor); pitchBendEnvelope.getQuantizedBlock(*bends, bendStepFactor);
else else
pitchBendEnvelope.getBlock(bends); pitchBendEnvelope.getBlock(*bends);
applyGain<float>(bends, frequencies); applyGain<float>(*bends, *frequencies);
waveOscillator.processModulated(frequencies.data(), leftSpan.data(), buffer.getNumFrames()); waveOscillator.processModulated(frequencies->data(), leftSpan.data(), buffer.getNumFrames());
copy<float>(leftSpan, rightSpan); copy<float>(leftSpan, rightSpan);
} }
} }