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/Wavetables.cpp
|
||||
sfizz/Tuning.cpp
|
||||
sfizz/RegionSet.cpp
|
||||
sfizz/PolyphonyGroup.cpp
|
||||
sfizz/VoiceStealing.cpp
|
||||
sfizz/RTSemaphore.cpp
|
||||
sfizz/Panning.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 {
|
||||
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
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 {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue