commit
bf0786ca63
3 changed files with 119 additions and 46 deletions
|
|
@ -144,6 +144,7 @@ void sfz::Voice::startVoice(Region* region, int delay, const TriggerEvent& event
|
||||||
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, triggerEvent.value, sampleRate);
|
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, triggerEvent.value, sampleRate);
|
||||||
|
|
||||||
resources.modMatrix.initVoice(id, region->getId(), delay);
|
resources.modMatrix.initVoice(id, region->getId(), delay);
|
||||||
|
saveModulationTargets(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sfz::Voice::getCurrentSampleQuality() const noexcept
|
int sfz::Voice::getCurrentSampleQuality() const noexcept
|
||||||
|
|
@ -370,22 +371,20 @@ void sfz::Voice::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
|
||||||
const auto numSamples = modulationSpan.size();
|
const auto numSamples = modulationSpan.size();
|
||||||
|
|
||||||
ModMatrix& mm = resources.modMatrix;
|
ModMatrix& mm = resources.modMatrix;
|
||||||
const ModKey volumeKey = ModKey::createNXYZ(ModId::Volume, region->getId());
|
|
||||||
const ModKey amplitudeKey = ModKey::createNXYZ(ModId::Amplitude, region->getId());
|
|
||||||
|
|
||||||
// AmpEG envelope
|
// AmpEG envelope
|
||||||
egEnvelope.getBlock(modulationSpan);
|
egEnvelope.getBlock(modulationSpan);
|
||||||
|
|
||||||
// Amplitude envelope
|
// Amplitude envelope
|
||||||
applyGain1<float>(baseGain, modulationSpan);
|
applyGain1<float>(baseGain, modulationSpan);
|
||||||
if (float* mod = mm.getModulationByKey(amplitudeKey)) {
|
if (float* mod = mm.getModulation(amplitudeTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
modulationSpan[i] *= normalizePercents(mod[i]);
|
modulationSpan[i] *= normalizePercents(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Volume envelope
|
// Volume envelope
|
||||||
applyGain1<float>(db2mag(baseVolumedB), modulationSpan);
|
applyGain1<float>(db2mag(baseVolumedB), modulationSpan);
|
||||||
if (float* mod = mm.getModulationByKey(volumeKey)) {
|
if (float* mod = mm.getModulation(volumeTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
modulationSpan[i] *= db2mag(mod[i]);
|
modulationSpan[i] *= db2mag(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -437,14 +436,13 @@ void sfz::Voice::panStageMono(AudioSpan<float> buffer) noexcept
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ModMatrix& mm = resources.modMatrix;
|
ModMatrix& mm = resources.modMatrix;
|
||||||
const ModKey panKey = ModKey::createNXYZ(ModId::Pan, region->getId());
|
|
||||||
|
|
||||||
// Prepare for stereo output
|
// Prepare for stereo output
|
||||||
copy<float>(leftBuffer, rightBuffer);
|
copy<float>(leftBuffer, rightBuffer);
|
||||||
|
|
||||||
// Apply panning
|
// Apply panning
|
||||||
fill(*modulationSpan, region->pan);
|
fill(*modulationSpan, region->pan);
|
||||||
if (float* mod = mm.getModulationByKey(panKey)) {
|
if (float* mod = mm.getModulation(panTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -463,13 +461,10 @@ void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ModMatrix& mm = resources.modMatrix;
|
ModMatrix& mm = resources.modMatrix;
|
||||||
const ModKey panKey = ModKey::createNXYZ(ModId::Pan, region->getId());
|
|
||||||
const ModKey widthKey = ModKey::createNXYZ(ModId::Width, region->getId());
|
|
||||||
const ModKey positionKey = ModKey::createNXYZ(ModId::Position, region->getId());
|
|
||||||
|
|
||||||
// Apply panning
|
// Apply panning
|
||||||
fill(*modulationSpan, region->pan);
|
fill(*modulationSpan, region->pan);
|
||||||
if (float* mod = mm.getModulationByKey(panKey)) {
|
if (float* mod = mm.getModulation(panTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -477,14 +472,14 @@ void sfz::Voice::panStageStereo(AudioSpan<float> buffer) noexcept
|
||||||
|
|
||||||
// Apply the width/position process
|
// Apply the width/position process
|
||||||
fill(*modulationSpan, region->width);
|
fill(*modulationSpan, region->width);
|
||||||
if (float* mod = mm.getModulationByKey(widthKey)) {
|
if (float* mod = mm.getModulation(widthTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
||||||
}
|
}
|
||||||
width(*modulationSpan, leftBuffer, rightBuffer);
|
width(*modulationSpan, leftBuffer, rightBuffer);
|
||||||
|
|
||||||
fill(*modulationSpan, region->position);
|
fill(*modulationSpan, region->position);
|
||||||
if (float* mod = mm.getModulationByKey(positionKey)) {
|
if (float* mod = mm.getModulation(positionTarget)) {
|
||||||
for (size_t i = 0; i < numSamples; ++i)
|
for (size_t i = 0; i < numSamples; ++i)
|
||||||
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
(*modulationSpan)[i] += normalizePercents(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -889,9 +884,8 @@ void sfz::Voice::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
|
||||||
applyGain<float>(*bends, pitchSpan);
|
applyGain<float>(*bends, pitchSpan);
|
||||||
|
|
||||||
ModMatrix& mm = resources.modMatrix;
|
ModMatrix& mm = resources.modMatrix;
|
||||||
const ModKey pitchKey = ModKey::createNXYZ(ModId::Pitch, region->getId());
|
|
||||||
|
|
||||||
if (float* mod = mm.getModulationByKey(pitchKey)) {
|
if (float* mod = mm.getModulation(pitchTarget)) {
|
||||||
for (size_t i = 0; i < numFrames; ++i)
|
for (size_t i = 0; i < numFrames; ++i)
|
||||||
pitchSpan[i] *= centsFactor(mod[i]);
|
pitchSpan[i] *= centsFactor(mod[i]);
|
||||||
}
|
}
|
||||||
|
|
@ -902,3 +896,14 @@ void sfz::Voice::resetSmoothers() noexcept
|
||||||
bendSmoother.reset(1.0f);
|
bendSmoother.reset(1.0f);
|
||||||
gainSmoother.reset(0.0f);
|
gainSmoother.reset(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::Voice::saveModulationTargets(const Region* region) noexcept
|
||||||
|
{
|
||||||
|
ModMatrix& mm = resources.modMatrix;
|
||||||
|
amplitudeTarget = mm.findTarget(ModKey::createNXYZ(ModId::Amplitude, region->getId()));
|
||||||
|
volumeTarget = mm.findTarget(ModKey::createNXYZ(ModId::Volume, region->getId()));
|
||||||
|
panTarget = mm.findTarget(ModKey::createNXYZ(ModId::Pan, region->getId()));
|
||||||
|
positionTarget = mm.findTarget(ModKey::createNXYZ(ModId::Position, region->getId()));
|
||||||
|
widthTarget = mm.findTarget(ModKey::createNXYZ(ModId::Width, region->getId()));
|
||||||
|
pitchTarget = mm.findTarget(ModKey::createNXYZ(ModId::Pitch, region->getId()));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,12 @@ private:
|
||||||
*/
|
*/
|
||||||
void switchState(State s);
|
void switchState(State s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save the modulation targets to avoid recomputing them in every callback.
|
||||||
|
* Must be called during startVoice() ideally.
|
||||||
|
*/
|
||||||
|
void saveModulationTargets(const Region* region) noexcept;
|
||||||
|
|
||||||
const NumericId<Voice> id;
|
const NumericId<Voice> id;
|
||||||
StateListener* stateListener = nullptr;
|
StateListener* stateListener = nullptr;
|
||||||
|
|
||||||
|
|
@ -466,6 +472,13 @@ private:
|
||||||
Smoother xfadeSmoother;
|
Smoother xfadeSmoother;
|
||||||
void resetSmoothers() noexcept;
|
void resetSmoothers() noexcept;
|
||||||
|
|
||||||
|
ModMatrix::TargetId amplitudeTarget;
|
||||||
|
ModMatrix::TargetId volumeTarget;
|
||||||
|
ModMatrix::TargetId panTarget;
|
||||||
|
ModMatrix::TargetId positionTarget;
|
||||||
|
ModMatrix::TargetId widthTarget;
|
||||||
|
ModMatrix::TargetId pitchTarget;
|
||||||
|
|
||||||
PowerFollower powerFollower;
|
PowerFollower powerFollower;
|
||||||
|
|
||||||
LEAK_DETECTOR(Voice);
|
LEAK_DETECTOR(Voice);
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,13 @@ struct ModMatrix::Impl {
|
||||||
absl::flat_hash_map<ModKey, uint32_t> sourceIndex_;
|
absl::flat_hash_map<ModKey, uint32_t> sourceIndex_;
|
||||||
absl::flat_hash_map<ModKey, uint32_t> targetIndex_;
|
absl::flat_hash_map<ModKey, uint32_t> targetIndex_;
|
||||||
|
|
||||||
|
std::vector<uint32_t> sourceIndicesForGlobal_;
|
||||||
|
std::vector<uint32_t> targetIndicesForGlobal_;
|
||||||
|
|
||||||
|
int maxRegionIdx_ { -1 };
|
||||||
|
std::vector<std::vector<uint32_t>> sourceIndicesForRegion_;
|
||||||
|
std::vector<std::vector<uint32_t>> targetIndicesForRegion_;
|
||||||
|
|
||||||
std::vector<Source> sources_;
|
std::vector<Source> sources_;
|
||||||
std::vector<Target> targets_;
|
std::vector<Target> targets_;
|
||||||
};
|
};
|
||||||
|
|
@ -72,6 +79,11 @@ void ModMatrix::clear()
|
||||||
impl.targetIndex_.clear();
|
impl.targetIndex_.clear();
|
||||||
impl.sources_.clear();
|
impl.sources_.clear();
|
||||||
impl.targets_.clear();
|
impl.targets_.clear();
|
||||||
|
impl.sourceIndicesForGlobal_.clear();
|
||||||
|
impl.targetIndicesForGlobal_.clear();
|
||||||
|
impl.sourceIndicesForRegion_.clear();
|
||||||
|
impl.targetIndicesForRegion_.clear();
|
||||||
|
impl.maxRegionIdx_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModMatrix::setSampleRate(double sampleRate)
|
void ModMatrix::setSampleRate(double sampleRate)
|
||||||
|
|
@ -124,6 +136,8 @@ ModMatrix::SourceId ModMatrix::registerSource(const ModKey& key, ModGenerator& g
|
||||||
source.buffer.resize(impl.samplesPerBlock_);
|
source.buffer.resize(impl.samplesPerBlock_);
|
||||||
|
|
||||||
impl.sourceIndex_[key] = id.number();
|
impl.sourceIndex_[key] = id.number();
|
||||||
|
if (key.region().number() > impl.maxRegionIdx_)
|
||||||
|
impl.maxRegionIdx_ = key.region().number();
|
||||||
|
|
||||||
gen.setSampleRate(impl.sampleRate_);
|
gen.setSampleRate(impl.sampleRate_);
|
||||||
gen.setSamplesPerBlock(impl.samplesPerBlock_);
|
gen.setSamplesPerBlock(impl.samplesPerBlock_);
|
||||||
|
|
@ -148,6 +162,9 @@ ModMatrix::TargetId ModMatrix::registerTarget(const ModKey& key)
|
||||||
target.buffer.resize(impl.samplesPerBlock_);
|
target.buffer.resize(impl.samplesPerBlock_);
|
||||||
|
|
||||||
impl.targetIndex_[key] = id.number();
|
impl.targetIndex_[key] = id.number();
|
||||||
|
if (key.region().number() > impl.maxRegionIdx_)
|
||||||
|
impl.maxRegionIdx_ = key.region().number();
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,21 +210,50 @@ void ModMatrix::init()
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
if (impl.maxRegionIdx_ >= 0) {
|
||||||
|
const size_t numRegions = impl.maxRegionIdx_ + 1;
|
||||||
|
impl.sourceIndicesForRegion_.resize(numRegions);
|
||||||
|
impl.targetIndicesForRegion_.resize(numRegions);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < impl.sources_.size(); ++i) {
|
||||||
|
Impl::Source& source = impl.sources_[i];
|
||||||
const int flags = source.key.flags();
|
const int flags = source.key.flags();
|
||||||
if (flags & kModIsPerCycle)
|
if (flags & kModIsPerCycle) {
|
||||||
|
ASSERT(!source.key.region());
|
||||||
source.gen->init(source.key, {}, 0);
|
source.gen->init(source.key, {}, 0);
|
||||||
|
impl.sourceIndicesForGlobal_.push_back(i);
|
||||||
|
}
|
||||||
|
else if (flags & kModIsPerVoice) {
|
||||||
|
ASSERT(source.key.region());
|
||||||
|
impl.sourceIndicesForRegion_[source.key.region().number()].push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < impl.targets_.size(); ++i) {
|
||||||
|
Impl::Target& target = impl.targets_[i];
|
||||||
|
const int flags = target.key.flags();
|
||||||
|
if (flags & kModIsPerCycle) {
|
||||||
|
ASSERT(!target.key.region());
|
||||||
|
impl.targetIndicesForGlobal_.push_back(i);
|
||||||
|
}
|
||||||
|
else if (flags & kModIsPerVoice) {
|
||||||
|
ASSERT(target.key.region());
|
||||||
|
impl.targetIndicesForRegion_[target.key.region().number()].push_back(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModMatrix::initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId, unsigned delay)
|
void ModMatrix::initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId, unsigned delay)
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
|
ASSERT(regionId);
|
||||||
|
ASSERT(static_cast<size_t>(regionId.number()) < impl.sourceIndicesForRegion_.size());
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
const auto idNumber = static_cast<size_t>(regionId.number());
|
||||||
const int flags = source.key.flags();
|
for (auto idx: impl.sourceIndicesForRegion_[idNumber]) {
|
||||||
if ((flags & kModIsPerVoice) && source.key.region() == regionId)
|
const Impl::Source& source = impl.sources_[idx];
|
||||||
source.gen->init(source.key, voiceId, delay);
|
source.gen->init(source.key, voiceId, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,10 +261,12 @@ void ModMatrix::releaseVoice(NumericId<Voice> voiceId, NumericId<Region> regionI
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
ASSERT(regionId);
|
||||||
const int flags = source.key.flags();
|
|
||||||
if ((flags & kModIsPerVoice) && source.key.region() == regionId)
|
const auto idNumber = static_cast<size_t>(regionId.number());
|
||||||
source.gen->release(source.key, voiceId, delay);
|
for (auto idx: impl.sourceIndicesForRegion_[idNumber]) {
|
||||||
|
const Impl::Source& source = impl.sources_[idx];
|
||||||
|
source.gen->release(source.key, voiceId, delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,10 +276,14 @@ void ModMatrix::beginCycle(unsigned numFrames)
|
||||||
|
|
||||||
impl.numFrames_ = numFrames;
|
impl.numFrames_ = numFrames;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_)
|
for (auto idx: impl.sourceIndicesForGlobal_) {
|
||||||
|
Impl::Source& source = impl.sources_[idx];
|
||||||
source.bufferReady = false;
|
source.bufferReady = false;
|
||||||
for (Impl::Target &target : impl.targets_)
|
}
|
||||||
|
for (auto idx: impl.targetIndicesForGlobal_) {
|
||||||
|
Impl::Target& target = impl.targets_[idx];
|
||||||
target.bufferReady = false;
|
target.bufferReady = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModMatrix::endCycle()
|
void ModMatrix::endCycle()
|
||||||
|
|
@ -239,13 +291,11 @@ void ModMatrix::endCycle()
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
const uint32_t numFrames = impl.numFrames_;
|
const uint32_t numFrames = impl.numFrames_;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
for (auto idx: impl.sourceIndicesForGlobal_) {
|
||||||
|
Impl::Source& source = impl.sources_[idx];
|
||||||
if (!source.bufferReady) {
|
if (!source.bufferReady) {
|
||||||
const int flags = source.key.flags();
|
absl::Span<float> buffer(source.buffer.data(), numFrames);
|
||||||
if (flags & kModIsPerCycle) {
|
source.gen->generateDiscarded(source.key, {}, buffer);
|
||||||
absl::Span<float> buffer(source.buffer.data(), numFrames);
|
|
||||||
source.gen->generateDiscarded(source.key, {}, buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,15 +309,17 @@ void ModMatrix::beginVoice(NumericId<Voice> voiceId, NumericId<Region> regionId)
|
||||||
impl.currentVoiceId_ = voiceId;
|
impl.currentVoiceId_ = voiceId;
|
||||||
impl.currentRegionId_ = regionId;
|
impl.currentRegionId_ = regionId;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
ASSERT(regionId);
|
||||||
const int flags = source.key.flags();
|
|
||||||
if (flags & kModIsPerVoice)
|
const auto idNumber = static_cast<size_t>(regionId.number());
|
||||||
source.bufferReady = false;
|
for (auto idx: impl.sourceIndicesForRegion_[idNumber]) {
|
||||||
|
Impl::Source& source = impl.sources_[idx];
|
||||||
|
source.bufferReady = false;
|
||||||
}
|
}
|
||||||
for (Impl::Target &target : impl.targets_) {
|
|
||||||
const int flags = target.key.flags();
|
for (auto idx: impl.targetIndicesForRegion_[idNumber]) {
|
||||||
if (flags & kModIsPerVoice)
|
Impl::Target& target = impl.targets_[idx];
|
||||||
target.bufferReady = false;
|
target.bufferReady = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,13 +330,16 @@ void ModMatrix::endVoice()
|
||||||
const NumericId<Voice> voiceId = impl.currentVoiceId_;
|
const NumericId<Voice> voiceId = impl.currentVoiceId_;
|
||||||
const NumericId<Region> regionId = impl.currentRegionId_;
|
const NumericId<Region> regionId = impl.currentRegionId_;
|
||||||
|
|
||||||
for (Impl::Source &source : impl.sources_) {
|
ASSERT(regionId);
|
||||||
|
ASSERT(static_cast<size_t>(regionId.number()) < impl.sourceIndicesForRegion_.size());
|
||||||
|
|
||||||
|
const auto idNumber = static_cast<size_t>(regionId.number());
|
||||||
|
|
||||||
|
for (auto idx: impl.sourceIndicesForRegion_[idNumber]) {
|
||||||
|
const Impl::Source& source = impl.sources_[idx];
|
||||||
if (!source.bufferReady) {
|
if (!source.bufferReady) {
|
||||||
const int flags = source.key.flags();
|
absl::Span<float> buffer(source.buffer.data(), numFrames);
|
||||||
if ((flags & kModIsPerVoice) && source.key.region() == regionId) {
|
source.gen->generateDiscarded(source.key, voiceId, buffer);
|
||||||
absl::Span<float> buffer(source.buffer.data(), numFrames);
|
|
||||||
source.gen->generateDiscarded(source.key, voiceId, buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue