Implement fileg and pitcheg in the matrix

This commit is contained in:
Jean Pierre Cimalando 2020-09-23 23:32:47 +02:00
parent 2f12206c4c
commit b99b40b131
10 changed files with 225 additions and 0 deletions

1
dpf.mk
View file

@ -65,6 +65,7 @@ SFIZZ_SOURCES = \
src/sfizz/modulations/ModKey.cpp \
src/sfizz/modulations/ModKeyHash.cpp \
src/sfizz/modulations/ModMatrix.cpp \
src/sfizz/modulations/sources/ADSREnvelope.cpp \
src/sfizz/modulations/sources/Controller.cpp \
src/sfizz/modulations/sources/FlexEGDescription.cpp \
src/sfizz/modulations/sources/FlexEnvelope.cpp \

View file

@ -35,6 +35,7 @@ set (SFIZZ_HEADERS
sfizz/modulations/ModKeyHash.h
sfizz/modulations/ModMatrix.h
sfizz/modulations/ModGenerator.h
sfizz/modulations/sources/ADSREnvelope.h
sfizz/modulations/sources/Controller.h
sfizz/modulations/sources/FlexEnvelope.h
sfizz/modulations/sources/LFO.h
@ -151,6 +152,7 @@ set (SFIZZ_SOURCES
sfizz/modulations/ModMatrix.cpp
sfizz/modulations/sources/Controller.cpp
sfizz/modulations/sources/FlexEnvelope.cpp
sfizz/modulations/sources/ADSREnvelope.cpp
sfizz/modulations/sources/LFO.cpp
sfizz/utility/SpinMutex.cpp
sfizz/effects/Nothing.cpp

View file

@ -250,6 +250,8 @@ namespace Default
constexpr Range<int> egDepthRange { -12000, 12000 };
constexpr Range<float> egOnCCTimeRange { -100.0, 100.0 };
constexpr Range<float> egOnCCPercentRange { -100.0, 100.0 };
constexpr Range<float> pitchEgDepthRange { -12000.0, 12000.0 };
constexpr Range<float> filterEgDepthRange { -12000.0, 12000.0 };
// Flex envelope generators
constexpr int numFlexEGs { 4 };

View file

@ -1238,6 +1238,22 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
ModKey::createNXYZ(ModId::FilCutoff, id));
break;
case hash("pitcheg_depth"):
if (auto value = readOpcode(opcode.value, Default::pitchEgDepthRange))
getOrCreateConnection(
ModKey::createNXYZ(ModId::PitchEG, id),
ModKey::createNXYZ(ModId::Pitch, id)).sourceDepth = *value;
break;
case hash("fileg_depth"):
if (auto value = readOpcode(opcode.value, Default::filterEgDepthRange))
getOrCreateConnection(
ModKey::createNXYZ(ModId::FilEG, id),
ModKey::createNXYZ(ModId::FilCutoff, id)).sourceDepth = *value;
break;
// TODO(jpc): pitcheg_vel2depth
// TODO(jpc): fileg_vel2depth
// Flex envelopes
case hash("eg&_dynamic"):
{

View file

@ -19,6 +19,7 @@
#include "modulations/sources/Controller.h"
#include "modulations/sources/LFO.h"
#include "modulations/sources/FlexEnvelope.h"
#include "modulations/sources/ADSREnvelope.h"
#include "utility/XmlHelpers.h"
#include "pugixml.hpp"
#include "absl/algorithm/container.h"
@ -49,6 +50,7 @@ sfz::Synth::Synth(int numVoices)
genController.reset(new ControllerSource(resources));
genLFO.reset(new LFOSource(*this));
genFlexEnvelope.reset(new FlexEnvelopeSource(*this));
genADSREnvelope.reset(new ADSREnvelopeSource(*this));
}
sfz::Synth::~Synth()
@ -489,6 +491,8 @@ void sfz::Synth::finalizeSfzLoad()
size_t maxEQs { 0 };
size_t maxLFOs { 0 };
size_t maxFlexEGs { 0 };
bool havePitchEG { false };
bool haveFilterEG { false };
FlexEGs::clearUnusedCurves();
@ -613,6 +617,8 @@ void sfz::Synth::finalizeSfzLoad()
maxEQs = max(maxEQs, region->equalizers.size());
maxLFOs = max(maxLFOs, region->lfos.size());
maxFlexEGs = max(maxFlexEGs, region->flexEGs.size());
havePitchEG = havePitchEG || region->pitchEG != absl::nullopt;
haveFilterEG = haveFilterEG || region->filterEG != absl::nullopt;
++currentRegionIndex;
}
@ -624,6 +630,8 @@ void sfz::Synth::finalizeSfzLoad()
settingsPerVoice.maxEQs = maxEQs;
settingsPerVoice.maxLFOs = maxLFOs;
settingsPerVoice.maxFlexEGs = maxFlexEGs;
settingsPerVoice.havePitchEG = havePitchEG;
settingsPerVoice.haveFilterEG = haveFilterEG;
applySettingsPerVoice();
@ -1499,6 +1507,8 @@ void sfz::Synth::applySettingsPerVoice()
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
voice->setMaxLFOsPerVoice(settingsPerVoice.maxLFOs);
voice->setMaxFlexEGsPerVoice(settingsPerVoice.maxFlexEGs);
voice->setPitchEGEnabledPerVoice(settingsPerVoice.havePitchEG);
voice->setFilterEGEnabledPerVoice(settingsPerVoice.haveFilterEG);
}
}
@ -1520,6 +1530,10 @@ void sfz::Synth::setupModMatrix()
case ModId::Envelope:
gen = genFlexEnvelope.get();
break;
case ModId::PitchEG:
case ModId::FilEG:
gen = genADSREnvelope.get();
break;
default:
DBG("[sfizz] Have unknown type of source generator");
break;

