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/Region.cpp \
src/sfizz/RegionSet.cpp \
src/sfizz/Resources.cpp \
src/sfizz/RTSemaphore.cpp \
src/sfizz/ScopedFTZ.cpp \
src/sfizz/sfizz.cpp \

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@
#include <memory>
namespace sfz {
struct Resources;
class Resources;
struct Region;
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
#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 <memory>
namespace sfz
namespace sfz {
struct SynthConfig;
class BufferPool;
class MidiState;
class Logger;
class CurveSet;
class FilePool;
struct WavetablePool;
class Tuning;
class StretchTuning;
class ModMatrix;
class BeatClock;
class Metronome;
class Resources
{
class WavetableMulti;
public:
Resources();
~Resources();
struct Resources
{
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;
void setSampleRate(float samplerate);
void setSamplesPerBlock(int samplesPerBlock);
void clear();
void setSampleRate(float samplerate)
{
midiState.setSampleRate(samplerate);
modMatrix.setSampleRate(samplerate);
beatClock.setSampleRate(samplerate);
metronome.init(samplerate);
}
#define ACCESSOR_RW(Accessor, RetTy) \
RetTy const& Accessor() const noexcept; \
RetTy& Accessor() noexcept { return const_cast<RetTy&>(const_cast<const Resources*>(this)->Accessor()); }
void setSamplesPerBlock(int samplesPerBlock)
{
bufferPool.setBufferSize(samplesPerBlock);
midiState.setSamplesPerBlock(samplesPerBlock);
modMatrix.setSamplesPerBlock(samplesPerBlock);
beatClock.setSamplesPerBlock(samplesPerBlock);
}
ACCESSOR_RW(getSynthConfig, SynthConfig);
ACCESSOR_RW(getBufferPool, BufferPool);
ACCESSOR_RW(getMidiState, MidiState);
ACCESSOR_RW(getLogger, Logger);
ACCESSOR_RW(getCurves, CurveSet);
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);
void clear()
{
curves = CurveSet::createPredefined();
filePool.clear();
wavePool.clearFileWaves();
logger.clear();
midiState.reset();
modMatrix.clear();
beatClock.clear();
metronome.clear();
}
#undef ACCESSOR_RW
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
}
} // namespace sfz

View file

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

View file

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

View file

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

View file

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

View file

@ -37,8 +37,8 @@ ControllerSource::~ControllerSource()
float ControllerSource::Impl::getLastTransformedValue(uint16_t cc, uint8_t curveIndex) const noexcept
{
ASSERT(res_);
const Curve& curve = res_->curves.getCurve(curveIndex);
const auto lastCCValue = res_->midiState.getCCValue(cc);
const Curve& curve = res_->getCurves().getCurve(curveIndex);
const auto lastCCValue = res_->getMidiState().getCCValue(cc);
return curve.evalNormalized(lastCCValue);
}
@ -89,8 +89,8 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
{
const ModKey::Parameters p = sourceKey.parameters();
const Resources& res = *impl_->res_;
const Curve& curve = res.curves.getCurve(p.curve);
const MidiState& ms = res.midiState;
const Curve& curve = res.getCurves().getCurve(p.curve);
const MidiState& ms = res.getMidiState();
bool canShortcut = false;
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 float fillValue =
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));
canShortcut = true;

View file

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

View file

@ -421,7 +421,7 @@ TEST_CASE("[Files] Default path is ignored for generators")
TEST_CASE("[Files] Set CC applies properly")
{
Synth synth;
const auto& midiState = synth.getResources().midiState;
const MidiState& midiState = synth.getResources().getMidiState();
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
REQUIRE(midiState.getCCValue(142) == 63_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")
{
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");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
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")
{
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");
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
REQUIRE(midiState.getCCValue(61) == Approx(0.1234));

View file

@ -8,6 +8,7 @@
#include "sfizz/Synth.h"
#include "sfizz/AudioBuffer.h"
#include "sfizz/FlexEnvelope.h"
#include "sfizz/modulations/ModMatrix.h"
#include "catch2/catch.hpp"
#include "TestHelpers.h"
#include <array>
@ -42,7 +43,7 @@ TEST_CASE("[FlexEG] Values")
REQUIRE( egDescription.points[4].time == .4_a );
REQUIRE( egDescription.points[4].level == 1.0_a );
REQUIRE( egDescription.sustain == 3 );
REQUIRE(synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({
REQUIRE(synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({
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[2].time == .1_a );
REQUIRE( egDescription.points[2].level == .25_a );
REQUIRE( synth.getResources().modMatrix.toDotGraph() == createDefaultGraph({}) );
REQUIRE( synth.getResources().getModMatrix().toDotGraph() == createDefaultGraph({}) );
}
TEST_CASE("[FlexEG] Connections")
@ -86,7 +87,7 @@ TEST_CASE("[FlexEG] Connections")
REQUIRE(synth.getNumRegions() == 6);
REQUIRE( synth.getRegionView(0)->flexEGs.size() == 1 );
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 {1}" -> "Pan {1}")",
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.
// 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/ModKey.h"
#include "sfizz/Synth.h"
@ -89,7 +90,7 @@ pan_oncc36=12.5 pan_stepcc36=0.5
width_oncc425=29
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("Controller 20 {curve=3, smooth=0, step=0}" -> "Amplitude {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
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}")",
@ -126,7 +127,7 @@ TEST_CASE("[Modulations] EQ CC connections")
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({
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}")",
@ -147,7 +148,7 @@ TEST_CASE("[Modulations] LFO Filter connections")
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({
R"("LFO 1 {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("EG 1 {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("LFO 1 {0}" -> "EqBandwidth {0, N=1}")",
R"("LFO 2 {0}" -> "EqFrequency {0, N=2}")",
@ -219,7 +220,7 @@ TEST_CASE("[Modulations] EG EQ connections")
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({
R"("EG 1 {0}" -> "EqBandwidth {0, N=1}")",
R"("EG 2 {0}" -> "EqFrequency {0, N=2}")",
@ -244,7 +245,7 @@ TEST_CASE("[Modulations] FlexEG Ampeg target")
eg1_ampeg=1
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({
R"("Controller 10 {curve=1, smooth=10, step=0}" -> "Pan {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({
R"("AmplitudeEG {0}" -> "MasterAmplitude {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createModulationDotGraph({
R"("AmplitudeEG {0}" -> "MasterAmplitude {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("ChannelAftertouch" -> "FilterCutoff {0, N=1}")",
R"("ChannelAftertouch" -> "FilterCutoff {1, N=2}")",
@ -362,7 +363,7 @@ TEST_CASE("[Modulations] LFO v1 connections")
<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({
R"("AmplitudeLFO {0}" -> "Volume {0}")",
R"("PitchLFO {1}" -> "Pitch {1}")",
@ -379,7 +380,7 @@ TEST_CASE("[Modulations] LFO v1 CC connections")
<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({
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFODepth {0}")",
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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("Controller 1 {curve=0, smooth=0, step=0}" -> "AmplitudeLFOFrequency {0}")",
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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("ChannelAftertouch" -> "AmplitudeLFODepth {0}")",
R"("ChannelAftertouch" -> "PitchLFODepth {1}")",
@ -439,7 +440,7 @@ TEST_CASE("[Modulations] LFO v1 aftertouch frequency connections")
<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({
R"("ChannelAftertouch" -> "AmplitudeLFOFrequency {0}")",
R"("ChannelAftertouch" -> "PitchLFOFrequency {1}")",
@ -459,7 +460,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch connections")
<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({
R"("PolyAftertouch" -> "AmplitudeLFODepth {0}")",
R"("PolyAftertouch" -> "PitchLFODepth {1}")",
@ -479,7 +480,7 @@ TEST_CASE("[Modulations] LFO v1 poly aftertouch frequency connections")
<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({
R"("PolyAftertouch" -> "AmplitudeLFOFrequency {0}")",
R"("PolyAftertouch" -> "PitchLFOFrequency {1}")",
@ -498,7 +499,7 @@ TEST_CASE("[Modulations] EG v1 CC connections")
<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({
R"("Controller 2 {curve=0, smooth=0, step=0}" -> "PitchEGDepth {0}")",
R"("Controller 3 {curve=0, smooth=0, step=0}" -> "FilterEGDepth {1}")",
@ -523,7 +524,7 @@ TEST_CASE("[Modulations] LFO CC connections")
pitch_oncc137=1200
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("Controller 128 {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
)");
const std::string graph = synth.getResources().modMatrix.toDotGraph();
const std::string graph = synth.getResources().getModMatrix().toDotGraph();
REQUIRE(graph == createDefaultGraph({
R"("LFO 1 {0}" -> "Volume {0}")",
R"("LFO 2 {0}" -> "Pitch {0}")",

View file

@ -141,7 +141,7 @@ TEST_CASE("[Synth] Reset all controllers")
{
sfz::Synth synth;
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.renderBlock(buffer);
REQUIRE(midiState.getCCValue(12) == 64_norm);
@ -401,7 +401,7 @@ TEST_CASE("[Synth] Gain to mix")
TEST_CASE("[Synth] Basic curves")
{
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"(
<region> sample=*sine
<curve>curve_index=18 v000=0 v095=0.5 v127=1