Allow modulations to trigger at a frame-accurate instant (LFO)

This commit is contained in:
Jean Pierre Cimalando 2020-08-21 14:39:55 +02:00
parent 68712140d6
commit a71c1eb0f3
12 changed files with 19 additions and 16 deletions

View file

@ -49,7 +49,7 @@ void LFO::configure(const LFODescription* desc)
impl_->desc_ = desc ? desc : &LFODescription::getDefault();
}
void LFO::start()
void LFO::start(unsigned triggerDelay)
{
Impl& impl = *impl_;
const LFODescription& desc = *impl.desc_;
@ -59,7 +59,8 @@ void LFO::start()
impl.sampleHoldMem_.fill(0.0f);
const float delay = desc.delay;
impl.delayFramesLeft_ = (delay > 0) ? static_cast<size_t>(std::ceil(sampleRate * delay)) : 0u;
size_t delayFrames = (delay > 0) ? static_cast<size_t>(std::ceil(sampleRate * delay)) : 0u;
impl.delayFramesLeft_ = triggerDelay + delayFrames;
impl.fadePosition_ = (desc.fade > 0) ? 0.0f : 1.0f;
}

View file

@ -67,7 +67,7 @@ public:
Start processing a LFO as a region is triggered.
Prepares the delay, phases, fade-in, etc..
*/
void start();
void start(unsigned triggerDelay);
/**
Process a cycle of the oscillator.

View file

@ -150,7 +150,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
bendSmoother.reset(centsFactor(region->getBendInCents(resources.midiState.getPitchBend())));
egEnvelope.reset(region->amplitudeEG, *region, resources.midiState, delay, value, sampleRate);
resources.modMatrix.initVoice(id, region->getId());
resources.modMatrix.initVoice(id, region->getId(), delay);
}
int sfz::Voice::getCurrentSampleQuality() const noexcept

View file

@ -36,8 +36,9 @@ public:
*
* @param sourceKey identifier of the source to initialize
* @param voiceId the particular voice to initialize, if per-voice
* @param delay the frame time when it happens
*/
virtual void init(const ModKey& sourceKey, NumericId<Voice> voiceId) = 0;
virtual void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) = 0;
/**
* @brief Generate a cycle of the modulator

View file

@ -196,18 +196,18 @@ void ModMatrix::init()
for (Impl::Source &source : impl.sources_) {
const int flags = source.key.flags();
if (flags & kModIsPerCycle)
source.gen->init(source.key, {});
source.gen->init(source.key, {}, 0);
}
}
void ModMatrix::initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId)
void ModMatrix::initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId, unsigned delay)
{
Impl& impl = *impl_;
for (Impl::Source &source : impl.sources_) {
const int flags = source.key.flags();
if ((flags & kModIsPerVoice) && source.key.region() == regionId)
source.gen->init(source.key, voiceId);
source.gen->init(source.key, voiceId, delay);
}
}

View file

@ -106,7 +106,7 @@ public:
* @brief Reinitialize modulation source for a given voice.
* This must be called first after a voice enters active state.
*/
void initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId);
void initVoice(NumericId<Voice> voiceId, NumericId<Region> regionId, unsigned delay);
/**
* @brief Start modulation processing for the entire cycle.

View file

@ -49,9 +49,10 @@ void ControllerSource::setSamplesPerBlock(unsigned count)
(void)count;
}
void ControllerSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId)
void ControllerSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
{
(void)voiceId;
(void)delay;
const ModKey::Parameters p = sourceKey.parameters();
if (p.smooth > 0) {

View file

@ -18,7 +18,7 @@ public:
~ControllerSource();
void setSampleRate(double sampleRate) override;
void setSamplesPerBlock(unsigned count) override;
void init(const ModKey& sourceKey, NumericId<Voice> voiceId) override;
void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override;
void generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer) override;
private:

View file

@ -19,7 +19,7 @@ LFOSource::LFOSource(Synth &synth)
{
}
void LFOSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId)
void LFOSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
{
Synth& synth = *synth_;
unsigned lfoIndex = sourceKey.parameters().N;
@ -38,7 +38,7 @@ void LFOSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId)
LFO* lfo = voice->getLFO(lfoIndex);
lfo->configure(&region->lfos[lfoIndex]);
lfo->start();
lfo->start(delay);
}
void LFOSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer)

View file

@ -13,7 +13,7 @@ class Synth;
class LFOSource : public ModGenerator {
public:
explicit LFOSource(Synth &synth);
void init(const ModKey& sourceKey, NumericId<Voice> voiceId) override;
void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override;
void generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer) override;
private:

View file

@ -31,7 +31,7 @@ static bool computeLFO(DataPoints& dp, const fs::path& sfzPath, double sampleRat
std::vector<float> outputMemory(numLfos * numFrames);
for (size_t l = 0; l < numLfos; ++l) {
lfos[l].start();
lfos[l].start(0);
}
std::vector<absl::Span<float>> lfoOutputs(numLfos);

View file

@ -119,7 +119,7 @@ int main(int argc, char* argv[])
std::vector<float> outputMemory(numLfos * numFrames);
for (size_t l = 0; l < numLfos; ++l) {
lfos[l].start();
lfos[l].start(0);
}
std::vector<absl::Span<float>> lfoOutputs(numLfos);