View file

@ -29,6 +29,7 @@ namespace sfz {
class ControllerSource;
class LFOSource;
class FlexEnvelopeSource;
class ADSREnvelopeSource;
/**
* @brief This class is the core of the sfizz library. In C++ it is the main point
@ -550,6 +551,7 @@ public:
*/
void disableFreeWheeling() noexcept;
Resources& getResources() noexcept { return resources; }
const Resources& getResources() const noexcept { return resources; }
/**
@ -922,6 +924,7 @@ private:
std::unique_ptr<ControllerSource> genController;
std::unique_ptr<LFOSource> genLFO;
std::unique_ptr<FlexEnvelopeSource> genFlexEnvelope;
std::unique_ptr<ADSREnvelopeSource> genADSREnvelope;
// Settings per voice
struct SettingsPerVoice {
@ -929,6 +932,8 @@ private:
size_t maxEQs { 0 };
size_t maxLFOs { 0 };
size_t maxFlexEGs { 0 };
bool havePitchEG { false };
bool haveFilterEG { false };
};
SettingsPerVoice settingsPerVoice;

View file

@ -903,6 +903,22 @@ void sfz::Voice::setMaxFlexEGsPerVoice(size_t numFlexEGs)
}
}
void sfz::Voice::setPitchEGEnabledPerVoice(bool havePitchEG)
{
if (havePitchEG)
egPitch.reset(new ADSREnvelope<float>);
else
egPitch.reset();
}
void sfz::Voice::setFilterEGEnabledPerVoice(bool haveFilterEG)
{
if (haveFilterEG)
egFilter.reset(new ADSREnvelope<float>);
else
egFilter.reset();
}
void sfz::Voice::setupOscillatorUnison()
{
const int m = region->oscillatorMulti;

View file

@ -296,6 +296,18 @@ public:
* @param numFlexEGs
*/
void setMaxFlexEGsPerVoice(size_t numFlexEGs);
/**
* @brief Set whether SFZv1 pitch EG is enabled on this voice
*
* @param havePitchEG
*/
void setPitchEGEnabledPerVoice(bool havePitchEG);
/**
* @brief Set whether SFZv1 filter EG is enabled on this voice
*
* @param haveFilterEG
*/
void setFilterEGEnabledPerVoice(bool haveFilterEG);
/**
* @brief Release the voice after a given delay
*
@ -324,6 +336,20 @@ public:
Duration getLastFilterDuration() const noexcept { return filterDuration; }
Duration getLastPanningDuration() const noexcept { return panningDuration; }
/**
* @brief Get the SFZv1 pitch EG, if existing
*/
ADSREnvelope<float>* getPitchEG() { return egPitch.get(); }
/**
* @brief Get the SFZv1 filter EG, if existing
*/
ADSREnvelope<float>* getFilterEG() { return egFilter.get(); }
/**
* @brief Get the trigger event
*/
const TriggerEvent& getTriggerEvent() { return triggerEvent; }
private:
/**
* @brief Fill a span with data from a file source. This is the first step
@ -462,6 +488,8 @@ private:
std::vector<std::unique_ptr<FlexEnvelope>> flexEGs;
ADSREnvelope<float> egAmplitude;
std::unique_ptr<ADSREnvelope<float>> egPitch;
std::unique_ptr<ADSREnvelope<float>> egFilter;
float bendStepFactor { centsFactor(1) };
WavetableOscillator waveOscillators[config::oscillatorsPerVoice];

View file

@ -0,0 +1,117 @@
// 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 "ADSREnvelope.h"
#include "../../ADSREnvelope.h"
#include "../../Synth.h"
#include "../../Voice.h"
#include "../../Config.h"
#include "../../Debug.h"
// TODO(jpc): also matrix the ampeg
namespace sfz {
ADSREnvelopeSource::ADSREnvelopeSource(Synth &synth)
: synth_(&synth)
{
}
void ADSREnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
{
Synth& synth = *synth_;
Voice* voice = synth.getVoiceById(voiceId);
if (!voice) {
ASSERTFALSE;
return;
}
const Region* region = voice->getRegion();
ADSREnvelope<float>* eg = nullptr;
const EGDescription* desc = nullptr;
switch (sourceKey.id()) {
case ModId::PitchEG:
eg = voice->getPitchEG();
ASSERT(eg);
desc = &*region->pitchEG;
break;
case ModId::FilEG:
eg = voice->getFilterEG();
ASSERT(eg);
desc = &*region->filterEG;
break;
default:
ASSERTFALSE;
return;
}
Resources& resources = synth.getResources();
const TriggerEvent& triggerEvent = voice->getTriggerEvent();
const float sampleRate = voice->getSampleRate();
eg->reset(*desc, *region, resources.midiState, delay, triggerEvent.value, sampleRate);
}
void ADSREnvelopeSource::release(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
{
Synth& synth = *synth_;
Voice* voice = synth.getVoiceById(voiceId);
if (!voice) {
ASSERTFALSE;
return;
}
ADSREnvelope<float>* eg = nullptr;
switch (sourceKey.id()) {
case ModId::PitchEG:
eg = voice->getPitchEG();
ASSERT(eg);
break;
case ModId::FilEG:
eg = voice->getFilterEG();
ASSERT(eg);
break;
default:
ASSERTFALSE;
return;
}
eg->startRelease(delay);
}
void ADSREnvelopeSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer)
{
Synth& synth = *synth_;
Voice* voice = synth.getVoiceById(voiceId);
if (!voice) {
ASSERTFALSE;
return;
}
ADSREnvelope<float>* eg = nullptr;
switch (sourceKey.id()) {
case ModId::PitchEG:
eg = voice->getPitchEG();
ASSERT(eg);
break;
case ModId::FilEG:
eg = voice->getFilterEG();
ASSERT(eg);
break;
default:
ASSERTFALSE;
return;
}
eg->getBlock(buffer);
}
} // namespace sfz

View file

@ -0,0 +1,24 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "../ModGenerator.h"
namespace sfz {
class Synth;
class ADSREnvelopeSource : public ModGenerator {
public:
explicit ADSREnvelopeSource(Synth &synth);
void init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override;
void release(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay) override;
void generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer) override;
private:
Synth* synth_ = nullptr;
};
} // namespace sfz