Put stuff into cpp files and add docs

This commit is contained in:
Paul Ferrand 2020-06-23 00:14:15 +02:00
parent 32fe0cacf1
commit 8251160633
8 changed files with 250 additions and 112 deletions

View file

@ -23,6 +23,9 @@ set (SFIZZ_SOURCES
sfizz/Smoothers.cpp
sfizz/Wavetables.cpp
sfizz/Tuning.cpp
sfizz/RegionSet.cpp
sfizz/PolyphonyGroup.cpp
sfizz/VoiceStealing.cpp
sfizz/RTSemaphore.cpp
sfizz/Panning.cpp
sfizz/Effects.cpp

View 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; });
}

View file

@ -15,23 +15,43 @@ namespace sfz
{
class PolyphonyGroup {
public:
void setPolyphonyLimit(unsigned limit)
{
polyphonyLimit = limit;
voices.reserve(limit);
}
unsigned getPolyphonyLimit() const { return polyphonyLimit; }
void registerVoice(Voice* voice)
{
if (absl::c_find(voices, voice) == voices.end())
voices.push_back(voice);
}
void removeVoice(const Voice* voice)
{
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
}
const std::vector<Voice*>& getActiveVoices() const { return voices; }
std::vector<Voice*>& getActiveVoices() { return voices; }
/**
* @brief Set the polyphony limit for this polyphony group.
*
* @param limit
*/
void setPolyphonyLimit(unsigned limit) noexcept;
/**
* @brief Register an active voice in this polyphony group.
*
* @param voice
*/
void registerVoice(Voice* voice) noexcept;
/**
* @brief Remove a voice from this polyphony group.
* If the voice was not registered before, this has no effect.
*
* @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:
unsigned polyphonyLimit { config::maxVoices };
std::vector<Voice*> voices;

48
src/sfizz/RegionSet.cpp Normal file
View 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();
}
}

View file

@ -16,53 +16,93 @@ namespace sfz
class RegionSet {
public:
void setPolyphonyLimit(unsigned limit)
{
polyphonyLimit = limit;
voices.reserve(limit);
}
unsigned getPolyphonyLimit() const { return polyphonyLimit; }
void addRegion(Region* region)
{
if (absl::c_find(regions, region) == regions.end())
regions.push_back(region);
}
void addSubset(RegionSet* group)
{
if (absl::c_find(subsets, group) == subsets.end())
subsets.push_back(group);
}
void registerVoice(Voice* voice)
{
if (absl::c_find(voices, voice) == voices.end())
voices.push_back(voice);
}
void removeVoice(const Voice* voice)
{
swapAndPopFirst(voices, [voice](const Voice* v) { return v == voice; });
}
static void registerVoiceInHierarchy(const Region* region, Voice* voice)
{
auto* parent = region->parent;
while (parent != nullptr) {
parent->registerVoice(voice);
parent = parent->getParent();
}
}
static void removeVoiceFromHierarchy(const Region* region, const Voice* voice)
{
auto* parent = region->parent;
while (parent != nullptr) {
parent->removeVoice(voice);
parent = parent->getParent();
}
}
RegionSet* getParent() const { return parent; }
void setParent(RegionSet* parent) { this->parent = parent; }
const std::vector<Voice*>& getActiveVoices() const { return voices; }
std::vector<Voice*>& getActiveVoices() { return voices; }
const std::vector<Region*>& getRegions() const { return regions; }
const std::vector<RegionSet*>& getSubsets() const { return subsets; }
/**
* @brief Set the polyphony limit for the set
*
* @param limit
*/
void setPolyphonyLimit(unsigned limit) noexcept;
/**
* @brief Add a region to the set
*
* @param region
*/
void addRegion(Region* region) noexcept;
/**
* @brief Add a subset to the set
*
* @param group
*/
void addSubset(RegionSet* group) noexcept;
/**
* @brief Register a voice as active in this set
*
* @param voice
*/
void registerVoice(Voice* voice) noexcept;
/**
* @brief Remove an active voice for this set.
* If the voice was not registered this has no effect.
*
* @param voice
*/
void removeVoice(const Voice* voice) noexcept;
/**
* @brief Register a voice in the whole parent hierarchy of the region
*
* @param region
* @param voice
*/
static void registerVoiceInHierarchy(const Region* region, Voice* voice) noexcept;
/**
* @brief Remove an active voice from the whole parent hierarchy of the region.
*
* @param region
* @param voice
*/
static void removeVoiceFromHierarchy(const Region* region, const Voice* voice) noexcept;
/**
* @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:
RegionSet* parent { nullptr };
std::vector<Region*> regions;

View file

@ -14,7 +14,7 @@ namespace sfz
struct SisterVoiceRing {
template<class F, class T,
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();
while (v != voice) {
@ -25,7 +25,7 @@ struct SisterVoiceRing {
lambda(voice);
}
static unsigned countSisterVoices(const Voice* start)
static unsigned countSisterVoices(const Voice* start) noexcept
{
if (!start)
return 0;
@ -50,7 +50,7 @@ struct SisterVoiceRing {
*/
class SisterVoiceRingBuilder {
public:
~SisterVoiceRingBuilder() {
~SisterVoiceRingBuilder() noexcept {
if (lastStartedVoice != nullptr) {
ASSERT(firstStartedVoice);
lastStartedVoice->setNextSisterVoice(firstStartedVoice);
@ -63,7 +63,7 @@ public:
*
* @param voice
*/
void addVoiceToRing(Voice* voice) {
void addVoiceToRing(Voice* voice) noexcept {
if (firstStartedVoice == nullptr)
firstStartedVoice = voice;

View 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;
}

View file

@ -17,50 +17,14 @@ namespace sfz
class VoiceStealing
{
public:
VoiceStealing()
{
voiceScores.reserve(config::maxVoices);
}
Voice* steal(absl::Span<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;
}
VoiceStealing();
/**
* @brief Propose a voice to steal from a set of voices
*
* @param voices
* @return Voice*
*/
Voice* steal(absl::Span<Voice*> voices) noexcept;
private:
struct VoiceScore
{