commit
b9f58f1fe4
8 changed files with 100 additions and 84 deletions
|
|
@ -132,6 +132,13 @@ namespace config {
|
||||||
static constexpr int loopXfadeCurve = 2; // 0: linear
|
static constexpr int loopXfadeCurve = 2; // 0: linear
|
||||||
// 1: use curves 5 & 6
|
// 1: use curves 5 & 6
|
||||||
// 2: use S-shaped curve
|
// 2: use S-shaped curve
|
||||||
|
/**
|
||||||
|
* @brief Overflow voices in the engine, relative to the required voices.
|
||||||
|
* These are additional voices that more or less hold the "dying" voices
|
||||||
|
* due to engine polyphony being reached.
|
||||||
|
*/
|
||||||
|
static constexpr float overflowVoiceMultiplier { 1.5f };
|
||||||
|
static_assert(overflowVoiceMultiplier >= 1.0f, "This needs to add voices");
|
||||||
} // namespace config
|
} // namespace config
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
|
|
@ -53,3 +53,8 @@ unsigned sfz::RegionSet::numPlayingVoices() const noexcept
|
||||||
return !v->releasedOrFree();
|
return !v->releasedOrFree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::removeAllVoices() noexcept
|
||||||
|
{
|
||||||
|
voices.clear();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,11 @@ public:
|
||||||
* @return const std::vector<RegionSet*>&
|
* @return const std::vector<RegionSet*>&
|
||||||
*/
|
*/
|
||||||
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove all voices from the set
|
||||||
|
*/
|
||||||
|
void removeAllVoices() noexcept;
|
||||||
private:
|
private:
|
||||||
RegionSet* parent { nullptr };
|
RegionSet* parent { nullptr };
|
||||||
OpcodeScope level { kOpcodeScopeGeneric };
|
OpcodeScope level { kOpcodeScopeGeneric };
|
||||||
|
|
|
||||||
|
|
@ -61,13 +61,14 @@ struct SisterVoiceRing {
|
||||||
*
|
*
|
||||||
* @param voice
|
* @param voice
|
||||||
* @param delay
|
* @param delay
|
||||||
|
* @param fast whether to apply a fast release
|
||||||
*/
|
*/
|
||||||
template<class T,
|
template<class T,
|
||||||
absl::enable_if_t<std::is_same<Voice, absl::remove_const_t<T>>::value, int> = 0>
|
absl::enable_if_t<std::is_same<Voice, absl::remove_const_t<T>>::value, int> = 0>
|
||||||
static void offAllSisters(T* voice, int delay) {
|
static void offAllSisters(T* voice, int delay, bool fast = false) {
|
||||||
if (voice != nullptr) {
|
if (voice != nullptr) {
|
||||||
SisterVoiceRing::applyToRing(voice, [&] (Voice* v) {
|
SisterVoiceRing::applyToRing(voice, [&] (Voice* v) {
|
||||||
v->off(delay);
|
v->off(delay, fast);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ sfz::Synth::Synth(int numVoices)
|
||||||
initializeSIMDDispatchers();
|
initializeSIMDDispatchers();
|
||||||
|
|
||||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
engineSet = absl::make_unique<RegionSet>(nullptr, OpcodeScope::kOpcodeScopeGeneric);
|
||||||
parser.setListener(this);
|
parser.setListener(this);
|
||||||
effectFactory.registerStandardEffectTypes();
|
effectFactory.registerStandardEffectTypes();
|
||||||
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
effectBuses.reserve(5); // sufficient room for main and fx1-4
|
||||||
|
|
@ -70,6 +71,7 @@ void sfz::Synth::onVoiceStateChanged(NumericId<Voice> id, Voice::State state)
|
||||||
if (state == Voice::State::idle) {
|
if (state == Voice::State::idle) {
|
||||||
auto voice = getVoiceById(id);
|
auto voice = getVoiceById(id);
|
||||||
RegionSet::removeVoiceFromHierarchy(voice->getRegion(), voice);
|
RegionSet::removeVoiceFromHierarchy(voice->getRegion(), voice);
|
||||||
|
engineSet->removeVoice(voice);
|
||||||
polyphonyGroups[voice->getRegion()->group].removeVoice(voice);
|
polyphonyGroups[voice->getRegion()->group].removeVoice(voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,6 +389,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
default:
|
default:
|
||||||
DBG("Unsupported value for hint_stealing: " << member.value);
|
DBG("Unsupported value for hint_stealing: " << member.value);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Unsupported control opcode
|
// Unsupported control opcode
|
||||||
DBG("Unsupported control opcode: " << member.opcode);
|
DBG("Unsupported control opcode: " << member.opcode);
|
||||||
|
|
@ -742,23 +745,8 @@ sfz::Voice* sfz::Synth::findFreeVoice() noexcept
|
||||||
if (freeVoice != voices.end())
|
if (freeVoice != voices.end())
|
||||||
return freeVoice->get();
|
return freeVoice->get();
|
||||||
|
|
||||||
// Engine polyphony reached
|
DBG("Engine hard polyphony reached");
|
||||||
Voice* stolenVoice = stealer.steal(absl::MakeSpan(voiceViewArray));
|
return {};
|
||||||
if (stolenVoice == nullptr)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// Never kill age 0 voices
|
|
||||||
if (stolenVoice->getAge() == 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
|
|
||||||
auto tempSpan = resources.bufferPool.getStereoBuffer(samplesPerBlock);
|
|
||||||
SisterVoiceRing::applyToRing(stolenVoice, [&] (Voice* v) {
|
|
||||||
renderVoiceToOutputs(*v, *tempSpan);
|
|
||||||
v->reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
return stolenVoice;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept
|
int sfz::Synth::getNumActiveVoices(bool recompute) const noexcept
|
||||||
|
|
@ -813,20 +801,6 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Synth::renderVoiceToOutputs(Voice& voice, AudioSpan<float>& tempSpan) noexcept
|
|
||||||
{
|
|
||||||
const Region* region = voice.getRegion();
|
|
||||||
ASSERT(region != nullptr);
|
|
||||||
|
|
||||||
voice.renderBlock(tempSpan);
|
|
||||||
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
|
|
||||||
if (auto& bus = effectBuses[i]) {
|
|
||||||
float addGain = region->getGainToEffectBus(i);
|
|
||||||
bus->addToInputs(tempSpan, addGain, tempSpan.getNumFrames());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
{
|
{
|
||||||
ScopedFTZ ftz;
|
ScopedFTZ ftz;
|
||||||
|
|
@ -865,19 +839,19 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
ModMatrix& mm = resources.modMatrix;
|
ModMatrix& mm = resources.modMatrix;
|
||||||
mm.beginCycle(numFrames);
|
mm.beginCycle(numFrames);
|
||||||
|
|
||||||
|
{ // Clear effect busses
|
||||||
|
ScopedTiming logger { callbackBreakdown.effects };
|
||||||
|
for (auto& bus : effectBuses) {
|
||||||
|
if (bus)
|
||||||
|
bus->clearInputs(numFrames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
activeVoices = 0;
|
activeVoices = 0;
|
||||||
{ // Main render block
|
{ // Main render block
|
||||||
ScopedTiming logger { callbackBreakdown.renderMethod, ScopedTiming::Operation::addToDuration };
|
ScopedTiming logger { callbackBreakdown.renderMethod, ScopedTiming::Operation::addToDuration };
|
||||||
tempMixSpan->fill(0.0f);
|
tempMixSpan->fill(0.0f);
|
||||||
|
|
||||||
// Ramp out whatever is in the buffer at this point; should only be killed voice data
|
|
||||||
linearRamp<float>(*rampSpan, 1.0f, -1.0f / static_cast<float>(numFrames));
|
|
||||||
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
|
|
||||||
if (auto& bus = effectBuses[i]) {
|
|
||||||
bus->applyGain(rampSpan->data(), numFrames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& voice : voices) {
|
for (auto& voice : voices) {
|
||||||
if (voice->isFree())
|
if (voice->isFree())
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -885,7 +859,17 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
mm.beginVoice(voice->getId(), voice->getRegion()->getId(), voice->getTriggerEvent().value);
|
mm.beginVoice(voice->getId(), voice->getRegion()->getId(), voice->getTriggerEvent().value);
|
||||||
|
|
||||||
activeVoices++;
|
activeVoices++;
|
||||||
renderVoiceToOutputs(*voice, *tempSpan);
|
|
||||||
|
const Region* region = voice->getRegion();
|
||||||
|
ASSERT(region != nullptr);
|
||||||
|
|
||||||
|
voice->renderBlock(*tempSpan);
|
||||||
|
for (size_t i = 0, n = effectBuses.size(); i < n; ++i) {
|
||||||
|
if (auto& bus = effectBuses[i]) {
|
||||||
|
float addGain = region->getGainToEffectBus(i);
|
||||||
|
bus->addToInputs(*tempSpan, addGain, numFrames);
|
||||||
|
}
|
||||||
|
}
|
||||||
callbackBreakdown.data += voice->getLastDataDuration();
|
callbackBreakdown.data += voice->getLastDataDuration();
|
||||||
callbackBreakdown.amplitude += voice->getLastAmplitudeDuration();
|
callbackBreakdown.amplitude += voice->getLastAmplitudeDuration();
|
||||||
callbackBreakdown.filters += voice->getLastFilterDuration();
|
callbackBreakdown.filters += voice->getLastFilterDuration();
|
||||||
|
|
@ -934,14 +918,6 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
// Reset the dispatch counter
|
// Reset the dispatch counter
|
||||||
dispatchDuration = Duration(0);
|
dispatchDuration = Duration(0);
|
||||||
|
|
||||||
{ // Clear for the next run
|
|
||||||
ScopedTiming logger { callbackBreakdown.effects };
|
|
||||||
for (auto& bus : effectBuses) {
|
|
||||||
if (bus)
|
|
||||||
bus->clearInputs(numFrames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
|
ASSERT(!hasNanInf(buffer.getConstSpan(0)));
|
||||||
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
|
ASSERT(!hasNanInf(buffer.getConstSpan(1)));
|
||||||
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
|
SFIZZ_CHECK(isReasonableAudio(buffer.getConstSpan(0)));
|
||||||
|
|
@ -993,6 +969,7 @@ void sfz::Synth::startVoice(Region* region, int delay, const TriggerEvent& trigg
|
||||||
checkRegionPolyphony(region, delay);
|
checkRegionPolyphony(region, delay);
|
||||||
checkGroupPolyphony(region, delay);
|
checkGroupPolyphony(region, delay);
|
||||||
checkSetPolyphony(region, delay);
|
checkSetPolyphony(region, delay);
|
||||||
|
checkEnginePolyphony(delay);
|
||||||
|
|
||||||
Voice* selectedVoice = findFreeVoice();
|
Voice* selectedVoice = findFreeVoice();
|
||||||
if (selectedVoice == nullptr)
|
if (selectedVoice == nullptr)
|
||||||
|
|
@ -1001,6 +978,7 @@ void sfz::Synth::startVoice(Region* region, int delay, const TriggerEvent& trigg
|
||||||
ASSERT(selectedVoice->isFree());
|
ASSERT(selectedVoice->isFree());
|
||||||
selectedVoice->startVoice(region, delay, triggerEvent);
|
selectedVoice->startVoice(region, delay, triggerEvent);
|
||||||
ring.addVoiceToRing(selectedVoice);
|
ring.addVoiceToRing(selectedVoice);
|
||||||
|
engineSet->registerVoice(selectedVoice);
|
||||||
RegionSet::registerVoiceInHierarchy(region, selectedVoice);
|
RegionSet::registerVoiceInHierarchy(region, selectedVoice);
|
||||||
polyphonyGroups[region->group].registerVoice(selectedVoice);
|
polyphonyGroups[region->group].registerVoice(selectedVoice);
|
||||||
}
|
}
|
||||||
|
|
@ -1042,12 +1020,9 @@ void sfz::Synth::noteOffDispatch(int delay, int noteNumber, float velocity) noex
|
||||||
void sfz::Synth::checkRegionPolyphony(const Region* region, int delay) noexcept
|
void sfz::Synth::checkRegionPolyphony(const Region* region, int delay) noexcept
|
||||||
{
|
{
|
||||||
tempPolyphonyArray.clear();
|
tempPolyphonyArray.clear();
|
||||||
|
absl::c_copy_if(voiceViewArray,
|
||||||
for (Voice* voice : voiceViewArray) {
|
std::back_inserter(tempPolyphonyArray),
|
||||||
if (voice->getRegion() == region && !voice->releasedOrFree()) {
|
[region](Voice* v) { return v->getRegion() == region && !v->releasedOrFree(); });
|
||||||
tempPolyphonyArray.push_back(voice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempPolyphonyArray.size() >= region->polyphony) {
|
if (tempPolyphonyArray.size() >= region->polyphony) {
|
||||||
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
||||||
|
|
@ -1096,11 +1071,8 @@ void sfz::Synth::checkGroupPolyphony(const Region* region, int delay) noexcept
|
||||||
{
|
{
|
||||||
const auto& activeVoices = polyphonyGroups[region->group].getActiveVoices();
|
const auto& activeVoices = polyphonyGroups[region->group].getActiveVoices();
|
||||||
tempPolyphonyArray.clear();
|
tempPolyphonyArray.clear();
|
||||||
for (Voice* voice : activeVoices) {
|
absl::c_copy_if(activeVoices,
|
||||||
if (!voice->releasedOrFree()) {
|
std::back_inserter(tempPolyphonyArray), [](Voice* v) { return !v->releasedOrFree(); });
|
||||||
tempPolyphonyArray.push_back(voice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempPolyphonyArray.size() >= polyphonyGroups[region->group].getPolyphonyLimit()) {
|
if (tempPolyphonyArray.size() >= polyphonyGroups[region->group].getPolyphonyLimit()) {
|
||||||
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
||||||
|
|
@ -1114,11 +1086,8 @@ void sfz::Synth::checkSetPolyphony(const Region* region, int delay) noexcept
|
||||||
while (parent != nullptr) {
|
while (parent != nullptr) {
|
||||||
const auto& activeVoices = parent->getActiveVoices();
|
const auto& activeVoices = parent->getActiveVoices();
|
||||||
tempPolyphonyArray.clear();
|
tempPolyphonyArray.clear();
|
||||||
for (Voice* voice : activeVoices) {
|
absl::c_copy_if(activeVoices,
|
||||||
if (!voice->releasedOrFree()) {
|
std::back_inserter(tempPolyphonyArray), [](Voice* v) { return !v->releasedOrFree(); });
|
||||||
tempPolyphonyArray.push_back(voice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempPolyphonyArray.size() >= parent->getPolyphonyLimit()) {
|
if (tempPolyphonyArray.size() >= parent->getPolyphonyLimit()) {
|
||||||
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
||||||
|
|
@ -1129,6 +1098,19 @@ void sfz::Synth::checkSetPolyphony(const Region* region, int delay) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::Synth::checkEnginePolyphony(int delay) noexcept
|
||||||
|
{
|
||||||
|
auto& activeVoices = engineSet->getActiveVoices();
|
||||||
|
|
||||||
|
if (activeVoices.size() >= static_cast<size_t>(numRequiredVoices)) {
|
||||||
|
tempPolyphonyArray.clear();
|
||||||
|
absl::c_copy_if(activeVoices,
|
||||||
|
std::back_inserter(tempPolyphonyArray), [](Voice* v) { return !v->releasedOrFree(); });
|
||||||
|
const auto voiceToSteal = stealer.steal(absl::MakeSpan(tempPolyphonyArray));
|
||||||
|
SisterVoiceRing::offAllSisters(voiceToSteal, delay, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept
|
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept
|
||||||
{
|
{
|
||||||
const auto randValue = randNoteDistribution(Random::randomGenerator);
|
const auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
|
|
@ -1519,7 +1501,7 @@ void sfz::Synth::setVolume(float volume) noexcept
|
||||||
|
|
||||||
int sfz::Synth::getNumVoices() const noexcept
|
int sfz::Synth::getNumVoices() const noexcept
|
||||||
{
|
{
|
||||||
return numVoices;
|
return numRequiredVoices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||||
|
|
@ -1528,7 +1510,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||||
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
const std::lock_guard<SpinMutex> disableCallback { callbackGuard };
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if (numVoices == this->numVoices)
|
if (numVoices == this->numRequiredVoices)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
resetVoices(numVoices);
|
resetVoices(numVoices);
|
||||||
|
|
@ -1536,16 +1518,25 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
||||||
|
|
||||||
void sfz::Synth::resetVoices(int numVoices)
|
void sfz::Synth::resetVoices(int numVoices)
|
||||||
{
|
{
|
||||||
|
numActualVoices =
|
||||||
|
static_cast<int>(config::overflowVoiceMultiplier * numVoices);
|
||||||
|
numRequiredVoices = numVoices;
|
||||||
|
|
||||||
|
for (auto& set : sets)
|
||||||
|
set->removeAllVoices();
|
||||||
|
engineSet->removeAllVoices();
|
||||||
|
engineSet->setPolyphonyLimit(numRequiredVoices);
|
||||||
|
|
||||||
voices.clear();
|
voices.clear();
|
||||||
voices.reserve(numVoices);
|
voices.reserve(numActualVoices);
|
||||||
|
|
||||||
voiceViewArray.clear();
|
voiceViewArray.clear();
|
||||||
voiceViewArray.reserve(numVoices);
|
voiceViewArray.reserve(numActualVoices);
|
||||||
|
|
||||||
tempPolyphonyArray.clear();
|
tempPolyphonyArray.clear();
|
||||||
tempPolyphonyArray.reserve(numVoices);
|
tempPolyphonyArray.reserve(numActualVoices);
|
||||||
|
|
||||||
for (int i = 0; i < numVoices; ++i) {
|
for (int i = 0; i < numActualVoices; ++i) {
|
||||||
auto voice = absl::make_unique<Voice>(i, resources);
|
auto voice = absl::make_unique<Voice>(i, resources);
|
||||||
voice->setStateListener(this);
|
voice->setStateListener(this);
|
||||||
voiceViewArray.push_back(voice.get());
|
voiceViewArray.push_back(voice.get());
|
||||||
|
|
@ -1557,8 +1548,6 @@ void sfz::Synth::resetVoices(int numVoices)
|
||||||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->numVoices = numVoices;
|
|
||||||
|
|
||||||
applySettingsPerVoice();
|
applySettingsPerVoice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -742,13 +742,10 @@ private:
|
||||||
void setupModMatrix();
|
void setupModMatrix();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Render the voice to its designated outputs and effect busses.
|
* @brief Get the modification time of all included sfz files
|
||||||
*
|
*
|
||||||
* @param voice
|
* @return fs::file_time_type
|
||||||
* @param tempSpan a temporary span used for rendering
|
|
||||||
*/
|
*/
|
||||||
void renderVoiceToOutputs(Voice& voice, AudioSpan<float>& tempSpan) noexcept;
|
|
||||||
|
|
||||||
fs::file_time_type checkModificationTime();
|
fs::file_time_type checkModificationTime();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -819,6 +816,9 @@ private:
|
||||||
// These are more general "groups" than sfz and encapsulates the full hierarchy
|
// These are more general "groups" than sfz and encapsulates the full hierarchy
|
||||||
RegionSet* currentSet { nullptr };
|
RegionSet* currentSet { nullptr };
|
||||||
std::vector<RegionSetPtr> sets;
|
std::vector<RegionSetPtr> sets;
|
||||||
|
// This region set holds the engine set of voices, which tries to respect the required
|
||||||
|
// engine polyphony
|
||||||
|
RegionSetPtr engineSet;
|
||||||
|
|
||||||
// These are the `group=` groups where you can off voices
|
// These are the `group=` groups where you can off voices
|
||||||
std::vector<PolyphonyGroup> polyphonyGroups;
|
std::vector<PolyphonyGroup> polyphonyGroups;
|
||||||
|
|
@ -862,6 +862,13 @@ private:
|
||||||
*/
|
*/
|
||||||
void checkSetPolyphony(const Region* region, int delay) noexcept;
|
void checkSetPolyphony(const Region* region, int delay) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check the engine polyphony, fast releasing voices if necessary
|
||||||
|
*
|
||||||
|
* @param delay
|
||||||
|
*/
|
||||||
|
void checkEnginePolyphony(int delay) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start a voice for a specific region.
|
* @brief Start a voice for a specific region.
|
||||||
* This will do the needed polyphony checks and voice stealing.
|
* This will do the needed polyphony checks and voice stealing.
|
||||||
|
|
@ -902,7 +909,8 @@ private:
|
||||||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||||
float sampleRate { config::defaultSampleRate };
|
float sampleRate { config::defaultSampleRate };
|
||||||
float volume { Default::globalVolume };
|
float volume { Default::globalVolume };
|
||||||
int numVoices { config::numVoices };
|
int numRequiredVoices { config::numVoices };
|
||||||
|
int numActualVoices { static_cast<int>(config::numVoices * config::overflowVoiceMultiplier) };
|
||||||
int activeVoices { 0 };
|
int activeVoices { 0 };
|
||||||
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
Oversampling oversamplingFactor { config::defaultOversamplingFactor };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,11 +167,11 @@ void sfz::Voice::release(int delay) noexcept
|
||||||
resources.modMatrix.releaseVoice(id, region->getId(), delay);
|
resources.modMatrix.releaseVoice(id, region->getId(), delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Voice::off(int delay) noexcept
|
void sfz::Voice::off(int delay, bool fast) noexcept
|
||||||
{
|
{
|
||||||
if (!region->flexAmpEG) {
|
if (!region->flexAmpEG) {
|
||||||
if (region->offMode == SfzOffMode::fast) {
|
if (region->offMode == SfzOffMode::fast || fast) {
|
||||||
egAmplitude.setReleaseTime( Default::offTime );
|
egAmplitude.setReleaseTime(Default::offTime);
|
||||||
} else if (region->offMode == SfzOffMode::time) {
|
} else if (region->offMode == SfzOffMode::time) {
|
||||||
egAmplitude.setReleaseTime(region->offTime);
|
egAmplitude.setReleaseTime(region->offTime);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -328,8 +328,9 @@ public:
|
||||||
* and set the envelopes if necessary.
|
* and set the envelopes if necessary.
|
||||||
*
|
*
|
||||||
* @param delay
|
* @param delay
|
||||||
|
* @param fast whether to apply a fast release regardless of the off mode
|
||||||
*/
|
*/
|
||||||
void off(int delay) noexcept;
|
void off(int delay, bool fast = false) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief gets the age of the Voice
|
* @brief gets the age of the Voice
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue