Put stuff into cpp files and add docs
This commit is contained in:
parent
32fe0cacf1
commit
8251160633
8 changed files with 250 additions and 112 deletions
|
|
@ -23,6 +23,9 @@ set (SFIZZ_SOURCES
|
||||||
sfizz/Smoothers.cpp
|
sfizz/Smoothers.cpp
|
||||||
sfizz/Wavetables.cpp
|
sfizz/Wavetables.cpp
|
||||||
sfizz/Tuning.cpp
|
sfizz/Tuning.cpp
|
||||||
|
sfizz/RegionSet.cpp
|
||||||
|
sfizz/PolyphonyGroup.cpp
|
||||||
|
sfizz/VoiceStealing.cpp
|
||||||
sfizz/RTSemaphore.cpp
|
sfizz/RTSemaphore.cpp
|
||||||
sfizz/Panning.cpp
|
sfizz/Panning.cpp
|
||||||
sfizz/Effects.cpp
|
sfizz/Effects.cpp
|
||||||
|
|
|
||||||
18
src/sfizz/PolyphonyGroup.cpp
Normal file
18
src/sfizz/PolyphonyGroup.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "PolyphonyGroup.h"
|
||||||
|
|
||||||
|
void sfz::PolyphonyGroup::setPolyphonyLimit(unsigned limit) noexcept
|
||||||
|
{
|
||||||
|
polyphonyLimit = limit;
|
||||||
|
voices.reserve(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::PolyphonyGroup::registerVoice(Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
if (absl::c_find(voices, voice) == voices.end())
|
||||||
|
voices.push_back(voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::PolyphonyGroup::removeVoice(const Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
|
||||||
|
}
|
||||||
|
|
@ -15,23 +15,43 @@ namespace sfz
|
||||||
{
|
{
|
||||||
class PolyphonyGroup {
|
class PolyphonyGroup {
|
||||||
public:
|
public:
|
||||||
void setPolyphonyLimit(unsigned limit)
|
/**
|
||||||
{
|
* @brief Set the polyphony limit for this polyphony group.
|
||||||
polyphonyLimit = limit;
|
*
|
||||||
voices.reserve(limit);
|
* @param limit
|
||||||
}
|
*/
|
||||||
unsigned getPolyphonyLimit() const { return polyphonyLimit; }
|
void setPolyphonyLimit(unsigned limit) noexcept;
|
||||||
void registerVoice(Voice* voice)
|
/**
|
||||||
{
|
* @brief Register an active voice in this polyphony group.
|
||||||
if (absl::c_find(voices, voice) == voices.end())
|
*
|
||||||
voices.push_back(voice);
|
* @param voice
|
||||||
}
|
*/
|
||||||
void removeVoice(const Voice* voice)
|
void registerVoice(Voice* voice) noexcept;
|
||||||
{
|
/**
|
||||||
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
|
* @brief Remove a voice from this polyphony group.
|
||||||
}
|
* If the voice was not registered before, this has no effect.
|
||||||
const std::vector<Voice*>& getActiveVoices() const { return voices; }
|
*
|
||||||
std::vector<Voice*>& getActiveVoices() { return voices; }
|
* @param voice
|
||||||
|
*/
|
||||||
|
void removeVoice(const Voice* voice) noexcept;
|
||||||
|
/**
|
||||||
|
* @brief Get the polyphony limit for this group
|
||||||
|
*
|
||||||
|
* @return unsigned
|
||||||
|
*/
|
||||||
|
unsigned getPolyphonyLimit() const noexcept { return polyphonyLimit; }
|
||||||
|
/**
|
||||||
|
* @brief Get the active voices
|
||||||
|
*
|
||||||
|
* @return const std::vector<Voice*>&
|
||||||
|
*/
|
||||||
|
const std::vector<Voice*>& getActiveVoices() const noexcept { return voices; }
|
||||||
|
/**
|
||||||
|
* @brief Get the active voices
|
||||||
|
*
|
||||||
|
* @return std::vector<Voice*>&
|
||||||
|
*/
|
||||||
|
std::vector<Voice*>& getActiveVoices() noexcept { return voices; }
|
||||||
private:
|
private:
|
||||||
unsigned polyphonyLimit { config::maxVoices };
|
unsigned polyphonyLimit { config::maxVoices };
|
||||||
std::vector<Voice*> voices;
|
std::vector<Voice*> voices;
|
||||||
|
|
|
||||||
48
src/sfizz/RegionSet.cpp
Normal file
48
src/sfizz/RegionSet.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#include "RegionSet.h"
|
||||||
|
|
||||||
|
void sfz::RegionSet::setPolyphonyLimit(unsigned limit) noexcept
|
||||||
|
{
|
||||||
|
polyphonyLimit = limit;
|
||||||
|
voices.reserve(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::addRegion(Region* region) noexcept
|
||||||
|
{
|
||||||
|
if (absl::c_find(regions, region) == regions.end())
|
||||||
|
regions.push_back(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::addSubset(RegionSet* group) noexcept
|
||||||
|
{
|
||||||
|
if (absl::c_find(subsets, group) == subsets.end())
|
||||||
|
subsets.push_back(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::registerVoice(Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
if (absl::c_find(voices, voice) == voices.end())
|
||||||
|
voices.push_back(voice);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::removeVoice(const Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::registerVoiceInHierarchy(const Region* region, Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
auto* parent = region->parent;
|
||||||
|
while (parent != nullptr) {
|
||||||
|
parent->registerVoice(voice);
|
||||||
|
parent = parent->getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::RegionSet::removeVoiceFromHierarchy(const Region* region, const Voice* voice) noexcept
|
||||||
|
{
|
||||||
|
auto* parent = region->parent;
|
||||||
|
while (parent != nullptr) {
|
||||||
|
parent->removeVoice(voice);
|
||||||
|
parent = parent->getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,53 +16,93 @@ namespace sfz
|
||||||
|
|
||||||
class RegionSet {
|
class RegionSet {
|
||||||
public:
|
public:
|
||||||
void setPolyphonyLimit(unsigned limit)
|
/**
|
||||||
{
|
* @brief Set the polyphony limit for the set
|
||||||
polyphonyLimit = limit;
|
*
|
||||||
voices.reserve(limit);
|
* @param limit
|
||||||
}
|
*/
|
||||||
unsigned getPolyphonyLimit() const { return polyphonyLimit; }
|
void setPolyphonyLimit(unsigned limit) noexcept;
|
||||||
void addRegion(Region* region)
|
/**
|
||||||
{
|
* @brief Add a region to the set
|
||||||
if (absl::c_find(regions, region) == regions.end())
|
*
|
||||||
regions.push_back(region);
|
* @param region
|
||||||
}
|
*/
|
||||||
void addSubset(RegionSet* group)
|
void addRegion(Region* region) noexcept;
|
||||||
{
|
/**
|
||||||
if (absl::c_find(subsets, group) == subsets.end())
|
* @brief Add a subset to the set
|
||||||
subsets.push_back(group);
|
*
|
||||||
}
|
* @param group
|
||||||
void registerVoice(Voice* voice)
|
*/
|
||||||
{
|
void addSubset(RegionSet* group) noexcept;
|
||||||
if (absl::c_find(voices, voice) == voices.end())
|
/**
|
||||||
voices.push_back(voice);
|
* @brief Register a voice as active in this set
|
||||||
}
|
*
|
||||||
void removeVoice(const Voice* voice)
|
* @param voice
|
||||||
{
|
*/
|
||||||
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
|
void registerVoice(Voice* voice) noexcept;
|
||||||
}
|
/**
|
||||||
static void registerVoiceInHierarchy(const Region* region, Voice* voice)
|
* @brief Remove an active voice for this set.
|
||||||
{
|
* If the voice was not registered this has no effect.
|
||||||
auto* parent = region->parent;
|
*
|
||||||
while (parent != nullptr) {
|
* @param voice
|
||||||
parent->registerVoice(voice);
|
*/
|
||||||
parent = parent->getParent();
|
void removeVoice(const Voice* voice) noexcept;
|
||||||
}
|
/**
|
||||||
}
|
* @brief Register a voice in the whole parent hierarchy of the region
|
||||||
static void removeVoiceFromHierarchy(const Region* region, const Voice* voice)
|
*
|
||||||
{
|
* @param region
|
||||||
auto* parent = region->parent;
|
* @param voice
|
||||||
while (parent != nullptr) {
|
*/
|
||||||
parent->removeVoice(voice);
|
static void registerVoiceInHierarchy(const Region* region, Voice* voice) noexcept;
|
||||||
parent = parent->getParent();
|
/**
|
||||||
}
|
* @brief Remove an active voice from the whole parent hierarchy of the region.
|
||||||
}
|
*
|
||||||
RegionSet* getParent() const { return parent; }
|
* @param region
|
||||||
void setParent(RegionSet* parent) { this->parent = parent; }
|
* @param voice
|
||||||
const std::vector<Voice*>& getActiveVoices() const { return voices; }
|
*/
|
||||||
std::vector<Voice*>& getActiveVoices() { return voices; }
|
static void removeVoiceFromHierarchy(const Region* region, const Voice* voice) noexcept;
|
||||||
const std::vector<Region*>& getRegions() const { return regions; }
|
/**
|
||||||
const std::vector<RegionSet*>& getSubsets() const { return subsets; }
|
* @brief Get the polyphony limit
|
||||||
|
*
|
||||||
|
* @return unsigned
|
||||||
|
*/
|
||||||
|
unsigned getPolyphonyLimit() const noexcept { return polyphonyLimit; }
|
||||||
|
/**
|
||||||
|
* @brief Get the parent set
|
||||||
|
*
|
||||||
|
* @return RegionSet*
|
||||||
|
*/
|
||||||
|
RegionSet* getParent() const noexcept { return parent; }
|
||||||
|
/**
|
||||||
|
* @brief Set the parent set
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
void setParent(RegionSet* parent) noexcept { this->parent = parent; }
|
||||||
|
/**
|
||||||
|
* @brief Get the active voices
|
||||||
|
*
|
||||||
|
* @return const std::vector<Voice*>&
|
||||||
|
*/
|
||||||
|
const std::vector<Voice*>& getActiveVoices() const noexcept { return voices; }
|
||||||
|
/**
|
||||||
|
* @brief Get the active voices
|
||||||
|
*
|
||||||
|
* @return std::vector<Voice*>&
|
||||||
|
*/
|
||||||
|
std::vector<Voice*>& getActiveVoices() noexcept { return voices; }
|
||||||
|
/**
|
||||||
|
* @brief Get the regions in the set
|
||||||
|
*
|
||||||
|
* @return const std::vector<Region*>&
|
||||||
|
*/
|
||||||
|
const std::vector<Region*>& getRegions() const noexcept { return regions; }
|
||||||
|
/**
|
||||||
|
* @brief Get the region subsets in this set
|
||||||
|
*
|
||||||
|
* @return const std::vector<RegionSet*>&
|
||||||
|
*/
|
||||||
|
const std::vector<RegionSet*>& getSubsets() const noexcept { return subsets; }
|
||||||
private:
|
private:
|
||||||
RegionSet* parent { nullptr };
|
RegionSet* parent { nullptr };
|
||||||
std::vector<Region*> regions;
|
std::vector<Region*> regions;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ namespace sfz
|
||||||
struct SisterVoiceRing {
|
struct SisterVoiceRing {
|
||||||
template<class F, class T,
|
template<class F, 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 applyToRing(T* voice, F&& lambda)
|
static void applyToRing(T* voice, F&& lambda) noexcept
|
||||||
{
|
{
|
||||||
auto v = voice->getNextSisterVoice();
|
auto v = voice->getNextSisterVoice();
|
||||||
while (v != voice) {
|
while (v != voice) {
|
||||||
|
|
@ -25,7 +25,7 @@ struct SisterVoiceRing {
|
||||||
lambda(voice);
|
lambda(voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned countSisterVoices(const Voice* start)
|
static unsigned countSisterVoices(const Voice* start) noexcept
|
||||||
{
|
{
|
||||||
if (!start)
|
if (!start)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -50,7 +50,7 @@ struct SisterVoiceRing {
|
||||||
*/
|
*/
|
||||||
class SisterVoiceRingBuilder {
|
class SisterVoiceRingBuilder {
|
||||||
public:
|
public:
|
||||||
~SisterVoiceRingBuilder() {
|
~SisterVoiceRingBuilder() noexcept {
|
||||||
if (lastStartedVoice != nullptr) {
|
if (lastStartedVoice != nullptr) {
|
||||||
ASSERT(firstStartedVoice);
|
ASSERT(firstStartedVoice);
|
||||||
lastStartedVoice->setNextSisterVoice(firstStartedVoice);
|
lastStartedVoice->setNextSisterVoice(firstStartedVoice);
|
||||||
|
|
@ -63,7 +63,7 @@ public:
|
||||||
*
|
*
|
||||||
* @param voice
|
* @param voice
|
||||||
*/
|
*/
|
||||||
void addVoiceToRing(Voice* voice) {
|
void addVoiceToRing(Voice* voice) noexcept {
|
||||||
if (firstStartedVoice == nullptr)
|
if (firstStartedVoice == nullptr)
|
||||||
firstStartedVoice = voice;
|
firstStartedVoice = voice;
|
||||||
|
|
||||||
|
|
|
||||||
45
src/sfizz/VoiceStealing.cpp
Normal file
45
src/sfizz/VoiceStealing.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#include "VoiceStealing.h"
|
||||||
|
|
||||||
|
sfz::VoiceStealing::VoiceStealing()
|
||||||
|
{
|
||||||
|
voiceScores.reserve(config::maxVoices);
|
||||||
|
}
|
||||||
|
|
||||||
|
sfz::Voice* sfz::VoiceStealing::steal(absl::Span<sfz::Voice*> voices) noexcept
|
||||||
|
{
|
||||||
|
// Start of the voice stealing algorithm
|
||||||
|
absl::c_sort(voices, voiceOrdering);
|
||||||
|
|
||||||
|
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
|
||||||
|
return sum + v->getAverageEnvelope();
|
||||||
|
});
|
||||||
|
const auto envThreshold = sumEnvelope
|
||||||
|
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
|
||||||
|
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
|
||||||
|
|
||||||
|
Voice* returnedVoice = voices.front();
|
||||||
|
unsigned idx = 0;
|
||||||
|
while (idx < voices.size()) {
|
||||||
|
const auto ref = voices[idx];
|
||||||
|
|
||||||
|
if (ref->getAge() < ageThreshold) {
|
||||||
|
// Went too far, we'll kill the oldest note.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
float maxEnvelope { 0.0f };
|
||||||
|
SisterVoiceRing::applyToRing(ref, [&](Voice* v) {
|
||||||
|
maxEnvelope = max(maxEnvelope, v->getAverageEnvelope());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (maxEnvelope < envThreshold) {
|
||||||
|
returnedVoice = ref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jump over the sister voices in the set
|
||||||
|
do { idx++; }
|
||||||
|
while (idx < voices.size() && sisterVoices(ref, voices[idx]));
|
||||||
|
}
|
||||||
|
return returnedVoice;
|
||||||
|
}
|
||||||
|
|
@ -17,50 +17,14 @@ namespace sfz
|
||||||
class VoiceStealing
|
class VoiceStealing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VoiceStealing()
|
VoiceStealing();
|
||||||
{
|
/**
|
||||||
voiceScores.reserve(config::maxVoices);
|
* @brief Propose a voice to steal from a set of voices
|
||||||
}
|
*
|
||||||
|
* @param voices
|
||||||
Voice* steal(absl::Span<Voice*> voices) noexcept
|
* @return Voice*
|
||||||
{
|
*/
|
||||||
// Start of the voice stealing algorithm
|
Voice* steal(absl::Span<Voice*> voices) noexcept;
|
||||||
absl::c_sort(voices, voiceOrdering);
|
|
||||||
|
|
||||||
const auto sumEnvelope = absl::c_accumulate(voices, 0.0f, [](float sum, const Voice* v) {
|
|
||||||
return sum + v->getAverageEnvelope();
|
|
||||||
});
|
|
||||||
const auto envThreshold = sumEnvelope
|
|
||||||
/ static_cast<float>(voices.size()) * config::stealingEnvelopeCoeff;
|
|
||||||
const auto ageThreshold = voices.front()->getAge() * config::stealingAgeCoeff;
|
|
||||||
|
|
||||||
Voice* returnedVoice = voices.front();
|
|
||||||
unsigned idx = 0;
|
|
||||||
while (idx < voices.size()) {
|
|
||||||
const auto ref = voices[idx];
|
|
||||||
|
|
||||||
if (ref->getAge() < ageThreshold) {
|
|
||||||
// Went too far, we'll kill the oldest note.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
float maxEnvelope { 0.0f };
|
|
||||||
SisterVoiceRing::applyToRing(ref, [&](Voice* v) {
|
|
||||||
maxEnvelope = max(maxEnvelope, v->getAverageEnvelope());
|
|
||||||
});
|
|
||||||
|
|
||||||
if (maxEnvelope < envThreshold) {
|
|
||||||
returnedVoice = ref;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump over the sister voices in the set
|
|
||||||
do { idx++; }
|
|
||||||
while (idx < voices.size() && sisterVoices(ref, voices[idx]));
|
|
||||||
}
|
|
||||||
return returnedVoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct VoiceScore
|
struct VoiceScore
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue