Implement fileg and pitcheg in the matrix
This commit is contained in:
parent
2f12206c4c
commit
b99b40b131
10 changed files with 225 additions and 0 deletions
1
dpf.mk
1
dpf.mk
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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"):
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
117
src/sfizz/modulations/sources/ADSREnvelope.cpp
Normal file
117
src/sfizz/modulations/sources/ADSREnvelope.cpp
Normal 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
|
||||
24
src/sfizz/modulations/sources/ADSREnvelope.h
Normal file
24
src/sfizz/modulations/sources/ADSREnvelope.h
Normal 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
|
||||
Loading…
Add table
Reference in a new issue