Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Region sets #275

Merged
merged 11 commits into from
Jul 2, 2020
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/sfizz/Opcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ enum OpcodeScope {
kOpcodeScopeGlobal,
//! control scope
kOpcodeScopeControl,
//! Master scope
kOpcodeScopeMaster,
//! group scope
kOpcodeScopeGroup,
//! region scope
Expand Down
18 changes: 18 additions & 0 deletions src/sfizz/PolyphonyGroup.cpp
Original file line number Diff line number Diff line change
@@ -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; });
}
60 changes: 60 additions & 0 deletions src/sfizz/PolyphonyGroup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: BSD-2-Clause

// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz

#pragma once

#include "Region.h"
#include "Voice.h"
#include "SwapAndPop.h"
#include "absl/algorithm/container.h"

namespace sfz
{
class PolyphonyGroup {
public:
/**
* @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;
};

}
4 changes: 4 additions & 0 deletions src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
DBG("Unkown off mode:" << std::string(opcode.value));
}
break;
case hash("polyphony"):
if (auto value = readOpcode(opcode.value, Default::polyphonyRange))
polyphony = *value;
break;
case hash("note_polyphony"):
if (auto value = readOpcode(opcode.value, Default::polyphonyRange))
notePolyphony = *value;
Expand Down
8 changes: 7 additions & 1 deletion src/sfizz/Region.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include <vector>

namespace sfz {

class RegionSet;

/**
* @brief Regions are the basic building blocks for the SFZ parsing and handling code.
* All SFZ files are made of regions that are activated when a key is pressed or a CC
Expand Down Expand Up @@ -282,7 +285,8 @@ struct Region {
uint32_t group { Default::group }; // group
absl::optional<uint32_t> offBy {}; // off_by
SfzOffMode offMode { Default::offMode }; // off_mode
absl::optional<uint32_t> notePolyphony {};
absl::optional<uint32_t> notePolyphony {}; // note_polyphony
unsigned polyphony { config::maxVoices }; // polyphony
SfzSelfMask selfMask { Default::selfMask };

// Region logic: key mapping
Expand Down Expand Up @@ -365,6 +369,8 @@ struct Region {
// Modifiers
ModifierArray<CCMap<Modifier>> modifiers;

// Parent
RegionSet* parent { nullptr };
private:
const MidiState& midiState;
bool keySwitched { true };
Expand Down
48 changes: 48 additions & 0 deletions src/sfizz/RegionSet.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
}
114 changes: 114 additions & 0 deletions src/sfizz/RegionSet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// SPDX-License-Identifier: BSD-2-Clause

// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz

#pragma once

#include "Region.h"
#include "Voice.h"
#include "SwapAndPop.h"
#include <vector>

namespace sfz
{

class RegionSet {
public:
/**
* @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;
std::vector<RegionSet*> subsets;
std::vector<Voice*> voices;
unsigned polyphonyLimit { config::maxVoices };
};

}
Loading