Build time optimization: resources

This commit is contained in:
Jean Pierre Cimalando 2021-04-25 03:52:14 +02:00
parent f5bed98356
commit 8551caebf6
20 changed files with 494 additions and 268 deletions

View file

@ -107,6 +107,7 @@ SFIZZ_SOURCES = \
src/sfizz/PowerFollower.cpp \ src/sfizz/PowerFollower.cpp \
src/sfizz/Region.cpp \ src/sfizz/Region.cpp \
src/sfizz/RegionSet.cpp \ src/sfizz/RegionSet.cpp \
src/sfizz/Resources.cpp \
src/sfizz/RTSemaphore.cpp \ src/sfizz/RTSemaphore.cpp \
src/sfizz/ScopedFTZ.cpp \ src/sfizz/ScopedFTZ.cpp \
src/sfizz/sfizz.cpp \ src/sfizz/sfizz.cpp \

View file

@ -161,6 +161,7 @@ set(SFIZZ_SOURCES
sfizz/WindowedSinc.cpp sfizz/WindowedSinc.cpp
sfizz/Interpolators.cpp sfizz/Interpolators.cpp
sfizz/Layer.cpp sfizz/Layer.cpp
sfizz/Resources.cpp
sfizz/modulations/ModId.cpp sfizz/modulations/ModId.cpp
sfizz/modulations/ModKey.cpp sfizz/modulations/ModKey.cpp
sfizz/modulations/ModKeyHash.cpp sfizz/modulations/ModKeyHash.cpp

View file

@ -1,4 +1,7 @@
#include "EQPool.h" #include "EQPool.h"
#include "Region.h"
#include "Resources.h"
#include "BufferPool.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "utility/SwapAndPop.h" #include "utility/SwapAndPop.h"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
@ -31,9 +34,10 @@ void sfz::EQHolder::setup(const Region& region, unsigned eqId, float velocity)
baseBandwidth = description->bandwidth; baseBandwidth = description->bandwidth;
baseGain = description->gain + velocity * description->vel2gain; baseGain = description->gain + velocity * description->vel2gain;
gainTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId)); const ModMatrix& mm = resources.getModMatrix();
bandwidthTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId)); gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqGain, region.id, eqId));
frequencyTarget = resources.modMatrix.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId)); bandwidthTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqBandwidth, region.id, eqId));
frequencyTarget = mm.findTarget(ModKey::createNXYZ(ModId::EqFrequency, region.id, eqId));
// Disables smoothing of the parameters on the first call // Disables smoothing of the parameters on the first call
prepared = false; prepared = false;
@ -47,10 +51,11 @@ void sfz::EQHolder::process(const float** inputs, float** outputs, unsigned numF
return; return;
} }
ModMatrix& mm = resources.modMatrix; ModMatrix& mm = resources.getModMatrix();
auto frequencySpan = resources.bufferPool.getBuffer(numFrames); BufferPool& bufferPool = resources.getBufferPool();
auto bandwidthSpan = resources.bufferPool.getBuffer(numFrames); auto frequencySpan = bufferPool.getBuffer(numFrames);
auto gainSpan = resources.bufferPool.getBuffer(numFrames); auto bandwidthSpan = bufferPool.getBuffer(numFrames);
auto gainSpan = bufferPool.getBuffer(numFrames);
if (!frequencySpan || !bandwidthSpan || !gainSpan) if (!frequencySpan || !bandwidthSpan || !gainSpan)
return; return;

View file

@ -1,12 +1,14 @@
#pragma once #pragma once
#include "SfzFilter.h" #include "SfzFilter.h"
#include "Region.h" #include "Defaults.h"
#include "Resources.h" #include "modulations/ModMatrix.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
namespace sfz namespace sfz {
{ struct Region;
class Resources;
struct EQDescription;
class EQHolder class EQHolder
{ {
@ -52,4 +54,4 @@ private:
ModMatrix::TargetId bandwidthTarget; ModMatrix::TargetId bandwidthTarget;
}; };
} } // namespace sfz

View file

@ -1,4 +1,7 @@
#include "FilterPool.h" #include "FilterPool.h"
#include "Region.h"
#include "Resources.h"
#include "BufferPool.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "utility/SwapAndPop.h" #include "utility/SwapAndPop.h"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
@ -42,7 +45,7 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN
baseGain = description->gain; baseGain = description->gain;
baseResonance = description->resonance; baseResonance = description->resonance;
ModMatrix& mm = resources.modMatrix; ModMatrix& mm = resources.getModMatrix();
gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilGain, region.id, filterId)); gainTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilGain, region.id, filterId));
cutoffTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilCutoff, region.id, filterId)); cutoffTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilCutoff, region.id, filterId));
resonanceTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilResonance, region.id, filterId)); resonanceTarget = mm.findTarget(ModKey::createNXYZ(ModId::FilResonance, region.id, filterId));
@ -62,10 +65,11 @@ void sfz::FilterHolder::process(const float** inputs, float** outputs, unsigned
return; return;
} }
ModMatrix& mm = resources.modMatrix; ModMatrix& mm = resources.getModMatrix();
auto cutoffSpan = resources.bufferPool.getBuffer(numFrames); BufferPool& bufferPool = resources.getBufferPool();
auto resonanceSpan = resources.bufferPool.getBuffer(numFrames); auto cutoffSpan = bufferPool.getBuffer(numFrames);
auto gainSpan = resources.bufferPool.getBuffer(numFrames); auto resonanceSpan = bufferPool.getBuffer(numFrames);
auto gainSpan = bufferPool.getBuffer(numFrames);
if (!cutoffSpan || !resonanceSpan || !gainSpan) if (!cutoffSpan || !resonanceSpan || !gainSpan)
return; return;

View file

@ -1,13 +1,14 @@
#pragma once #pragma once
#include "SfzFilter.h" #include "SfzFilter.h"
#include "Region.h"
#include "Resources.h"
#include "Defaults.h" #include "Defaults.h"
#include "modulations/ModMatrix.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
namespace sfz namespace sfz {
{ struct Region;
class Resources;
struct FilterDescription;
class FilterHolder class FilterHolder
{ {
@ -54,4 +55,4 @@ private:
bool prepared { false }; bool prepared { false };
}; };
} } // namespace sfz

View file

@ -10,6 +10,10 @@
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "Config.h" #include "Config.h"
#include "Resources.h" #include "Resources.h"
#include "BufferPool.h"
#include "BeatClock.h"
#include "MidiState.h"
#include "modulations/ModMatrix.h"
#include "modulations/ModKey.h" #include "modulations/ModKey.h"
#include "modulations/ModId.h" #include "modulations/ModId.h"
#include <array> #include <array>
@ -61,7 +65,7 @@ void LFO::setSampleRate(double sampleRate)
void LFO::configure(const LFODescription* desc) void LFO::configure(const LFODescription* desc)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
ModMatrix& modMatrix = impl.resources_.modMatrix; ModMatrix& modMatrix = impl.resources_.getModMatrix();
impl.desc_ = desc ? desc : &LFODescription::getDefault(); impl.desc_ = desc ? desc : &LFODescription::getDefault();
impl.beatsKeyId = modMatrix.findTarget(desc->beatsKey); impl.beatsKeyId = modMatrix.findTarget(desc->beatsKey);
impl.freqKeyId = modMatrix.findTarget(desc->freqKey); impl.freqKeyId = modMatrix.findTarget(desc->freqKey);
@ -73,7 +77,7 @@ void LFO::start(unsigned triggerDelay)
Impl& impl = *impl_; Impl& impl = *impl_;
const LFODescription& desc = *impl.desc_; const LFODescription& desc = *impl.desc_;
const float sampleRate = impl.sampleRate_; const float sampleRate = impl.sampleRate_;
const MidiState& state = impl.resources_.midiState; const MidiState& state = impl.resources_.getMidiState();
impl.subPhases_.fill(0.0f); impl.subPhases_.fill(0.0f);
impl.sampleHoldMem_.fill(0.0f); impl.sampleHoldMem_.fill(0.0f);
@ -230,7 +234,7 @@ void LFO::process(absl::Span<float> out)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
const LFODescription& desc = *impl.desc_; const LFODescription& desc = *impl.desc_;
BufferPool& pool = impl.resources_.bufferPool; BufferPool& pool = impl.resources_.getBufferPool();
size_t numFrames = out.size(); size_t numFrames = out.size();
fill(out, 0.0f); fill(out, 0.0f);
@ -323,9 +327,9 @@ void LFO::processFadeIn(absl::Span<float> out)
void LFO::generatePhase(unsigned nth, absl::Span<float> phases) void LFO::generatePhase(unsigned nth, absl::Span<float> phases)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
BufferPool& bufferPool = impl.resources_.bufferPool; BufferPool& bufferPool = impl.resources_.getBufferPool();
BeatClock& beatClock = impl.resources_.beatClock; BeatClock& beatClock = impl.resources_.getBeatClock();
ModMatrix& modMatrix = impl.resources_.modMatrix; ModMatrix& modMatrix = impl.resources_.getModMatrix();
const LFODescription& desc = *impl.desc_; const LFODescription& desc = *impl.desc_;
const LFODescription::Sub& sub = desc.sub[nth]; const LFODescription::Sub& sub = desc.sub[nth];
const float samplePeriod = 1.0f / impl.sampleRate_; const float samplePeriod = 1.0f / impl.sampleRate_;

View file

@ -10,7 +10,7 @@
#include <memory> #include <memory>
namespace sfz { namespace sfz {
struct Resources; class Resources;
struct Region; struct Region;
enum class LFOWave : int; enum class LFOWave : int;

137
src/sfizz/Resources.cpp Normal file
View file

@ -0,0 +1,137 @@
// 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
#include "Resources.h"
#include "SynthConfig.h"
#include "MidiState.h"
#include "FilePool.h"
#include "BufferPool.h"
#include "Logger.h"
#include "Wavetables.h"
#include "Curve.h"
#include "Tuning.h"
#include "BeatClock.h"
#include "Metronome.h"
#include "modulations/ModMatrix.h"
namespace sfz {
struct Resources::Impl {
SynthConfig synthConfig;
BufferPool bufferPool;
MidiState midiState;
Logger logger;
CurveSet curves;
FilePool filePool { logger };
WavetablePool wavePool;
Tuning tuning;
absl::optional<StretchTuning> stretch;
ModMatrix modMatrix;
BeatClock beatClock;
Metronome metronome;
};
Resources::Resources()
: impl_(new Impl)
{
}
Resources::~Resources()
{
}
void Resources::setSampleRate(float samplerate)
{
Impl& impl = *impl_;
impl.midiState.setSampleRate(samplerate);
impl.modMatrix.setSampleRate(samplerate);
impl.beatClock.setSampleRate(samplerate);
impl.metronome.init(samplerate);
}
void Resources::setSamplesPerBlock(int samplesPerBlock)
{
Impl& impl = *impl_;
impl.bufferPool.setBufferSize(samplesPerBlock);
impl.midiState.setSamplesPerBlock(samplesPerBlock);
impl.modMatrix.setSamplesPerBlock(samplesPerBlock);
impl.beatClock.setSamplesPerBlock(samplesPerBlock);
}
void Resources::clear()
{
Impl& impl = *impl_;
impl.curves = CurveSet::createPredefined();
impl.filePool.clear();
impl.wavePool.clearFileWaves();
impl.logger.clear();
impl.midiState.reset();
impl.modMatrix.clear();
impl.beatClock.clear();
impl.metronome.clear();
}
const SynthConfig& Resources::getSynthConfig() const noexcept
{
return impl_->synthConfig;
}
const BufferPool& Resources::getBufferPool() const noexcept
{
return impl_->bufferPool;
}
const MidiState& Resources::getMidiState() const noexcept
{
return impl_->midiState;
}
const Logger& Resources::getLogger() const noexcept
{
return impl_->logger;
}
const CurveSet& Resources::getCurves() const noexcept
{
return impl_->curves;
}
const FilePool& Resources::getFilePool() const noexcept
{
return impl_->filePool;
}
const WavetablePool& Resources::getWavePool() const noexcept
{
return impl_->wavePool;
}
const Tuning& Resources::getTuning() const noexcept
{
return impl_->tuning;
}
const absl::optional<StretchTuning>& Resources::getStretch() const noexcept
{
return impl_->stretch;
}
const ModMatrix& Resources::getModMatrix() const noexcept
{
return impl_->modMatrix;
}
const BeatClock& Resources::getBeatClock() const noexcept
{
return impl_->beatClock;
}
const Metronome& Resources::getMetronome() const noexcept
{
return impl_->metronome;
}
} // namespace sfz

View file

@ -5,64 +5,56 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once #pragma once
#include "SynthConfig.h"
#include "MidiState.h"
#include "FilePool.h"
#include "BufferPool.h"
#include "Logger.h"
#include "Wavetables.h"
#include "Curve.h"
#include "Tuning.h"
#include "BeatClock.h"
#include "Metronome.h"
#include "modulations/ModMatrix.h"
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include <memory>
namespace sfz namespace sfz {
{
class WavetableMulti;
struct Resources struct SynthConfig;
{ class BufferPool;
SynthConfig synthConfig; class MidiState;
BufferPool bufferPool; class Logger;
MidiState midiState; class CurveSet;
Logger logger; class FilePool;
CurveSet curves; struct WavetablePool;
FilePool filePool { logger }; class Tuning;
WavetablePool wavePool; class StretchTuning;
Tuning tuning; class ModMatrix;
absl::optional<StretchTuning> stretch; class BeatClock;
ModMatrix modMatrix; class Metronome;
BeatClock beatClock;
Metronome metronome;
void setSampleRate(float samplerate) class Resources
{ {
midiState.setSampleRate(samplerate); public:
modMatrix.setSampleRate(samplerate); Resources();
beatClock.setSampleRate(samplerate); ~Resources();
metronome.init(samplerate);
}
void setSamplesPerBlock(int samplesPerBlock) void setSampleRate(float samplerate);
{ void setSamplesPerBlock(int samplesPerBlock);
bufferPool.setBufferSize(samplesPerBlock); void clear();
midiState.setSamplesPerBlock(samplesPerBlock);
modMatrix.setSamplesPerBlock(samplesPerBlock);
beatClock.setSamplesPerBlock(samplesPerBlock);
}
void clear() #define ACCESSOR_RW(Accessor, RetTy) \
{ RetTy const& Accessor() const noexcept; \
curves = CurveSet::createPredefined(); RetTy& Accessor() noexcept { return const_cast<RetTy&>(const_cast<const Resources*>(this)->Accessor()); }
filePool.clear();
wavePool.clearFileWaves(); ACCESSOR_RW(getSynthConfig, SynthConfig);
logger.clear(); ACCESSOR_RW(getBufferPool, BufferPool);
midiState.reset(); ACCESSOR_RW(getMidiState, MidiState);
modMatrix.clear(); ACCESSOR_RW(getLogger, Logger);
beatClock.clear(); ACCESSOR_RW(getCurves, CurveSet);
metronome.clear(); ACCESSOR_RW(getFilePool, FilePool);
} ACCESSOR_RW(getWavePool, WavetablePool);
ACCESSOR_RW(getTuning, Tuning);
ACCESSOR_RW(getStretch, absl::optional<StretchTuning>);
ACCESSOR_RW(getModMatrix, ModMatrix);
ACCESSOR_RW(getBeatClock, BeatClock);
ACCESSOR_RW(getMetronome, Metronome);
#undef ACCESSOR_RW
private:
struct Impl;
std::unique_ptr<Impl> impl_;
}; };
}
} // namespace sfz

View file

@ -16,6 +16,13 @@
#include "Region.h" #include "Region.h"
#include "RegionSet.h" #include "RegionSet.h"
#include "Resources.h" #include "Resources.h"
#include "BufferPool.h"
#include "FilePool.h"
#include "Wavetables.h"
#include "Tuning.h"
#include "BeatClock.h"
#include "Metronome.h"
#include "SynthConfig.h"
#include "ScopedFTZ.h" #include "ScopedFTZ.h"
#include "utility/StringViewHelpers.h" #include "utility/StringViewHelpers.h"
#include "utility/XmlHelpers.h" #include "utility/XmlHelpers.h"
@ -59,18 +66,19 @@ Synth::Impl::Impl()
resetVoices(config::numVoices); resetVoices(config::numVoices);
// modulation sources // modulation sources
MidiState& midiState = resources_.getMidiState();
genController_.reset(new ControllerSource(resources_, voiceManager_)); genController_.reset(new ControllerSource(resources_, voiceManager_));
genLFO_.reset(new LFOSource(voiceManager_)); genLFO_.reset(new LFOSource(voiceManager_));
genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_)); genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_));
genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, resources_.midiState)); genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, midiState));
genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, resources_.midiState)); genChannelAftertouch_.reset(new ChannelAftertouchSource(voiceManager_, midiState));
genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, resources_.midiState)); genPolyAftertouch_.reset(new PolyAftertouchSource(voiceManager_, midiState));
} }
Synth::Impl::~Impl() Synth::Impl::~Impl()
{ {
voiceManager_.reset(); voiceManager_.reset();
resources_.filePool.emptyFileLoadingQueues(); resources_.getFilePool().emptyFileLoadingQueues();
} }
void Synth::Impl::onParseFullBlock(const std::string& header, const std::vector<Opcode>& members) void Synth::Impl::onParseFullBlock(const std::string& header, const std::vector<Opcode>& members)
@ -113,7 +121,7 @@ void Synth::Impl::onParseFullBlock(const std::string& header, const std::vector<
buildRegion(members); buildRegion(members);
break; break;
case hash("curve"): case hash("curve"):
resources_.curves.addCurveFromHeader(members); resources_.getCurves().addCurveFromHeader(members);
break; break;
case hash("effect"): case hash("effect"):
handleEffectOpcodes(members); handleEffectOpcodes(members);
@ -138,7 +146,8 @@ void Synth::Impl::onParseWarning(const SourceRange& range, const std::string& me
void Synth::Impl::buildRegion(const std::vector<Opcode>& regionOpcodes) void Synth::Impl::buildRegion(const std::vector<Opcode>& regionOpcodes)
{ {
int regionNumber = static_cast<int>(layers_.size()); int regionNumber = static_cast<int>(layers_.size());
Layer* lastLayer = new Layer(regionNumber, defaultPath_, resources_.midiState); MidiState& midiState = resources_.getMidiState();
Layer* lastLayer = new Layer(regionNumber, defaultPath_, midiState);
layers_.emplace_back(lastLayer); layers_.emplace_back(lastLayer);
Region* lastRegion = &lastLayer->getRegion(); Region* lastRegion = &lastLayer->getRegion();
@ -221,8 +230,11 @@ void Synth::Impl::buildRegion(const std::vector<Opcode>& regionOpcodes)
void Synth::Impl::clear() void Synth::Impl::clear()
{ {
FilePool& filePool = resources_.getFilePool();
MidiState& midiState = resources_.getMidiState();
// Clear the background queues before removing everyone // Clear the background queues before removing everyone
resources_.filePool.waitForBackgroundLoading(); filePool.waitForBackgroundLoading();
voiceManager_.reset(); voiceManager_.reset();
for (auto& list : lastKeyswitchLists_) for (auto& list : lastKeyswitchLists_)
@ -253,9 +265,9 @@ void Synth::Impl::clear()
currentSwitch_ = absl::nullopt; currentSwitch_ = absl::nullopt;
defaultPath_ = ""; defaultPath_ = "";
image_ = ""; image_ = "";
resources_.midiState.reset(); midiState.reset();
resources_.filePool.clear(); filePool.clear();
resources_.filePool.setRamLoading(config::loadInRam); filePool.setRamLoading(config::loadInRam);
clearCCLabels(); clearCCLabels();
currentUsedCCs_.clear(); currentUsedCCs_.clear();
changedCCsThisCycle_.clear(); changedCCsThisCycle_.clear();
@ -399,13 +411,16 @@ void Synth::Impl::handleControlOpcodes(const std::vector<Opcode>& members)
octaveOffset_ = member.read(Default::octaveOffset); octaveOffset_ = member.read(Default::octaveOffset);
break; break;
case hash("hint_ram_based"): case hash("hint_ram_based"):
{
FilePool& filePool = resources_.getFilePool();
if (member.value == "1") if (member.value == "1")
resources_.filePool.setRamLoading(true); filePool.setRamLoading(true);
else if (member.value == "0") else if (member.value == "0")
resources_.filePool.setRamLoading(false); filePool.setRamLoading(false);
else else
DBG("Unsupported value for hint_ram_based: " << member.value); DBG("Unsupported value for hint_ram_based: " << member.value);
break; break;
}
case hash("hint_stealing"): case hash("hint_stealing"):
switch(hash(member.value)) { switch(hash(member.value)) {
case hash("first"): case hash("first"):
@ -550,7 +565,8 @@ bool Synth::loadSfzString(const fs::path& path, absl::string_view text)
void Synth::Impl::finalizeSfzLoad() void Synth::Impl::finalizeSfzLoad()
{ {
const fs::path& rootDirectory = parser_.originalDirectory(); const fs::path& rootDirectory = parser_.originalDirectory();
resources_.filePool.setRootDirectory(rootDirectory); FilePool& filePool = resources_.getFilePool();
filePool.setRootDirectory(rootDirectory);
// a string representation used for OSC purposes // a string representation used for OSC purposes
rootPath_ = rootDirectory.u8string(); rootPath_ = rootDirectory.u8string();
@ -583,13 +599,16 @@ void Synth::Impl::finalizeSfzLoad()
absl::optional<FileInformation> fileInformation; absl::optional<FileInformation> fileInformation;
FilePool& filePool = resources_.getFilePool();
WavetablePool& wavePool = resources_.getWavePool();
if (!region.isGenerator()) { if (!region.isGenerator()) {
if (!resources_.filePool.checkSampleId(*region.sampleId)) { if (!filePool.checkSampleId(*region.sampleId)) {
removeCurrentRegion(); removeCurrentRegion();
continue; continue;
} }
fileInformation = resources_.filePool.getFileInformation(*region.sampleId); fileInformation = filePool.getFileInformation(*region.sampleId);
if (!fileInformation) { if (!fileInformation) {
removeCurrentRegion(); removeCurrentRegion();
continue; continue;
@ -598,7 +617,7 @@ void Synth::Impl::finalizeSfzLoad()
region.hasWavetableSample = fileInformation->wavetable.has_value(); region.hasWavetableSample = fileInformation->wavetable.has_value();
if (fileInformation->end < config::wavetableMaxFrames) { if (fileInformation->end < config::wavetableMaxFrames) {
auto sample = resources_.filePool.loadFile(*region.sampleId); auto sample = filePool.loadFile(*region.sampleId);
bool allZeros = true; bool allZeros = true;
int numChannels = sample->information.numChannels; int numChannels = sample->information.numChannels;
for (int i = 0; i < numChannels; ++i) { for (int i = 0; i < numChannels; ++i) {
@ -653,13 +672,13 @@ void Synth::Impl::finalizeSfzLoad()
return Default::offsetMod.bounds.clamp(sumOffsetCC); return Default::offsetMod.bounds.clamp(sumOffsetCC);
}(); }();
if (!resources_.filePool.preloadFile(*region.sampleId, maxOffset)) { if (!filePool.preloadFile(*region.sampleId, maxOffset)) {
removeCurrentRegion(); removeCurrentRegion();
continue; continue;
} }
} }
else if (!region.isGenerator()) { else if (!region.isGenerator()) {
if (!resources_.wavePool.createFileWave(resources_.filePool, std::string(region.sampleId->filename()))) { if (!wavePool.createFileWave(filePool, std::string(region.sampleId->filename()))) {
removeCurrentRegion(); removeCurrentRegion();
continue; continue;
} }
@ -698,8 +717,9 @@ void Synth::Impl::finalizeSfzLoad()
} }
// Defaults // Defaults
MidiState& midiState = resources_.getMidiState();
for (int cc = 0; cc < config::numCCs; cc++) { for (int cc = 0; cc < config::numCCs; cc++) {
layer.registerCC(cc, resources_.midiState.getCCValue(cc)); layer.registerCC(cc, midiState.getCCValue(cc));
} }
@ -819,37 +839,37 @@ void Synth::Impl::finalizeSfzLoad()
bool Synth::loadScalaFile(const fs::path& path) bool Synth::loadScalaFile(const fs::path& path)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.tuning.loadScalaFile(path); return impl.resources_.getTuning().loadScalaFile(path);
} }
bool Synth::loadScalaString(const std::string& text) bool Synth::loadScalaString(const std::string& text)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.tuning.loadScalaString(text); return impl.resources_.getTuning().loadScalaString(text);
} }
void Synth::setScalaRootKey(int rootKey) void Synth::setScalaRootKey(int rootKey)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
impl.resources_.tuning.setScalaRootKey(rootKey); impl.resources_.getTuning().setScalaRootKey(rootKey);
} }
int Synth::getScalaRootKey() const int Synth::getScalaRootKey() const
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.tuning.getScalaRootKey(); return impl.resources_.getTuning().getScalaRootKey();
} }
void Synth::setTuningFrequency(float frequency) void Synth::setTuningFrequency(float frequency)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
impl.resources_.tuning.setTuningFrequency(frequency); impl.resources_.getTuning().setTuningFrequency(frequency);
} }
float Synth::getTuningFrequency() const float Synth::getTuningFrequency() const
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.tuning.getTuningFrequency(); return impl.resources_.getTuning().getTuningFrequency();
} }
void Synth::loadStretchTuningByRatio(float ratio) void Synth::loadStretchTuningByRatio(float ratio)
@ -858,10 +878,11 @@ void Synth::loadStretchTuningByRatio(float ratio)
SFIZZ_CHECK(ratio >= 0.0f && ratio <= 1.0f); SFIZZ_CHECK(ratio >= 0.0f && ratio <= 1.0f);
ratio = clamp(ratio, 0.0f, 1.0f); ratio = clamp(ratio, 0.0f, 1.0f);
absl::optional<StretchTuning>& stretch = impl.resources_.getStretch();
if (ratio > 0.0f) if (ratio > 0.0f)
impl.resources_.stretch = StretchTuning::createRailsbackFromRatio(ratio); stretch = StretchTuning::createRailsbackFromRatio(ratio);
else else
impl.resources_.stretch.reset(); stretch.reset();
} }
int Synth::getNumActiveVoices() const noexcept int Synth::getNumActiveVoices() const noexcept
@ -931,8 +952,12 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
return; return;
} }
if (impl.resources_.synthConfig.freeWheeling) const SynthConfig& synthConfig = impl.resources_.getSynthConfig();
impl.resources_.filePool.waitForBackgroundLoading(); FilePool& filePool = impl.resources_.getFilePool();
BufferPool& bufferPool = impl.resources_.getBufferPool();
if (synthConfig.freeWheeling)
filePool.waitForBackgroundLoading();
const auto now = std::chrono::high_resolution_clock::now(); const auto now = std::chrono::high_resolution_clock::now();
const auto timeSinceLastCollection = const auto timeSinceLastCollection =
@ -940,25 +965,27 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
if (timeSinceLastCollection.count() > config::fileClearingPeriod) { if (timeSinceLastCollection.count() > config::fileClearingPeriod) {
impl.lastGarbageCollection_ = now; impl.lastGarbageCollection_ = now;
impl.resources_.filePool.triggerGarbageCollection(); filePool.triggerGarbageCollection();
} }
auto tempSpan = impl.resources_.bufferPool.getStereoBuffer(numFrames); auto tempSpan = bufferPool.getStereoBuffer(numFrames);
auto tempMixSpan = impl.resources_.bufferPool.getStereoBuffer(numFrames); auto tempMixSpan = bufferPool.getStereoBuffer(numFrames);
auto rampSpan = impl.resources_.bufferPool.getBuffer(numFrames); auto rampSpan = bufferPool.getBuffer(numFrames);
if (!tempSpan || !tempMixSpan || !rampSpan) { if (!tempSpan || !tempMixSpan || !rampSpan) {
DBG("[sfizz] Could not get a temporary buffer; exiting callback... "); DBG("[sfizz] Could not get a temporary buffer; exiting callback... ");
return; return;
} }
ModMatrix& mm = impl.resources_.modMatrix; ModMatrix& mm = impl.resources_.getModMatrix();
mm.beginCycle(numFrames); mm.beginCycle(numFrames);
BeatClock& bc = impl.resources_.beatClock; BeatClock& bc = impl.resources_.getBeatClock();
bc.beginCycle(numFrames); bc.beginCycle(numFrames);
if (impl.playheadMoved_ && impl.resources_.beatClock.isPlaying()) { MidiState& midiState = impl.resources_.getMidiState();
impl.resources_.midiState.flushEvents();
if (impl.playheadMoved_ && bc.isPlaying()) {
midiState.flushEvents();
impl.genController_->resetSmoothers(); impl.genController_->resetSmoothers();
impl.playheadMoved_ = false; impl.playheadMoved_ = false;
} }
@ -1028,7 +1055,8 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
// Process the metronome (debugging tool for host time info) // Process the metronome (debugging tool for host time info)
constexpr bool metronomeEnabled = false; constexpr bool metronomeEnabled = false;
if (metronomeEnabled) { if (metronomeEnabled) {
impl.resources_.metronome.processAdding( Metronome& metro = impl.resources_.getMetronome();
metro.processAdding(
bc.getRunningBeatNumber().data(), bc.getRunningBeatsPerBar().data(), bc.getRunningBeatNumber().data(), bc.getRunningBeatsPerBar().data(),
buffer.getChannel(0), buffer.getChannel(1), numFrames); buffer.getChannel(0), buffer.getChannel(1), numFrames);
} }
@ -1045,11 +1073,12 @@ void Synth::renderBlock(AudioSpan<float> buffer) noexcept
{ // Clear events and advance midi time { // Clear events and advance midi time
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.midiState.advanceTime(buffer.getNumFrames()); midiState.advanceTime(buffer.getNumFrames());
} }
callbackBreakdown.dispatch = impl.dispatchDuration_; callbackBreakdown.dispatch = impl.dispatchDuration_;
impl.resources_.logger.logCallbackTime( Logger& logger = impl.resources_.getLogger();
logger.logCallbackTime(
callbackBreakdown, impl.voiceManager_.getNumActiveVoices(), numFrames); callbackBreakdown, impl.voiceManager_.getNumActiveVoices(), numFrames);
// Reset the dispatch counter // Reset the dispatch counter
@ -1073,7 +1102,7 @@ void Synth::hdNoteOn(int delay, int noteNumber, float normalizedVelocity) noexce
ASSERT(noteNumber >= 0); ASSERT(noteNumber >= 0);
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.midiState.noteOnEvent(delay, noteNumber, normalizedVelocity); impl.resources_.getMidiState().noteOnEvent(delay, noteNumber, normalizedVelocity);
impl.noteOnDispatch(delay, noteNumber, normalizedVelocity); impl.noteOnDispatch(delay, noteNumber, normalizedVelocity);
} }
@ -1093,8 +1122,9 @@ void Synth::hdNoteOff(int delay, int noteNumber, float normalizedVelocity) noexc
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a // FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
// way in sfz to specify that a release trigger should NOT use the note-on velocity? // way in sfz to specify that a release trigger should NOT use the note-on velocity?
// auto replacedVelocity = (velocity == 0 ? getNoteVelocity(noteNumber) : velocity); // auto replacedVelocity = (velocity == 0 ? getNoteVelocity(noteNumber) : velocity);
impl.resources_.midiState.noteOffEvent(delay, noteNumber, normalizedVelocity); MidiState& midiState = impl.resources_.getMidiState();
const auto replacedVelocity = impl.resources_.midiState.getNoteVelocity(noteNumber); midiState.noteOffEvent(delay, noteNumber, normalizedVelocity);
const auto replacedVelocity = midiState.getNoteVelocity(noteNumber);
for (auto& voice : impl.voiceManager_) for (auto& voice : impl.voiceManager_)
voice.registerNoteOff(delay, noteNumber, replacedVelocity); voice.registerNoteOff(delay, noteNumber, replacedVelocity);
@ -1275,6 +1305,8 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM
changedCCsThisCycle_.set(ccNumber); changedCCsThisCycle_.set(ccNumber);
MidiState& midiState = resources_.getMidiState();
if (asMidi) { if (asMidi) {
if (ccNumber == config::resetCC) { if (ccNumber == config::resetCC) {
resetAllControllers(delay); resetAllControllers(delay);
@ -1284,7 +1316,7 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM
if (ccNumber == config::allNotesOffCC || ccNumber == config::allSoundOffCC) { if (ccNumber == config::allNotesOffCC || ccNumber == config::allSoundOffCC) {
for (auto& voice : voiceManager_) for (auto& voice : voiceManager_)
voice.reset(); voice.reset();
resources_.midiState.allNotesOff(delay); midiState.allNotesOff(delay);
return; return;
} }
} }
@ -1293,7 +1325,7 @@ void Synth::Impl::performHdcc(int delay, int ccNumber, float normValue, bool asM
voice.registerCC(delay, ccNumber, normValue); voice.registerCC(delay, ccNumber, normValue);
ccDispatch(delay, ccNumber, normValue); ccDispatch(delay, ccNumber, normValue);
resources_.midiState.ccEvent(delay, ccNumber, normValue); midiState.ccEvent(delay, ccNumber, normValue);
} }
void Synth::Impl::setDefaultHdcc(int ccNumber, float value) void Synth::Impl::setDefaultHdcc(int ccNumber, float value)
@ -1301,7 +1333,7 @@ void Synth::Impl::setDefaultHdcc(int ccNumber, float value)
ASSERT(ccNumber >= 0); ASSERT(ccNumber >= 0);
ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber < config::numCCs);
defaultCCValues_[ccNumber] = value; defaultCCValues_[ccNumber] = value;
resources_.midiState.ccEvent(0, ccNumber, value); resources_.getMidiState().ccEvent(0, ccNumber, value);
} }
float Synth::getHdcc(int ccNumber) float Synth::getHdcc(int ccNumber)
@ -1309,7 +1341,7 @@ float Synth::getHdcc(int ccNumber)
ASSERT(ccNumber >= 0); ASSERT(ccNumber >= 0);
ASSERT(ccNumber < config::numCCs); ASSERT(ccNumber < config::numCCs);
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.midiState.getCCValue(ccNumber); return impl.resources_.getMidiState().getCCValue(ccNumber);
} }
float Synth::getDefaultHdcc(int ccNumber) float Synth::getDefaultHdcc(int ccNumber)
@ -1331,7 +1363,7 @@ void Synth::hdPitchWheel(int delay, float normalizedPitch) noexcept
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.midiState.pitchBendEvent(delay, normalizedPitch); impl.resources_.getMidiState().pitchBendEvent(delay, normalizedPitch);
for (const Impl::LayerPtr& layer : impl.layers_) { for (const Impl::LayerPtr& layer : impl.layers_) {
layer->registerPitchWheel(normalizedPitch); layer->registerPitchWheel(normalizedPitch);
@ -1355,7 +1387,7 @@ void Synth::hdChannelAftertouch(int delay, float normAftertouch) noexcept
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.midiState.channelAftertouchEvent(delay, normAftertouch); impl.resources_.getMidiState().channelAftertouchEvent(delay, normAftertouch);
for (const Impl::LayerPtr& layerPtr : impl.layers_) { for (const Impl::LayerPtr& layerPtr : impl.layers_) {
layerPtr->registerAftertouch(normAftertouch); layerPtr->registerAftertouch(normAftertouch);
@ -1379,7 +1411,7 @@ void Synth::hdPolyAftertouch(int delay, int noteNumber, float normAftertouch) no
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.midiState.polyAftertouchEvent(delay, noteNumber, normAftertouch); impl.resources_.getMidiState().polyAftertouchEvent(delay, noteNumber, normAftertouch);
for (auto& voice : impl.voiceManager_) for (auto& voice : impl.voiceManager_)
voice.registerPolyAftertouch(delay, noteNumber, normAftertouch); voice.registerPolyAftertouch(delay, noteNumber, normAftertouch);
@ -1393,7 +1425,7 @@ void Synth::tempo(int delay, float secondsPerBeat) noexcept
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.beatClock.setTempo(delay, secondsPerBeat); impl.resources_.getBeatClock().setTempo(delay, secondsPerBeat);
} }
void Synth::bpmTempo(int delay, float beatsPerMinute) noexcept void Synth::bpmTempo(int delay, float beatsPerMinute) noexcept
@ -1407,7 +1439,7 @@ void Synth::timeSignature(int delay, int beatsPerBar, int beatUnit)
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.beatClock.setTimeSignature(delay, TimeSignature(beatsPerBar, beatUnit)); impl.resources_.getBeatClock().setTimeSignature(delay, TimeSignature(beatsPerBar, beatUnit));
} }
void Synth::timePosition(int delay, int bar, double barBeat) void Synth::timePosition(int delay, int bar, double barBeat)
@ -1415,16 +1447,18 @@ void Synth::timePosition(int delay, int bar, double barBeat)
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
BeatClock& beatClock = impl.resources_.getBeatClock();
const auto newPosition = BBT(bar, barBeat); const auto newPosition = BBT(bar, barBeat);
const auto newBeatPosition = newPosition.toBeats(impl.resources_.beatClock.getTimeSignature()); const auto newBeatPosition = newPosition.toBeats(beatClock.getTimeSignature());
const auto currentBeatPosition = impl.resources_.beatClock.getLastBeatPosition(); const auto currentBeatPosition = beatClock.getLastBeatPosition();
const auto positionDifference = std::abs(newBeatPosition - currentBeatPosition); const auto positionDifference = std::abs(newBeatPosition - currentBeatPosition);
const auto threshold = config::playheadMovedFrames * impl.resources_.beatClock.getBeatsPerFrame(); const auto threshold = config::playheadMovedFrames * beatClock.getBeatsPerFrame();
if (positionDifference > threshold) if (positionDifference > threshold)
impl.playheadMoved_ = true; impl.playheadMoved_ = true;
impl.resources_.beatClock.setTimePosition(delay, newPosition); beatClock.setTimePosition(delay, newPosition);
} }
void Synth::playbackState(int delay, int playbackState) void Synth::playbackState(int delay, int playbackState)
@ -1432,7 +1466,7 @@ void Synth::playbackState(int delay, int playbackState)
Impl& impl = *impl_; Impl& impl = *impl_;
ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration };
impl.resources_.beatClock.setPlaying(delay, playbackState == 1); impl.resources_.getBeatClock().setPlaying(delay, playbackState == 1);
} }
int Synth::getNumRegions() const noexcept int Synth::getNumRegions() const noexcept
@ -1456,7 +1490,7 @@ int Synth::getNumMasters() const noexcept
int Synth::getNumCurves() const noexcept int Synth::getNumCurves() const noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return static_cast<int>(impl.resources_.curves.getNumCurves()); return static_cast<int>(impl.resources_.getCurves().getNumCurves());
} }
std::string Synth::exportMidnam(absl::string_view model) const std::string Synth::exportMidnam(absl::string_view model) const
@ -1638,17 +1672,18 @@ const std::vector<std::string>& Synth::getUnknownOpcodes() const noexcept
size_t Synth::getNumPreloadedSamples() const noexcept size_t Synth::getNumPreloadedSamples() const noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.filePool.getNumPreloadedSamples(); return impl.resources_.getFilePool().getNumPreloadedSamples();
} }
int Synth::getSampleQuality(ProcessMode mode) int Synth::getSampleQuality(ProcessMode mode)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
SynthConfig& synthConfig = impl.resources_.getSynthConfig();
switch (mode) { switch (mode) {
case ProcessLive: case ProcessLive:
return impl.resources_.synthConfig.liveSampleQuality; return synthConfig.liveSampleQuality;
case ProcessFreewheeling: case ProcessFreewheeling:
return impl.resources_.synthConfig.freeWheelingSampleQuality; return synthConfig.freeWheelingSampleQuality;
default: default:
SFIZZ_CHECK(false); SFIZZ_CHECK(false);
return 0; return 0;
@ -1660,13 +1695,14 @@ void Synth::setSampleQuality(ProcessMode mode, int quality)
SFIZZ_CHECK(quality >= 0 && quality <= 10); SFIZZ_CHECK(quality >= 0 && quality <= 10);
Impl& impl = *impl_; Impl& impl = *impl_;
quality = clamp(quality, 0, 10); quality = clamp(quality, 0, 10);
SynthConfig& synthConfig = impl.resources_.getSynthConfig();
switch (mode) { switch (mode) {
case ProcessLive: case ProcessLive:
impl.resources_.synthConfig.liveSampleQuality = quality; synthConfig.liveSampleQuality = quality;
break; break;
case ProcessFreewheeling: case ProcessFreewheeling:
impl.resources_.synthConfig.freeWheelingSampleQuality = quality; synthConfig.freeWheelingSampleQuality = quality;
break; break;
default: default:
SFIZZ_CHECK(false); SFIZZ_CHECK(false);
@ -1677,11 +1713,12 @@ void Synth::setSampleQuality(ProcessMode mode, int quality)
int Synth::getOscillatorQuality(ProcessMode mode) int Synth::getOscillatorQuality(ProcessMode mode)
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
SynthConfig& synthConfig = impl.resources_.getSynthConfig();
switch (mode) { switch (mode) {
case ProcessLive: case ProcessLive:
return impl.resources_.synthConfig.liveOscillatorQuality; return synthConfig.liveOscillatorQuality;
case ProcessFreewheeling: case ProcessFreewheeling:
return impl.resources_.synthConfig.freeWheelingOscillatorQuality; return synthConfig.freeWheelingOscillatorQuality;
default: default:
SFIZZ_CHECK(false); SFIZZ_CHECK(false);
return 0; return 0;
@ -1693,13 +1730,14 @@ void Synth::setOscillatorQuality(ProcessMode mode, int quality)
SFIZZ_CHECK(quality >= 0 && quality <= 3); SFIZZ_CHECK(quality >= 0 && quality <= 3);
Impl& impl = *impl_; Impl& impl = *impl_;
quality = clamp(quality, 0, 3); quality = clamp(quality, 0, 3);
SynthConfig& synthConfig = impl.resources_.getSynthConfig();
switch (mode) { switch (mode) {
case ProcessLive: case ProcessLive:
impl.resources_.synthConfig.liveOscillatorQuality = quality; synthConfig.liveOscillatorQuality = quality;
break; break;
case ProcessFreewheeling: case ProcessFreewheeling:
impl.resources_.synthConfig.freeWheelingOscillatorQuality = quality; synthConfig.freeWheelingOscillatorQuality = quality;
break; break;
default: default:
SFIZZ_CHECK(false); SFIZZ_CHECK(false);
@ -1770,7 +1808,7 @@ void Synth::Impl::applySettingsPerVoice()
void Synth::Impl::setupModMatrix() void Synth::Impl::setupModMatrix()
{ {
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
for (const LayerPtr& layerPtr : layers_) { for (const LayerPtr& layerPtr : layers_) {
const Region& region = layerPtr->getRegion(); const Region& region = layerPtr->getRegion();
@ -1851,40 +1889,43 @@ void Synth::Impl::setupModMatrix()
void Synth::setPreloadSize(uint32_t preloadSize) noexcept void Synth::setPreloadSize(uint32_t preloadSize) noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
FilePool& filePool = impl.resources_.getFilePool();
// fast path // fast path
if (preloadSize == impl.resources_.filePool.getPreloadSize()) if (preloadSize == filePool.getPreloadSize())
return; return;
impl.resources_.filePool.setPreloadSize(preloadSize); filePool.setPreloadSize(preloadSize);
} }
uint32_t Synth::getPreloadSize() const noexcept uint32_t Synth::getPreloadSize() const noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.filePool.getPreloadSize(); return impl.resources_.getFilePool().getPreloadSize();
} }
void Synth::enableFreeWheeling() noexcept void Synth::enableFreeWheeling() noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
if (!impl.resources_.synthConfig.freeWheeling) { SynthConfig& synthConfig = impl.resources_.getSynthConfig();
impl.resources_.synthConfig.freeWheeling = true; if (!synthConfig.freeWheeling) {
synthConfig.freeWheeling = true;
DBG("Enabling freewheeling"); DBG("Enabling freewheeling");
} }
} }
void Synth::disableFreeWheeling() noexcept void Synth::disableFreeWheeling() noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
if (impl.resources_.synthConfig.freeWheeling) { SynthConfig& synthConfig = impl.resources_.getSynthConfig();
impl.resources_.synthConfig.freeWheeling = false; if (synthConfig.freeWheeling) {
synthConfig.freeWheeling = false;
DBG("Disabling freewheeling"); DBG("Disabling freewheeling");
} }
} }
void Synth::Impl::resetAllControllers(int delay) noexcept void Synth::Impl::resetAllControllers(int delay) noexcept
{ {
resources_.midiState.resetAllControllers(delay); resources_.getMidiState().resetAllControllers(delay);
for (auto& voice : voiceManager_) { for (auto& voice : voiceManager_) {
voice.registerPitchWheel(delay, 0); voice.registerPitchWheel(delay, 0);
@ -1931,19 +1972,19 @@ bool Synth::shouldReloadFile()
bool Synth::shouldReloadScala() bool Synth::shouldReloadScala()
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
return impl.resources_.tuning.shouldReloadScala(); return impl.resources_.getTuning().shouldReloadScala();
} }
void Synth::enableLogging(absl::string_view prefix) noexcept void Synth::enableLogging(absl::string_view prefix) noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
impl.resources_.logger.enableLogging(prefix); impl.resources_.getLogger().enableLogging(prefix);
} }
void Synth::disableLogging() noexcept void Synth::disableLogging() noexcept
{ {
Impl& impl = *impl_; Impl& impl = *impl_;
impl.resources_.logger.disableLogging(); impl.resources_.getLogger().disableLogging();
} }
void Synth::allSoundOff() noexcept void Synth::allSoundOff() noexcept
@ -2038,7 +2079,7 @@ BitArray<config::numCCs> Synth::Impl::collectAllUsedCCs()
BitArray<config::numCCs> used; BitArray<config::numCCs> used;
for (const LayerPtr& layerPtr : layers_) for (const LayerPtr& layerPtr : layers_)
collectUsedCCsFromRegion(used, layerPtr->getRegion()); collectUsedCCsFromRegion(used, layerPtr->getRegion());
collectUsedCCsFromModulations(used, resources_.modMatrix); collectUsedCCsFromModulations(used, resources_.getModMatrix());
return used; return used;
} }

View file

@ -5,6 +5,9 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "SynthPrivate.h" #include "SynthPrivate.h"
#include "FilePool.h"
#include "Curve.h"
#include "MidiState.h"
#include "utility/StringViewHelpers.h" #include "utility/StringViewHelpers.h"
#include <absl/strings/ascii.h> #include <absl/strings/ascii.h>
#include <cstring> #include <cstring>
@ -67,11 +70,11 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
} break; } break;
MATCH("/num_curves", "") { MATCH("/num_curves", "") {
client.receive<'i'>(delay, path, int(impl.resources_.curves.getNumCurves())); client.receive<'i'>(delay, path, int(impl.resources_.getCurves().getNumCurves()));
} break; } break;
MATCH("/num_samples", "") { MATCH("/num_samples", "") {
client.receive<'i'>(delay, path, int(impl.resources_.filePool.getNumPreloadedSamples())); client.receive<'i'>(delay, path, int(impl.resources_.getFilePool().getNumPreloadedSamples()));
} break; } break;
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -139,13 +142,13 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
if (indices[0] >= config::numCCs) if (indices[0] >= config::numCCs)
break; break;
// Note: result value is not frame-exact // Note: result value is not frame-exact
client.receive<'f'>(delay, path, impl.resources_.midiState.getCCValue(indices[0])); client.receive<'f'>(delay, path, impl.resources_.getMidiState().getCCValue(indices[0]));
} break; } break;
MATCH("/cc&/value", "f") { MATCH("/cc&/value", "f") {
if (indices[0] >= config::numCCs) if (indices[0] >= config::numCCs)
break; break;
impl.resources_.midiState.ccEvent(delay, indices[0], args[0].f); impl.resources_.getMidiState().ccEvent(delay, indices[0], args[0].f);
} break; } break;
MATCH("/cc&/label", "") { MATCH("/cc&/label", "") {

View file

@ -26,6 +26,11 @@
#include "SfzHelpers.h" #include "SfzHelpers.h"
#include "SIMDHelpers.h" #include "SIMDHelpers.h"
#include "Smoothers.h" #include "Smoothers.h"
#include "FilePool.h"
#include "Wavetables.h"
#include "Tuning.h"
#include "BufferPool.h"
#include "SynthConfig.h"
#include "utility/Macros.h" #include "utility/Macros.h"
#include <absl/algorithm/container.h> #include <absl/algorithm/container.h>
#include <absl/types/span.h> #include <absl/types/span.h>
@ -387,10 +392,11 @@ const ExtendedCCValues& Voice::getExtendedCCValues() const noexcept
void Voice::Impl::updateExtendedCCValues() noexcept void Voice::Impl::updateExtendedCCValues() noexcept
{ {
extendedCCValues_.unipolar = resources_.midiState.getCCValue(ExtendedCCs::unipolarRandom); MidiState& midiState = resources_.getMidiState();
extendedCCValues_.bipolar = resources_.midiState.getCCValue(ExtendedCCs::bipolarRandom); extendedCCValues_.unipolar = midiState.getCCValue(ExtendedCCs::unipolarRandom);
extendedCCValues_.alternate = resources_.midiState.getCCValue(ExtendedCCs::alternate); extendedCCValues_.bipolar = midiState.getCCValue(ExtendedCCs::bipolarRandom);
extendedCCValues_.noteGate = resources_.midiState.getCCValue(ExtendedCCs::keyboardNoteGate); extendedCCValues_.alternate = midiState.getCCValue(ExtendedCCs::alternate);
extendedCCValues_.noteGate = midiState.getCCValue(ExtendedCCs::keyboardNoteGate);
} }
bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept
@ -399,6 +405,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
ASSERT(event.value >= 0.0f && event.value <= 1.0f); ASSERT(event.value >= 0.0f && event.value <= 1.0f);
Resources& resources = impl.resources_; Resources& resources = impl.resources_;
MidiState& midiState = resources.getMidiState();
const Region& region = layer->getRegion(); const Region& region = layer->getRegion();
impl.region_ = &region; impl.region_ = &region;
@ -408,7 +415,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
impl.triggerEvent_.number = region.pitchKeycenter; impl.triggerEvent_.number = region.pitchKeycenter;
if (region.velocityOverride == VelocityOverride::previous) if (region.velocityOverride == VelocityOverride::previous)
impl.triggerEvent_.value = resources.midiState.getVelocityOverride(); impl.triggerEvent_.value = midiState.getVelocityOverride();
if (region.disabled()) { if (region.disabled()) {
impl.switchState(State::cleanMeUp); impl.switchState(State::cleanMeUp);
@ -424,26 +431,27 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
delay = 0; delay = 0;
if (region.isOscillator()) { if (region.isOscillator()) {
WavetablePool& wavePool = resources.getWavePool();
const WavetableMulti* wave = nullptr; const WavetableMulti* wave = nullptr;
if (!region.isGenerator()) if (!region.isGenerator())
wave = resources.wavePool.getFileWave(region.sampleId->filename()); wave = wavePool.getFileWave(region.sampleId->filename());
else { else {
switch (hash(region.sampleId->filename())) { switch (hash(region.sampleId->filename())) {
default: default:
case hash("*silence"): case hash("*silence"):
break; break;
case hash("*sine"): case hash("*sine"):
wave = resources.wavePool.getWaveSin(); wave = wavePool.getWaveSin();
break; break;
case hash("*triangle"): // fallthrough case hash("*triangle"): // fallthrough
case hash("*tri"): case hash("*tri"):
wave = resources.wavePool.getWaveTriangle(); wave = wavePool.getWaveTriangle();
break; break;
case hash("*square"): case hash("*square"):
wave = resources.wavePool.getWaveSquare(); wave = wavePool.getWaveSquare();
break; break;
case hash("*saw"): case hash("*saw"):
wave = resources.wavePool.getWaveSaw(); wave = wavePool.getWaveSaw();
break; break;
} }
} }
@ -457,27 +465,29 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
} }
impl.setupOscillatorUnison(); impl.setupOscillatorUnison();
} else { } else {
impl.currentPromise_ = resources.filePool.getFilePromise(region.sampleId); FilePool& filePool = resources.getFilePool();
impl.currentPromise_ = filePool.getFilePromise(region.sampleId);
if (!impl.currentPromise_) { if (!impl.currentPromise_) {
impl.switchState(State::cleanMeUp); impl.switchState(State::cleanMeUp);
return false; return false;
} }
impl.updateLoopInformation(); impl.updateLoopInformation();
impl.speedRatio_ = static_cast<float>(impl.currentPromise_->information.sampleRate / impl.sampleRate_); impl.speedRatio_ = static_cast<float>(impl.currentPromise_->information.sampleRate / impl.sampleRate_);
impl.sourcePosition_ = region.getOffset(resources.midiState); impl.sourcePosition_ = region.getOffset(midiState);
} }
// do Scala retuning and reconvert the frequency into a 12TET key number // do Scala retuning and reconvert the frequency into a 12TET key number
const float numberRetuned = resources.tuning.getKeyFractional12TET(impl.triggerEvent_.number); Tuning& tuning = resources.getTuning();
const float numberRetuned = tuning.getKeyFractional12TET(impl.triggerEvent_.number);
impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value); impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value);
// apply stretch tuning if set // apply stretch tuning if set
if (resources.stretch) if (absl::optional<StretchTuning>& stretch = resources.getStretch())
impl.pitchRatio_ *= resources.stretch->getRatioForFractionalKey(numberRetuned); impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned);
impl.pitchKeycenter_ = region.pitchKeycenter; impl.pitchKeycenter_ = region.pitchKeycenter;
impl.baseVolumedB_ = region.getBaseVolumedB(resources.midiState, impl.triggerEvent_.number); impl.baseVolumedB_ = region.getBaseVolumedB(midiState, impl.triggerEvent_.number);
impl.baseGain_ = region.getBaseGain(); impl.baseGain_ = region.getBaseGain();
if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous)
impl.baseGain_ *= region.getNoteGain(impl.triggerEvent_.number, impl.triggerEvent_.value); impl.baseGain_ *= region.getNoteGain(impl.triggerEvent_.number, impl.triggerEvent_.value);
@ -494,26 +504,27 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
} }
impl.triggerDelay_ = delay; impl.triggerDelay_ = delay;
impl.initialDelay_ = delay + static_cast<int>(region.getDelay(resources.midiState) * impl.sampleRate_); impl.initialDelay_ = delay + static_cast<int>(region.getDelay(midiState) * impl.sampleRate_);
impl.baseFrequency_ = resources.tuning.getFrequencyOfKey(impl.triggerEvent_.number); impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number);
impl.sampleEnd_ = int(region.getSampleEnd(resources.midiState)); impl.sampleEnd_ = int(region.getSampleEnd(midiState));
impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1;
impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_);
impl.bendSmoother_.reset(region.getBendInCents(resources.midiState.getPitchBend())); impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend()));
resources.modMatrix.initVoice(impl.id_, region.getId(), impl.initialDelay_); ModMatrix& modMatrix = resources.getModMatrix();
modMatrix.initVoice(impl.id_, region.getId(), impl.initialDelay_);
impl.saveModulationTargets(&region); impl.saveModulationTargets(&region);
if (region.checkSustain) { if (region.checkSustain) {
const bool sustainPressed = const bool sustainPressed =
resources.midiState.getCCValue(region.sustainCC) >= region.sustainThreshold; midiState.getCCValue(region.sustainCC) >= region.sustainThreshold;
impl.sustainState_ = impl.sustainState_ =
sustainPressed ? Impl::SustainState::Sustaining : Impl::SustainState::Up; sustainPressed ? Impl::SustainState::Sustaining : Impl::SustainState::Up;
} }
if (region.checkSostenuto) { if (region.checkSostenuto) {
const bool sostenutoPressed = const bool sostenutoPressed =
resources.midiState.getCCValue(region.sostenutoCC) >= region.sostenutoThreshold; midiState.getCCValue(region.sostenutoCC) >= region.sostenutoThreshold;
impl.sostenutoState_ = impl.sostenutoState_ =
sostenutoPressed ? Impl::SostenutoState::PreviouslyDown : Impl::SostenutoState::Up; sostenutoPressed ? Impl::SostenutoState::PreviouslyDown : Impl::SostenutoState::Up;
} }
@ -524,7 +535,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc
int Voice::Impl::getCurrentSampleQuality() const noexcept int Voice::Impl::getCurrentSampleQuality() const noexcept
{ {
return (region_ && region_->sampleQuality) ? return (region_ && region_->sampleQuality) ?
*region_->sampleQuality : resources_.synthConfig.currentSampleQuality(); *region_->sampleQuality : resources_.getSynthConfig().currentSampleQuality();
} }
int Voice::getCurrentSampleQuality() const noexcept int Voice::getCurrentSampleQuality() const noexcept
@ -536,7 +547,7 @@ int Voice::getCurrentSampleQuality() const noexcept
int Voice::Impl::getCurrentOscillatorQuality() const noexcept int Voice::Impl::getCurrentOscillatorQuality() const noexcept
{ {
return (region_ && region_->oscillatorQuality) ? return (region_ && region_->oscillatorQuality) ?
*region_->oscillatorQuality : resources_.synthConfig.currentOscillatorQuality(); *region_->oscillatorQuality : resources_.getSynthConfig().currentOscillatorQuality();
} }
int Voice::getCurrentOscillatorQuality() const noexcept int Voice::getCurrentOscillatorQuality() const noexcept
@ -571,7 +582,8 @@ void Voice::Impl::release(int delay) noexcept
switchState(State::cleanMeUp); switchState(State::cleanMeUp);
} }
resources_.modMatrix.releaseVoice(id_, region_->getId(), delay); ModMatrix& modMatrix = resources_.getModMatrix();
modMatrix.releaseVoice(id_, region_->getId(), delay);
} }
void Voice::off(int delay, bool fast) noexcept void Voice::off(int delay, bool fast) noexcept
@ -805,13 +817,15 @@ void Voice::Impl::resetCrossfades() noexcept
float xfadeValue { 1.0f }; float xfadeValue { 1.0f };
const auto xfCurve = region_->crossfadeCCCurve; const auto xfCurve = region_->crossfadeCCCurve;
MidiState& midiState = resources_.getMidiState();
for (const auto& mod : region_->crossfadeCCInRange) { for (const auto& mod : region_->crossfadeCCInRange) {
const auto value = resources_.midiState.getCCValue(mod.cc); const auto value = midiState.getCCValue(mod.cc);
xfadeValue *= crossfadeIn(mod.data, value, xfCurve); xfadeValue *= crossfadeIn(mod.data, value, xfCurve);
} }
for (const auto& mod : region_->crossfadeCCOutRange) { for (const auto& mod : region_->crossfadeCCOutRange) {
const auto value = resources_.midiState.getCCValue(mod.cc); const auto value = midiState.getCCValue(mod.cc);
xfadeValue *= crossfadeOut(mod.data, value, xfCurve); xfadeValue *= crossfadeOut(mod.data, value, xfCurve);
} }
@ -823,8 +837,11 @@ void Voice::Impl::applyCrossfades(absl::Span<float> modulationSpan) noexcept
const auto numSamples = modulationSpan.size(); const auto numSamples = modulationSpan.size();
const auto xfCurve = region_->crossfadeCCCurve; const auto xfCurve = region_->crossfadeCCCurve;
auto tempSpan = resources_.bufferPool.getBuffer(numSamples); MidiState& midiState = resources_.getMidiState();
auto xfadeSpan = resources_.bufferPool.getBuffer(numSamples); BufferPool& bufferPool = resources_.getBufferPool();
auto tempSpan = bufferPool.getBuffer(numSamples);
auto xfadeSpan = bufferPool.getBuffer(numSamples);
if (!tempSpan || !xfadeSpan) if (!tempSpan || !xfadeSpan)
return; return;
@ -833,7 +850,7 @@ void Voice::Impl::applyCrossfades(absl::Span<float> modulationSpan) noexcept
bool canShortcut = true; bool canShortcut = true;
for (const auto& mod : region_->crossfadeCCInRange) { for (const auto& mod : region_->crossfadeCCInRange) {
const auto& events = resources_.midiState.getCCEvents(mod.cc); const auto& events = midiState.getCCEvents(mod.cc);
canShortcut &= (events.size() == 1); canShortcut &= (events.size() == 1);
linearEnvelope(events, *tempSpan, [&](float x) { linearEnvelope(events, *tempSpan, [&](float x) {
return crossfadeIn(mod.data, x, xfCurve); return crossfadeIn(mod.data, x, xfCurve);
@ -842,7 +859,7 @@ void Voice::Impl::applyCrossfades(absl::Span<float> modulationSpan) noexcept
} }
for (const auto& mod : region_->crossfadeCCOutRange) { for (const auto& mod : region_->crossfadeCCOutRange) {
const auto& events = resources_.midiState.getCCEvents(mod.cc); const auto& events = midiState.getCCEvents(mod.cc);
canShortcut &= (events.size() == 1); canShortcut &= (events.size() == 1);
linearEnvelope(events, *tempSpan, [&](float x) { linearEnvelope(events, *tempSpan, [&](float x) {
return crossfadeOut(mod.data, x, xfCurve); return crossfadeOut(mod.data, x, xfCurve);
@ -859,7 +876,7 @@ void Voice::Impl::amplitudeEnvelope(absl::Span<float> modulationSpan) noexcept
{ {
const auto numSamples = modulationSpan.size(); const auto numSamples = modulationSpan.size();
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
// Amplitude EG // Amplitude EG
absl::Span<const float> ampegOut(mm.getModulation(masterAmplitudeTarget_), numSamples); absl::Span<const float> ampegOut(mm.getModulation(masterAmplitudeTarget_), numSamples);
@ -891,7 +908,9 @@ void Voice::Impl::ampStageMono(AudioSpan<float> buffer) noexcept
const auto numSamples = buffer.getNumFrames(); const auto numSamples = buffer.getNumFrames();
const auto leftBuffer = buffer.getSpan(0); const auto leftBuffer = buffer.getSpan(0);
auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); BufferPool& bufferPool = resources_.getBufferPool();
auto modulationSpan = bufferPool.getBuffer(numSamples);
if (!modulationSpan) if (!modulationSpan)
return; return;
@ -904,8 +923,10 @@ void Voice::Impl::ampStageStereo(AudioSpan<float> buffer) noexcept
{ {
ScopedTiming logger { amplitudeDuration_ }; ScopedTiming logger { amplitudeDuration_ };
BufferPool& bufferPool = resources_.getBufferPool();
const auto numSamples = buffer.getNumFrames(); const auto numSamples = buffer.getNumFrames();
auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); auto modulationSpan = bufferPool.getBuffer(numSamples);
if (!modulationSpan) if (!modulationSpan)
return; return;
@ -922,11 +943,13 @@ void Voice::Impl::panStageMono(AudioSpan<float> buffer) noexcept
const auto leftBuffer = buffer.getSpan(0); const auto leftBuffer = buffer.getSpan(0);
const auto rightBuffer = buffer.getSpan(1); const auto rightBuffer = buffer.getSpan(1);
auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); BufferPool& bufferPool = resources_.getBufferPool();
auto modulationSpan = bufferPool.getBuffer(numSamples);
if (!modulationSpan) if (!modulationSpan)
return; return;
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
// Prepare for stereo output // Prepare for stereo output
copy<float>(leftBuffer, rightBuffer); copy<float>(leftBuffer, rightBuffer);
@ -947,11 +970,13 @@ void Voice::Impl::panStageStereo(AudioSpan<float> buffer) noexcept
const auto leftBuffer = buffer.getSpan(0); const auto leftBuffer = buffer.getSpan(0);
const auto rightBuffer = buffer.getSpan(1); const auto rightBuffer = buffer.getSpan(1);
auto modulationSpan = resources_.bufferPool.getBuffer(numSamples); BufferPool& bufferPool = resources_.getBufferPool();
auto modulationSpan = bufferPool.getBuffer(numSamples);
if (!modulationSpan) if (!modulationSpan)
return; return;
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
// Apply panning // Apply panning
fill(*modulationSpan, region_->pan); fill(*modulationSpan, region_->pan);
@ -1029,15 +1054,18 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
auto source = currentPromise_->getData(); auto source = currentPromise_->getData();
BufferPool& bufferPool = resources_.getBufferPool();
const CurveSet& curves = resources_.getCurves();
// calculate interpolation data // calculate interpolation data
// indices: integral position in the source audio // indices: integral position in the source audio
// coeffs: fractional position normalized 0-1 // coeffs: fractional position normalized 0-1
auto coeffs = resources_.bufferPool.getBuffer(numSamples); auto coeffs = bufferPool.getBuffer(numSamples);
auto indices = resources_.bufferPool.getIndexBuffer(numSamples); auto indices = bufferPool.getIndexBuffer(numSamples);
if (!indices || !coeffs) if (!indices || !coeffs)
return; return;
{ {
auto jumps = resources_.bufferPool.getBuffer(numSamples); auto jumps = bufferPool.getBuffer(numSamples);
if (!jumps) if (!jumps)
return; return;
@ -1091,7 +1119,7 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
SpanHolder<absl::Span<int>> partitionBuffers[2]; SpanHolder<absl::Span<int>> partitionBuffers[2];
if (shouldLoop) { if (shouldLoop) {
for (auto& buf : partitionBuffers) { for (auto& buf : partitionBuffers) {
buf = resources_.bufferPool.getIndexBuffer(numSamples); buf = bufferPool.getIndexBuffer(numSamples);
if (!buf) if (!buf)
return; return;
} }
@ -1198,9 +1226,9 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
source, ptBuffer, ptIndices, ptCoeffs, {}, quality); source, ptBuffer, ptIndices, ptCoeffs, {}, quality);
if (ptType == kPartitionLoopXfade) { if (ptType == kPartitionLoopXfade) {
auto xfTemp1 = resources_.bufferPool.getBuffer(numSamples); auto xfTemp1 = bufferPool.getBuffer(numSamples);
auto xfTemp2 = resources_.bufferPool.getBuffer(numSamples); auto xfTemp2 = bufferPool.getBuffer(numSamples);
auto xfIndicesTemp = resources_.bufferPool.getIndexBuffer(numSamples); auto xfIndicesTemp = bufferPool.getIndexBuffer(numSamples);
if (!xfTemp1 || !xfTemp2 || !xfIndicesTemp) if (!xfTemp1 || !xfTemp2 || !xfIndicesTemp)
return; return;
@ -1224,7 +1252,7 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
xfCurve[i] = xfIn.evalNormalized(1.0f - xfCurvePos[i]); xfCurve[i] = xfIn.evalNormalized(1.0f - xfCurvePos[i]);
} }
else IF_CONSTEXPR (config::loopXfadeCurve == 1) { else IF_CONSTEXPR (config::loopXfadeCurve == 1) {
const Curve& xfOut = resources_.curves.getCurve(6); const Curve& xfOut = curves.getCurve(6);
for (unsigned i = 0; i < ptSize; ++i) for (unsigned i = 0; i < ptSize; ++i)
xfCurve[i] = xfOut.evalNormalized(xfCurvePos[i]); xfCurve[i] = xfOut.evalNormalized(xfCurvePos[i]);
} }
@ -1274,7 +1302,7 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]); xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]);
} }
else IF_CONSTEXPR (config::loopXfadeCurve == 1) { else IF_CONSTEXPR (config::loopXfadeCurve == 1) {
const Curve& xfIn = resources_.curves.getCurve(5); const Curve& xfIn = curves.getCurve(5);
for (unsigned i = 0; i < applySize; ++i) for (unsigned i = 0; i < applySize; ++i)
xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]); xfCurve[i] = xfIn.evalNormalized(xfInCurvePos[i]);
} }
@ -1462,7 +1490,10 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
} else { } else {
const size_t numFrames = buffer.getNumFrames(); const size_t numFrames = buffer.getNumFrames();
auto frequencies = resources_.bufferPool.getBuffer(numFrames); BufferPool& bufferPool = resources_.getBufferPool();
ModMatrix& modMatrix = resources_.getModMatrix();
auto frequencies = bufferPool.getBuffer(numFrames);
if (!frequencies) if (!frequencies)
return; return;
@ -1475,7 +1506,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
for (size_t i = 0; i < numFrames; ++i) for (size_t i = 0; i < numFrames; ++i)
(*frequencies)[i] = baseRatio * centsFactor(pitch[i]); (*frequencies)[i] = baseRatio * centsFactor(pitch[i]);
auto detuneSpan = resources_.bufferPool.getBuffer(numFrames); auto detuneSpan = bufferPool.getBuffer(numFrames);
if (!detuneSpan) if (!detuneSpan)
return; return;
@ -1485,7 +1516,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
if (oscillatorMode <= 0 && oscillatorMulti < 2) { if (oscillatorMode <= 0 && oscillatorMulti < 2) {
// single oscillator // single oscillator
auto tempSpan = resources_.bufferPool.getBuffer(numFrames); auto tempSpan = bufferPool.getBuffer(numFrames);
if (!tempSpan) if (!tempSpan)
return; return;
@ -1498,13 +1529,13 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
} }
else if (oscillatorMode <= 0 && oscillatorMulti >= 3) { else if (oscillatorMode <= 0 && oscillatorMulti >= 3) {
// unison oscillator // unison oscillator
auto tempSpan = resources_.bufferPool.getBuffer(numFrames); auto tempSpan = bufferPool.getBuffer(numFrames);
auto tempLeftSpan = resources_.bufferPool.getBuffer(numFrames); auto tempLeftSpan = bufferPool.getBuffer(numFrames);
auto tempRightSpan = resources_.bufferPool.getBuffer(numFrames); auto tempRightSpan = bufferPool.getBuffer(numFrames);
if (!tempSpan || !tempLeftSpan || !tempRightSpan) if (!tempSpan || !tempLeftSpan || !tempRightSpan)
return; return;
const float* detuneMod = resources_.modMatrix.getModulation(oscillatorDetuneTarget_); const float* detuneMod = modMatrix.getModulation(oscillatorDetuneTarget_);
for (unsigned u = 0, uSize = waveUnisonSize_; u < uSize; ++u) { for (unsigned u = 0, uSize = waveUnisonSize_; u < uSize; ++u) {
WavetableOscillator& osc = waveOscillators_[u]; WavetableOscillator& osc = waveOscillators_[u];
osc.setQuality(quality); osc.setQuality(quality);
@ -1531,7 +1562,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
} }
else { else {
// modulated oscillator // modulated oscillator
auto tempSpan = resources_.bufferPool.getBuffer(numFrames); auto tempSpan = bufferPool.getBuffer(numFrames);
if (!tempSpan) if (!tempSpan)
return; return;
@ -1541,11 +1572,11 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
oscMod.setQuality(quality); oscMod.setQuality(quality);
// compute the modulator // compute the modulator
auto modulatorSpan = resources_.bufferPool.getBuffer(numFrames); auto modulatorSpan = bufferPool.getBuffer(numFrames);
if (!modulatorSpan) if (!modulatorSpan)
return; return;
const float* detuneMod = resources_.modMatrix.getModulation(oscillatorDetuneTarget_); const float* detuneMod = modMatrix.getModulation(oscillatorDetuneTarget_);
if (!detuneMod) if (!detuneMod)
fill(*detuneSpan, waveDetuneRatio_[1]); fill(*detuneSpan, waveDetuneRatio_[1]);
else { else {
@ -1560,7 +1591,7 @@ void Voice::Impl::fillWithGenerator(AudioSpan<float> buffer) noexcept
const float oscillatorModDepth = region_->oscillatorModDepth; const float oscillatorModDepth = region_->oscillatorModDepth;
if (oscillatorModDepth != 1.0f) if (oscillatorModDepth != 1.0f)
applyGain1(oscillatorModDepth, *modulatorSpan); applyGain1(oscillatorModDepth, *modulatorSpan);
const float* modDepthMod = resources_.modMatrix.getModulation(oscillatorModDepthTarget_); const float* modDepthMod = modMatrix.getModulation(oscillatorModDepthTarget_);
if (modDepthMod) if (modDepthMod)
applyGain(absl::MakeConstSpan(modDepthMod, numFrames), *modulatorSpan); applyGain(absl::MakeConstSpan(modDepthMod, numFrames), *modulatorSpan);
@ -1672,12 +1703,12 @@ void Voice::Impl::updateLoopInformation() noexcept
if (!region_->shouldLoop()) if (!region_->shouldLoop())
return; return;
Resources& resources = resources_; MidiState& midiState = resources_.getMidiState();
const FileInformation& info = currentPromise_->information; const FileInformation& info = currentPromise_->information;
const double rate = info.sampleRate; const double rate = info.sampleRate;
loop_.start = static_cast<int>(region_->loopStart(resources.midiState)); loop_.start = static_cast<int>(region_->loopStart(midiState));
loop_.end = max(static_cast<int>(region_->loopEnd(resources.midiState)), loop_.start); loop_.end = max(static_cast<int>(region_->loopEnd(midiState)), loop_.start);
loop_.size = loop_.end + 1 - loop_.start; loop_.size = loop_.end + 1 - loop_.start;
loop_.xfSize = static_cast<int>(lroundPositive(region_->loopCrossfade * rate)); loop_.xfSize = static_cast<int>(lroundPositive(region_->loopCrossfade * rate));
// Clamp the crossfade to the part available before the loop starts // Clamp the crossfade to the part available before the loop starts
@ -1906,7 +1937,8 @@ void Voice::Impl::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
{ {
const size_t numFrames = pitchSpan.size(); const size_t numFrames = pitchSpan.size();
const EventVector& events = resources_.midiState.getPitchEvents(); const MidiState& midiState = resources_.getMidiState();
const EventVector& events = midiState.getPitchEvents();
const auto bendLambda = [this](float bend) { const auto bendLambda = [this](float bend) {
return region_->getBendInCents(bend); return region_->getBendInCents(bend);
}; };
@ -1917,7 +1949,7 @@ void Voice::Impl::pitchEnvelope(absl::Span<float> pitchSpan) noexcept
linearEnvelope(events, pitchSpan, bendLambda); linearEnvelope(events, pitchSpan, bendLambda);
bendSmoother_.process(pitchSpan, pitchSpan); bendSmoother_.process(pitchSpan, pitchSpan);
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
if (float* mod = mm.getModulation(pitchTarget_)) if (float* mod = mm.getModulation(pitchTarget_))
add<float>(absl::MakeSpan(mod, numFrames), pitchSpan); add<float>(absl::MakeSpan(mod, numFrames), pitchSpan);
@ -1931,7 +1963,7 @@ void Voice::Impl::resetSmoothers() noexcept
void Voice::Impl::saveModulationTargets(const Region* region) noexcept void Voice::Impl::saveModulationTargets(const Region* region) noexcept
{ {
ModMatrix& mm = resources_.modMatrix; ModMatrix& mm = resources_.getModMatrix();
masterAmplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::MasterAmplitude, region->getId())); masterAmplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::MasterAmplitude, region->getId()));
amplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Amplitude, region->getId())); amplitudeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Amplitude, region->getId()));
volumeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Volume, region->getId())); volumeTarget_ = mm.findTarget(ModKey::createNXYZ(ModId::Volume, region->getId()));

View file

@ -10,6 +10,7 @@
#include "Region.h" #include "Region.h"
#include "Resources.h" #include "Resources.h"
#include "AudioSpan.h" #include "AudioSpan.h"
#include "Logger.h"
#include "utility/NumericId.h" #include "utility/NumericId.h"
#include "utility/LeakDetector.h" #include "utility/LeakDetector.h"
#include <memory> #include <memory>

View file

@ -37,8 +37,8 @@ ControllerSource::~ControllerSource()
float ControllerSource::Impl::getLastTransformedValue(uint16_t cc, uint8_t curveIndex) const noexcept float ControllerSource::Impl::getLastTransformedValue(uint16_t cc, uint8_t curveIndex) const noexcept
{ {
ASSERT(res_); ASSERT(res_);
const Curve& curve = res_->curves.getCurve(curveIndex); const Curve& curve = res_->getCurves().getCurve(curveIndex);
const auto lastCCValue = res_->midiState.getCCValue(cc); const auto lastCCValue = res_->getMidiState().getCCValue(cc);
return curve.evalNormalized(lastCCValue); return curve.evalNormalized(lastCCValue);
} }
@ -89,8 +89,8 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
{ {
const ModKey::Parameters p = sourceKey.parameters(); const ModKey::Parameters p = sourceKey.parameters();
const Resources& res = *impl_->res_; const Resources& res = *impl_->res_;
const Curve& curve = res.curves.getCurve(p.curve); const Curve& curve = res.getCurves().getCurve(p.curve);
const MidiState& ms = res.midiState; const MidiState& ms = res.getMidiState();
bool canShortcut = false; bool canShortcut = false;
auto transformValue = [p, &curve](float x) { auto transformValue = [p, &curve](float x) {
@ -110,7 +110,7 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
const auto voice = impl_->voiceManager_->getVoiceById(voiceId); const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = const float fillValue =
voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ? voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ?
impl_->res_->midiState.getPolyAftertouch(voice->getTriggerEvent().number) : 0.0f; impl_->res_->getMidiState().getPolyAftertouch(voice->getTriggerEvent().number) : 0.0f;
sfz::fill(buffer, quantize(fillValue)); sfz::fill(buffer, quantize(fillValue));
canShortcut = true; canShortcut = true;

View file

@ -11,7 +11,7 @@
namespace sfz { namespace sfz {
struct Resources; class Resources;
class ControllerSource : public ModGenerator { class ControllerSource : public ModGenerator {
public: public:

View file

@ -421,7 +421,7 @@ TEST_CASE("[Files] Default path is ignored for generators")
TEST_CASE("[Files] Set CC applies properly") TEST_CASE("[Files] Set CC applies properly")
{ {
Synth synth; Synth synth;
const auto& midiState = synth.getResources().midiState; const MidiState& midiState = synth.getResources().getMidiState();
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
REQUIRE(midiState.getCCValue(142) == 63_norm); REQUIRE(midiState.getCCValue(142) == 63_norm);
REQUIRE(midiState.getCCValue(61) == 122_norm); REQUIRE(midiState.getCCValue(61) == 122_norm);
@ -430,7 +430,7 @@ TEST_CASE("[Files] Set CC applies properly")
TEST_CASE("[Files] Set HDCC applies properly") TEST_CASE("[Files] Set HDCC applies properly")
{ {
sfz::Synth synth; sfz::Synth synth;
const auto& midiState = synth.getResources().midiState; const MidiState& midiState = synth.getResources().getMidiState();
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_hdcc.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_hdcc.sfz");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678)); REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
REQUIRE(midiState.getCCValue(61) == Approx(0.1234)); REQUIRE(midiState.getCCValue(61) == Approx(0.1234));
@ -439,7 +439,7 @@ TEST_CASE("[Files] Set HDCC applies properly")
TEST_CASE("[Files] Set RealCC applies properly") TEST_CASE("[Files] Set RealCC applies properly")
{ {
sfz::Synth synth; sfz::Synth synth;
const auto& midiState = synth.getResources().midiState; const MidiState& midiState = synth.getResources().getMidiState();
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_realcc.sfz"); synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_realcc.sfz");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678)); REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
REQUIRE(midiState.getCCValue(61) == Approx(0.1234)); REQUIRE(midiState.getCCValue(61) == Approx(0.1234));

View file

@ -8,6 +8,7 @@
#include "sfizz/Synth.h" #include "sfizz/Synth.h"
#include "sfizz/AudioBuffer.h" #include "sfizz/AudioBuffer.h"
#include "sfizz/FlexEnvelope.h" #include "sfizz/FlexEnvelope.h"
#include "sfizz/modulations/ModMatrix.h"
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "TestHelpers.h" #include "TestHelpers.h"
#include <array> #include <array>
@ -42,7 +43,7 @@ TEST_CASE("[FlexEG] Values")
REQUIRE( egDescription.points[4].time == .4_a ); REQUIRE( egDescription.points[4].time == .4_a );
REQUIRE( egDescription.points[4].level == 1.0_a ); REQUIRE( egDescription.points[4].level == 1.0_a );
REQUIRE( egDescription.sustain == 3 ); REQUIRE( egDescription.sustain == 3 );
REQUIRE(synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({ REQUIRE(synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({
R"("EG 1 {0}" -> "Amplitude {0}")", R"("EG 1 {0}" -> "Amplitude {0}")",
})); }));
} }
@ -68,7 +69,7 @@ TEST_CASE("[FlexEG] Default values")
REQUIRE( egDescription.points[1].level == 0.0_a ); REQUIRE( egDescription.points[1].level == 0.0_a );
REQUIRE( egDescription.points[2].time == .1_a ); REQUIRE( egDescription.points[2].time == .1_a );
REQUIRE( egDescription.points[2].level == .25_a ); REQUIRE( egDescription.points[2].level == .25_a );
REQUIRE( synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({}) ); REQUIRE( synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({}) );
} }
TEST_CASE("[FlexEG] Connections") TEST_CASE("[FlexEG] Connections")
@ -86,7 +87,7 @@ TEST_CASE("[FlexEG] Connections")
REQUIRE(synth.getNumRegions() == 6); REQUIRE(synth.getNumRegions() == 6);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 ); REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
REQUIRE( synth.getRegionView(0)->flexEGs[0].points.size() == 2 ); REQUIRE( synth.getRegionView(0)->flexEGs[0].points.size() == 2 );
REQUIRE( synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({ REQUIRE( synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({
R"("EG 1 {0}" -> "Amplitude {0}")", R"("EG 1 {0}" -> "Amplitude {0}")",
R"("EG 1 {1}" -> "Pan {1}")", R"("EG 1 {1}" -> "Pan {1}")",
R"("EG 1 {2}" -> "Width {2}")", R"("EG 1 {2}" -> "Width {2}")",

View file

@ -4,6 +4,7 @@
// license. You should have receive a LICENSE.md file along with the code. // 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 // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/modulations/ModMatrix.h"
#include "sfizz/modulations/ModId.h" #include "sfizz/modulations/ModId.h"
#include "sfizz/modulations/ModKey.h" #include "sfizz/modulations/ModKey.h"
#include "sfizz/Synth.h" #include "sfizz/Synth.h"
@ -89,7 +90,7 @@ pan_oncc36=12.5 pan_stepcc36=0.5
width_oncc425=29 width_oncc425=29
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 20 {curve=3, smooth=0, step=0}" -> "Amplitude {0}")", R"("Controller 20 {curve=3, smooth=0, step=0}" -> "Amplitude {0}")",
R"("Controller 42 {curve=0, smooth=32, step=0}" -> "Pitch {0}")", R"("Controller 42 {curve=0, smooth=32, step=0}" -> "Pitch {0}")",
@ -108,7 +109,7 @@ TEST_CASE("[Modulations] Filter CC connections")
resonance3=-1 resonance3_oncc1=2 resonance3_smoothcc1=10 resonance3=-1 resonance3_oncc1=2 resonance3_smoothcc1=10
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=10, step=0}" -> "FilterResonance {0, N=3}")", R"("Controller 1 {curve=0, smooth=10, step=0}" -> "FilterResonance {0, N=3}")",
R"("Controller 2 {curve=2, smooth=0, step=0}" -> "FilterCutoff {0, N=2}")", R"("Controller 2 {curve=2, smooth=0, step=0}" -> "FilterCutoff {0, N=2}")",
@ -126,7 +127,7 @@ TEST_CASE("[Modulations] EQ CC connections")
eq3_bw_oncc1=2 eq3_bw_smoothcc1=10 eq3_bw_oncc1=2 eq3_bw_smoothcc1=10
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=10, step=0}" -> "EqBandwidth {0, N=3}")", R"("Controller 1 {curve=0, smooth=10, step=0}" -> "EqBandwidth {0, N=3}")",
R"("Controller 2 {curve=0, smooth=0, step=0.1}" -> "EqGain {0, N=1}")", R"("Controller 2 {curve=0, smooth=0, step=0.1}" -> "EqGain {0, N=1}")",
@ -147,7 +148,7 @@ TEST_CASE("[Modulations] LFO Filter connections")
lfo6_freq=3 lfo6_fil1gain=-1 lfo6_freq=3 lfo6_fil1gain=-1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("LFO 1 {0}" -> "FilterCutoff {0, N=1}")", R"("LFO 1 {0}" -> "FilterCutoff {0, N=1}")",
R"("LFO 2 {0}" -> "FilterCutoff {0, N=1}")", R"("LFO 2 {0}" -> "FilterCutoff {0, N=1}")",
@ -171,7 +172,7 @@ TEST_CASE("[Modulations] EG Filter connections")
eg6_time1=3 eg6_fil1gain=-1 eg6_time1=3 eg6_fil1gain=-1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("EG 1 {0}" -> "FilterCutoff {0, N=1}")", R"("EG 1 {0}" -> "FilterCutoff {0, N=1}")",
R"("EG 2 {0}" -> "FilterCutoff {0, N=1}")", R"("EG 2 {0}" -> "FilterCutoff {0, N=1}")",
@ -195,7 +196,7 @@ TEST_CASE("[Modulations] LFO EQ connections")
lfo6_freq=3 lfo6_eq1freq=-1 lfo6_freq=3 lfo6_eq1freq=-1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("LFO 1 {0}" -> "EqBandwidth {0, N=1}")", R"("LFO 1 {0}" -> "EqBandwidth {0, N=1}")",
R"("LFO 2 {0}" -> "EqFrequency {0, N=2}")", R"("LFO 2 {0}" -> "EqFrequency {0, N=2}")",
@ -219,7 +220,7 @@ TEST_CASE("[Modulations] EG EQ connections")
eg6_freq=3 eg6_eq1freq=-1 eg6_freq=3 eg6_eq1freq=-1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("EG 1 {0}" -> "EqBandwidth {0, N=1}")", R"("EG 1 {0}" -> "EqBandwidth {0, N=1}")",
R"("EG 2 {0}" -> "EqFrequency {0, N=2}")", R"("EG 2 {0}" -> "EqFrequency {0, N=2}")",
@ -244,7 +245,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target")
eg1_ampeg=1 eg1_ampeg=1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({ REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")",
R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")",
@ -269,7 +270,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target with 2 FlexEGs")
eg2_ampeg=1 eg2_ampeg=1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({ REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")",
R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")",
@ -296,7 +297,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target with multiple EGs targeting ampeg")
eg2_ampeg=1 eg2_ampeg=1
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({ REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")",
R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")",
@ -313,7 +314,7 @@ TEST_CASE("[Modulations] Override the default volume controller")
<region> sample=*sine tune_oncc7=1200 <region> sample=*sine tune_oncc7=1200
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({ REQUIRE(graph == createModulationDotGraph({
R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")",
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")", R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {0}")",
@ -330,7 +331,7 @@ TEST_CASE("[Modulations] Override the default pan controller")
<region> sample=*sine on_locc10=127 on_hicc10=127 <region> sample=*sine on_locc10=127 on_hicc10=127
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({ REQUIRE(graph == createModulationDotGraph({
R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")", R"("AmplitudeEG {0}" -> "MasterAmplitude {0}")",
R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")", R"("Controller 11 {curve=4, smooth=10, step=0}" -> "Amplitude {0}")",
@ -346,7 +347,7 @@ TEST_CASE("[Modulations] Aftertouch connections")
<region> sample=*sine cutoff2_chanaft=1000 <region> sample=*sine cutoff2_chanaft=1000
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("ChannelAftertouch" -> "FilterCutoff {0, N=1}")", R"("ChannelAftertouch" -> "FilterCutoff {0, N=1}")",
R"("ChannelAftertouch" -> "FilterCutoff {1, N=2}")", R"("ChannelAftertouch" -> "FilterCutoff {1, N=2}")",
@ -362,7 +363,7 @@ TEST_CASE("[Modulations] LFO v1 connections")
<region> sample=*sine fillfo_freq=1.0 <region> sample=*sine fillfo_freq=1.0
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("AmplitudeLFO {0}" -> "Volume {0}")", R"("AmplitudeLFO {0}" -> "Volume {0}")",
R"("PitchLFO {1}" -> "Pitch {1}")", R"("PitchLFO {1}" -> "Pitch {1}")",
@ -379,7 +380,7 @@ TEST_CASE("[Modulations] LFO v1 CC connections")
<region> sample=*sine fillfo_depth_oncc3=-3600 <region> sample=*sine fillfo_depth_oncc3=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFODepth {0}")", R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFODepth {0}")",
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFODepth {1}")", R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFODepth {1}")",
@ -399,7 +400,7 @@ TEST_CASE("[Modulations] LFO v1 CC frequency connections")
<region> sample=*sine fillfo_freqcc3=-3600 <region> sample=*sine fillfo_freqcc3=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFOFrequency {0}")", R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFOFrequency {0}")",
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFOFrequency {1}")", R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchLFOFrequency {1}")",
@ -419,7 +420,7 @@ TEST_CASE("[Modulations] LFO v1 aftertouch connections")
<region> sample=*sine fillfo_depthchanaft=-3600 <region> sample=*sine fillfo_depthchanaft=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("ChannelAftertouch" -> "AmplitudeLFODepth {0}")", R"("ChannelAftertouch" -> "AmplitudeLFODepth {0}")",
R"("ChannelAftertouch" -> "PitchLFODepth {1}")", R"("ChannelAftertouch" -> "PitchLFODepth {1}")",
@ -439,7 +440,7 @@ TEST_CASE("[Modulations] LFO v1 aftertouch frequency connections")
<region> sample=*sine fillfo_freqchanaft=-3600 <region> sample=*sine fillfo_freqchanaft=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("ChannelAftertouch" -> "AmplitudeLFOFrequency {0}")", R"("ChannelAftertouch" -> "AmplitudeLFOFrequency {0}")",
R"("ChannelAftertouch" -> "PitchLFOFrequency {1}")", R"("ChannelAftertouch" -> "PitchLFOFrequency {1}")",
@ -459,7 +460,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch connections")
<region> sample=*sine fillfo_depthpolyaft=-3600 <region> sample=*sine fillfo_depthpolyaft=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("PolyAftertouch" -> "AmplitudeLFODepth {0}")", R"("PolyAftertouch" -> "AmplitudeLFODepth {0}")",
R"("PolyAftertouch" -> "PitchLFODepth {1}")", R"("PolyAftertouch" -> "PitchLFODepth {1}")",
@ -479,7 +480,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch frequency connections")
<region> sample=*sine fillfo_freqpolyaft=-3600 <region> sample=*sine fillfo_freqpolyaft=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("PolyAftertouch" -> "AmplitudeLFOFrequency {0}")", R"("PolyAftertouch" -> "AmplitudeLFOFrequency {0}")",
R"("PolyAftertouch" -> "PitchLFOFrequency {1}")", R"("PolyAftertouch" -> "PitchLFOFrequency {1}")",
@ -498,7 +499,7 @@ TEST_CASE("[Modulations] EG v1 CC connections")
<region> sample=*sine fileg_depth_oncc3=-3600 <region> sample=*sine fileg_depth_oncc3=-3600
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchEGDepth {0}")", R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchEGDepth {0}")",
R"("Controller 3 {curve=0, smooth=0, step=0}" -> "FilterEGDepth {1}")", R"("Controller 3 {curve=0, smooth=0, step=0}" -> "FilterEGDepth {1}")",
@ -523,7 +524,7 @@ TEST_CASE("[Modulations] LFO CC connections")
pitch_oncc137=1200 pitch_oncc137=1200
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("Controller 128 {curve=0, smooth=0, step=0}" -> "Pitch {0}")", R"("Controller 128 {curve=0, smooth=0, step=0}" -> "Pitch {0}")",
R"("Controller 129 {curve=0, smooth=0, step=0}" -> "Pitch {0}")", R"("Controller 129 {curve=0, smooth=0, step=0}" -> "Pitch {0}")",
@ -547,7 +548,7 @@ TEST_CASE("[Modulations] Extended CCs connections")
lfo3_freq=0.1 lfo3_phase_cc1=2 lfo3_phase_smoothcc1=10 lfo3_phase_stepcc1=0.2 lfo3_phase_curvecc1=1 lfo3_amplitude=50 lfo3_freq=0.1 lfo3_phase_cc1=2 lfo3_phase_smoothcc1=10 lfo3_phase_stepcc1=0.2 lfo3_phase_curvecc1=1 lfo3_amplitude=50
)"); )");
const std::string graph = synth.getResources().modMatrix.toDotGraph(); const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({ REQUIRE(graph == createDefaultGraph({
R"("LFO 1 {0}" -> "Volume {0}")", R"("LFO 1 {0}" -> "Volume {0}")",
R"("LFO 2 {0}" -> "Pitch {0}")", R"("LFO 2 {0}" -> "Pitch {0}")",

View file

@ -141,7 +141,7 @@ TEST_CASE("[Synth] Reset all controllers")
{ {
sfz::Synth synth; sfz::Synth synth;
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) }; sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
const auto& midiState = synth.getResources().midiState; const sfz::MidiState& midiState = synth.getResources().getMidiState();
synth.cc(0, 12, 64); synth.cc(0, 12, 64);
synth.renderBlock(buffer); synth.renderBlock(buffer);
REQUIRE(midiState.getCCValue(12) == 64_norm); REQUIRE(midiState.getCCValue(12) == 64_norm);
@ -401,7 +401,7 @@ TEST_CASE("[Synth] Gain to mix")
TEST_CASE("[Synth] Basic curves") TEST_CASE("[Synth] Basic curves")
{ {
sfz::Synth synth; sfz::Synth synth;
const auto& curves = synth.getResources().curves; const sfz::CurveSet& curves = synth.getResources().getCurves();
synth.loadSfzString(fs::current_path() / "tests/TestFiles/curves.sfz", R"( synth.loadSfzString(fs::current_path() / "tests/TestFiles/curves.sfz", R"(
<region> sample=*sine <region> sample=*sine
<curve>curve_index=18 v000=0 v095=0.5 v127=1 <curve>curve_index=18 v000=0 v095=0.5 v127=1